From f7c20f6f485583cce9eeaa673de0fd57d5786d71 Mon Sep 17 00:00:00 2001 From: Guillaume Chervet Date: Wed, 1 Jan 2025 22:20:55 +0100 Subject: [PATCH 1/3] feat(scope): scope can be change dynamically --- .../public/OidcTrustedDomains.js | 5 ++- examples/react-oidc-demo/src/MultiAuth.tsx | 1 + .../src/OidcServiceWorker.ts | 37 ++++++++++++++----- packages/oidc-client/src/oidc.ts | 4 +- packages/oidc-client/src/oidcClient.ts | 4 +- packages/oidc-client/src/renewTokens.ts | 30 ++++++++------- packages/oidc-client/src/silentLogin.ts | 8 ++-- 7 files changed, 57 insertions(+), 32 deletions(-) diff --git a/examples/react-oidc-demo/public/OidcTrustedDomains.js b/examples/react-oidc-demo/public/OidcTrustedDomains.js index b14bcca17..aea8b1965 100644 --- a/examples/react-oidc-demo/public/OidcTrustedDomains.js +++ b/examples/react-oidc-demo/public/OidcTrustedDomains.js @@ -33,11 +33,12 @@ trustedDomains.config_separate_oidc_access_token_domains = { trustedDomains.config_with_dpop = { domains: ['https://demo.duendesoftware.com'], demonstratingProofOfPossession: true, - demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: true, + demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false, + // allowMultiTabLogin: true, }; trustedDomains.config_multi_tab_login = { domains: ['https://demo.duendesoftware.com'], allowMultiTabLogin: true, }; -//# sourceMappingURL=OidcTrustedDomains.js.map +//# sourceMappingURL=OidcTrustedDomains.js.map \ No newline at end of file diff --git a/examples/react-oidc-demo/src/MultiAuth.tsx b/examples/react-oidc-demo/src/MultiAuth.tsx index 13167b779..0db5c26ae 100644 --- a/examples/react-oidc-demo/src/MultiAuth.tsx +++ b/examples/react-oidc-demo/src/MultiAuth.tsx @@ -90,6 +90,7 @@ const MultiAuth = ({ configurationName, handleConfigurationChange }) => { tenantId: '1234', }, true, + 'openid profile email' ) } > diff --git a/packages/oidc-client-service-worker/src/OidcServiceWorker.ts b/packages/oidc-client-service-worker/src/OidcServiceWorker.ts index d9f8f41e0..a7abadd1b 100644 --- a/packages/oidc-client-service-worker/src/OidcServiceWorker.ts +++ b/packages/oidc-client-service-worker/src/OidcServiceWorker.ts @@ -88,9 +88,11 @@ async function generateDpopAsync( ) { const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration; const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson; - headersExtras['dpop'] = await generateJwtDemonstratingProofOfPossessionAsync(self)( - dpopConfiguration, - )(jwk, 'POST', url, extrasClaims); + const method = originalRequest.method; + const dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)( + dpopConfiguration, + )(jwk, method, url, extrasClaims); + headersExtras['dpop'] = dpop; if (currentDatabase.demonstratingProofOfPossessionNonce != null) { headersExtras['nonce'] = currentDatabase.demonstratingProofOfPossessionNonce; } @@ -127,7 +129,7 @@ const handleFetch = async (event: FetchEvent) => { ) { requestMode = 'cors'; } - + console.log("url : ", url); let headers: { [p: string]: string }; if ( originalRequest.mode == 'navigate' && @@ -142,11 +144,28 @@ const handleFetch = async (event: FetchEvent) => { if (authorization) { authenticationMode = authorization.split(' ')[0]; } - headers = { - ...serializeHeaders(originalRequest.headers), - authorization: - authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, - }; + console.log('authenticationMode', authenticationMode); + + if (authenticationMode.toLowerCase() == 'dpop') { + const claimsExtras = { + ath: await base64urlOfHashOfASCIIEncodingAsync(currentDatabaseForRequestAccessToken.tokens.access_token), + }; + const dpopHeaders = await generateDpopAsync(originalRequest, currentDatabaseForRequestAccessToken, url, claimsExtras); + console.log('dpopHeaders', dpopHeaders); + headers = { + ...dpopHeaders, + authorization: + authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, + }; + } else{ + headers = { + ...serializeHeaders(originalRequest.headers), + authorization: + authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, + }; + } + console.log('headers', headers); + } let init: RequestInit; if (originalRequest.mode === 'navigate') { diff --git a/packages/oidc-client/src/oidc.ts b/packages/oidc-client/src/oidc.ts index 67dc88629..0c7f12c11 100644 --- a/packages/oidc-client/src/oidc.ts +++ b/packages/oidc-client/src/oidc.ts @@ -418,7 +418,7 @@ Please checkout that you are using OIDC hook inside a = null; - async renewTokensAsync(extras: StringMap = null) { + async renewTokensAsync(extras: StringMap = null, scope: string = null) { if (this.renewTokensPromise !== null) { return this.renewTokensPromise; } @@ -427,7 +427,7 @@ Please checkout that you are using OIDC hook inside a { this.renewTokensPromise = null; }); diff --git a/packages/oidc-client/src/oidcClient.ts b/packages/oidc-client/src/oidcClient.ts index 334bc81c5..729f3bac3 100644 --- a/packages/oidc-client/src/oidcClient.ts +++ b/packages/oidc-client/src/oidcClient.ts @@ -62,8 +62,8 @@ export class OidcClient { return this._oidc.silentLoginCallbackAsync(); } - renewTokensAsync(extras: StringMap = null): Promise { - return this._oidc.renewTokensAsync(extras); + renewTokensAsync(extras: StringMap = null, scope: string=null): Promise { + return this._oidc.renewTokensAsync(extras, scope); } loginCallbackAsync(): Promise { diff --git a/packages/oidc-client/src/renewTokens.ts b/packages/oidc-client/src/renewTokens.ts index 479dcd768..b91f542ac 100644 --- a/packages/oidc-client/src/renewTokens.ts +++ b/packages/oidc-client/src/renewTokens.ts @@ -8,7 +8,7 @@ import { _silentLoginAsync } from './silentLogin'; import timer from './timer.js'; import { OidcConfiguration, StringMap, TokenAutomaticRenewMode } from './types.js'; -async function syncTokens(oidc: Oidc, forceRefresh: boolean, extras: StringMap) { +async function syncTokens(oidc: Oidc, forceRefresh: boolean, extras: StringMap, scope:string=null) { const updateTokens = tokens => { oidc.tokens = tokens; }; @@ -17,6 +17,7 @@ async function syncTokens(oidc: Oidc, forceRefresh: boolean, extras: StringMap) 0, forceRefresh, extras, + scope ); const serviceWorker = await initWorkerAsync(oidc.configuration, oidc.configurationName); @@ -36,6 +37,7 @@ export async function renewTokensAndStartTimerAsync( oidc, forceRefresh = false, extras: StringMap = null, + scope:string=null ) { const configuration = oidc.configuration; const lockResourcesName = `${configuration.client_id}_${oidc.configurationName}_${configuration.authority}`; @@ -44,7 +46,7 @@ export async function renewTokensAndStartTimerAsync( const serviceWorker = await initWorkerAsync(oidc.configuration, oidc.configurationName); if ((configuration?.storage === window?.sessionStorage && !serviceWorker) || !navigator.locks) { - tokens = await syncTokens(oidc, forceRefresh, extras); + tokens = await syncTokens(oidc, forceRefresh, extras, scope); } else { let status: any = 'retry'; while (status === 'retry') { @@ -58,7 +60,7 @@ export async function renewTokensAndStartTimerAsync( }); return 'retry'; } - return await syncTokens(oidc, forceRefresh, extras); + return await syncTokens(oidc, forceRefresh, extras, scope); }, ); } @@ -71,13 +73,13 @@ export async function renewTokensAndStartTimerAsync( if (oidc.timeoutId) { // @ts-ignore - oidc.timeoutId = autoRenewTokens(oidc, oidc.tokens.expiresAt, extras); + oidc.timeoutId = autoRenewTokens(oidc, oidc.tokens.expiresAt, extras, scope); } return oidc.tokens; } -export const autoRenewTokens = (oidc: Oidc, expiresAt, extras: StringMap = null) => { +export const autoRenewTokens = (oidc: Oidc, expiresAt, extras: StringMap = null, scope:string=null) => { const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second; if (oidc.timeoutId) { @@ -87,7 +89,7 @@ export const autoRenewTokens = (oidc: Oidc, expiresAt, extras: StringMap = null) const timeLeft = computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt); const timeInfo = { timeLeft }; oidc.publishEvent(Oidc.eventNames.token_timer, timeInfo); - await renewTokensAndStartTimerAsync(oidc, false, extras); + await renewTokensAndStartTimerAsync(oidc, false, extras, scope); }, 1000); }; @@ -107,7 +109,7 @@ export const syncTokensInfoAsync = configuration: OidcConfiguration, configurationName: string, currentTokens: Tokens, - forceRefresh = false, + forceRefresh = false ) => { // Service Worker can be killed by the browser (when it wants,for example after 10 seconds of inactivity, so we retreieve the session if it happen) // const configuration = this.configuration; @@ -186,7 +188,7 @@ export const syncTokensInfoAsync = const synchroniseTokensAsync = (oidc: Oidc) => - async (updateTokens, index = 0, forceRefresh = false, extras: StringMap = null) => { + async (updateTokens, index = 0, forceRefresh = false, extras: StringMap = null, scope:string=null) => { if (!navigator.onLine && document.hidden) { return { tokens: oidc.tokens, status: 'GIVE_UP' }; } @@ -211,7 +213,7 @@ const synchroniseTokensAsync = oidc.publishEvent.bind(oidc), )(extras, state, scope); }; - const localsilentLoginAsync = async () => { + const localSilentLoginAsync = async () => { try { let loginParams; const serviceWorker = await initWorkerAsync(configuration, oidc.configurationName); @@ -225,6 +227,7 @@ const synchroniseTokensAsync = ...loginParams.extras, ...extras, prompt: 'none', + scope }); if (!silent_token_response) { updateTokens(null); @@ -250,7 +253,7 @@ const synchroniseTokensAsync = message: 'exceptionSilent', exception: exceptionSilent.message, }); - return await synchroniseTokensAsync(oidc)(updateTokens, nextIndex, forceRefresh, extras); + return await synchroniseTokensAsync(oidc)(updateTokens, nextIndex, forceRefresh, extras, scope); } }; @@ -297,7 +300,7 @@ const synchroniseTokensAsync = } oidc.publishEvent(eventNames.refreshTokensAsync_begin, { tryNumber: index }); - return await localsilentLoginAsync(); + return await localSilentLoginAsync(); default: { if ( configuration.token_automatic_renew_mode == @@ -314,7 +317,7 @@ const synchroniseTokensAsync = tryNumber: index, }); if (!tokens.refreshToken) { - return await localsilentLoginAsync(); + return await localSilentLoginAsync(); } const clientId = configuration.client_id; @@ -412,6 +415,7 @@ const synchroniseTokensAsync = nextIndex, forceRefresh, extras, + scope ); } }; @@ -429,7 +433,7 @@ const synchroniseTokensAsync = // so we need to brake calls chain and delay next call return new Promise((resolve, reject) => { setTimeout(() => { - synchroniseTokensAsync(oidc)(updateTokens, nextIndex, forceRefresh, extras) + synchroniseTokensAsync(oidc)(updateTokens, nextIndex, forceRefresh, extras, scope) .then(resolve) .catch(reject); }, 1000); diff --git a/packages/oidc-client/src/silentLogin.ts b/packages/oidc-client/src/silentLogin.ts index 48e15b195..c9d58820c 100644 --- a/packages/oidc-client/src/silentLogin.ts +++ b/packages/oidc-client/src/silentLogin.ts @@ -37,7 +37,7 @@ export const _silentLoginAsync = extras.state = state; } - if (scope) { + if (scope != null) { if (extras == null) { extras = {}; } @@ -136,11 +136,11 @@ export const defaultSilentLoginAsync = (extras: StringMap = null, scope: string = undefined) => { extras = { ...extras }; - const silentLoginAsync = (extras, state, scope) => { + const silentLoginAsync = (extras, state, scopeInternal) => { return _silentLoginAsync(configurationName, configuration, publishEvent.bind(oidc))( extras, state, - scope, + scopeInternal, ); }; @@ -170,7 +170,7 @@ export const defaultSilentLoginAsync = oidc.tokens = silentResult.tokens; publishEvent(eventNames.token_acquired, {}); // @ts-ignore - oidc.timeoutId = autoRenewTokens(oidc, oidc.tokens.expiresAt, extras); + oidc.timeoutId = autoRenewTokens(oidc, oidc.tokens.expiresAt, extras, scope); return {}; } } catch (e) { From af8076b276856eb061c8ace754134e9cb91ae0fe Mon Sep 17 00:00:00 2001 From: Guillaume Chervet Date: Fri, 3 Jan 2025 10:06:24 +0100 Subject: [PATCH 2/3] fix lint --- examples/react-oidc-demo/src/MultiAuth.tsx | 2 +- .../src/OidcServiceWorker.ts | 29 +++++++++----- packages/oidc-client/src/oidcClient.ts | 2 +- packages/oidc-client/src/renewTokens.ts | 40 ++++++++++++++----- packages/oidc-client/src/silentLogin.ts | 2 +- 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/examples/react-oidc-demo/src/MultiAuth.tsx b/examples/react-oidc-demo/src/MultiAuth.tsx index 0db5c26ae..1bf2550e2 100644 --- a/examples/react-oidc-demo/src/MultiAuth.tsx +++ b/examples/react-oidc-demo/src/MultiAuth.tsx @@ -90,7 +90,7 @@ const MultiAuth = ({ configurationName, handleConfigurationChange }) => { tenantId: '1234', }, true, - 'openid profile email' + 'openid profile email', ) } > diff --git a/packages/oidc-client-service-worker/src/OidcServiceWorker.ts b/packages/oidc-client-service-worker/src/OidcServiceWorker.ts index a7abadd1b..e1edb37d9 100644 --- a/packages/oidc-client-service-worker/src/OidcServiceWorker.ts +++ b/packages/oidc-client-service-worker/src/OidcServiceWorker.ts @@ -89,9 +89,12 @@ async function generateDpopAsync( const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration; const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson; const method = originalRequest.method; - const dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)( - dpopConfiguration, - )(jwk, method, url, extrasClaims); + const dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)(dpopConfiguration)( + jwk, + method, + url, + extrasClaims, + ); headersExtras['dpop'] = dpop; if (currentDatabase.demonstratingProofOfPossessionNonce != null) { headersExtras['nonce'] = currentDatabase.demonstratingProofOfPossessionNonce; @@ -129,7 +132,7 @@ const handleFetch = async (event: FetchEvent) => { ) { requestMode = 'cors'; } - console.log("url : ", url); + console.log('url : ', url); let headers: { [p: string]: string }; if ( originalRequest.mode == 'navigate' && @@ -148,24 +151,30 @@ const handleFetch = async (event: FetchEvent) => { if (authenticationMode.toLowerCase() == 'dpop') { const claimsExtras = { - ath: await base64urlOfHashOfASCIIEncodingAsync(currentDatabaseForRequestAccessToken.tokens.access_token), + ath: await base64urlOfHashOfASCIIEncodingAsync( + currentDatabaseForRequestAccessToken.tokens.access_token, + ), }; - const dpopHeaders = await generateDpopAsync(originalRequest, currentDatabaseForRequestAccessToken, url, claimsExtras); + const dpopHeaders = await generateDpopAsync( + originalRequest, + currentDatabaseForRequestAccessToken, + url, + claimsExtras, + ); console.log('dpopHeaders', dpopHeaders); headers = { ...dpopHeaders, authorization: - authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, + authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, }; - } else{ + } else { headers = { ...serializeHeaders(originalRequest.headers), authorization: - authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, + authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, }; } console.log('headers', headers); - } let init: RequestInit; if (originalRequest.mode === 'navigate') { diff --git a/packages/oidc-client/src/oidcClient.ts b/packages/oidc-client/src/oidcClient.ts index 729f3bac3..11b22cfa1 100644 --- a/packages/oidc-client/src/oidcClient.ts +++ b/packages/oidc-client/src/oidcClient.ts @@ -62,7 +62,7 @@ export class OidcClient { return this._oidc.silentLoginCallbackAsync(); } - renewTokensAsync(extras: StringMap = null, scope: string=null): Promise { + renewTokensAsync(extras: StringMap = null, scope: string = null): Promise { return this._oidc.renewTokensAsync(extras, scope); } diff --git a/packages/oidc-client/src/renewTokens.ts b/packages/oidc-client/src/renewTokens.ts index b91f542ac..67fb15ec4 100644 --- a/packages/oidc-client/src/renewTokens.ts +++ b/packages/oidc-client/src/renewTokens.ts @@ -8,7 +8,12 @@ import { _silentLoginAsync } from './silentLogin'; import timer from './timer.js'; import { OidcConfiguration, StringMap, TokenAutomaticRenewMode } from './types.js'; -async function syncTokens(oidc: Oidc, forceRefresh: boolean, extras: StringMap, scope:string=null) { +async function syncTokens( + oidc: Oidc, + forceRefresh: boolean, + extras: StringMap, + scope: string = null, +) { const updateTokens = tokens => { oidc.tokens = tokens; }; @@ -17,7 +22,7 @@ async function syncTokens(oidc: Oidc, forceRefresh: boolean, extras: StringMap, 0, forceRefresh, extras, - scope + scope, ); const serviceWorker = await initWorkerAsync(oidc.configuration, oidc.configurationName); @@ -37,7 +42,7 @@ export async function renewTokensAndStartTimerAsync( oidc, forceRefresh = false, extras: StringMap = null, - scope:string=null + scope: string = null, ) { const configuration = oidc.configuration; const lockResourcesName = `${configuration.client_id}_${oidc.configurationName}_${configuration.authority}`; @@ -79,7 +84,12 @@ export async function renewTokensAndStartTimerAsync( return oidc.tokens; } -export const autoRenewTokens = (oidc: Oidc, expiresAt, extras: StringMap = null, scope:string=null) => { +export const autoRenewTokens = ( + oidc: Oidc, + expiresAt, + extras: StringMap = null, + scope: string = null, +) => { const refreshTimeBeforeTokensExpirationInSecond = oidc.configuration.refresh_time_before_tokens_expiration_in_second; if (oidc.timeoutId) { @@ -109,7 +119,7 @@ export const syncTokensInfoAsync = configuration: OidcConfiguration, configurationName: string, currentTokens: Tokens, - forceRefresh = false + forceRefresh = false, ) => { // Service Worker can be killed by the browser (when it wants,for example after 10 seconds of inactivity, so we retreieve the session if it happen) // const configuration = this.configuration; @@ -188,7 +198,13 @@ export const syncTokensInfoAsync = const synchroniseTokensAsync = (oidc: Oidc) => - async (updateTokens, index = 0, forceRefresh = false, extras: StringMap = null, scope:string=null) => { + async ( + updateTokens, + index = 0, + forceRefresh = false, + extras: StringMap = null, + scope: string = null, + ) => { if (!navigator.onLine && document.hidden) { return { tokens: oidc.tokens, status: 'GIVE_UP' }; } @@ -227,7 +243,7 @@ const synchroniseTokensAsync = ...loginParams.extras, ...extras, prompt: 'none', - scope + scope, }); if (!silent_token_response) { updateTokens(null); @@ -253,7 +269,13 @@ const synchroniseTokensAsync = message: 'exceptionSilent', exception: exceptionSilent.message, }); - return await synchroniseTokensAsync(oidc)(updateTokens, nextIndex, forceRefresh, extras, scope); + return await synchroniseTokensAsync(oidc)( + updateTokens, + nextIndex, + forceRefresh, + extras, + scope, + ); } }; @@ -415,7 +437,7 @@ const synchroniseTokensAsync = nextIndex, forceRefresh, extras, - scope + scope, ); } }; diff --git a/packages/oidc-client/src/silentLogin.ts b/packages/oidc-client/src/silentLogin.ts index c9d58820c..8651a2794 100644 --- a/packages/oidc-client/src/silentLogin.ts +++ b/packages/oidc-client/src/silentLogin.ts @@ -140,7 +140,7 @@ export const defaultSilentLoginAsync = return _silentLoginAsync(configurationName, configuration, publishEvent.bind(oidc))( extras, state, - scopeInternal, + scopeInternal, ); }; From b61dce2cf58b56c3e8d3a768699f54b0abada953 Mon Sep 17 00:00:00 2001 From: Guillaume Chervet Date: Fri, 3 Jan 2025 12:13:29 +0100 Subject: [PATCH 3/3] remove console.log --- packages/oidc-client-service-worker/src/OidcServiceWorker.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/oidc-client-service-worker/src/OidcServiceWorker.ts b/packages/oidc-client-service-worker/src/OidcServiceWorker.ts index e1edb37d9..339f7ea5c 100644 --- a/packages/oidc-client-service-worker/src/OidcServiceWorker.ts +++ b/packages/oidc-client-service-worker/src/OidcServiceWorker.ts @@ -132,7 +132,6 @@ const handleFetch = async (event: FetchEvent) => { ) { requestMode = 'cors'; } - console.log('url : ', url); let headers: { [p: string]: string }; if ( originalRequest.mode == 'navigate' && @@ -147,7 +146,6 @@ const handleFetch = async (event: FetchEvent) => { if (authorization) { authenticationMode = authorization.split(' ')[0]; } - console.log('authenticationMode', authenticationMode); if (authenticationMode.toLowerCase() == 'dpop') { const claimsExtras = { @@ -161,7 +159,6 @@ const handleFetch = async (event: FetchEvent) => { url, claimsExtras, ); - console.log('dpopHeaders', dpopHeaders); headers = { ...dpopHeaders, authorization: @@ -174,7 +171,6 @@ const handleFetch = async (event: FetchEvent) => { authenticationMode + ' ' + currentDatabaseForRequestAccessToken.tokens.access_token, }; } - console.log('headers', headers); } let init: RequestInit; if (originalRequest.mode === 'navigate') {