wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
 
import {
    AuthorizationCodePayload,
    CommonAuthorizationCodeRequest,
    AuthorizationCodeClient,
    CcsCredential,
    Logger,
    ServerError,
    IPerformanceClient,
    PerformanceEvents,
    invokeAsync,
    CcsCredentialType,
    AuthorizeResponse,
    AuthorizeProtocol,
    CommonAuthorizationUrlRequest,
} from "@azure/msal-common/browser";
import { ApiId } from "../utils/BrowserConstants.js";
import * as BrowserPerformanceEvents from "../telemetry/BrowserPerformanceEvents.js";
import { BrowserCacheManager } from "../cache/BrowserCacheManager.js";
import {
    createBrowserAuthError,
    BrowserAuthErrorCodes,
} from "../error/BrowserAuthError.js";
import { AuthenticationResult } from "../response/AuthenticationResult.js";
 
/**
 * Abstract class which defines operations for a browser interaction handling class.
 */
export class InteractionHandler {
    protected authModule: AuthorizationCodeClient;
    protected browserStorage: BrowserCacheManager;
    protected authCodeRequest: CommonAuthorizationCodeRequest;
    protected logger: Logger;
    protected performanceClient: IPerformanceClient;
 
    constructor(
        authCodeModule: AuthorizationCodeClient,
        storageImpl: BrowserCacheManager,
        authCodeRequest: CommonAuthorizationCodeRequest,
        logger: Logger,
        performanceClient: IPerformanceClient
    ) {
        this.authModule = authCodeModule;
        this.browserStorage = storageImpl;
        this.authCodeRequest = authCodeRequest;
        this.logger = logger;
        this.performanceClient = performanceClient;
    }
 
    /**
     * Function to handle response parameters from hash.
     * @param locationHash
     */
    async handleCodeResponse(
        response: AuthorizeResponse,
        request: CommonAuthorizationUrlRequest,
        apiId: ApiId
    ): Promise<AuthenticationResult> {
        let authCodeResponse;
        try {
            authCodeResponse = AuthorizeProtocol.getAuthorizationCodePayload(
                response,
                request.state
            );
        } catch (e) {
            if (
                e instanceof ServerError &&
                e.subError === BrowserAuthErrorCodes.userCancelled
            ) {
                // Translate server error caused by user closing native prompt to corresponding first class MSAL error
                throw createBrowserAuthError(
                    BrowserAuthErrorCodes.userCancelled
                );
            } else {
                throw e;
            }
        }
 
        return invokeAsync(
            this.handleCodeResponseFromServer.bind(this),
            PerformanceEvents.HandleCodeResponseFromServer,
            this.logger,
            this.performanceClient,
            request.correlationId
        )(authCodeResponse, request, apiId);
    }
 
    /**
     * Process auth code response from AAD
     * @param authCodeResponse
     * @param request
     * @param validateNonce
     * @returns
     */
    async handleCodeResponseFromServer(
        authCodeResponse: AuthorizationCodePayload,
        request: CommonAuthorizationUrlRequest,
        apiId: ApiId,
        validateNonce: boolean = true
    ): Promise<AuthenticationResult> {
        this.logger.trace(
            "InteractionHandler.handleCodeResponseFromServer called",
            request.correlationId
        );
 
        // Assign code to request
        this.authCodeRequest.code = authCodeResponse.code;
 
        // Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
        if (validateNonce) {
            // TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
            authCodeResponse.nonce = request.nonce || undefined;
        }
 
        authCodeResponse.state = request.state;
 
        // Add CCS parameters if available
        if (authCodeResponse.client_info) {
            this.authCodeRequest.clientInfo = authCodeResponse.client_info;
        } else {
            const ccsCred = this.createCcsCredentials(request);
            if (ccsCred) {
                this.authCodeRequest.ccsCredential = ccsCred;
            }
        }
 
        // Acquire token with retrieved code.
        const tokenResponse = (await invokeAsync(
            this.authModule.acquireToken.bind(this.authModule),
            BrowserPerformanceEvents.AuthClientAcquireToken,
            this.logger,
            this.performanceClient,
            request.correlationId
        )(
            this.authCodeRequest,
            apiId,
            authCodeResponse
        )) as AuthenticationResult;
        return tokenResponse;
    }
 
    /**
     * Build ccs creds if available
     */
    protected createCcsCredentials(
        request: CommonAuthorizationUrlRequest
    ): CcsCredential | null {
        if (request.account) {
            return {
                credential: request.account.homeAccountId,
                type: CcsCredentialType.HOME_ACCOUNT_ID,
            };
        } else if (request.loginHint) {
            return {
                credential: request.loginHint,
                type: CcsCredentialType.UPN,
            };
        }
 
        return null;
    }
}