Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "error surfacing changes",
"packageName": "@azure/msal-common",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "error surfacing changes",
"packageName": "@azure/msal-node-extensions",
"email": "[email protected]",
"dependentChangeType": "patch"
}
39 changes: 27 additions & 12 deletions extensions/msal-node-extensions/src/broker/NativeBrokerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,46 +660,61 @@ export class NativeBrokerPlugin implements INativeBrokerPlugin {
const enhancedErrorContext = errorContext
? `${errorContext} (Error Code: ${errorCode}, Tag: ${tagString})`
: `(Error Code: ${errorCode}, Tag: ${tagString})`;

const nativeAuthError = new NativeAuthError(
ErrorStatus[errorStatus],
enhancedErrorContext,
errorCode,
errorTag
);

let wrappedError;

switch (errorStatus) {
case ErrorStatus.InteractionRequired:
case ErrorStatus.AccountUnusable:
return new InteractionRequiredAuthError(
wrappedError = new InteractionRequiredAuthError(
ErrorCodes.INTERATION_REQUIRED_ERROR_CODE,
enhancedErrorContext
);
break;
case ErrorStatus.NoNetwork:
case ErrorStatus.NetworkTemporarilyUnavailable:
return createClientAuthError(
wrappedError = createClientAuthError(
ClientAuthErrorCodes.noNetworkConnectivity
);
break;
case ErrorStatus.ServerTemporarilyUnavailable:
return new ServerError(
wrappedError = new ServerError(
ErrorCodes.SERVER_UNAVAILABLE,
errorContext
);
break;
case ErrorStatus.UserCanceled:
return createClientAuthError(
wrappedError = createClientAuthError(
ClientAuthErrorCodes.userCanceled
);
break;
case ErrorStatus.AuthorityUntrusted:
return createClientConfigurationError(
wrappedError = createClientConfigurationError(
ClientConfigurationErrorCodes.untrustedAuthority
);
break;
case ErrorStatus.UserSwitched:
// Not an error case, if there's customer demand we can surface this as a response property
return null;
case ErrorStatus.AccountNotFound:
return createClientAuthError(
wrappedError = createClientAuthError(
ClientAuthErrorCodes.noAccountFound
);
break;
default:
return new NativeAuthError(
ErrorStatus[errorStatus],
enhancedErrorContext,
errorCode,
errorTag
);
wrappedError = nativeAuthError;
break;
}

wrappedError.msalNodeRuntimeError = nativeAuthError;
return wrappedError;
}
throw error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,69 @@ if (process.platform === "win32") {
done();
});
});

it("Attaches msalNodeRuntimeError with runtime details to wrapped MSAL.js errors", (done) => {
const testCorrelationId = generateCorrelationId();

jest.spyOn(
msalNodeRuntime,
"SignInSilentlyAsync"
).mockImplementation(
(
authParams: AuthParameters,
correlationId: string,
callback: (result: AuthResult) => void
) => {
const result: AuthResult = {
idToken: "",
accessToken: "",
authorizationHeader: "",
rawIdToken: "",
grantedScopes: "",
expiresOn: 0,
isPopAuthorization: false,
account: testMsalRuntimeAccount,
CheckError: () => {
const testError: MsalRuntimeError = {
errorCode: 0,
errorStatus:
ErrorStatus.InteractionRequired,
errorContext: "",
errorTag: 0,
};
throw testError;
},
telemetryData: "",
};
expect(correlationId).toEqual(testCorrelationId);
callback(result);

return asyncHandle;
}
);

const nativeBrokerPlugin = new NativeBrokerPlugin();
const request: NativeRequest = {
clientId: TEST_CLIENT_ID,
scopes: [],
correlationId: testCorrelationId,
authority: "",
redirectUri: TEST_REDIRECTURI,
};

nativeBrokerPlugin
.acquireTokenSilent(request)
.catch((error) => {
expect(error).toBeInstanceOf(
InteractionRequiredAuthError
);
expect(error.msalNodeRuntimeError).toBeDefined();
expect(error.msalNodeRuntimeError).toBeInstanceOf(
NativeAuthError
);
done();
});
});
});
});
} else if (process.platform === "darwin") {
Expand Down
6 changes: 6 additions & 0 deletions lib/msal-common/src/error/AuthError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { Constants } from "../utils/Constants.js";
import * as AuthErrorCodes from "./AuthErrorCodes.js";
import type { NativeAuthError } from "./NativeAuthError.js";
export { AuthErrorCodes };

export const AuthErrorMessages = {
Expand Down Expand Up @@ -52,6 +53,11 @@ export class AuthError extends Error {
*/
correlationId: string;

/**
* Default NativeAuthError from MsalNodeRuntime when broker is enabled
*/
msalNodeRuntimeError?: NativeAuthError;

constructor(errorCode?: string, errorMessage?: string, suberror?: string) {
const errorString = errorMessage
? `${errorCode}: ${errorMessage}`
Expand Down
34 changes: 34 additions & 0 deletions lib/msal-common/src/error/NativeAuthError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { AuthError } from "./AuthError.js";

/**
* Error class for MSAL Runtime errors that preserves detailed broker information
*/
export class NativeAuthError extends AuthError {
/**
* Numeric error code from MSAL Runtime
*/
public statusCode: number;

/**
* Error tag from MSAL Runtime
*/
public tag: string;

constructor(
errorStatus: string,
errorContext: string,
errorCode: number,
errorTag: number
) {
super(errorStatus, errorContext);
this.name = "NativeAuthError";
this.statusCode = errorCode;
this.tag = errorTag.toString();
Object.setPrototypeOf(this, NativeAuthError.prototype);
}
}
1 change: 1 addition & 0 deletions lib/msal-common/src/exports-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export {
AuthErrorCodes,
createAuthError,
} from "./error/AuthError.js";
export { NativeAuthError } from "./error/NativeAuthError.js";
export { ServerError } from "./error/ServerError.js";
export { NetworkError, createNetworkError } from "./error/NetworkError.js";
export {
Expand Down