使用 MSAL.js 进行单一登录Single sign-on with MSAL.js

借助单一登录 (SSO),用户只需输入其凭据一次即可登录,并建立可在多个应用程序中重复使用的会话,而无需再次进行身份验证。Single Sign-On (SSO) enables users to enter their credentials once to sign in and establish a session which can be reused across multiple applications without requiring to authenticate again. 这样可为用户提供无缝的体验,并减少重复提示用户输入凭据的次数。This provides a seamless experience to the user and reduces the repeated prompts for credentials.

当用户首次进行身份验证时,Azure AD 将设置会话 Cookie,以此在应用程序中提供 SSO 功能。Azure AD provides SSO capabilities to applications by setting a session cookie when the user authenticates the first time. MSAL.js 库允许应用程序以多种方式利用此功能。The MSAL.js library allows applications to leverage this in a few ways.

浏览器标签页之间的 SSOSSO between browser tabs

如果应用程序已在多个标签页中打开,当你首次在一个标签页中将用户登录时,该用户也会在其他标签页中登录,而不会看到提示。When your application is open in multiple tabs and you first sign in the user on one tab, the user is also signed in on the other tabs without being prompted. MSAL.js 会在浏览器 localStorage 中缓存用户的 ID 令牌,并在其他打开的标签页中将用户登录到应用程序。MSAL.js caches the ID token for the user in the browser localStorage and will sign the user in to the application on the other open tabs.

默认情况下,MSAL.js 使用 sessionStorage(不允许在标签页之间共享会话)。By default, MSAL.js uses sessionStorage which does not allow the session to be shared between tabs. 若要在标签页之间实现 SSO,请务必将 MSAL.js 中的 cacheLocation 设置为 localStorage,如下所示。To get SSO between tabs, make sure to set the cacheLocation in MSAL.js to localStorage as shown below.

const config = {
    auth: {
        clientId: "abcd-ef12-gh34-ikkl-ashdjhlhsdg"
    },
    cache: {
        cacheLocation: 'localStorage'
    }
}

const myMSALObj = new UserAgentApplication(config);

应用之间的 SSOSSO between apps

当用户进行身份验证时,将在浏览器中的 Azure AD 域上设置会话 Cookie。When a user authenticates, a session cookie is set on the Azure AD domain in the browser. MSAL.js 依赖于此会话 Cookie 来为不同应用程序之间的用户提供 SSO。MSAL.js relies on this session cookie to provide SSO for the user between different applications. MSAL.js 还会在每个应用程序域的浏览器存储中缓存用户的 ID 令牌和访问令牌。MSAL.js also caches the ID tokens and access tokens of the user in the browser storage per application domain. 因此,SSO 行为根据不同的情况而异:As a result, the SSO behavior varies for different cases:

同一域中的应用程序Applications on the same domain

如果应用程序托管在同一个域中,则用户可以登录到应用一次,然后可在其他应用中完成身份验证,而不会看到提示。When applications are hosted on the same domain, the user can sign into an app once and then get authenticated to the other apps without a prompt. MSAL.js 利用域中为用户缓存的令牌来提供 SSO。MSAL.js leverages the tokens cached for the user on the domain to provide SSO.

不同域中的应用程序Applications on different domain

如果应用程序托管在不同的域中,则域 B 中的 MSAL.js 无法访问域 A 中缓存的令牌。When applications are hosted on different domains, the tokens cached on domain A cannot be accessed by MSAL.js in domain B.

这意味着,当已登录到域 A 的用户导航到域 B 中的应用程序时,他们将被重定向,或者在 Azure AD 页面中看到提示。This means that when users signed in on domain A navigate to an application on domain B, they will be redirected or prompted with the Azure AD page. 由于 Azure AD 仍包含用户会话 Cookie,它会将用户登录,而用户不需要重新输入凭据。Since Azure AD still has the user session cookie, it will sign in the user and they will not have to re-enter the credentials. 如果用户在 Azure AD 的会话中包含多个用户帐户,则系统会提示用户选择相关的帐户用于登录。If the user has multiple user accounts in session with Azure AD, the user will be prompted to pick the relevant account to sign in with.

自动在 Azure AD 上选择帐户Automatically select account on Azure AD

在某些情况下,应用程序有权访问用户的身份验证上下文,并希望避免在登录了多个帐户时出现 Azure AD 帐户选择提示。In certain cases, the application has access to the user's authentication context and wants to avoid the Azure AD account selection prompt when multiple accounts are signed in. 可通过多种不同的方式实现此目的:This can be done a few different ways:

使用会话 ID (SID)Using Session ID (SID)

会话 ID 是可以在 ID 令牌中配置的可选声明Session ID is an optional claim that can be configured in the ID tokens. 不管用户的帐户名或用户名是什么,应用程序都可以使用此声明识别用户的 Azure AD 会话。This claim allows the application to identify the user’s Azure AD session independent of the user’s account name or username. 可将请求参数中的 SID 传递给 acquireTokenSilent 调用。You can pass the SID in the request parameters to the acquireTokenSilent call. 这样,Azure AD 就可以绕过帐户选择。This will allow Azure AD to bypass the account selection. SID 绑定到会话 Cookie,不会跨越浏览器上下文。SID is bound to the session cookie and will not cross browser contexts.

var request = {
    scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"],
    sid: sid
}

userAgentApplication.acquireTokenSilent(request).then(function(response) {
        const token = response.accessToken
    }
).catch(function (error) {  
        //handle error
});

备注

SID 只能与 MSAL.js 中的 acquireTokenSilent 调用发出的静默身份验证请求配合使用。SID can be used only with silent authentication requests made by acquireTokenSilent call in MSAL.js. 可在此处找到有关在应用程序清单中配置可选声明的步骤。You can find the steps to configure optional claims in your application manifest here.

