// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { AuthConfig, AuthTokens, AuthUserPoolConfig, CognitoUserPoolConfig, } from '@aws-amplify/core'; import { AuthError } from '../../../errors/AuthError'; import { CognitoAuthTokens, DeviceMetadata } from '../tokenProvider/types'; import { DEVICE_METADATA_NOT_FOUND_EXCEPTION, TOKEN_REFRESH_EXCEPTION, USER_UNAUTHENTICATED_EXCEPTION, } from '../../../errors/constants'; export function isTypeUserPoolConfig( authConfig?: AuthConfig, ): authConfig is AuthUserPoolConfig { if ( authConfig && authConfig.Cognito.userPoolId && authConfig.Cognito.userPoolClientId ) { return true; } return false; } export function assertAuthTokens( tokens?: AuthTokens | null, ): asserts tokens is AuthTokens { if (!tokens || !tokens.accessToken) { throw new AuthError({ name: USER_UNAUTHENTICATED_EXCEPTION, message: 'User needs to be authenticated to call this API.', recoverySuggestion: 'Sign in before calling this API again.', }); } } export function assertIdTokenInAuthTokens( tokens?: AuthTokens, ): asserts tokens is AuthTokens { if (!tokens || !tokens.idToken) { throw new AuthError({ name: USER_UNAUTHENTICATED_EXCEPTION, message: 'User needs to be authenticated to call this API.', recoverySuggestion: 'Sign in before calling this API again.', }); } } export const oAuthTokenRefreshException = new AuthError({ name: TOKEN_REFRESH_EXCEPTION, message: `Token refresh is not supported when authenticated with the 'implicit grant' (token) oauth flow. Please change your oauth configuration to use 'code grant' flow.`, recoverySuggestion: `Please logout and change your Amplify configuration to use "code grant" flow. E.g { responseType: 'code' }`, }); export const tokenRefreshException = new AuthError({ name: USER_UNAUTHENTICATED_EXCEPTION, message: 'User needs to be authenticated to call this API.', recoverySuggestion: 'Sign in before calling this API again.', }); export function assertAuthTokensWithRefreshToken( tokens?: CognitoAuthTokens | null, ): asserts tokens is CognitoAuthTokens & { refreshToken: string } { if (isAuthenticatedWithImplicitOauthFlow(tokens)) { throw oAuthTokenRefreshException; } if (!isAuthenticatedWithRefreshToken(tokens)) { throw tokenRefreshException; } } type NonNullableDeviceMetadata = DeviceMetadata & { deviceKey: string; deviceGroupKey: string; }; export function assertDeviceMetadata( deviceMetadata?: DeviceMetadata | null, ): asserts deviceMetadata is NonNullableDeviceMetadata { if ( !deviceMetadata || !deviceMetadata.deviceKey || !deviceMetadata.deviceGroupKey || !deviceMetadata.randomPassword ) { throw new AuthError({ name: DEVICE_METADATA_NOT_FOUND_EXCEPTION, message: 'Either deviceKey, deviceGroupKey or secretPassword were not found during the sign-in process.', recoverySuggestion: 'Make sure to not clear storage after calling the signIn API.', }); } } export const OAuthStorageKeys = { inflightOAuth: 'inflightOAuth', inflightOAuthDeadline: 'inflightOAuthDeadline', oauthSignIn: 'oauthSignIn', oauthPKCE: 'oauthPKCE', oauthState: 'oauthState', }; export interface OAuthStore { setAuthConfig(authConfigParam: CognitoUserPoolConfig): void; loadOAuthInFlight(): Promise; /** * Returns the timestamp (epoch ms) until which token consumers should block * on the inflight OAuth flow, or `undefined` when there is nothing to block * on (no flow in flight, or its blocking deadline has passed). * * Optional so that custom {@link OAuthStore} implementations written before * this method existed keep compiling; callers must treat its absence as * "no deadline available". */ loadOAuthInFlightDeadline?(): Promise; storeOAuthInFlight(inflight: boolean): Promise; loadOAuthSignIn(): Promise<{ isOAuthSignIn: boolean; preferPrivateSession: boolean; }>; storeOAuthSignIn( oauthSignIn: boolean, preferPrivateSession: boolean, ): Promise; loadOAuthState(): Promise; storeOAuthState(state: string): Promise; loadPKCE(): Promise; storePKCE(pkce: string): Promise; clearOAuthInflightData(): Promise; clearOAuthData(): Promise; } function isAuthenticated(tokens?: CognitoAuthTokens | null) { return tokens?.accessToken || tokens?.idToken; } function isAuthenticatedWithRefreshToken(tokens?: CognitoAuthTokens | null) { return isAuthenticated(tokens) && tokens?.refreshToken; } function isAuthenticatedWithImplicitOauthFlow( tokens?: CognitoAuthTokens | null, ) { return isAuthenticated(tokens) && !tokens?.refreshToken; }