你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用 MSAL.js 进行单一登录

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

当用户首次进行身份验证时,Azure Active Directory (Azure AD) 通过设置会话 Cookie 来启用 SSO。 MSAL.js 还会在每个应用程序域的浏览器存储中缓存用户的 ID 令牌和访问令牌。 这两种机制(即 Azure AD 会话 Cookie 和 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

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

有用户提示

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

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

使用会话 ID

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

const request = {
  scopes: ["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
    }
}

使用登录提示

若要绕过在交互式身份验证请求期间通常会显示的帐户选择提示(或对于未配置 sid 可选声明时的静默请求),请提供 loginHint。 在多租户应用程序中,还包括 domainHint

const request = {
  scopes: ["user.read"],
  loginHint: "preferred_username",
  domainHint: "preferred_tenant_id"
};

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
    }
}

从用户的“ID 令牌”中获取 loginHintdomainHint 的值:

  • loginHint:使用 ID 令牌的 preferred_username 声明值。

  • domainHint:使用 ID 令牌的 tid 声明值。 在使用 /common 颁发机构的多租户应用程序发出的请求中是必需的。 对于其他应用,是可选的。

有关登录提示和域提示的详细信息,请参阅 Microsoft 标识平台和 OAuth 2.0 授权代码流

使用帐户对象

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

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

const request = {
    scopes: ["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: ["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 并通过 Azure AD 重复使用现有会话。 这在阻止第三方 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 无效

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

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

对于 Azure AD 身份验证方案,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 的更多信息,请参阅: