使用 MSAL.js 进行单一登录

单一登录 (SSO) 可减少要求用户提供凭据的次数,从而提供了更加无缝的体验。 用户只需输入一次凭据,同一设备上的其他应用程序就可以重复使用已建立的会话,而无需进一步提示。

当用户首次进行身份验证时,Microsoft Entra ID 通过设置会话 Cookie 来启用 SSO。 MSAL.js 还会在每个应用程序域的浏览器存储中缓存用户的 ID 令牌和访问令牌。 Microsoft Entra 会话 Cookie 和 Microsoft Authentication Library (MSAL) 缓存这两种机制相互独立,但共同提供 SSO 行为。

同一应用的浏览器选项卡之间的 SSO

当用户在多个选项卡中打开你的应用程序并在其中一个选项卡中登录时,他们可能在其他选项卡中登录同一应用,而不会看到提示。 为此,你需要将 MSAL.js 配置对象中的 cacheLocation 设置为 localStorage,如以下示例所示:

const config = {
  auth: {
    clientId: "1111-2222-3333-4444-55555555",
  },
  cache: {
    cacheLocation: "localStorage",
  },
};

const msalInstance = new msal.PublicClientApplication(config);

在这种情况下,不同浏览器选项卡中的应用程序实例使用相同的 MSAL 缓存,从而在它们之间共享身份验证状态。 当用户从其他浏览器选项卡或窗口登录时,还可以使用 MSAL 事件更新应用程序实例。 有关详细信息,请参阅:在不同选项卡和窗口间同步登录状态

不同应用之间的 SSO

当用户进行身份验证时,将在浏览器中的 Microsoft Entra 域上设置会话 Cookie。 MSAL.js 依赖于此会话 Cookie 来为不同应用程序之间的用户提供 SSO。 特别是,MSAL.js 提供 ssoSilent 方法用于在不交互的情况下登录用户并获取令牌。 但是,如果用户在 Microsoft Entra ID 的会话中拥有多个用户帐户,系统会提示用户选择用于登录的帐户。 因此,可通过两种方式使用 ssoSilent 方法实现 SSO。

有用户提示

为了提高性能并确保授权服务器将寻找正确的帐户会话,可以在 ssoSilent 方法的请求对象中传递以下选项之一,以静默方式获取令牌。

  • login_hint:可从 account 对象用户名属性或 ID 令牌的 upn 声明中检索。 如果应用使用 B2C 对用户进行身份验证,请参阅:配置 B2C 用户流以在 ID 令牌中发出用户名
  • 会话 ID sid:可从 account 对象的 idTokenClaims 中检索。
  • account:可通过使用其中一种帐户方法检索

建议使用向 ssoSilent 提供的login_hint可选 ID 令牌声明作为 loginHint,因为它是无提示和交互式请求的最可靠的帐户提示。

使用登录提示

login_hint可选声明向 Microsoft Entra ID 提供有关尝试登录的用户帐户的提示。 若要绕过一般在交互式身份验证请求期间显示的帐户选择提示,请提供 loginHint,如下所示:

const silentRequest = {
    scopes: ["User.Read", "Mail.Read"],
    loginHint: "user@contoso.com"
};

try {
    const loginResponse = await msalInstance.ssoSilent(silentRequest);
} catch (err) {
    if (err instanceof InteractionRequiredAuthError) {
        const loginResponse = await msalInstance.loginPopup(silentRequest).catch(error => {
            // handle error
        });
    } else {
        // handle error
    }
}

在此示例中,loginHint 包含用户的电子邮件或 UPN,这在交互式令牌请求期间用作提示。 可以在应用程序之间传递提示,以便于无提示 SSO,应用程序 A 可以让用户登录,读取 loginHint,然后将声明和当前租户上下文发送到应用程序 B。Microsoft Entra ID 将尝试预先填写登录表单或绕过帐户选择提示,并直接继续指定用户的身份验证过程。

如果 login_hint 声明中的信息与任何现有用户不匹配,则会进行重定向以完成标准登录体验,包括帐户选择。

使用会话 ID

若要使用会话 ID,请将 sid 作为可选声明添加到应用的 ID 令牌。 不管帐户名或用户名是什么,应用程序都可以使用sid声明识别用户的 Microsoft Entra 会话。 若要了解如何添加可选声明(例如 sid),请参阅向应用提供可选声明。 在使用 MSAL.js 中的 ssoSilent 发出的静默身份验证请求中使用会话 ID (SID)。

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

 try {
    const loginResponse = await msalInstance.ssoSilent(request);
} catch (err) {
    if (err instanceof InteractionRequiredAuthError) {
        const loginResponse = await msalInstance.loginPopup(request).catch(error => {
            // handle error
        });
    } else {
        // handle error
    }
}

