Om du använder OpenID Connect (OIDC) med ett React-frontend och har kämpat med tyst tokenförnyelse, utgångna tokens som utlöser oändliga API-anrop, eller Content Security Policy (CSP) som blockerar iframes, är den här artikeln för dig.
Efter att ha brottats med alla dessa problem lyckades jag äntligen bygga en stabil och säker lösning, och här är en genomgång av resan och hur du kan tillämpa den i din egen app.
Problemen jag stötte på när jag använde OpenID Connect (OIDC) med ett React-frontend
1. Tyst tokenförnyelse fungerade inte
OIDC erbjuder ett sätt att tyst förnya åtkomsttokens via en iframe, men denna metod misslyckas med applikationer som har en strikt CSP, särskilt med frame-ancestors ‘none’.
Som en konsekvens går tokens ut obemärkt och användare loggas ut utan någon avisering.
2. Oändliga API-slingor efter att token gått ut
Utgångna tokens upptäcktes inte tillräckligt tidigt. När frontend gjorde API-anrop med en utgången token svarade servern med 401:or och vår interceptor-logik försökte göra om samma förfrågan om och om igen. Detta ledde till en oändlig slinga av misslyckade förfrågningar, försämrad prestanda och förvirrade användare.
3. Förlorad omdirigeringsväg efter inloggning
Efter att ha omdirigerats till inloggningssidan på grund av en utgången token, återfördes användarna inte till den ursprungliga sidan de befann sig på.
Den slutgiltiga lösningen för problem som uppstår vid användning av OIDC
För att åtgärda dessa problem gjorde jag följande strukturella ändringar:
- Problem: Implementerad tyst förnyelse fungerar inte
Lösning: Lade till en dedikeradsilentrenew.html-sida och åtgärdade CSP-problemet - Problem: API-slinga vid tokenutgång
Lösning: Flyttade tokenvalidering utanför interceptorer - Problem: CSP-iframe blockerad
Lösning: Uppdaterade serverns CSP-policy för att tillåta'self'för iframes - Problem: Förlorad omdirigeringsväg
Lösning: Användestateparameter ellerredirectToquery i inloggningsflödet
Viktiga lösningar på OIDC-problem förklaras
1. silentrenew.html:
Tysta tokenförnyelser sker i en dold iframe. För detta behöver du en statisk HTML-fil som exekverar den tysta callbacken:
<!-- silentrenew.html -->
<script src="https://unpkg.com/oidc-client-ts/dist/umd/oidc-client-ts.min.js"></script>
<script>
new Oidc.UserManager().signinSilentCallback();
</script>Se till att lägga till URL:en för denna sida till din identitetsleverantörs omdirigerings-URI:er och uppdatera din CSP:
Omdirigerings-URI:er:
https://your-app-url/login-redirect
https://your-app-url/silentrenew.htmlOmdirigerings-URI efter utloggning:
https://your-app-url/loginAktivera offlineåtkomst:
AllowOfflineAccess: trueKonfigurera Content Security Policy (CSP) i identitetsserverprojektet:
Content-Security-Policy: frame-ancestors 'self'
2. Validera tokens innan API-anrop görs
Ett vanligt misstag är att utföra tokenförnyelse inuti dina axios förfrågnings- eller svarsinterceptorer.
Även om det verkar bekvämt, orsakar detta stora problem:
- Tokenförnyelse kan utlösas parallellt av flera förfrågningar.
- Utgångna tokens kan fortfarande skickas innan förnyelsen är klar.
- 401-svar kan utlösa oändliga omförsöksslingor.
Lösning:
Flytta logiken för tokenvalidering utanför interceptorn, till en centraliserad plats som körs innan något API-anrop görs.
function getValidAccessToken() {
const user = userManager.configuration.getUser() //Calls to get the new token
if (!user || user.expired) {
return null
}
return user.access_token
}
const token = await getValidAccessToken()
if (!token) {
redirectToLoginWithState()
} else {
axios.defaults.headers.Authorization = `Bearer ${token}`
}
Varför jag tog bort tokenlogik från Axios-interceptorer
Interceptors är bra men inte för tokenförnyelse.
Före:
axios.interceptors.response.use(async (response) => {
if (response.status === 401) {
await renewToken()
return axios(response.config) // ❌ Risk of loops
}
})Problem:
- Om många API-anrop görs samtidigt med en utgången token, utlöser alla
renewToken(). - Initiala förfrågningar görs igen innan den nya token blir tillgänglig.
3. Hantera utgång med händelser
Livscykelhändelser som addUserLoaded och addSilentRenewError tillhandahålls av OIDC-klienten, vilket gör dem användbara för uppgifter som tokenunderhåll.
import { User, UserManager, UserManagerSettings, WebStorageStateStore } from 'oidc-client-ts'
const configuration = new UserManager(config)
configuration.events.addUserLoaded(user => {
storeTokenGlobally(user.access_token, user.expires_at)
})
configuration.events.addSilentRenewError(() => {
// Fallback: maybe trigger login redirect after some time
})
4. Hantera inloggningsomdirigeringar på ett snyggt sätt
När jag omdirigerar till inloggning bevarar jag nu användarens nuvarande rutt med hjälp av query-parametrar eller state:
const targetPath = window.location.pathname + window.location.search
const loginUrl = `/login?redirectTo=${encodeURIComponent(targetPath)}`
window.location.replace(loginUrl)Efter lyckad inloggning läser appen redirectTo -värdet och skickar tillbaka användaren dit de var.
Bonus: Miljöinställningar
Se till att inkludera offline_access -scope om du vill använda refresh tokens eller utökade tysta förnyelsefunktioner:
REACT_APP_IDENTITY_SERVER_SCOPE='openid offline_access'Se även till att ha följande standardinställningar.
const config: UserManagerSettings = {
authority: appConfig.identityServer.authority,
client_id: appConfig.identityServer.client_id,
redirect_uri: `${window.location.origin}/login-redirect`,
client_secret: appConfig.identityServer.clientSecret,
scope: appConfig.identityServer.scope,
response_type: 'code',
post_logout_redirect_uri: `${window.location.origin}/login`,
automaticSilentRenew: true,
silent_redirect_uri: `${window.location.origin}/silentrenew.html`,
accessTokenExpiringNotificationTimeInSeconds: 120,
includeIdTokenInSilentRenew: true,
revokeTokensOnSignout: false,
}
Resultat
Med dessa ändringar:
✅ Tyst tokenförnyelse fungerar konsekvent
✅ Oändliga API-loopar elimineras
✅ Omdirigeringar hanteras smidigt
✅ CSP respekteras och är säkert



