@azure/msal-browser
Version:
Microsoft Authentication Library for js
167 lines (149 loc) • 5.97 kB
text/typescript
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { CustomAuthInteractionClientBase } from "../CustomAuthInteractionClientBase.js";
import {
MfaRequestChallengeParams,
MfaSubmitChallengeParams,
} from "./parameter/MfaClientParameters.js";
import {
MfaVerificationRequiredResult,
MfaCompletedResult,
createMfaVerificationRequiredResult,
createMfaCompletedResult,
} from "./result/MfaActionResult.js";
import {
DefaultCustomAuthApiCodeLength,
ChallengeType,
GrantType,
} from "../../../CustomAuthConstants.js";
import * as PublicApiId from "../../telemetry/PublicApiId.js";
import {
SignInChallengeRequest,
SignInOobTokenRequest,
} from "../../network_client/custom_auth_api/types/ApiRequestTypes.js";
import { ensureArgumentIsNotEmptyString } from "../../utils/ArgumentValidator.js";
import { CustomAuthApiError } from "../../error/CustomAuthApiError.js";
import * as CustomAuthApiErrorCode from "../../network_client/custom_auth_api/types/ApiErrorCodes.js";
import { initializeServerTelemetryManager } from "../../../../interaction_client/BaseInteractionClient.js";
/**
* MFA client for handling multi-factor authentication flows.
*/
export class MfaClient extends CustomAuthInteractionClientBase {
/**
* Requests an MFA challenge to be sent to the user.
* @param parameters The parameters for requesting the challenge.
* @returns Promise that resolves to either MfaVerificationRequiredResult.
*/
async requestChallenge(
parameters: MfaRequestChallengeParams
): Promise<MfaVerificationRequiredResult> {
const apiId = PublicApiId.MFA_REQUEST_CHALLENGE;
const correlationId = parameters.correlationId || this.correlationId;
const telemetryManager = initializeServerTelemetryManager(
apiId,
this.config.auth.clientId,
correlationId,
this.browserStorage,
this.logger
);
this.logger.verbose(
"Calling challenge endpoint for MFA.",
correlationId
);
const challengeReq: SignInChallengeRequest = {
challenge_type: this.getChallengeTypes(parameters.challengeType),
continuation_token: parameters.continuationToken,
id: parameters.authMethodId,
correlationId: correlationId,
telemetryManager: telemetryManager,
};
const challengeResponse =
await this.customAuthApiClient.signInApi.requestChallenge(
challengeReq
);
this.logger.verbose(
"Challenge endpoint called for MFA.",
correlationId
);
if (challengeResponse.challenge_type === ChallengeType.OOB) {
// Verification required - code will be sent
return createMfaVerificationRequiredResult({
correlationId:
challengeResponse.correlation_id || correlationId,
continuationToken: challengeResponse.continuation_token ?? "",
challengeChannel: challengeResponse.challenge_channel ?? "",
challengeTargetLabel:
challengeResponse.challenge_target_label ?? "",
codeLength:
challengeResponse.code_length ??
DefaultCustomAuthApiCodeLength,
bindingMethod: challengeResponse.binding_method ?? "",
});
}
this.logger.error(
`Unsupported challenge type '${challengeResponse.challenge_type}' for MFA.`,
challengeResponse.correlation_id || correlationId
);
throw new CustomAuthApiError(
CustomAuthApiErrorCode.UNSUPPORTED_CHALLENGE_TYPE,
`Unsupported challenge type '${challengeResponse.challenge_type}'.`,
challengeResponse.correlation_id || correlationId
);
}
/**
* Submits the MFA challenge response (e.g., OTP code).
* @param parameters The parameters for submitting the challenge.
* @returns Promise that resolves to MfaCompletedResult.
*/
async submitChallenge(
parameters: MfaSubmitChallengeParams
): Promise<MfaCompletedResult> {
const correlationId = parameters.correlationId || this.correlationId;
ensureArgumentIsNotEmptyString(
"parameters.challenge",
parameters.challenge,
correlationId
);
const apiId = PublicApiId.MFA_SUBMIT_CHALLENGE;
const telemetryManager = initializeServerTelemetryManager(
apiId,
this.config.auth.clientId,
correlationId,
this.browserStorage,
this.logger
);
const scopes = this.getScopes(parameters.scopes);
const request: SignInOobTokenRequest = {
continuation_token: parameters.continuationToken,
oob: parameters.challenge,
grant_type: GrantType.MFA_OOB,
scope: scopes.join(" "),
correlationId: correlationId,
telemetryManager: telemetryManager,
...(parameters.claims && {
claims: parameters.claims,
}),
};
this.logger.verbose(
"Calling token endpoint for MFA challenge submission.",
correlationId
);
const tokenResponse =
await this.customAuthApiClient.signInApi.requestTokensWithOob(
request
);
// Save tokens and create authentication result
const result = await this.handleTokenResponse(
tokenResponse,
scopes,
tokenResponse.correlation_id || correlationId,
apiId
);
return createMfaCompletedResult({
correlationId: tokenResponse.correlation_id || correlationId,
authenticationResult: result,
});
}
}