使用登录提示Using Login Hint

如果未配置 SID 声明或者需要在交互式身份验证调用中绕过帐户选择提示,可以在请求参数中提供 login_hint,并选择性地在 MSAL.js 交互式方法(loginPopuploginRedirectacquireTokenPopupacquireTokenRedirect)中提供 extraQueryParameters 形式的 domain_hintIf you do not have SID claim configured or need to bypass the account selection prompt in interactive authentication calls, you can do so by providing a login_hint in the request parameters and optionally a domain_hint as extraQueryParameters in the MSAL.js interactive methods (loginPopup, loginRedirect, acquireTokenPopup and acquireTokenRedirect). 例如:For example:

var request = {
    scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"],
    loginHint: preferred_username,
    extraQueryParameters: {domain_hint: 'organizations'}
}

userAgentApplication.loginRedirect(request);

可以通过读取用户 ID 令牌中返回的声明来获取 login_hint 和 domain_hint 的值。You can get the values for login_hint and domain_hint by reading the claims returned in the ID token for the user.

  • loginHint 应设置为 ID 令牌中的 preferred_username 声明。loginHint should be set to the preferred_username claim in the ID token.

  • 仅当使用 /common 颁发机构时,才需要传递 domain_hintdomain_hint is only required to be passed when using the /common authority. 域提示按租户 ID (TID) 确定。The domain hint is determined by tenant ID(tid). 如果 ID 令牌中的 tid 声明为 9188040d-6c67-4c5b-b112-36a304b66dad,则域提示为使用者。If the tid claim in the ID token is 9188040d-6c67-4c5b-b112-36a304b66dad it is consumers. 否则为组织。Otherwise, it is organizations.

有关登录提示和域提示的值的详细信息,请参阅此文Read here for more information on the values for login hint and domain hint.

备注

不能同时传递 SID 和 login_hint。You cannot pass SID and login_hint at the same time. 这会导致错误响应。This will result in error response.

不使用 MSAL.js 登录名进行 SSOSSO without MSAL.js login

根据设计,在获取 API 的令牌之前,MSAL.js 需要调用某个登录方法来建立用户上下文。By design, MSAL.js requires that a login method is called to establish a user context before getting tokens for APIs. 由于登录方法是交互式的,用户会看到提示。Since login methods are interactive, the user sees a prompt.

在某些情况下,应用程序有权通过另一应用程序中启动的身份验证访问经过身份验证的用户的上下文或 ID 令牌,并希望在不先通过 MSAL.js 登录的情况下,利用 SSO 获取令牌。There are certain cases in which applications have access to the authenticated user's context or ID token through authentication initiated in another application and want to leverage SSO to acquire tokens without first signing in through MSAL.js.

此方案的示例如下:用户已登录到父 Web 应用程序,该应用程序托管作为加载项或插件运行的另一个 JavaScript 应用程序。An example of this is: A user is signed into a parent web application which hosts another JavaScript application running as an add-on or plugin.

可按如下所述实现此方案中的 SSO 体验:The SSO experience in this scenario can be achieved as follows:

将可用的 sid(或 login_hint 和可选的 domain_hint)作为请求参数传递给 MSAL.js acquireTokenSilent 调用,如下所示:Pass the sid if available (or login_hint and optionally domain_hint) as request parameters to the MSAL.js acquireTokenSilent call as follows:

var request = {
    scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"],
    loginHint: preferred_username,
    extraQueryParameters: {domain_hint: 'organizations'}
}

userAgentApplication.acquireTokenSilent(request).then(function(response) {
        const token = response.accessToken
    }
).catch(function (error) {  
        //handle error
});

从 ADAL.js 更新到 MSAL.js 之后的 SSOSSO in ADAL.js to MSAL.js update

对于 Azure AD 身份验证方案,MSAL.js 引入了与 ADAL.js 的功能奇偶一致性。MSAL.js brings feature parity with ADAL.js for Azure AD authentication scenarios. 为了轻松从 ADAL.js 迁移到 MSAL.js 并避免再次提示用户登录,库会在 ADAL.js 缓存中读取代表用户会话的 ID 令牌,并在 MSAL.js 中无缝地将用户登录。To make the migration from ADAL.js to MSAL.js easy and to avoid prompting your users to sign in again, the library reads the ID token representing user’s session in ADAL.js cache, and seamlessly signs in the user in MSAL.js.

若要在从 ADAL.js 更新时利用单一登录 (SSO) 行为,需确保库使用 localStorage 来缓存令牌。To take advantage of the single sign-on (SSO) behavior when updating from ADAL.js, you will need to ensure the libraries are using localStorage for caching tokens. 在初始化时,将 MSAL.js 和 ADAL.js 配置中的 cacheLocation 设置为 localStorage,如下所示:Set the cacheLocation to localStorage in both the MSAL.js and ADAL.js configuration at initialization as follows:

// In ADAL.js
window.config = {
   clientId: 'g075edef-0efa-453b-997b-de1337c29185',
   cacheLocation: 'localStorage'
};

var authContext = new AuthenticationContext(config);


// In latest MSAL.js version
const config = {
    auth: {
        clientId: "abcd-ef12-gh34-ikkl-ashdjhlhsdg"
    },
    cache: {
        cacheLocation: 'localStorage'
    }
}

const myMSALObj = new UserAgentApplication(config);

完成配置后,MSAL.js 可以在 ADAL.js 中读取经过身份验证的用户的缓存状态,并使用该状态在 MSAL.js 中提供 SSO。Once this is configured, MSAL.js will be able to read the cached state of the authenticated user in ADAL.js and use that to provide SSO in MSAL.js.