UNPKG

@auth0/auth0-spa-js

Version:

Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE

187 lines (166 loc) 5.45 kB
import { ICache, InMemoryCache, LocalStorageCache } from './cache'; import { Auth0ClientOptions, AuthorizationParams, AuthorizeOptions, ClientAuthorizationParams, LogoutOptions } from './global'; import { scopesToRequest } from './scope'; /** * @ignore */ export const GET_TOKEN_SILENTLY_LOCK_KEY = 'auth0.lock.getTokenSilently'; /** * @ignore */ export const buildGetTokenSilentlyLockKey = ( clientId: string, audience: string ) => `${GET_TOKEN_SILENTLY_LOCK_KEY}.${clientId}.${audience}`; /** * @ignore */ export const buildOrganizationHintCookieName = (clientId: string) => `auth0.${clientId}.organization_hint`; /** * @ignore */ export const OLD_IS_AUTHENTICATED_COOKIE_NAME = 'auth0.is.authenticated'; /** * @ignore */ export const buildIsAuthenticatedCookieName = (clientId: string) => `auth0.${clientId}.is.authenticated`; /** * @ignore */ const cacheLocationBuilders: Record<string, () => ICache> = { memory: () => new InMemoryCache().enclosedCache, localstorage: () => new LocalStorageCache() }; /** * @ignore */ export const cacheFactory = (location: string) => { return cacheLocationBuilders[location]; }; /** * @ignore */ export const getAuthorizeParams = ( clientOptions: Auth0ClientOptions & { authorizationParams: ClientAuthorizationParams; }, scope: Record<string, string>, authorizationParams: AuthorizationParams & { scope?: string }, state: string, nonce: string, code_challenge: string, redirect_uri: string | undefined, response_mode: string | undefined, thumbprint: string | undefined ): AuthorizeOptions => { return { client_id: clientOptions.clientId, ...clientOptions.authorizationParams, ...authorizationParams, scope: scopesToRequest(scope, authorizationParams.scope, authorizationParams.audience), response_type: 'code', response_mode: response_mode || 'query', state, nonce, redirect_uri: redirect_uri || clientOptions.authorizationParams.redirect_uri, code_challenge, code_challenge_method: 'S256', dpop_jkt: thumbprint }; }; /** * @ignore * * Function used to provide support for the deprecated onRedirect through openUrl. */ export const patchOpenUrlWithOnRedirect = < T extends Pick<LogoutOptions, 'openUrl' | 'onRedirect'> >( options: T ) => { const { openUrl, onRedirect, ...originalOptions } = options; const result = { ...originalOptions, openUrl: openUrl === false || openUrl ? openUrl : onRedirect }; return result as T; }; /** * @ignore * * Checks if all scopes are included inside other array of scopes */ export const allScopesAreIncluded = (scopeToInclude?: string, scopes?: string): boolean => { const scopeGroup = scopes?.split(" ") || []; const scopesToInclude = scopeToInclude?.split(" ") || []; return scopesToInclude.every((key) => scopeGroup.includes(key)); } /** * @ignore * * Returns the scopes that are missing after a refresh */ export const getMissingScopes = (requestedScope?: string, respondedScope?: string): string => { const requestedScopes = requestedScope?.split(" ") || []; const respondedScopes = respondedScope?.split(" ") || []; const missingScopes = requestedScopes.filter((scope) => respondedScopes.indexOf(scope) == -1); return missingScopes.join(","); } /** * @ignore * * For backward compatibility we are going to check if we are going to downscope while doing a refresh request * while MRRT is allowed. If the audience is the same for the refresh_token we are going to use and it has * lower scopes than the ones originally in the token, we are going to return the scopes that were stored * with the refresh_token in the tokenset. * @param useMrrt Setting that the user can activate to use MRRT in their requests * @param authorizationParams Contains the audience and scope that the user requested to obtain a token * @param cachedAudience Audience stored with the refresh_token wich we are going to use in the request * @param cachedScope Scope stored with the refresh_token wich we are going to use in the request */ export const getScopeToRequest = ( useMrrt: boolean | undefined, authorizationParams: { audience?: string, scope: string }, cachedAudience?: string, cachedScope?: string ): string => { if (useMrrt && cachedAudience && cachedScope) { if (authorizationParams.audience !== cachedAudience) { return authorizationParams.scope; } const cachedScopes = cachedScope.split(" "); const newScopes = authorizationParams.scope?.split(" ") || []; const newScopesAreIncluded = newScopes.every((scope) => cachedScopes.includes(scope)); return cachedScopes.length >= newScopes.length && newScopesAreIncluded ? cachedScope : authorizationParams.scope; } return authorizationParams.scope; } /** * @ignore * * Checks if the refresh request has been done using MRRT * @param cachedAudience Audience from the refresh token used to refresh * @param cachedScope Scopes from the refresh token used to refresh * @param requestAudience Audience sent to the server * @param requestScope Scopes sent to the server */ export const isRefreshWithMrrt = ( cachedAudience: string | undefined, cachedScope: string | undefined, requestAudience: string | undefined, requestScope: string, ): boolean => { if (cachedAudience !== requestAudience) { return true; } return !allScopesAreIncluded(requestScope, cachedScope); }