- What browsers are supported by MSAL.js?
- Does MSAL.js support mobile browsers?
- I am moving from MSAL.js 1.x to MSAL.js to 2.x. What should I know?
- Does this library work for iframed applications?
- Will MSAL 2.x support B2C?
- Is MSAL.js 2.x compatible with Azure App Proxy
- Can I use MSAL.js 2.x with Microsoft Graph JavaScript SDK?
- Can I provision a single-page application via command-line?
- I don't understand the redirect flow. How does the handleRedirectPromise function work?
- How can I support authentication with personal Microsoft accounts only?
- How do I get an authorization code from the library?
- How do I implement self-service sign-up?
- How to get single sign-on in my application with MSAL.js?
- How can my application recognize a user after sign-in? How do I correlate users between applications?
- Troubleshooting single sigon-on
- In what scenarios will getAllAccounts return multiple accounts?
- Is the result of getAllAccounts sorted in any order?
- If an account is returned by getAllAccounts does that mean the user has an active session on the server?
- How can I switch between multiple logged in users?
- How can I avoid prompting users if SSO for one or more accounts is available?
- What is the difference between sessionStorage and localStorage?
- What are the possible configuration options?
- Where is the authority string on Azure AD Portal?
- What should I set my redirectUri to?
- Why is fragment the only valid field for responseMode in msal-browser?
- How do I configure the position and dimensions of popups?
- How do I acquire an access token? How do I use it?
- How do I acquire a refresh token?
- How do I renew tokens with MSAL.js?
- How can I acquire tokens faster?
- I'm seeing scopes openid, profile, email, offline_access in my tokens, even though I haven't requested them. What are they?
- How long do tokens last? How long are they valid for?
- What are the differences between supported audiences and account types?
- How do I specify which B2C policy/user flow I would like to use?
- How do I handle the password-reset user-flow?
- Why is getAccountByUsername returning null, even though I'm signed in?
- I logged out of my application. Why am I not asked for credentials when I try to log back in?
- Why am I not signed in when returning from an invite link?
- Why is there no access token returned from acquireTokenSilent?
- What should I do if I believe my issue is with the B2C service itself rather than with the library
Common Issues
MSAL.js has been tested and supports the last 2 stable and supported versions of the following browsers:
- Chrome
- Edge (Chromium)
- Firefox
- Safari
- Opera
MSAL.js also supports the following environments:
- WebViews
- Office Add-ins (see the sample)
- Chromium Extensions (see the sample)
- Teams Applications (see the sample)
There are certain known issues and mitigations documented for the following browsers:
In general, MSAL.js does support mobile browsers. However, since MSAL.js isn't tested specifically against mobile browsers, support may be limited and some features and flows may not work.
For example, while MSAL's loginPopup
and acquireTokenPopup
APIs may work on some mobile browsers, using popup APIs is discouraged on mobile devices as not all browsers will support this flow. We recommend using redirect APIs such as loginRedirect
and acquireTokenRedirect
when your application is running on a mobile device.
For problems specific to mobile browsers not listed here, please open an issue with clear instructions to reproduce.
Please refer to our migration guide here.
MSAL.js can be used in iframed applications under certain conditions. For more information, please refer to: Using MSAL in iframed apps
We are also working on solutions for applications affected by ITP 2.x changes.
MSAL.js v2 supports B2C of October 2020.
Unfortunately, at this time MSAL.js 2.x is not compatible with Azure App Proxy. Single-page applications will need to use MSAL.js 1.x as a workaround. We will post an update when this incompatibility has been fixed. See this issue for more information.
Yes, MSAL.js 2.x can be used as a custom authentication provider for the Microsoft Graph JavaScript SDK. For an implementation, please refer to the sample: JavaScript SPA calling Graph API.
Yes, we recommend the new Powershell Graph SDK for doing so. For instance, the script below creates an Azure AD application with redirect URI of type SPA and User.Read permission for Microsoft Graph in a tenant specified by the user, and then provisions a service principal in the same tenant based on this application object:
Import-Module Microsoft.Graph.Applications
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Connect-MgGraph -TenantId "ENTER_TENANT_ID_HERE" -Scopes "Application.ReadWrite.All"
# User.Read delegated permission for Microsoft Graph
$mgUserReadScope = @{
"Id" = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" # permission Id
"Type" = "Scope"
}
# Add additional permissions to array below
$mgResourceAccess = @($mgUserReadScope)
[object[]]$requiredResourceAccess = @{
"ResourceAppId" = "00000003-0000-0000-c000-000000000000" # MS Graph App Id
"ResourceAccess" = $mgResourceAccess
}
# Create the application
$msalApplication = New-MgApplication -displayName myMsalSpa `
-SignInAudience AzureADMyOrg `
-Spa @{RedirectUris = "http://localhost:3000", "http://localhost:3000/redirect"} `
-RequiredResourceAccess $requiredResourceAccess
# Provision the service principal
New-MgServicePrincipal -AppId $msalApplication.AppId
For a full implementation, please refer to the app creation scripts in the Vanilla JS Quickstart Sample
The redirect flow can be confusing, as redirecting away from the page means you are creating a whole new instance of the application when you return. This means that calling a redirect method cannot return anything. Rather, what happens is that the page is redirected away, you enter your credentials, and you are redirected back to your application with the response in the url hash.
If navigateToLoginRequestUrl
property in MSAL configuration parameters is set to true, you will be redirected again to the page you were on when you called loginRedirect
, unless that page was also set as your redirectUri
. On the final page your application must call handleRedirectPromise()
in order to process the hash and cache tokens in local/session storage.
As this function returns a promise you can call .then
and .catch
, similar to loginPopup
.
Please ensure handleRedirectPromise
has resolved before invoking any other MSAL method. If your app was not loaded as a result of a redirect operation handleRedirectPromise
will immediately return null
.
Please review one of our samples (for instance) to see the redirect flow in action.
Simply set your authority
in your MSAL app configuration to consumers tenant e.g. https://login.microsoftonline.com/consumers.
Currently the msal-browser package is designed for Single-Page Applications that are handling all authentication through the browser client. We have not yet optimized this to work with server-side components. As such, requests to retrieve the authorization code from the first leg of the flow can't be met currently. We are currently working on an implementation of msal that will run in node libraries, and as part of that we will explore options to make msal-browser work with server-side components.
MSAL Browser supports self-service sign-up in the auth code flow. Please see our docs here for supported prompt values in the request and their expected outcomes, and here for an overview of self-service sign-up and configuration changes that need to be made to your Azure tenant. Please note that that self-service sign-up is not available in B2C and test environments.
Please read the documentation on Single Sign-On to learn about different scenarios in which MSAL.js enables single sign-on.
How can my application recognize a user after sign-in? How do I correlate users between applications?
You can use the homeAccountId
property on the Account in the AuthenticationResult
.
loginPopup().then((response) => {
const uniqueID = response.account.homeAccountId;
});
The following is a list of common causes for SSO failures when using MSAL Browser:
Silent SSO requires third-party cookie access so the authentication service can persist a user's session accross tabs. If third-party cookies are blocked, silent SSO will fail and interaction will be required.
2. There is a content security policy or HTTP header blocking the iframe from loading your redirect URI page
When using ssoSilent
, the service will attempt to load your redirect URI page in an invisible embedded iframe. Content security policies and HTTP header values present in your app's redirect URI page response, such as X-FRAME-OPTIONS: DENY
and X-FRAME-OPTIONS: SAMEORIGIN
, can prevent your app from loading in said iframe, effectively blocking silent SSO.
If you intend you use ssoSilent
, please make sure the redirect URI points to a page that does not implement any such policies.
Because of cross-origin request limitations, in order for MSAL to have access to the hidden iframe's window.location.href
property, the redirectUri
configured in the ssoSilent
request must share the same origin as the calling page. If the page calling ssoSilent
and the redirectUri
for the request have different domains, the request is expected to fail with a monitor_window_timeout
error.
4. A service error occurred that prevented the STS from redirecting back to the configured redirectUri
A variety of service errors can occur which prevent the service from redirecting back to the redirectUri configured in the request. These are usually accompanied by an X-Frame-Options DENY
error and can be troubleshooted by opening the error URL in a new tab to see the full error message and context.
getAllAccounts
will return multiple accounts when your app has made multiple interactive token requests using either an acquireToken
or login
API and the user has selected different accounts on the server's account selection screen. Each successful call to an acquireToken
or login
API will return exactly one account which can be the same or different from the account returned in a previous call. Each account is saved in local or sessionStorage, depending on how you've configured MSAL, and will be available to any page that lives on the same domain.
If you would like to force the server account selection screen you can pass prompt: "select_account"
or prompt: "login"
to the acquireToken
or login
API.
No, accounts are not sorted nor are they guaranteed to maintain any particular order across multiple calls.
If an account is returned by getAllAccounts
does that mean the user has an active session on the server?
No, the account APIs reflect local account state only. If you need to ensure the user has an active session on the server you should call acquireTokenSilent
or ssoSilent
and fallback to interaction if needed.
Deciding which account to use to acquire tokens is app dependent, however, @azure/msal-browser
provides 2 convenient APIs to help you keep track of which account is currently "active" and should be used for token requests. Once you've determined which account you wish to use you can call the setActiveAccount()
API to make sure MSAL uses this account for all subsequent requests. If you would like to switch to a different account, simply call setActiveAccount()
again and pass it the new account you would like to use. If you need to know which account is currently "active" you can use the getActiveAccount()
API.
You can read more about the account APIs here.
You can also find an example implementation of an account switcher using the @azure/msal-react
wrapper in our react-router-sample.
The following flow diagram can help you avoid unnecessary authentication prompts when an account (or multiple accounts) is available for SSO.
We offer two methods of storage for Msal, localStorage
and sessionStorage
. Our recommendation is to use sessionStorage
because it is more secure in storing tokens that are acquired by your users, but localStorage
will give you Single Sign On across tabs and user sessions. We encourage you to explore the options and make the best decision for your application.
For MSAL.js 2.x, please review this document.
The authority
string that you need to supplant to MSAL app configuration is not explicitly listed among the Endpoint links on Azure Portal/AzureAD/App Registration/Overview
page. It is simply the domain part of a /token
or /authorize
endpoint, followed by the tenant name or ID e.g. https://login.microsoftonline.com/common
.
If the developer provides azureCloudOptions
, MSAL.js will overwrite any value provided in the authority
. MSAL.js will also give preference to the parameters provided in a request
over configuration
. Please note that if azureCloudOptions
are set in the configuration, they will take precedence over authority
in the request
. If the developer needs to overwrite this, they need to set azureCloudOptions
in the request
.
When you attempt to authenticate MSAL will navigate to your IDP's sign in page either in the current window, a popup window or a hidden iframe depending on whether you used a redirect, popup or silent API respectively. When authentication is complete the IDP will redirect the window to the redirectUri
specified in the request with the authentication response in the url hash. You can use any page in your application as your redirectUri
but there are some additional considerations you should be aware of depending on which API you are using. All pages used as a redirectUri
must be registered as a Reply Url of type "SPA" on your app registration. Additionally, the redirectUri
must be of the same domain as the application requesting the token.
When using popup and silent APIs we recommend setting the redirectUri
to a blank page, a page that does not implement MSAL, or a page that does not itself require a user be authenticated. This will help prevent potential issues as well as improve performance. If your application is only using popup and silent APIs you can set this on the PublicClientApplication
config. If your application also needs to support redirect APIs you can set the redirectUri
on a per request basis.
When using the redirect APIs you must set your redirectUri
to a page that implements MSAL and that page must also invoke handleRedirectPromise
in order to process the response. If using msal-react
or msal-angular
, handleRedirectPromise
may be invoked by default, please refer to the docs for those libraries for more specific guidance.
Additional notes:
- If the page that you use as your
redirectUri
requires authentication and automatically invokes a login API, you should ensure thathandleRedirectPromise
has resolved and a user is not signed in before invoking the login API. - If the page that invokes
loginRedirect
is different than yourredirectUri
you will first be redirected to theredirectUri
then back to the original page. You should invokehandleRedirectPromise
on both theredirectUri
and the original page. If this is undesired and you would like to stay on theredirectUri
you can setnavigateToLoginRequestUrl
tofalse
in thePublicClientApplication
config.
The library is built to specifically use the fragment response mode. This is a security consideration as the fragment of the URL is not sent to the server and modifying/clearing the fragment does not result in a new page load. We are considering implementing support for other responseMode
types in the future, specifically to use multiple libraries in the same app.
A popup window's position and dimension can be configured by passing the height, width, top position, and left position in the request. If no configurations are passed, MSAL defaults will be used. See the request documentation for PopupRequest and EndSessionPopupRequest for more details.
Note that popup dimensions should be positioned on screen and sized smaller than the parent window. Popups that are positioned off-screen or larger than the parent window will use MSAL defaults instead.
const loginRequest = {
scopes: ["user.read", "mail.send"],
popupWindowAttributes: {
popupSize: {
height: 100,
width: 100,
},
popupPosition: {
top: 100,
left: 100,
},
},
};
try {
const loginResponse = await msalInstance.loginPopup(loginRequest);
} catch (err) {
// handle error
}
Please refer to token guide here.
MSAL.js abstracts away all refresh token complexity and thus refresh tokens are not exposed by MSAL APIs by design. When you need an access token please call the acquireTokenSilent
API which will return to you a valid token from the cache or internally use the refresh token to acquire a new access token. If you have a backend that needs to be able to use access tokens to call other APIs, your backend should use a server-side library, such as MSAL Node, to acquire tokens for itself.
MSAL.js provides the acquireTokenSilent
method which handles token renewal by making silent token requests without prompting the user. The method first looks for a valid cached token in the browser storage. If it does not find one, the library makes the silent request to Azure AD and if there is an active user session (determined by a cookie set in browser on the Azure AD domain), a fresh token is returned. The library does not automatically invoke the acquireTokenSilent
method. It is recommended that you call acquireTokenSilent
in your app before making an API call to get the valid token.
In certain cases, the acquireTokenSilent
method's attempt to get the token silently fails. Some examples of this are when there is an expired user session with Azure AD or a password change by the user, etc. which requires user interaction. When the acquireTokenSilent
fails, you need to call one of the interactive acquire token methods (acquireTokenPopup
or acquireTokenRedirect
).
The tokens returned by Azure AD have a default lifetime of 1 hour. However, as long as the user is active on your application and a valid Azure AD session exists in the browser, the acquireTokenSilent
method can be used to renew tokens. The Azure AD session is valid for 24 hours and can be extended by the user by choosing "Keep me signed in" option on the sign-in screen. For more details, read the token and session lifetimes document.
Please refer to our performance guide here
I'm seeing scopes openid
, profile
, email
, offline_access
and User.Read
in my tokens, even though I haven't requested them. What are they?
The first four (openid
, profile
, email
and offline_access
) are called default scopes. They are added to Azure AD as part of Azure AD - OAuth 2.0/OpenID Connect compliance. They are not part of any particular API. You can read more about them here.
The scope User.Read
, on the other hand, is an MS Graph API scope. It is also added by default to every app registration. However if your application is not calling MS Graph API, you can simply ignore it.
Token lifetimes are 1 hour and the session lifetime is 24 hours. This means that if no requests have been made in 24 hours, you will need to login again before requesting a new token.
Please see the documentation on Tenancy in Azure Active Directory
My application has multiple resources it needs to access to. How should I handle scopes for access tokens?
Please see the doc about resources and scopes here
How to register custom scopes for my web API.
The policy (a.k.a. user flow) can be appended to the authority url you provide as part of the PublicClientApplication
configuration or as part of an individual request.
const config = {
auth: {
clientId: "your-client-id",
authority:
"https://yourApp.b2clogin.com/yourApp.onmicrosoft.com/your_policy",
knownAuthorities: ["yourApp.b2clogin.com"],
},
};
const pca = new PublicClientApplication(config);
// You can also provide the authority as part of the request object
const request = {
scopes: ["openid"],
authority:
"https://yourApp.b2clogin.com/yourApp.onmicrosoft.com/your_policy",
};
pca.loginRedirect(request);
Note: Msal.js does not support providing the user flow as a query parameter e.g. https://yourApp.b2clogin.com/yourApp.onmicrosoft.com/?p=your_policy
. Please make sure your authority is formatted as shown above.
The new password reset experience is now part of the sign-up or sign-in policy. When the user selects the Forgot your password? link, they are immediately sent to the Forgot Password experience. You don't need a separate policy for password reset anymore.
Our recommendation is to move to the new password reset experience since it simplifies the app state and reduces error handling on the user-end. If for some reason you have to use the legacy password-reset user-flow, you'll have to handle the AADB2C90118
error code returned from B2C service when a user selects the Forgot your password? link:
pca.loginPopup()
.then((response) => {
// do something with auth response
})
.catch((error) => {
// Error handling
if (error.errorMessage) {
// Check for forgot password error
// Learn more about AAD error codes at https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes
if (error.errorMessage.indexOf("AADB2C90118") > -1) {
// For password reset, initiate a login request against tenant-specific authority with user-flow string appended
pca.loginPopup({
authority:
"https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_reset",
});
}
}
});
For a full implementation, see the sample: MSAL.js v2 B2C sample
In order to use getAccountByUsername()
in B2C scenarios you must enable your idTokens
to return the emails
claim in your B2C tenant. MSAL will fill the username
field on the AccountInfo
object with the first element of the array returned on the emails
claim. In most cases this array will only have one element, however, if you notice that your idTokens are returning more than one email on this claim, ensure you are calling getAccountByUsername
with the first email.
To enable this claim open up your User Flow configuration in the Azure Portal. Click the User Attributes
tab and make sure Email Address
is checked. Then click the Application Claims
tab and make sure Email Addresses
is checked. You can verify that the emails
claim is now being returned by acquiring an idToken
and inspecting its contents.
When you log out of a B2C application by calling MSAL's logout()
API, MSAL.js will first clear browser storage of your user's tokens then redirect you to the Azure B2C logout endpoint. The B2C service will then close your session but may not log you out of your federated IDP. This happens because the service does not make any assumptions about other apps you may want to log out of. What this means in practice is that when a user attempts to login again the B2C service will prompt the user to select which Social IDP they would like to sign in with. When the user makes their selection, they may be signed back in without interaction.
You can read more about this behavior here
MSAL.js will only process tokens which it originally requested. If your flow requires that you send a user a link they can use to sign up, you will need to ensure that the link points to your app, not the B2C service directly. An example flow can be seen in the working with B2C doc.
In that case, please file a support ticket with the B2C team by following the instructions here: B2C support options.