UNPKG

@azure/msal-common

Version:
378 lines (342 loc) 10.9 kB
/* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { extractTokenClaims } from "../../account/AuthToken.js"; import { TokenClaims } from "../../account/TokenClaims.js"; import { CloudDiscoveryMetadata } from "../../authority/CloudDiscoveryMetadata.js"; import { OpenIdConfigResponse } from "../../authority/OpenIdConfigResponse.js"; import { ClientAuthErrorCodes, createClientAuthError, } from "../../error/ClientAuthError.js"; import * as Constants from "../../utils/Constants.js"; import * as TimeUtils from "../../utils/TimeUtils.js"; import { AccessTokenEntity } from "../entities/AccessTokenEntity.js"; import { AppMetadataEntity } from "../entities/AppMetadataEntity.js"; import { AuthorityMetadataEntity } from "../entities/AuthorityMetadataEntity.js"; import { CredentialEntity } from "../entities/CredentialEntity.js"; import { IdTokenEntity } from "../entities/IdTokenEntity.js"; import { RefreshTokenEntity } from "../entities/RefreshTokenEntity.js"; /** * Create IdTokenEntity * @param homeAccountId * @param authenticationResult * @param clientId * @param authority */ export function createIdTokenEntity( homeAccountId: string, environment: string, idToken: string, clientId: string, tenantId: string ): IdTokenEntity { const idTokenEntity: IdTokenEntity = { credentialType: Constants.CredentialType.ID_TOKEN, homeAccountId: homeAccountId, environment: environment, clientId: clientId, secret: idToken, realm: tenantId, lastUpdatedAt: Date.now().toString(), // Set the last updated time to now }; return idTokenEntity; } /** * Create AccessTokenEntity * @param homeAccountId * @param environment * @param accessToken * @param clientId * @param tenantId * @param scopes * @param expiresOn * @param extExpiresOn */ export function createAccessTokenEntity( homeAccountId: string, environment: string, accessToken: string, clientId: string, tenantId: string, scopes: string, expiresOn: number, extExpiresOn: number, base64Decode: (input: string) => string, refreshOn?: number, tokenType?: Constants.AuthenticationScheme, userAssertionHash?: string, keyId?: string ): AccessTokenEntity { const atEntity: AccessTokenEntity = { homeAccountId: homeAccountId, credentialType: Constants.CredentialType.ACCESS_TOKEN, secret: accessToken, cachedAt: TimeUtils.nowSeconds().toString(), expiresOn: expiresOn.toString(), extendedExpiresOn: extExpiresOn.toString(), environment: environment, clientId: clientId, realm: tenantId, target: scopes, tokenType: tokenType || Constants.AuthenticationScheme.BEARER, lastUpdatedAt: Date.now().toString(), // Set the last updated time to now }; if (userAssertionHash) { atEntity.userAssertionHash = userAssertionHash; } if (refreshOn) { atEntity.refreshOn = refreshOn.toString(); } /* * Create Access Token With Auth Scheme instead of regular access token * Cast to lower to handle "bearer" from ADFS */ if ( atEntity.tokenType?.toLowerCase() !== Constants.AuthenticationScheme.BEARER.toLowerCase() ) { atEntity.credentialType = Constants.CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME; switch (atEntity.tokenType) { case Constants.AuthenticationScheme.POP: // Make sure keyId is present and add it to credential const tokenClaims: TokenClaims | null = extractTokenClaims( accessToken, base64Decode ); if (!tokenClaims?.cnf?.kid) { throw createClientAuthError( ClientAuthErrorCodes.tokenClaimsCnfRequiredForSignedJwt ); } atEntity.keyId = tokenClaims.cnf.kid; break; case Constants.AuthenticationScheme.SSH: atEntity.keyId = keyId; } } return atEntity; } /** * Create RefreshTokenEntity * @param homeAccountId * @param authenticationResult * @param clientId * @param authority */ export function createRefreshTokenEntity( homeAccountId: string, environment: string, refreshToken: string, clientId: string, familyId?: string, userAssertionHash?: string, expiresOn?: number ): RefreshTokenEntity { const rtEntity: RefreshTokenEntity = { credentialType: Constants.CredentialType.REFRESH_TOKEN, homeAccountId: homeAccountId, environment: environment, clientId: clientId, secret: refreshToken, lastUpdatedAt: Date.now().toString(), }; if (userAssertionHash) { rtEntity.userAssertionHash = userAssertionHash; } if (familyId) { rtEntity.familyId = familyId; } if (expiresOn) { rtEntity.expiresOn = expiresOn.toString(); } return rtEntity; } export function isCredentialEntity(entity: object): entity is CredentialEntity { return ( entity.hasOwnProperty("homeAccountId") && entity.hasOwnProperty("environment") && entity.hasOwnProperty("credentialType") && entity.hasOwnProperty("clientId") && entity.hasOwnProperty("secret") ); } /** * Validates an entity: checks for all expected params * @param entity */ export function isAccessTokenEntity( entity: object ): entity is AccessTokenEntity { if (!entity) { return false; } return ( isCredentialEntity(entity) && entity.hasOwnProperty("realm") && entity.hasOwnProperty("target") && (entity["credentialType"] === Constants.CredentialType.ACCESS_TOKEN || entity["credentialType"] === Constants.CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME) ); } /** * Validates an entity: checks for all expected params * @param entity */ export function isIdTokenEntity(entity: object): entity is IdTokenEntity { if (!entity) { return false; } return ( isCredentialEntity(entity) && entity.hasOwnProperty("realm") && entity["credentialType"] === Constants.CredentialType.ID_TOKEN ); } /** * Validates an entity: checks for all expected params * @param entity */ export function isRefreshTokenEntity( entity: object ): entity is RefreshTokenEntity { if (!entity) { return false; } return ( isCredentialEntity(entity) && entity["credentialType"] === Constants.CredentialType.REFRESH_TOKEN ); } /** * validates if a given cache entry is "Telemetry", parses <key,value> * @param key * @param entity */ export function isServerTelemetryEntity(key: string, entity?: object): boolean { const validateKey: boolean = key.indexOf(Constants.SERVER_TELEM_CACHE_KEY) === 0; let validateEntity: boolean = true; if (entity) { validateEntity = entity.hasOwnProperty("failedRequests") && entity.hasOwnProperty("errors") && entity.hasOwnProperty("cacheHits"); } return validateKey && validateEntity; } /** * validates if a given cache entry is "Throttling", parses <key,value> * @param key * @param entity */ export function isThrottlingEntity(key: string, entity?: object): boolean { let validateKey: boolean = false; if (key) { validateKey = key.indexOf(Constants.THROTTLING_PREFIX) === 0; } let validateEntity: boolean = true; if (entity) { validateEntity = entity.hasOwnProperty("throttleTime"); } return validateKey && validateEntity; } /** * Generate AppMetadata Cache Key as per the schema: appmetadata-<environment>-<client_id> */ export function generateAppMetadataKey({ environment, clientId, }: AppMetadataEntity): string { const appMetaDataKeyArray: Array<string> = [ Constants.APP_METADATA, environment, clientId, ]; return appMetaDataKeyArray .join(Constants.CACHE_KEY_SEPARATOR) .toLowerCase(); } /* * Validates an entity: checks for all expected params * @param entity */ export function isAppMetadataEntity(key: string, entity: object): boolean { if (!entity) { return false; } return ( key.indexOf(Constants.APP_METADATA) === 0 && entity.hasOwnProperty("clientId") && entity.hasOwnProperty("environment") ); } /** * Validates an entity: checks for all expected params * @param entity */ export function isAuthorityMetadataEntity( key: string, entity: object ): boolean { if (!entity) { return false; } return ( key.indexOf(Constants.AUTHORITY_METADATA_CACHE_KEY) === 0 && entity.hasOwnProperty("aliases") && entity.hasOwnProperty("preferred_cache") && entity.hasOwnProperty("preferred_network") && entity.hasOwnProperty("canonical_authority") && entity.hasOwnProperty("authorization_endpoint") && entity.hasOwnProperty("token_endpoint") && entity.hasOwnProperty("issuer") && entity.hasOwnProperty("aliasesFromNetwork") && entity.hasOwnProperty("endpointsFromNetwork") && entity.hasOwnProperty("expiresAt") && entity.hasOwnProperty("jwks_uri") ); } /** * Reset the exiresAt value */ export function generateAuthorityMetadataExpiresAt(): number { return ( TimeUtils.nowSeconds() + Constants.AUTHORITY_METADATA_REFRESH_TIME_SECONDS ); } export function updateAuthorityEndpointMetadata( authorityMetadata: AuthorityMetadataEntity, updatedValues: OpenIdConfigResponse, fromNetwork: boolean ): void { authorityMetadata.authorization_endpoint = updatedValues.authorization_endpoint; authorityMetadata.token_endpoint = updatedValues.token_endpoint; authorityMetadata.end_session_endpoint = updatedValues.end_session_endpoint; authorityMetadata.issuer = updatedValues.issuer; authorityMetadata.endpointsFromNetwork = fromNetwork; authorityMetadata.jwks_uri = updatedValues.jwks_uri; } export function updateCloudDiscoveryMetadata( authorityMetadata: AuthorityMetadataEntity, updatedValues: CloudDiscoveryMetadata, fromNetwork: boolean ): void { authorityMetadata.aliases = updatedValues.aliases; authorityMetadata.preferred_cache = updatedValues.preferred_cache; authorityMetadata.preferred_network = updatedValues.preferred_network; authorityMetadata.aliasesFromNetwork = fromNetwork; } /** * Returns whether or not the data needs to be refreshed */ export function isAuthorityMetadataExpired( metadata: AuthorityMetadataEntity ): boolean { return metadata.expiresAt <= TimeUtils.nowSeconds(); }