Skip to content

Commit

Permalink
feat(session): ✨ Tokens are now exposed via. userdata not in session …
Browse files Browse the repository at this point in the history
…cookie
  • Loading branch information
itpropro committed Sep 24, 2024
1 parent 19bd6a7 commit 3b28a3f
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 26 deletions.
4 changes: 1 addition & 3 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ export default defineNuxtConfig({
additionalLogoutParameters: {
logoutHint: '',
},
exposeIdToken: true,
exposeAccessToken: false,
allowedClientAuthParameters: [
'test',
],
Expand Down Expand Up @@ -65,7 +63,6 @@ export default defineNuxtConfig({
clientId: '',
clientSecret: '',
redirectUri: 'http://localhost:3000/auth/keycloak/callback',
exposeAccessToken: false,
userNameClaim: 'preferred_username',
},
cognito: {
Expand All @@ -75,6 +72,7 @@ export default defineNuxtConfig({
scope: ['openid', 'email', 'profile'],
logoutRedirectUri: 'https://google.com',
baseUrl: '',
exposeIdToken: true,
}
},
session: {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/composables/oidcAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function useOidcAuth() {
Accept: 'text/json',
},
method: 'POST',
}).catch(() => (undefined)) as UserSession)
}).catch(() => login()) as UserSession)
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/runtime/providers/cognito.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ export const cognito = defineOidcProvider<OidcProviderConfig, CognitoRequiredFie
automaticRefresh: true,
expirationThreshold: 240,
},
exposeIdToken: true,
logoutRedirectParameterName: 'logout_uri',
})
5 changes: 5 additions & 0 deletions src/runtime/providers/entra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ export const entra = defineOidcProvider<EntraProviderConfig, EntraIdRequiredFiel
openIdConfig.issuer = [`https://${parsedUrl.host}/${tenantId}/v2.0`, openIdConfig.issuer]
return openIdConfig
},
sessionConfiguration: {
expirationCheck: true,
automaticRefresh: true,
expirationThreshold: 1800,
},
validateAccessToken: false,
validateIdToken: true,
})
5 changes: 5 additions & 0 deletions src/runtime/providers/keycloak.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export const keycloak = defineOidcProvider<KeycloakProviderConfig, KeycloakRequi
additionalLogoutParameters: {
idTokenHint: '',
},
sessionConfiguration: {
expirationCheck: true,
automaticRefresh: true,
expirationThreshold: 240,
},
validateAccessToken: true,
validateIdToken: false,
exposeIdToken: true,
Expand Down
14 changes: 4 additions & 10 deletions src/runtime/server/lib/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,20 +243,14 @@ export function callbackEventHandler({ onSuccess }: OAuthConfig<UserSession>) {
config.optionalClaims.forEach(claim => parsedIdToken[claim] && ((user.claims as Record<string, unknown>)[claim] = (parsedIdToken[claim])))
}

// Expose access token
if (config.exposeAccessToken)
user.accessToken = tokenResponse.access_token

if (config.exposeIdToken)
user.idToken = tokenResponse.id_token

