Skip to content

Commit eb0997d

Browse files
authored
[Native Auth] Add MFA related states and results (#8060)
1 parent 579f775 commit eb0997d

File tree

16 files changed

+1070
-10
lines changed

16 files changed

+1070
-10
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "[Native Auth] Add MFA related state and results",
4+
"packageName": "@azure/msal-browser",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

lib/msal-browser/src/custom_auth/core/auth_flow/AuthFlowErrorBase.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ export abstract class AuthFlowErrorBase {
6666
this.errorData.subError ===
6767
CustomAuthApiSuberror.INVALID_OOB_VALUE) ||
6868
(this.errorData instanceof InvalidArgumentError &&
69-
this.errorData.errorDescription?.includes("code") === true)
69+
(this.errorData.errorDescription?.includes("code") ||
70+
this.errorData.errorDescription?.includes("challenge")) ===
71+
true)
7072
);
7173
}
7274

@@ -140,13 +142,24 @@ export abstract class AuthFlowErrorBase {
140142
);
141143
}
142144

143-
protected isInvalidAuthMethodRegistrationInputError(): boolean {
145+
protected isInvalidInputError(): boolean {
144146
return (
145147
this.errorData instanceof CustomAuthApiError &&
146148
this.errorData.error === CustomAuthApiErrorCode.INVALID_REQUEST &&
147149
this.errorData.errorCodes?.includes(901001) === true
148150
);
149151
}
152+
153+
protected isVerificationContactBlockedError(): boolean {
154+
return (
155+
this.errorData instanceof CustomAuthApiError &&
156+
this.errorData.error === CustomAuthApiErrorCode.INVALID_REQUEST &&
157+
this.errorData.errorCodes?.includes(550024) === true &&
158+
this.errorData.errorDescription?.includes(
159+
"multi-factor authentication method is blocked"
160+
) === true
161+
);
162+
}
150163
}
151164

