diff --git a/client/src/js/modules/oidcProvider.js b/client/src/js/modules/oidcProvider.js
index 334690fd..23cbf03b 100644
--- a/client/src/js/modules/oidcProvider.js
+++ b/client/src/js/modules/oidcProvider.js
@@ -20,11 +20,28 @@ async function authorize({clientId, oidcProvider, scope, autoRefresh}) {
}
else {
// exchange authorization_code for token
+ const lastOidc = JSON.parse(localStorage.getItem('last-oidc') ?? '{}')
+ lastOidc.redirectHref = window.location.href
const [redirectUrl, paramStr] = window.location.href.split('#')
const params = processRedirectParams(paramStr)
- let beforeTime = new Date().getTime()
- const tokens = await requestToken(getTokenRequestBody(params.code, redirectUrl))
- let clientTime = (beforeTime + new Date().getTime()) / 2
+
+ if (lastOidc.state !== params.state) {
+ throw new Error(`ERROR: OIDC redirection from unknown state.
Expected: ${lastOidc.state}
Actual: ${params.state}
Retry authorization.`)
+ }
+
+ const beforeTime = new Date().getTime()
+ const tokenRequestBody = getTokenRequestBody(params.code, lastOidc.pkce.codeVerifier, redirectUrl)
+ let tokens
+ try {
+ lastOidc.tokenEndpoint = state.oidcConfiguration.token_endpoint
+ lastOidc.tokenRequestBody = tokenRequestBody.toString()
+ tokens = await requestToken(tokenRequestBody)
+ }
+ catch (e) {
+ e.message = `
Retry authorization.`
+ throw e
+ }
+ const clientTime = (beforeTime + new Date().getTime()) / 2
setTokens(tokens, clientTime)
window.history.replaceState(window.history.state, '', redirectUrl)
return tokens
@@ -182,24 +199,14 @@ async function requestRefresh() {
return response.json()
}
-function getTokenRequestBody(code, redirectUri) {
- if (!localStorage.getItem('oidc-code-verifier')) {
- // Will assume this function was called while handling a replayed request,
- // perhaps from a bookmarked URL of an earlier authorization request.
- // Try to restart authorization from the beginning by redirecting to our entry point.
- window.location.href = redirectUri
- throw new Error('Redirecting after not finding code verifier')
- }
+function getTokenRequestBody(code, codeVerifier, redirectUri) {
const params = new URLSearchParams()
params.append('code', code)
params.append('grant_type', 'authorization_code')
params.append('client_id', state.clientId)
params.append('redirect_uri', redirectUri)
- params.append('code_verifier', localStorage.getItem('oidc-code-verifier'))
-
- // Clear saved code verifier to prevent replay error scenarios
- localStorage.removeItem('oidc-code-verifier')
-
+ params.append('code_verifier', codeVerifier)
+
return params
}
@@ -222,11 +229,11 @@ function processRedirectParams(paramStr) {
async function getAuthorizationUrl() {
const pkce = await getPkce()
-
+ const oidcState = crypto.randomUUID()
const params = new URLSearchParams()
params.append('client_id', state.clientId)
params.append('redirect_uri', window.location.href)
- params.append('state', crypto.randomUUID())
+ params.append('state', oidcState)
params.append('response_mode', 'fragment')
params.append('response_type', 'code')
params.append('scope', state.scope)
@@ -235,11 +242,15 @@ async function getAuthorizationUrl() {
params.append('code_challenge_method', 'S256')
const authEndpoint = state.oidcConfiguration.authorization_endpoint
+ const authRequest = `${authEndpoint}?${params.toString()}`
- // Save the code verifier for use after the OP redirect back to us
- localStorage.setItem('oidc-code-verifier', pkce.codeVerifier)
+ localStorage.setItem('last-oidc', JSON.stringify({
+ state: oidcState,
+ pkce,
+ authRequest
+ }))
- return `${authEndpoint}?${params.toString()}`
+ return authRequest
}
async function getPkce() {
diff --git a/client/src/js/stigman.js b/client/src/js/stigman.js
index c0382f48..86181bfe 100644
--- a/client/src/js/stigman.js
+++ b/client/src/js/stigman.js
@@ -48,7 +48,7 @@ async function start () {
}
}
catch (e) {
- el.innerHTML += `
`
+ el.innerHTML += `
`
}
}