Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: detailed status during web app bootstrap; handle token errors #1450

Merged
merged 4 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions client/src/js/init.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { stylesheets, scripts, isMinimizedSource } from './resources.js'
import * as OP from './modules/oidcProvider.js'

const statusEl = document.getElementById("loading-text")
window.oidcProvider = OP

function appendStatus(html) {
statusEl.innerHTML += `${statusEl.innerHTML ? '<br/><br/>' : ''}${html}`
}

function getScopeStr() {
const scopePrefix = STIGMAN.Env.oauth.scopePrefix
let scopes = [
Expand Down Expand Up @@ -56,19 +62,23 @@ async function authorizeOidc() {
scope: getScopeStr()
})
if (tokens) {
appendStatus(`Loading App ${STIGMAN?.Env?.version}`)
loadResources()
}
}
catch (e) {
document.getElementById("loading-text").innerHTML = e.message
appendStatus(e.message)
}
}

if (window.isSecureContext) {
document.getElementById("loading-text").innerHTML = `Loading ${STIGMAN?.Env?.version}`
appendStatus(`Authorizing`)
authorizeOidc()
}
else {
document.getElementById("loading-text").innerHTML = `SECURE CONTEXT REQUIRED<br><br>The App is not executing in a <a href=https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts target="_blank">secure context</a> and cannot continue. <br><br>To be considered secure, resources that are not local must be served over https:// URLs and the security properties of the network channel used to deliver the resource must not be considered deprecated.`
appendStatus(`SECURE CONTEXT REQUIRED<br><br>
The App is not executing in a <a href=https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts target="_blank">secure context</a> and cannot continue.
<br><br>To be considered secure, resources that are not local must be served over https:// URLs and the security
properties of the network channel used to deliver the resource must not be considered deprecated.`)
}

24 changes: 23 additions & 1 deletion client/src/js/modules/oidcProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ function setTokens(tokens, clientTime) {
state.tokens = tokens
token = state.tokens.access_token
refreshToken = state.tokens.refresh_token

tokenParsed = decodeToken(token)
refreshTokenParsed = decodeToken(refreshToken)

state.timeSkew = clientTime ? Math.floor(clientTime / 1000) - tokenParsed.iat : 0
console.log('[OIDCPROVIDER] Estimated time difference between browser and server is ' + state.timeSkew + ' seconds')
console.log('[OIDCPROVIDER] Token expires ' + new Date(tokenParsed.exp * 1000))

const tokenExpiresIn = (tokenParsed.exp - (new Date().getTime() / 1000) + state.timeSkew) * 1000
if (tokenExpiresIn <= 0) {
expiredCallback()
Expand All @@ -67,6 +70,7 @@ function setTokens(tokens, clientTime) {
}
state.tokenTimeoutHandle = setTimeout(expiredCallback, tokenExpiresIn)
}

if (state.autoRefresh && refreshToken) {
const now = new Date().getTime()
const expiration = refreshTokenParsed ? refreshTokenParsed.exp : tokenParsed.exp
Expand Down Expand Up @@ -158,6 +162,10 @@ async function requestToken(body) {
method: 'post',
body
})
if (!response.ok) {
const text = await response.text()
throw new Error(text)
}
return response.json()
}

Expand All @@ -168,18 +176,30 @@ async function requestRefresh() {
body
})
if (!response.ok) {
throw new Error()
const text = await response.text()
throw new Error(text)
}
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')
}
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')

return params
}

Expand Down Expand Up @@ -215,6 +235,8 @@ async function getAuthorizationUrl() {
params.append('code_challenge_method', 'S256')

const authEndpoint = state.oidcConfiguration.authorization_endpoint

// Save the code verifier for use after the OP redirect back to us
localStorage.setItem('oidc-code-verifier', pkce.codeVerifier)

return `${authEndpoint}?${params.toString()}`
Expand Down
13 changes: 5 additions & 8 deletions client/src/js/stigman.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,22 @@ function myContextMenu (e,t,eOpts) {
Ext.Ajax.disableCaching = false

async function start () {
let timer
const el = Ext.get('loading-text').dom

try {
if ('serviceWorker' in navigator) {
await navigator.serviceWorker.register('serviceWorker.js')
}
timer = setTimeout(() => {
Ext.get( 'loading-text' ).dom.innerHTML = "Getting configuration..."
}, 250)
el.innerHTML += "<br/><br/>Fetching user data"
await SM.GetUserObject()
if (curUser.username !== undefined) {
clearTimeout(timer)
loadApp();
} else {
Ext.get( 'loading-text' ).dom.innerHTML =`No account for ${window.oidcProvider.token}`;
el.innerHTML += `<br/>No account for ${window.oidcProvider.token}`
}
}
catch (e) {
clearTimeout(timer)
Ext.get( 'loading-text' ).dom.innerHTML = e.message
el.innerHTML += `<br/>${e.message}`
cd-rite marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Loading