Skip to main content

Azure AD B2C and MSAL

· 3 min read
Shaw Innes
Builder of things

I recently tried to create a simple demonstration of using Azure B2C in a React App using the MSAL-browser and MSAL-react libraries following the official documentation. I found it very confusing to understand which endpoints I should be using to use B2C authentication rather than AAD, and to subsequently obtain an access token.

If you don't care about my struggles and just want to copy and paste a solution: Give me the code!

MSAL.js

MSAL.js enables single-page applications to sign-in users with Azure AD B2C using the authorization code flow with PKCE grant. With MSAL.js and Azure AD B2C:

  • Users can authenticate with their social and local identities.
  • Users can be authorized to access Azure AD B2C protected resources (but not Azure AD protected resources).
  • Users cannot obtain tokens for Microsoft APIs (for example, MS Graph API) using delegated permissions.
  • Users with administrator privileges *can obtain tokens for Microsoft APIs (for example, MS Graph API) using delegated permissions.

MSAL and B2C

Many of the samples and StackOverflow posts have reference to login.microsoftonline.com as the authority as shown here:

const msalConfig = {
auth: {
clientId: 'your_client_id',
authority: 'https://login.microsoftonline.com/common/'
}
};

const scopes = [
'read' // defined as a scope in AzureAD B2C
]

const loginRequest: RedirectRequest = {
scopes: scopes
}

Creating an instance of MSAL within React:

const msalInstance = new msal.PublicClientApplication(msalConfig);

And finally, to call the login from a React component:

<button onClick={() => instance.loginRedirect(loginRequest)}>Sign In</button>;

invalid_client error!

The above "default" approach to using MSAL does not support B2C, so it gives an invalid_client error on redirect.

b2clogin.com

However, according to this page, that endpoint is only for multi-tenant authentication and not B2C.

Elsewhere I found documentation that stated the following:

You should no longer reference login.microsoftonline.com in your applications and APIs for authenticating users with Azure AD B2C. Instead, use b2clogin.com for all new applications, and migrate existing applications from login.microsoftonline.com to b2clogin.com.

Okay, so I went down that rabbithole. I was able to change my msalConfig to the following:

export const msalConfig = {
auth: {
clientId: 'your_client_id',
authority: "https://tenant.b2clogin.com/tenant.onmicrosoft.com/b2c_1_susi",
knownAuthorities: ["tenant.b2clogin.com"],
}
};

But for some reason this wasn't giving me scopres or an access token when calling acquireTokenSilent():

const account = msalInstance.getAllAccounts()[0]; 
const accessTokenResponse = await msalInstance.acquireTokenSilent({
scopes: scopes,
account: account
});
console.log(accessTokenResponce);

Console output:

{
"authority": "https://tenant.b2clogin.com/tenant.onmicrosoft.com/b2c_1_susi/",
"scopes": [],
"idToken": "eyJ0...",
"accessToken": ""
}

The solution

It turned out, after hours of combinatorial explosion, trial and error, and anguish... I had been using the wrong scope. It turns out that that by requesting a scope matching your ClientId, B2C will correctly issue an access token.

So, the final working solution that would return an access token...

const msalConfig = {
auth: {
clientId: 'your_client_id',
authority: "https://tenant.b2clogin.com/tenant.onmicrosoft.com/b2c_1_susi",
knownAuthorities: ["tenant.b2clogin.com"],
}
};

const scopes = [
'your_client_id' // <--- this was the key to success!
]

const loginRequest: RedirectRequest = {
scopes: scopes
}

And now when calling acquireTokenSilent():

const account = msalInstance.getAllAccounts()[0]; 
const accessTokenResponse = await msalInstance.acquireTokenSilent({
scopes: scopes,
account: account
});
console.log(accessTokenResponce);

Console output:

{
"authority": "https://tenant.b2clogin.com/tenant.onmicrosoft.com/b2c_1_susi/",
"scopes": [
"your_client_id"
],
"idToken": "eyJ0...",
"accessToken": "eyJ0..."
}

References

MSAL Supported Scenarios

https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-b2c-overview

MSAL for AAD (Not B2C!)

https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-client-application-configuration#authority

MSAL for B2C

https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AAD-B2C-specifics

Even this document references endpoints which aren't mentioned anywhere else, I've included it here for completeness.

Migration to B2C Login

https://docs.microsoft.com/en-us/azure/active-directory-b2c/b2clogin