使用帐户对象

如果知道用户帐户信息,则还可以使用 getAccountByUsername()getAccountByHomeId() 方法检索用户帐户:

const username = "test@contoso.com";
const myAccount  = msalInstance.getAccountByUsername(username);

const request = {
    scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"],
    account: myAccount
};

try {
    const loginResponse = await msalInstance.ssoSilent(request);
} catch (err) {
    if (err instanceof InteractionRequiredAuthError) {
        const loginResponse = await msalInstance.loginPopup(request).catch(error => {
            // handle error
        });
    } else {
        // handle error
    }
}

无用户提示

可以尝试使用 ssoSilent 方法,而不传递任何 accountsidlogin_hint,如下面的代码所示:

const request = {
    scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"]
};

try {
    const loginResponse = await msalInstance.ssoSilent(request);
} catch (err) {
    if (err instanceof InteractionRequiredAuthError) {
        const loginResponse = await msalInstance.loginPopup(request).catch(error => {
            // handle error
        });
    } else {
        // handle error
    }
}

但是,如果应用程序在单个浏览器会话中有多个用户,或者如果用户在该单个浏览器会话中有多个帐户,则可能会出现静默登录错误。 如果有多个帐户可用,则可能会显示以下错误:

InteractionRequiredAuthError: interaction_required: AADSTS16000: Either multiple user identities are available for the current request or selected account is not supported for the scenario.

该错误表明服务器无法确定要登录的帐户,并且需要上述示例中的参数之一(accountlogin_hintsid)或交互式登录来选择帐户。

使用 ssoSilent 时的注意事项

重定向 URI(回复 URL)

为了获得更好的性能并帮助避免问题,请将 redirectUri 设置为空白页或不使用 MSAL 的其他页面。

  • 如果应用程序用户仅使用弹出和静默方法,请在 PublicClientApplication 配置对象上设置 redirectUri
  • 如果应用程序还使用重定向方法,请根据每个请求设置 redirectUri

第三方 Cookie

ssoSilent尝试打开隐藏的 iframe 并通过 Microsoft Entra ID 重复使用现有会话。 这在阻止第三方 Cookie 的浏览器(例如 Safari)中不起作用,并且会导致交互错误:

InteractionRequiredAuthError: login_required: AADSTS50058: A silent sign-in request was sent but no user is signed in. The cookies used to represent the user's session were not sent in the request to Azure AD

若要解决该错误,用户必须使用 loginPopup()loginRedirect() 创建交互式身份验证请求。 在某些情况下,可将提示值 none 与交互式 MSAL.js 方法配合使用以实现 SSO。 有关详细信息,请参阅包含 prompt=none 的交互式请求。 如果已经拥有用户的登录信息,则可以传递 loginHintsid 可选参数来登录特定帐户。

使 prompt=login 的 SSO 无效

如果希望 Microsoft Entra ID 提示用户输入凭据(尽管授权服务器存在活动会话),可以通过 MSAL.js 在请求中使用登录提示参数。 有关详细信息,请参阅 MSAL.js 提示行为

在 ADAL.js 和 MSAL.js 之间共享身份验证状态

对于 Microsoft Entra 身份验证方案,MSAL.js 引入了与 ADAL.js 的功能奇偶一致性。 为简化从 ADAL.js 到 MSAL.js 的迁移并在应用之间轻松共享身份验证状态,库会读取 ADAL.js 缓存中表示用户会话的 ID 令牌。 若要在从 ADAL.js 迁移时利用这一点,需确保库使用 localStorage 来缓存令牌。 在初始化时,将 MSAL.js 和 ADAL.js 配置中的 cacheLocation 设置为 localStorage,如下所示:


// In ADAL.js
window.config = {
  clientId: "1111-2222-3333-4444-55555555",
  cacheLocation: "localStorage",
};

var authContext = new AuthenticationContext(config);

// In latest MSAL.js version
const config = {
  auth: {
    clientId: "1111-2222-3333-4444-55555555",
  },
  cache: {
    cacheLocation: "localStorage",
  },
};

const msalInstance = new msal.PublicClientApplication(config);

后续步骤

有关 SSO 的更多信息,请参阅: