Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export type AccountInfo = {
homeAccountId?: string;
environment: string;
tenantId?: string;
username: string;
localAccountId?: string;
name?: string;
idToken?: string; // idTokenClaims can be parsed from idToken in MSAL.js
platformBrokerId?: string; // Used by WAM previous called nativeAccountId
idTokenClaims?: object;
};
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export type AuthBridgeResponse = string | { data: string };
export interface AuthBridge {
addEventListener: (
eventName: string,
callback: (response: AuthBridgeResponse) => void
) => void;
postMessage: (message: string) => void;
removeEventListener: (
eventName: string,
callback: (response: AuthBridgeResponse) => void
) => void;
}
+12
View File
@@ -0,0 +1,12 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { AccountInfo } from "./AccountInfo.js";
import { TokenResponse } from "./TokenResponse.js";
export type AuthResult = {
token: TokenResponse;
account: AccountInfo;
};
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* AccountContext is used to pass account information when the bridge is initialized
*
* NAA (MetaOS) apps are created and destroyed for the same session multiple times.
* `AccountContext` helps in booting up the cached account when the bridge
* is recreated for a new NAA instance in the same auth session.
*/
export interface AccountContext {
homeAccountId: string;
environment: string;
tenantId: string;
}
+9
View File
@@ -0,0 +1,9 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
// Capabilities are intended to future proof the bridge against any feature support
export interface BridgeCapabilities {
queryAccount?: boolean;
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { BridgeStatusCode } from "./BridgeStatusCode.js";
export type BridgeError = {
status: BridgeStatusCode;
code?: string; // auth_flow_last_error such as invalid_grant
subError?: string; // server_suberror_code such as consent_required
description?: string;
properties?: object; // additional telemetry info
};
export function isBridgeError(error: unknown): error is BridgeError {
return (error as BridgeError).status !== undefined;
}
+233
View File
@@ -0,0 +1,233 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { AuthBridge, AuthBridgeResponse } from "./AuthBridge.js";
import { AuthResult } from "./AuthResult.js";
import { BridgeCapabilities } from "./BridgeCapabilities.js";
import { AccountContext } from "./BridgeAccountContext.js";
import { BridgeError } from "./BridgeError.js";
import { BridgeRequest } from "./BridgeRequest.js";
import {
BridgeRequestEnvelope,
BridgeMethods,
} from "./BridgeRequestEnvelope.js";
import { BridgeResponseEnvelope } from "./BridgeResponseEnvelope.js";
import { BridgeStatusCode } from "./BridgeStatusCode.js";
import { IBridgeProxy } from "./IBridgeProxy.js";
import { InitContext } from "./InitContext.js";
import { TokenRequest } from "./TokenRequest.js";
import * as BrowserCrypto from "../crypto/BrowserCrypto.js";
import { BrowserConstants } from "../utils/BrowserConstants.js";
import { version } from "../packageMetadata.js";
declare global {
interface Window {
nestedAppAuthBridge: AuthBridge;
}
}
/**
* BridgeProxy
* Provides a proxy for accessing a bridge to a host app and/or
* platform broker
*/
export class BridgeProxy implements IBridgeProxy {
static bridgeRequests: BridgeRequest[] = [];
sdkName: string;
sdkVersion: string;
capabilities?: BridgeCapabilities;
accountContext?: AccountContext;
/**
* initializeNestedAppAuthBridge - Initializes the bridge to the host app
* @returns a promise that resolves to an InitializeBridgeResponse or rejects with an Error
* @remarks This method will be called by the create factory method
* @remarks If the bridge is not available, this method will throw an error
*/
protected static async initializeNestedAppAuthBridge(): Promise<InitContext> {
if (window === undefined) {
throw new Error("window is undefined");
}
if (window.nestedAppAuthBridge === undefined) {
throw new Error("window.nestedAppAuthBridge is undefined");
}
try {
window.nestedAppAuthBridge.addEventListener(
"message",
(response: AuthBridgeResponse) => {
const responsePayload =
typeof response === "string" ? response : response.data;
const responseEnvelope: BridgeResponseEnvelope =
JSON.parse(responsePayload);
const request = BridgeProxy.bridgeRequests.find(
(element) =>
element.requestId === responseEnvelope.requestId
);
if (request !== undefined) {
BridgeProxy.bridgeRequests.splice(
BridgeProxy.bridgeRequests.indexOf(request),
1
);
if (responseEnvelope.success) {
request.resolve(responseEnvelope);
} else {
request.reject(responseEnvelope.error);
}
}
}
);
const bridgeResponse = await new Promise<BridgeResponseEnvelope>(
(resolve, reject) => {
const message = BridgeProxy.buildRequest("GetInitContext");
const request: BridgeRequest = {
requestId: message.requestId,
method: message.method,
resolve: resolve,
reject: reject,
};
BridgeProxy.bridgeRequests.push(request);
window.nestedAppAuthBridge.postMessage(
JSON.stringify(message)
);
}
);
return BridgeProxy.validateBridgeResultOrThrow(
bridgeResponse.initContext
);
} catch (error) {
window.console.log(error);
throw error;
}
}
/**
* getTokenInteractive - Attempts to get a token interactively from the bridge
* @param request A token request
* @returns a promise that resolves to an auth result or rejects with a BridgeError
*/
public getTokenInteractive(request: TokenRequest): Promise<AuthResult> {
return this.getToken("GetTokenPopup", request);
}
/**
* getTokenSilent Attempts to get a token silently from the bridge
* @param request A token request
* @returns a promise that resolves to an auth result or rejects with a BridgeError
*/
public getTokenSilent(request: TokenRequest): Promise<AuthResult> {
return this.getToken("GetToken", request);
}
private async getToken(
requestType: BridgeMethods,
request: TokenRequest
): Promise<AuthResult> {
const result = await this.sendRequest(requestType, {
tokenParams: request,
});
return {
token: BridgeProxy.validateBridgeResultOrThrow(result.token),
account: BridgeProxy.validateBridgeResultOrThrow(result.account),
};
}
public getHostCapabilities(): BridgeCapabilities | null {
return this.capabilities ?? null;
}
public getAccountContext(): AccountContext | null {
return this.accountContext ? this.accountContext : null;
}
private static buildRequest(
method: BridgeMethods,
requestParams?: Partial<BridgeRequestEnvelope>
): BridgeRequestEnvelope {
return {
messageType: "NestedAppAuthRequest",
method: method,
requestId: BrowserCrypto.createNewGuid(),
sendTime: Date.now(),
clientLibrary: BrowserConstants.MSAL_SKU,
clientLibraryVersion: version,
...requestParams,
};
}
/**
* A method used to send a request to the bridge
* @param request A token request
* @returns a promise that resolves to a response of provided type or rejects with a BridgeError
*/
private sendRequest(
method: BridgeMethods,
requestParams?: Partial<BridgeRequestEnvelope>
): Promise<BridgeResponseEnvelope> {
const message = BridgeProxy.buildRequest(method, requestParams);
const promise = new Promise<BridgeResponseEnvelope>(
(resolve, reject) => {
const request: BridgeRequest = {
requestId: message.requestId,
method: message.method,
resolve: resolve,
reject: reject,
};
BridgeProxy.bridgeRequests.push(request);
window.nestedAppAuthBridge.postMessage(JSON.stringify(message));
}
);
return promise;
}
private static validateBridgeResultOrThrow<T>(input: T | undefined): T {
if (input === undefined) {
const bridgeError: BridgeError = {
status: BridgeStatusCode.NestedAppAuthUnavailable,
};
throw bridgeError;
}
return input;
}
/**
* Private constructor for BridgeProxy
* @param sdkName The name of the SDK being used to make requests on behalf of the app
* @param sdkVersion The version of the SDK being used to make requests on behalf of the app
* @param capabilities The capabilities of the bridge / SDK / platform broker
*/
private constructor(
sdkName: string,
sdkVersion: string,
accountContext?: AccountContext,
capabilities?: BridgeCapabilities
) {
this.sdkName = sdkName;
this.sdkVersion = sdkVersion;
this.accountContext = accountContext;
this.capabilities = capabilities;
}
/**
* Factory method for creating an implementation of IBridgeProxy
* @returns A promise that resolves to a BridgeProxy implementation
*/
public static async create(): Promise<IBridgeProxy> {
const response = await BridgeProxy.initializeNestedAppAuthBridge();
return new BridgeProxy(
response.sdkName,
response.sdkVersion,
response.accountContext,
response.capabilities
);
}
}
export default BridgeProxy;
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { BridgeResponseEnvelope } from "./BridgeResponseEnvelope.js";
export type BridgeRequest = {
requestId: string;
method: string;
resolve: (
value: BridgeResponseEnvelope | PromiseLike<BridgeResponseEnvelope>
) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reject: (reason?: any) => void;
};
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { TokenRequest } from "./TokenRequest.js";
export type BridgeMethods = "GetToken" | "GetInitContext" | "GetTokenPopup";
export type BridgeRequestEnvelope = {
messageType: "NestedAppAuthRequest";
method: BridgeMethods;
sendTime?: number; // Assume this is epoch
clientLibrary?: string;
clientLibraryVersion?: string;
requestId: string;
tokenParams?: TokenRequest;
};
export function isBridgeRequestEnvelope(
obj: unknown
): obj is BridgeRequestEnvelope {
return (
(obj as BridgeRequestEnvelope).messageType !== undefined &&
(obj as BridgeRequestEnvelope).messageType === "NestedAppAuthRequest" &&
(obj as BridgeRequestEnvelope).method !== undefined &&
(obj as BridgeRequestEnvelope).requestId !== undefined
);
}
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { BridgeError } from "./BridgeError.js";
import { TokenResponse } from "./TokenResponse.js";
import { AccountInfo } from "./AccountInfo.js";
import { InitContext } from "./InitContext.js";
export type BridgeResponseEnvelope = {
messageType: "NestedAppAuthResponse";
requestId: string;
success: boolean; // false if body is error
token?: TokenResponse;
error?: BridgeError;
account?: AccountInfo;
initContext?: InitContext;
};
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export const BridgeStatusCode = {
UserInteractionRequired: "USER_INTERACTION_REQUIRED",
UserCancel: "USER_CANCEL",
NoNetwork: "NO_NETWORK",
TransientError: "TRANSIENT_ERROR",
PersistentError: "PERSISTENT_ERROR",
Disabled: "DISABLED",
AccountUnavailable: "ACCOUNT_UNAVAILABLE",
NestedAppAuthUnavailable: "NESTED_APP_AUTH_UNAVAILABLE", // NAA is unavailable in the current context, can retry with standard browser based auth
} as const;
export type BridgeStatusCode =
(typeof BridgeStatusCode)[keyof typeof BridgeStatusCode];
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { AuthResult } from "./AuthResult.js";
import { AccountContext } from "./BridgeAccountContext.js";
import { BridgeCapabilities } from "./BridgeCapabilities.js";
import { TokenRequest } from "./TokenRequest.js";
export interface IBridgeProxy {
getTokenInteractive(request: TokenRequest): Promise<AuthResult>;
getTokenSilent(request: TokenRequest): Promise<AuthResult>;
getHostCapabilities(): BridgeCapabilities | null;
getAccountContext(): AccountContext | null;
}
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { BridgeCapabilities } from "./BridgeCapabilities.js";
import { AccountContext } from "./BridgeAccountContext.js";
export interface InitContext {
capabilities?: BridgeCapabilities;
sdkName: string;
sdkVersion: string;
accountContext?: AccountContext;
}
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export type TokenRequest = {
platformBrokerId?: string; // Account identifier used by OneAuth
clientId: string;
authority?: string;
scope: string;
correlationId: string;
claims?: string;
state?: string;
reqCnf?: string; // Having OneAuth own the keypair is better for hardware token binding support
keyId?: string; // Having OneAuth own the keypair is better for hardware token binding support
authenticationScheme?: string;
shrClaims?: string;
shrNonce?: string;
resourceRequestMethod?: string;
resourceRequestUri?: string;
extendedExpiryToken?: boolean;
extraParameters?: Map<string, string>;
};
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
export type TokenResponse = {
access_token: string;
expires_in: number;
id_token: string;
properties: TokenResponseProperties | null;
scope?: string;
shr?: string; // token binding enabled at native layer it is the access token, not the signing keys
extendedLifetimeToken?: boolean;
authority?: string;
};
export type TokenResponseProperties = {
MATS?: string;
};
/*
* Sample response below
* {
* "body":{
* "access_token":"<token>",
* "account":{"environment":"login.microsoftonline.com",
* "homeAccountId":"2995ae49-d9dd-409d-8d62-ba969ce58a81.51178b70-16cc-41b5-bef1-ae1808139065",
* "idTokenClaims":{
* "aud":"a076930c-cfc9-4ebd-9607-7963bccbf666",
* "exp":"1680557128",
* "graph_url":"https://graph.microsoft.com",
* "iat":"1680553228",
* "iss":"https://login.microsoftonline.com/51178b70-16cc-41b5-bef1-ae1808139065/v2.0",
* "name":"Adele Vance",
* "nbf":"1680553228",
* "oid":"2995ae49-d9dd-409d-8d62-ba969ce58a81",
* "preferred_username":"AdeleV@vc6w6.onmicrosoft.com",
* "rh":"0.AX0AcIsXUcwWtUG-8a4YCBOQZQyTdqDJz71Olgd5Y7zL9maaAHs.",
* "sovereignty2":"Global",
* "sub":"wtxUI1WD2C--Bl8vN1p-P-VgadGud8QSqXD4Vp5i9sc",
* "tid":"51178b70-16cc-41b5-bef1-ae1808139065",
* "uti":"39pEKQyYDU6SXjD_phaCAA",
* "ver":"2.0"},
* "localAccountId":"2995ae49-d9dd-409d-8d62-ba969ce58a81",
* "name":"Adele Vance",
* "tenantId":"51178b70-16cc-41b5-bef1-ae1808139065",
* "username":"AdeleV@vc6w6.onmicrosoft.com"
* },
* "client_info":"",
* "expires_in":4290,
* "id_token":"",
* "properties":null,
* "scope":"User.Read",
* "state":""},
* "requestId":"8863d6e8-a539-43e3-84b6-ca1bf64943f3",
* "success":true}
*/
@@ -0,0 +1,319 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { TokenRequest } from "../TokenRequest.js";
import { AccountInfo as NaaAccountInfo } from "../AccountInfo.js";
import { RedirectRequest } from "../../request/RedirectRequest.js";
import { PopupRequest } from "../../request/PopupRequest.js";
import {
AccountInfo as MsalAccountInfo,
AuthError,
ClientAuthError,
ClientConfigurationError,
InteractionRequiredAuthError,
ServerError,
ICrypto,
Logger,
AuthToken,
TokenClaims,
ClientAuthErrorCodes,
AuthenticationScheme,
RequestParameterBuilder,
StringUtils,
createClientAuthError,
OIDC_DEFAULT_SCOPES,
AccountInfo,
IdTokenEntity,
AccessTokenEntity,
TenantProfile,
buildTenantProfile,
} from "@azure/msal-common/browser";
import { isBridgeError } from "../BridgeError.js";
import { BridgeStatusCode } from "../BridgeStatusCode.js";
import { AuthenticationResult } from "../../response/AuthenticationResult.js";
import {} from "../../error/BrowserAuthErrorCodes.js";
import { AuthResult } from "../AuthResult.js";
import { SsoSilentRequest } from "../../request/SsoSilentRequest.js";
import { SilentRequest } from "../../request/SilentRequest.js";
export class NestedAppAuthAdapter {
protected crypto: ICrypto;
protected logger: Logger;
protected clientId: string;
protected clientCapabilities: string[];
constructor(
clientId: string,
clientCapabilities: string[],
crypto: ICrypto,
logger: Logger
) {
this.clientId = clientId;
this.clientCapabilities = clientCapabilities;
this.crypto = crypto;
this.logger = logger;
}
public toNaaTokenRequest(
request:
| PopupRequest
| RedirectRequest
| SilentRequest
| SsoSilentRequest
): TokenRequest {
let extraParams: Map<string, string>;
if (request.extraQueryParameters === undefined) {
extraParams = new Map<string, string>();
} else {
extraParams = new Map<string, string>(
Object.entries(request.extraQueryParameters)
);
}
const correlationId =
request.correlationId || this.crypto.createNewGuid();
const requestBuilder = new RequestParameterBuilder(correlationId);
const claims = requestBuilder.addClientCapabilitiesToClaims(
request.claims,
this.clientCapabilities
);
const scopes = request.scopes || OIDC_DEFAULT_SCOPES;
const tokenRequest: TokenRequest = {
platformBrokerId: request.account?.homeAccountId,
clientId: this.clientId,
authority: request.authority,
scope: scopes.join(" "),
correlationId,
claims: !StringUtils.isEmptyObj(claims) ? claims : undefined,
state: request.state,
authenticationScheme:
request.authenticationScheme || AuthenticationScheme.BEARER,
extraParameters: extraParams,
};
return tokenRequest;
}
public fromNaaTokenResponse(
request: TokenRequest,
response: AuthResult,
reqTimestamp: number
): AuthenticationResult {
if (!response.token.id_token || !response.token.access_token) {
throw createClientAuthError(ClientAuthErrorCodes.nullOrEmptyToken);
}
const expiresOn = new Date(
(reqTimestamp + (response.token.expires_in || 0)) * 1000
);
const idTokenClaims = AuthToken.extractTokenClaims(
response.token.id_token,
this.crypto.base64Decode
);
const account = this.fromNaaAccountInfo(
response.account,
response.token.id_token,
idTokenClaims
);
const scopes = response.token.scope || request.scope;
const authenticationResult: AuthenticationResult = {
authority: response.token.authority || account.environment,
uniqueId: account.localAccountId,
tenantId: account.tenantId,
scopes: scopes.split(" "),
account,
idToken: response.token.id_token,
idTokenClaims,
accessToken: response.token.access_token,
fromCache: false,
expiresOn: expiresOn,
tokenType:
request.authenticationScheme || AuthenticationScheme.BEARER,
correlationId: request.correlationId,
extExpiresOn: expiresOn,
state: request.state,
};
return authenticationResult;
}
/*
* export type AccountInfo = {
* homeAccountId: string;
* environment: string;
* tenantId: string;
* username: string;
* localAccountId: string;
* name?: string;
* idToken?: string;
* idTokenClaims?: TokenClaims & {
* [key: string]:
* | string
* | number
* | string[]
* | object
* | undefined
* | unknown;
* };
* nativeAccountId?: string;
* authorityType?: string;
* };
*/
public fromNaaAccountInfo(
fromAccount: NaaAccountInfo,
idToken?: string,
idTokenClaims?: TokenClaims
): MsalAccountInfo {
const effectiveIdTokenClaims =
idTokenClaims || (fromAccount.idTokenClaims as TokenClaims);
const localAccountId =
fromAccount.localAccountId ||
effectiveIdTokenClaims?.oid ||
effectiveIdTokenClaims?.sub ||
"";
const tenantId =
fromAccount.tenantId || effectiveIdTokenClaims?.tid || "";
const homeAccountId =
fromAccount.homeAccountId || `${localAccountId}.${tenantId}`;
const username =
fromAccount.username ||
effectiveIdTokenClaims?.preferred_username ||
"";
const name = fromAccount.name || effectiveIdTokenClaims?.name;
const tenantProfiles = new Map<string, TenantProfile>();
const tenantProfile = buildTenantProfile(
homeAccountId,
localAccountId,
tenantId,
effectiveIdTokenClaims
);
tenantProfiles.set(tenantId, tenantProfile);
const account: MsalAccountInfo = {
homeAccountId,
environment: fromAccount.environment,
tenantId,
username,
localAccountId,
name,
idToken: idToken,
idTokenClaims: effectiveIdTokenClaims,
tenantProfiles,
};
return account;
}
/**
*
* @param error BridgeError
* @returns AuthError, ClientAuthError, ClientConfigurationError, ServerError, InteractionRequiredError
*/
public fromBridgeError(
error: unknown
):
| AuthError
| ClientAuthError
| ClientConfigurationError
| ServerError
| InteractionRequiredAuthError {
if (isBridgeError(error)) {
switch (error.status) {
case BridgeStatusCode.UserCancel:
return new ClientAuthError(
ClientAuthErrorCodes.userCanceled
);
case BridgeStatusCode.NoNetwork:
return new ClientAuthError(
ClientAuthErrorCodes.noNetworkConnectivity
);
case BridgeStatusCode.AccountUnavailable:
return new ClientAuthError(
ClientAuthErrorCodes.noAccountFound
);
case BridgeStatusCode.Disabled:
return new ClientAuthError(
ClientAuthErrorCodes.nestedAppAuthBridgeDisabled
);
case BridgeStatusCode.NestedAppAuthUnavailable:
return new ClientAuthError(
error.code ||
ClientAuthErrorCodes.nestedAppAuthBridgeDisabled,
error.description
);
case BridgeStatusCode.TransientError:
case BridgeStatusCode.PersistentError:
return new ServerError(error.code, error.description);
case BridgeStatusCode.UserInteractionRequired:
return new InteractionRequiredAuthError(
error.code,
error.description
);
default:
return new AuthError(error.code, error.description);
}
} else {
return new AuthError("unknown_error", "An unknown error occurred");
}
}
/**
* Returns an AuthenticationResult from the given cache items
*
* @param account
* @param idToken
* @param accessToken
* @param reqTimestamp
* @returns
*/
public toAuthenticationResultFromCache(
account: AccountInfo,
idToken: IdTokenEntity,
accessToken: AccessTokenEntity,
request: SilentRequest,
correlationId: string
): AuthenticationResult {
if (!idToken || !accessToken) {
throw createClientAuthError(ClientAuthErrorCodes.nullOrEmptyToken);
}
const idTokenClaims = AuthToken.extractTokenClaims(
idToken.secret,
this.crypto.base64Decode
);
const scopes = accessToken.target || request.scopes.join(" ");
const authenticationResult: AuthenticationResult = {
authority: accessToken.environment || account.environment,
uniqueId: account.localAccountId,
tenantId: account.tenantId,
scopes: scopes.split(" "),
account,
idToken: idToken.secret,
idTokenClaims: idTokenClaims || {},
accessToken: accessToken.secret,
fromCache: true,
expiresOn: new Date(Number(accessToken.expiresOn) * 1000),
tokenType:
request.authenticationScheme || AuthenticationScheme.BEARER,
correlationId,
extExpiresOn: new Date(
Number(accessToken.extendedExpiresOn) * 1000
),
state: request.state,
};
return authenticationResult;
}
}