if (tokenResponse.refresh_token) {
if (tokenResponse.refresh_token || config.exposeAccessToken || config.exposeIdToken) {
const tokenKey = process.env.NUXT_OIDC_TOKEN_KEY as string
const persistentSession: PersistentSession = {
exp: accessToken.exp as number,
iat: accessToken.iat as number,
accessToken: await encryptToken(tokenResponse.access_token, tokenKey),
refreshToken: await encryptToken(tokenResponse.refresh_token, tokenKey),
...tokenResponse.refresh_token && { refreshToken: await encryptToken(tokenResponse.refresh_token, tokenKey) },
...tokenResponse.id_token && { idToken: await encryptToken(tokenResponse.id_token, tokenKey) },
}
const userSessionId = await getUserSessionId(event)
await useStorage('oidc').setItem<PersistentSession>(userSessionId, persistentSession)
Expand All @@ -282,7 +276,7 @@ export function logoutEventHandler({ onSuccess }: OAuthConfig<UserSession>) {
const logoutRedirectUri = logoutParams.logoutRedirectUri || config.logoutRedirectUri || `${getRequestURL(event).protocol}//${getRequestURL(event).host}`

// Set logout_hint and id_token_hint dynamic parameters if specified. According to https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
const additionalLogoutParameters: Record<string, string> = {}
const additionalLogoutParameters: Record<string, string> = config.additionalLogoutParameters || {}
if (config.additionalLogoutParameters) {
const userSession = await getUserSession(event)
Object.keys(config.additionalLogoutParameters).forEach((key) => {
Expand Down
10 changes: 2 additions & 8 deletions src/runtime/server/utils/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ export async function refreshAccessToken(refreshToken: string, config: OidcProvi
}

// Construct tokens object
const tokens: Record<'refreshToken' | 'accessToken', string> = {
const tokens: Record<'refreshToken' | 'accessToken' | 'idToken', string> = {
refreshToken: tokenResponse.refresh_token || refreshToken,
accessToken: tokenResponse.access_token,
idToken: tokenResponse.id_token || '',
}

// Construct user object
Expand All @@ -75,13 +76,6 @@ export async function refreshAccessToken(refreshToken: string, config: OidcProvi
config.optionalClaims.forEach(claim => parsedIdToken[claim] && ((user.claims as Record<string, unknown>)[claim] = (parsedIdToken[claim])))
}

// Expose tokens
if (config.exposeAccessToken)
user.accessToken = tokenResponse.access_token

if (config.exposeIdToken)
user.idToken = tokenResponse.id_token

return {
user,
tokens,
Expand Down
26 changes: 23 additions & 3 deletions src/runtime/server/utils/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,19 @@ export async function refreshUserSession(event: H3Event) {
iat: accessToken.iat || Math.trunc(Date.now() / 1000),
accessToken: await encryptToken(tokens.accessToken, tokenKey),
refreshToken: await encryptToken(tokens.refreshToken, tokenKey),
...tokens.idToken && { idToken: await encryptToken(tokens.idToken, tokenKey) },
}

await useStorage('oidc').setItem<PersistentSession>(session.id as string, updatedPersistentSession)
await session.update(defu(user as UserSession, session.data))
const { accessToken: _accessToken, idToken: _idToken, ...userWithoutToken } = user

return session.data
await session.update(defu(userWithoutToken as UserSession, session.data))

return {
...session.data,
...(tokens.accessToken && (useRuntimeConfig(event).oidc.providers[provider]?.exposeAccessToken || providerPresets[provider].exposeAccessToken)) && { accessToken: tokens.accessToken },
...(tokens.idToken && (useRuntimeConfig(event).oidc.providers[provider]?.exposeIdToken || providerPresets[provider].exposeIdToken)) && { idToken: tokens.idToken },
}
}

// Deprecated, please use getUserSession
Expand Down Expand Up @@ -154,6 +161,19 @@ export async function getUserSession(event: H3Event) {
})
}
}
// Expose tokens if configured
if (useRuntimeConfig(event).oidc.providers[provider]?.exposeAccessToken || providerPresets[provider].exposeAccessToken) {
const persistentSession = await useStorage('oidc').getItem<PersistentSession>(session.id as string) as PersistentSession | null
const tokenKey = process.env.NUXT_OIDC_TOKEN_KEY as string
if (persistentSession)
userSession.accessToken = await decryptToken(persistentSession.accessToken, tokenKey)
}
if (useRuntimeConfig(event).oidc.providers[provider]?.exposeIdToken || providerPresets[provider].exposeIdToken) {
const persistentSession = await useStorage('oidc').getItem<PersistentSession>(session.id as string) as PersistentSession | null
const tokenKey = process.env.NUXT_OIDC_TOKEN_KEY as string
if (persistentSession?.idToken)
userSession.idToken = await decryptToken(persistentSession.idToken, tokenKey) || undefined
}
return userSession
}

Expand All @@ -169,7 +189,7 @@ function _useSession(event: H3Event) {
Object.keys(useRuntimeConfig(event).oidc.providers).map(
key => key as ProviderKeys,
).forEach(
key => providerSessionConfigs[key] = defu(useRuntimeConfig(event).oidc.providers[key]?.sessionConfiguration, {
key => providerSessionConfigs[key] = defu(useRuntimeConfig(event).oidc.providers[key]?.sessionConfiguration, providerPresets[key].sessionConfiguration, {
automaticRefresh: useRuntimeConfig(event).oidc.session.automaticRefresh,
expirationCheck: useRuntimeConfig(event).oidc.session.expirationCheck,
expirationThreshold: useRuntimeConfig(event).oidc.session.expirationThreshold,
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ export interface PersistentSession {
exp: number
iat: number
accessToken: EncryptedToken
refreshToken: EncryptedToken
refreshToken?: EncryptedToken
idToken?: EncryptedToken
}

export interface TokenRequest {
Expand Down

0 comments on commit 3b28a3f

Please sign in to comment.