152165
export abstract class AuthActionErrorBase extends AuthFlowErrorBase {

lib/msal-browser/src/custom_auth/core/auth_flow/jit/error_type/AuthMethodRegistrationError.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@ export class AuthMethodRegistrationChallengeMethodError extends AuthActionErrorB
1414
* @returns true if the input is incorrect, false otherwise.
1515
*/
1616
isInvalidInput(): boolean {
17-
return this.isInvalidAuthMethodRegistrationInputError();
17+
return this.isInvalidInputError();
18+
}
19+
20+
/**
21+
* Checks if the error is due to the verification contact (e.g., phone number or email) being blocked. Consider using a different email/phone number or a different authentication method.
22+
* @returns true if the error is due to the verification contact being blocked, false otherwise.
23+
*/
24+
isVerificationContactBlocked(): boolean {
25+
return this.isVerificationContactBlockedError();
1826
}
1927
}
2028

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { AuthActionErrorBase } from "../../AuthFlowErrorBase.js";
7+
8+
/**
9+
* Error that occurred during MFA challenge request.
10+
*/
11+
export class MfaRequestChallengeError extends AuthActionErrorBase {
12+
/**
13+
* Checks if the input for MFA challenge is incorrect.
14+
* @returns true if the input is incorrect, false otherwise.
15+
*/
16+
isInvalidInput(): boolean {
17+
return this.isInvalidInputError();
18+
}
19+
}
20+
21+
/**
22+
* Error that occurred during MFA challenge submission.
23+
*/
24+
export class MfaSubmitChallengeError extends AuthActionErrorBase {
25+
/**
26+
* Checks if the submitted challenge code (e.g., OTP code) is incorrect.
27+
* @returns true if the challenge code is invalid, false otherwise.
28+
*/
29+
isIncorrectChallenge(): boolean {
30+
return this.isInvalidCodeError();
31+
}
32+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { AuthFlowResultBase } from "../../AuthFlowResultBase.js";
7+
import { MfaRequestChallengeError } from "../error_type/MfaError.js";
8+
import { MfaFailedState } from "../state/MfaFailedState.js";
9+
import type { MfaVerificationRequiredState } from "../state/MfaState.js";
10+
11+
/**
12+
* Result of requesting an MFA challenge.
13+
* Uses base state type to avoid circular dependencies.
14+
*/
15+
export class MfaRequestChallengeResult extends AuthFlowResultBase<
16+
MfaRequestChallengeResultState,
17+
MfaRequestChallengeError
18+
> {
19+
/**
20+
* Creates an MfaRequestChallengeResult with an error.
21+
* @param error The error that occurred.
22+
* @returns The MfaRequestChallengeResult with error.
23+
*/
24+
static createWithError(error: unknown): MfaRequestChallengeResult {
25+
const result = new MfaRequestChallengeResult(new MfaFailedState());
26+
result.error = new MfaRequestChallengeError(
27+
MfaRequestChallengeResult.createErrorData(error)
28+
);
29+
return result;
30+
}
31+
32+
/**
33+
* Checks if the result indicates that verification is required.
34+
* @returns true if verification is required, false otherwise.
35+
* @warning This API is experimental. It may be changed in the future without notice. Do not use in production applications.
36+
*/
37+
isVerificationRequired(): boolean {
38+
return this.state.constructor?.name === "MfaVerificationRequiredState";
39+
}
40+
41+
/**
42+
* Checks if the result is in a failed state.
43+
* @returns true if the result is failed, false otherwise.
44+
* @warning This API is experimental. It may be changed in the future without notice. Do not use in production applications.
45+
*/
46+
isFailed(): boolean {
47+
return this.state instanceof MfaFailedState;
48+
}
49+
}
50+
51+
/**
52+
* The possible states for the MfaRequestChallengeResult.
53+
* This includes:
54+
* - MfaVerificationRequiredState: The user needs to verify their challenge.
55+
* - MfaFailedState: The MFA request failed.
56+
*/
57+
export type MfaRequestChallengeResultState =
58+
| MfaVerificationRequiredState
59+
| MfaFailedState;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { AuthFlowResultBase } from "../../AuthFlowResultBase.js";
7+
import { MfaSubmitChallengeError } from "../error_type/MfaError.js";
8+
import { CustomAuthAccountData } from "../../../../get_account/auth_flow/CustomAuthAccountData.js";
9+
import { MfaCompletedState } from "../state/MfaCompletedState.js";
10+
import { MfaFailedState } from "../state/MfaFailedState.js";
11+
12+
/**
13+
* Result of submitting an MFA challenge.
14+
*/
15+
export class MfaSubmitChallengeResult extends AuthFlowResultBase<
16+
MfaSubmitChallengeResultState,
17+
MfaSubmitChallengeError,
18+
CustomAuthAccountData
19+
> {
20+
/**
21+
* Creates an MfaSubmitChallengeResult with an error.
22+
* @param error The error that occurred.
23+
* @returns The MfaSubmitChallengeResult with error.
24+
*/
25+
static createWithError(error: unknown): MfaSubmitChallengeResult {
26+
const result = new MfaSubmitChallengeResult(new MfaFailedState());
27+
result.error = new MfaSubmitChallengeError(
28+
MfaSubmitChallengeResult.createErrorData(error)
29+
);
30+
return result;
31+
}
32+
33+
/**
34+
* Checks if the MFA flow is completed successfully.
35+
* @returns true if completed, false otherwise.
36+
* @warning This API is experimental. It may be changed in the future without notice. Do not use in production applications.
37+
*/
38+
isCompleted(): boolean {
39+
return this.state instanceof MfaCompletedState;
40+
}
41+
42+
/**
43+
* Checks if the result is in a failed state.
44+
* @returns true if the result is failed, false otherwise.
45+
* @warning This API is experimental. It may be changed in the future without notice. Do not use in production applications.
46+
*/
47+
isFailed(): boolean {
48+
return this.state instanceof MfaFailedState;
49+
}
50+
}
51+
52+
export type MfaSubmitChallengeResultState = MfaCompletedState | MfaFailedState;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { AuthFlowStateBase } from "../../AuthFlowState.js";
7+
8+
/**
9+
* State indicating that the MFA flow has completed successfully.
10+
*/
11+
export class MfaCompletedState extends AuthFlowStateBase {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { AuthFlowStateBase } from "../../AuthFlowState.js";
7+
8+
/**
9+
* State indicating that the MFA flow has failed.
10+
*/
11+
export class MfaFailedState extends AuthFlowStateBase {}

0 commit comments

Comments
 (0)