/*! @azure/msal-common v14.4.0 2023-11-07 */ 'use strict'; import { Separators, CacheAccountType, Constants } from '../../utils/Constants.mjs'; import { buildClientInfo } from '../../account/ClientInfo.mjs'; import { createClientAuthError } from '../../error/ClientAuthError.mjs'; import { AuthorityType } from '../../authority/AuthorityType.mjs'; import { ProtocolMode } from '../../authority/ProtocolMode.mjs'; import { invalidCacheEnvironment } from '../../error/ClientAuthErrorCodes.mjs'; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Type that defines required and optional parameters for an Account field (based on universal cache schema implemented by all MSALs). * * Key : Value Schema * * Key: -- * * Value Schema: * { * homeAccountId: home account identifier for the auth scheme, * environment: entity that issued the token, represented as a full host * realm: Full tenant or organizational identifier that the account belongs to * localAccountId: Original tenant-specific accountID, usually used for legacy cases * username: primary username that represents the user, usually corresponds to preferred_username in the v2 endpt * authorityType: Accounts authority type as a string * name: Full name for the account, including given name and family name, * lastModificationTime: last time this entity was modified in the cache * lastModificationApp: * idTokenClaims: Object containing claims parsed from ID token * nativeAccountId: Account identifier on the native device * } * @internal */ class AccountEntity { /** * Generate Account Id key component as per the schema: - */ generateAccountId() { const accountId = [this.homeAccountId, this.environment]; return accountId.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); } /** * Generate Account Cache Key as per the schema: -- */ generateAccountKey() { return AccountEntity.generateAccountCacheKey({ homeAccountId: this.homeAccountId, environment: this.environment, tenantId: this.realm, username: this.username, localAccountId: this.localAccountId, }); } /** * Returns the AccountInfo interface for this account. */ getAccountInfo() { return { homeAccountId: this.homeAccountId, environment: this.environment, tenantId: this.realm, username: this.username, localAccountId: this.localAccountId, name: this.name, idTokenClaims: this.idTokenClaims, nativeAccountId: this.nativeAccountId, authorityType: this.authorityType, }; } /** * Generates account key from interface * @param accountInterface */ static generateAccountCacheKey(accountInterface) { const accountKey = [ accountInterface.homeAccountId, accountInterface.environment || Constants.EMPTY_STRING, accountInterface.tenantId || Constants.EMPTY_STRING, ]; return accountKey.join(Separators.CACHE_KEY_SEPARATOR).toLowerCase(); } /** * Build Account cache from IdToken, clientInfo and authority/policy. Associated with AAD. * @param accountDetails */ static createAccount(accountDetails, authority) { const account = new AccountEntity(); if (authority.authorityType === AuthorityType.Adfs) { account.authorityType = CacheAccountType.ADFS_ACCOUNT_TYPE; } else if (authority.protocolMode === ProtocolMode.AAD) { account.authorityType = CacheAccountType.MSSTS_ACCOUNT_TYPE; } else { account.authorityType = CacheAccountType.GENERIC_ACCOUNT_TYPE; } account.clientInfo = accountDetails.clientInfo; account.homeAccountId = accountDetails.homeAccountId; account.nativeAccountId = accountDetails.nativeAccountId; const env = accountDetails.environment || (authority && authority.getPreferredCache()); if (!env) { throw createClientAuthError(invalidCacheEnvironment); } account.environment = env; // non AAD scenarios can have empty realm account.realm = accountDetails.idTokenClaims.tid || Constants.EMPTY_STRING; // How do you account for MSA CID here? account.localAccountId = accountDetails.idTokenClaims.oid || accountDetails.idTokenClaims.sub || Constants.EMPTY_STRING; /* * In B2C scenarios the emails claim is used instead of preferred_username and it is an array. * In most cases it will contain a single email. This field should not be relied upon if a custom * policy is configured to return more than 1 email. */ const preferredUsername = accountDetails.idTokenClaims.preferred_username || accountDetails.idTokenClaims.upn; const email = accountDetails.idTokenClaims.emails ? accountDetails.idTokenClaims.emails[0] : null; account.username = preferredUsername || email || Constants.EMPTY_STRING; account.name = accountDetails.idTokenClaims.name; account.cloudGraphHostName = accountDetails.cloudGraphHostName; account.msGraphHost = accountDetails.msGraphHost; return account; } /** * Creates an AccountEntity object from AccountInfo * @param accountInfo * @param cloudGraphHostName * @param msGraphHost * @returns */ static createFromAccountInfo(accountInfo, cloudGraphHostName, msGraphHost) { const account = new AccountEntity(); account.authorityType = accountInfo.authorityType || CacheAccountType.GENERIC_ACCOUNT_TYPE; account.homeAccountId = accountInfo.homeAccountId; account.localAccountId = accountInfo.localAccountId; account.nativeAccountId = accountInfo.nativeAccountId; account.realm = accountInfo.tenantId; account.environment = accountInfo.environment; account.username = accountInfo.username; account.name = accountInfo.name; account.cloudGraphHostName = cloudGraphHostName; account.msGraphHost = msGraphHost; return account; } /** * Generate HomeAccountId from server response * @param serverClientInfo * @param authType */ static generateHomeAccountId(serverClientInfo, authType, logger, cryptoObj, idTokenClaims) { const accountId = idTokenClaims?.sub ? idTokenClaims.sub : Constants.EMPTY_STRING; // since ADFS does not have tid and does not set client_info if (authType === AuthorityType.Adfs || authType === AuthorityType.Dsts) { return accountId; } // for cases where there is clientInfo if (serverClientInfo) { try { const clientInfo = buildClientInfo(serverClientInfo, cryptoObj); if (clientInfo.uid && clientInfo.utid) { return `${clientInfo.uid}${Separators.CLIENT_INFO_SEPARATOR}${clientInfo.utid}`; } } catch (e) { } } // default to "sub" claim logger.verbose("No client info in response"); return accountId; } /** * Validates an entity: checks for all expected params * @param entity */ static isAccountEntity(entity) { if (!entity) { return false; } return (entity.hasOwnProperty("homeAccountId") && entity.hasOwnProperty("environment") && entity.hasOwnProperty("realm") && entity.hasOwnProperty("localAccountId") && entity.hasOwnProperty("username") && entity.hasOwnProperty("authorityType")); } /** * Helper function to determine whether 2 accountInfo objects represent the same account * @param accountA * @param accountB * @param compareClaims - If set to true idTokenClaims will also be compared to determine account equality */ static accountInfoIsEqual(accountA, accountB, compareClaims) { if (!accountA || !accountB) { return false; } let claimsMatch = true; // default to true so as to not fail comparison below if compareClaims: false if (compareClaims) { const accountAClaims = (accountA.idTokenClaims || {}); const accountBClaims = (accountB.idTokenClaims || {}); // issued at timestamp and nonce are expected to change each time a new id token is acquired claimsMatch = accountAClaims.iat === accountBClaims.iat && accountAClaims.nonce === accountBClaims.nonce; } return (accountA.homeAccountId === accountB.homeAccountId && accountA.localAccountId === accountB.localAccountId && accountA.username === accountB.username && accountA.tenantId === accountB.tenantId && accountA.environment === accountB.environment && accountA.nativeAccountId === accountB.nativeAccountId && claimsMatch); } } export { AccountEntity }; //# sourceMappingURL=AccountEntity.mjs.map