AD FS OpenID Connect/OAuth 流和应用程序方案
适用于 AD FS 2019 及更高版本
方案 | 使用示例的方案演练 | OAuth 2.0 流/授予 | 客户端类型 |
---|---|---|---|
单页应用 | 使用 MSAL 的示例 | 隐式 | 公用 |
登录用户的 Web 应用 | 使用 OWIN 的示例 | 授权代码 | 公用、机密 |
调用 Web API 的本机应用 | 使用 MSAL 的示例 | 授权代码 | 公用 |
调用 Web API 的 Web 应用 | 使用 MSAL 的示例 | 授权代码 | 机密 |
PKCE 实现 | 授权代码 | 公开 | |
Web API 代表 (OBO) 用户调用另一个 Web API | 使用 MSAL 的示例 | 代表 | Web 应用充当机密 |
调用 Web API 的守护程序应用 | 客户端凭据 | 机密 | |
Web 应用使用用户凭据调用 Web API | 资源所有者密码凭据 | 公用、机密 | |
无浏览器应用调用 Web API | 设备代码 | 公用、机密 |
隐式授予流
注意
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中的隐式授权流的详细信息,请参阅 Microsoft 标识平台中的隐式授权流。
对于单页应用程序(AngularJS、Ember、React.js 等),AD FS 支持 OAuth 2.0 隐式授予流。 有关隐式流的说明,请参阅 OAuth 2.0 规范。 其主要优点在于允许应用从 AD FS 获取令牌,而无需执行后端服务器凭据交换。 此流程允许应用在客户端 JavaScript 代码中登录用户、维护会话并获取其他 Web API 的令牌。 专门针对客户端使用隐式流时,需要考虑几个重要的安全注意事项。
如果要使用隐式流和 AD FS 向 JavaScript 应用添加身份验证,请按照以下部分中的常规步骤进行操作。
协议图
下图显示了整个隐式登录流的样子,后续各部分更详细地介绍了每个步骤。
请求 ID 令牌和访问令牌
要在最初将用户登录到应用,可以发送 OpenID Connect 身份验证请求,并从 AD FS 终结点获取 id_token 和访问令牌。
// Line breaks for legibility only
https://adfs.contoso.com/adfs/oauth2/authorize?
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&response_type=id_token+token
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&scope=openid
&response_mode=fragment
&state=12345
参数 | 必需/可选 | 说明 |
---|---|---|
client_id | 必需 | AD FS 分配给应用的应用程序(客户端)ID。 |
response_type | 必填 | 必须包含 OpenID Connect 登录的 id_token 。 也可以包含 response_type token 。 使用此处的令牌可让应用立即从授权终结点接收访问令牌,而无需向令牌终结点发出第二次请求。 |
redirect_uri | 必填 | 应用的 redirect_uri,你的应用可在其中发送和接收身份验证响应。 它必须与你在 AD FS 中配置的其中一个 redirect_uris 完全匹配。 |
nonce | 必填 | 包含在请求中的值,由应用生成,这些值将作为声明包含在生成的 id_token 中。 应用程序接着便可确认此值,以减少令牌重新执行攻击。 该值通常是随机的唯一字符串,可用于标识请求的来源。 仅在请求 id_token 时是必需的。 |
scope | 可选 | 范围的空格分隔列表。 对于 OpenID Connect,它必须包含作用域 openid 。 |
resource | 可选 | Web API 的 URL。 注意–如果使用 MSAL 客户端库,则不会发送资源参数。 相反,资源 URL 作为范围参数的一部分发送: scope = [resource url]//[scope values e.g., openid] 如果资源未在此处或作用域内传递,则 AD FS 使用默认资源 urn:microsoft:userinfo。 不能自定义 userinfo 资源策略,如 MFA、Issuance 或授权策略。 |
response_mode | 可选 | 指定应该用于将生成的令牌发送回应用的方法。 默认为 fragment 。 |
state | 可选 | 同时随令牌响应返回的请求中所包含的值。 可以是想要的任何内容的字符串。 随机生成的唯一值通常用于防止跨站点请求伪造攻击。 该 state 也用于在身份验证请求出现之前,于应用中编码用户的状态信息,例如之前所在的网页或视图。 |
prompt | 可选 | 表示需要的用户交互类型。 此时唯一有效的值是“sign-in”和“none”。 - prompt=login 强制用户在该请求上输入其凭据,从而拒绝单点登录。 - prompt=none 则相反,它会确保用户不会看到任何交互式提示。 如果请求无法通过单一登录静默完成,AD FS 将返回 interaction_required 错误。 |
login_hint | 可选 | 如果事先知道用户名,可用于预先填充用户登录页的用户名/电子邮件地址字段。 通常,应用在重新身份验证期间使用此参数,并且已经使用 id_token 中的 upn 声明从前次登录提取用户名。 |
domain_hint | 可选 | 如果包含,它将跳过用户在登录页面上经历的基于域的发现过程,从而使用户体验稍微更简单。 |
此时,系统会要求用户输入凭据并完成身份验证。 用户进行身份验证后,AD FS 授权终结点将使用 response_mode 参数中指定的方法,将响应返回到指示 redirect_uri 处的应用。
成功的响应
使用 response_mode=fragment and response_type=id_token+token
的成功响应如下所示
// Line breaks for legibility only
GET https://localhost/myapp/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZEstZnl0aEV...
&token_type=Bearer
&expires_in=3599
&scope=openid
&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZstZnl0aEV1Q...
&state=12345
参数 | 说明 |
---|---|
access_token | 如果 response_type 包含 token ,则包含此参数。 |
token_type | 如果 response_type 包含 token ,则包含此参数。 始终为“Bearer”。 |
expires_in | 如果 response_type 包含 token ,则包含此参数。 指示令牌有效(以缓存为目的)的秒数。 |
scope | 指示 access_token 有效的范围。 |
id_token | 如果 response_type 包含 id_token ,则包含此参数。 有符号 JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
state | 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应验证请求和响应中的状态值是否相同。 |
刷新令牌
隐式授予不提供刷新令牌。 id_tokens
和 access_tokens
会在短时间后过期,因此应用必须准备好定期刷新这些令牌。 若要刷新任一类型的令牌,可以通过使用 prompt=none
参数控制 Microsoft 标识平台的行为,来执行上一节中的同一隐藏的 iframe 请求。 要想收到 new id_token
,请务必使用 response_type=id_token
。
授权代码授予流
注意
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中的授权码授权流的详细信息,请参阅 Microsoft 标识平台中的授权码授权流。
可以在 Web 应用中使用 OAuth 2.0 授权代码授予来访问受保护的资源,例如 Web API。 OAuth 2.0 规范第 4.1 部分描述了 OAuth 2.0 授权代码流。 它用于在大多数应用程序类型中执行身份验证和授权,包括 Web 应用和本机安装的应用。 流使应用能够安全地获取可用于访问信任 AD FS 的资源的 access_token。
协议图
在高级别上,本机应用程序的身份验证流看起来有点类似于:
请求授权代码
授权代码流始于客户端将用户定向到 /authorize 终结点。 在此请求中,客户端指示它需要从用户获取的权限:
// Line breaks for legibility only
https://adfs.contoso.com/adfs/oauth2/authorize?
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&resource=https://webapi.com/
&scope=openid
&state=12345
参数 | 必需/可选 | 说明 |
---|---|---|
client_id | 必需 | AD FS 分配给应用的应用程序(客户端)ID。 |
response_type | 必填 | 必须包含授权代码流的代码。 |
redirect_uri | 必需的 | 应用的 redirect_uri ,你的应用可通过该应用发送和接收身份验证响应。 它必须与你在客户端的 AD FS 中注册的其中一个 redirect_uris 完全匹配。 |
resource | 可选 | Web API 的 URL。 注意–如果使用 MSAL 客户端库,则不会发送资源参数。 相反,资源 URL 作为范围参数的一部分发送: scope = [resource url]//[scope values e.g., openid] 如果资源未在此处或作用域内传递,则 AD FS 使用默认资源 urn:microsoft:userinfo。 不能自定义 userinfo 资源策略,如 MFA、Issuance 或授权策略。 |
scope | 可选 | 范围的空格分隔列表。 |
response_mode | 可选 | 指定将生成的令牌送回到应用程序时应该使用的方法。 可以是以下方法之一: - query - fragment - form_post query 将代码作为查询字符串参数提供给你的重定向 URI。 如果你正在请求代码,则可以使用 query、fragment 或 form_post。 form_post 对重定向 URI 执行包含代码的 POST。 |
state | 可选 | 同时随令牌响应返回的请求中所包含的值。 可以是想要的任何内容的字符串。 随机生成的唯一值通常用于防止跨站点请求伪造攻击。 该值还可以用于在身份验证请求出现之前,在应用中编码有关用户状态的信息,例如他们所在的页面或视图。 |
prompt | 可选 | 表示需要的用户交互类型。 此时唯一有效的值是“sign-in”和“none”。 - prompt=login 强制用户在该请求上输入其凭据,从而拒绝单点登录。 - prompt=none 则相反,它会确保用户不会看到任何交互式提示。 如果请求无法通过单一登录静默完成,AD FS 将返回 interaction_required 错误。 |
login_hint | 可选 | 如果事先知道用户名,可用于预先填充用户登录页的用户名/电子邮件地址字段。 通常,应用在重新身份验证期间使用此参数,并且已经使用 id_token 中的 upn 声明从前次登录提取用户名。 |
domain_hint | 可选 | 如果包含,它将跳过用户在登录页面上经历的基于域的发现过程,从而使用户体验稍微更简单。 |
code_challenge_method | 可选 | 用于对 code_challenge 参数的 code_verifier 进行编码的方法。 可以是下列值之一: - plain - S256 如果排除,则假定 code_challenge 为 plaintext(如果包含了 code_challenge )。 AD FS 支持纯文本和 S256。 有关详细信息,请参阅 PKCE RFC。 |
code_challenge | 可选 | 用于通过本地客户端的 Proof Key for Code Exchange (PKCE) 保护授权代码授权。 如果包含 code_challenge_method ,则需要。 有关详细信息,请参阅 PKCE RFC |
此时,系统会要求用户输入凭据并完成身份验证。 用户进行身份验证后,AD FS 将使用 response_mode
参数中指定的方法,将响应返回到指示 redirect_uri
处的应用。
成功的响应
使用 response_mode=query 的成功响应如下所示:
GET https://adfs.contoso.com/common/oauth2/nativeclient?
code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
&state=12345
参数 | 说明 |
---|---|
code | 应用请求的 authorization_code 。 应用可以使用授权代码请求目标资源的访问令牌。 Authorization_codes 生存期较短,通常会在大约 10 分钟后过期。 |
state | 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应验证请求和响应中的状态值是否相同。 |
请求访问令牌
现在,你已获得 authorization_code
并已获得用户授予的权限,你可以将 access_token
的代码兑换到所需的资源。 通过向/token 端点发送 POST 请求来兑换代码:
// Line breaks for legibility only
POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com/
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for confidential clients (web apps)
参数 | 必需/可选 | 说明 |
---|---|---|
client_id | 必需 | AD FS 分配给应用的应用程序(客户端)ID。 |
grant_type | 必需 | 必须是授权代码流的 authorization_code 。 |
code | 必需的 | 在流的第一个阶段获取的 authorization_code 。 |
redirect_uri | 必需的 | 用于获取 authorization_code 的相同 redirect_uri 值。 |
client_secret | 对于 Web 应用是必需的 | 在 AD FS 中注册应用时创建的应用程序机密。 不应在本机应用中使用应用程序机密,因为 client_secrets 无法可靠地存储在设备上。 Web 应用和 Web API 是必需的,它们能够将 client_secret 安全地存储在服务器端。 在发送客户端机密之前,必须对其进行 URL 编码。 这些应用还可以通过对 JWT 进行签名并将其添加为 client_assertion 参数来使用基于密钥的身份验证。 |
code_verifier | 可选 | 用于获取 authorization_code 的相同 code_verifier 。 如果在授权码授权请求中使用 PKCE,则需要。 有关详细信息,请参阅 PKCE RFC。 此选项适用于 AD FS 2019 及更高版本 |
成功的响应
成功的令牌响应如下所示:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"token_type": "Bearer",
"expires_in": 3599,
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"refresh_token_expires_in": 28800,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
}
参数 | 说明 |
---|---|
access_token | 请求的访问令牌。 应用可以使用此令牌对受保护的资源 (Web API) 进行身份验证。 |
token_type | 指示令牌类型值。 AD FS 支持的唯一类型是 Bearer。 |
expires_in | 访问令牌有效的时间长度(以秒为单位)。 |
refresh_token | OAuth 2.0 刷新令牌。 应用可以使用此令牌,在当前访问令牌过期之后获取更多访问令牌。 refresh_token 的生存期较长,可用于长时间保留对资源的访问权限。 |
refresh_token_expires_in | 刷新令牌有效的时间长度(以秒为单位)。 |
id_token | JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
使用访问令牌
GET /v1.0/me/messages
Host: https://webapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
刷新令牌授予流
access_token 生存期较短,必须在过期后刷新,才能继续访问资源。 为此,可以将另一个 POST 请求提交到 /token
终结点,这一次提供 refresh_token 而不是代码。 对于客户端已经收到访问令牌的所有权限,刷新令牌都有效。
刷新令牌没有指定的生存期。 通常,刷新令牌的生存期相对较长。 但是,在某些情况下,刷新令牌会过期、被吊销,或缺少执行所需操作的足够权限。 应用程序需要正确预期和处理令牌颁发终结点返回的错误。
尽管刷新令牌在用于获取新的访问令牌时不会被吊销,但你应丢弃旧的刷新令牌。 按照 OAuth 2.0 规范指示:“授权服务器可能会发出一个新的刷新令牌,在这种情况下,客户端必须放弃旧的刷新令牌并将其替换为新的刷新令牌。 授权服务器可以在向客户端发出新的刷新令牌之后撤销旧的刷新令牌。如果新的刷新令牌生存期比以前的刷新令牌生存期长,AD FS 会发出刷新令牌。 若要查看 AD FS 刷新令牌生存期的其他信息,请访问 AD FS 单一登录设置。
// Line breaks for legibility only
POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for confidential clients (web apps)
参数 | 必需/可选 | 说明 |
---|---|---|
client_id | 必需 | AD FS 分配给应用的应用程序(客户端)ID。 |
grant_type | 必需 | 必须是授权代码流的此阶段的 refresh_token 。 |
resource | 可选 | Web API 的 URL。 注意–如果使用 MSAL 客户端库,则不会发送资源参数。 相反,资源 URL 作为范围参数的一部分发送: scope = [resource url]//[scope values e.g., openid] 如果资源未在此处或作用域内传递,则 AD FS 使用默认资源 urn:microsoft:userinfo。 不能自定义 userinfo 资源策略,如 MFA、Issuance 或授权策略。 |
scope | 可选 | 范围的空格分隔列表。 |
refresh_token | 必需的 | 在流的第二个阶段获取的 refresh_token。 |
client_secret | 对于 Web 应用是必需的 | 在应用注册门户中为应用创建的应用程序机密。 不应在本机应用中使用它,因为 client_secrets 无法可靠地存储在设备上。 Web 应用和 Web API 都需要应用程序密钥,它能够将 client_secret 安全地存储在服务器端。 这些应用还可以通过对 JWT 进行签名并将其添加为 client_assertion 参数来使用基于密钥的身份验证。 |
成功的响应
成功的令牌响应如下所示:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"token_type": "Bearer",
"expires_in": 3599,
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"refresh_token_expires_in": 28800,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
}
参数 | 说明 |
---|---|
access_token | 请求的访问令牌。 应用可以使用此令牌对受保护的资源(如 Web API)进行身份验证。 |
token_type | 指示令牌类型值。 AD FS 支持的唯一类型是 Bearer |
expires_in | 访问令牌有效的时间长度(以秒为单位)。 |
scope | access_token 的有效范围。 |
refresh_token | OAuth 2.0 刷新令牌。 应用可以使用此令牌,在当前访问令牌过期之后获取更多访问令牌。 refresh_token 的生存期较长,可用于长时间保留对资源的访问权限。 |
refresh_token_expires_in | 刷新令牌有效的时间长度(以秒为单位)。 |
id_token | JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
OAuth 的代码交换证明密钥(PKCE)支持
使用授权代码授予的 OAuth 公共客户端容易受到授权代码拦截攻击,如 RFC 7636 中所述。 为了缓解这些攻击,从 Windows Server 2019 开始,AD FS 现在支持 OAuth 授权代码授予流的代码交换(PKCE)证明密钥。
PKCE 支持规范向 OAuth 2.0 授权和访问令牌请求添加了更多参数。 下图显示了当客户端联系 Windows Server 2019 中的 AD FS 时 PKCE 过程的可视轮廓。
在标记为 A 的节中,客户端创建并记录一个命名 code_verifier
的机密,并派生名为的 t(code_verifier)
已转换版本的机密,也称为 code_challenge
。 然后,客户端将 OAuth 2.0 授权请求中的机密以及转换方法一起 t_m
发送。
在标记为 B 的节中,授权终结点会像往常一样响应,但会记录 t(code_verifier)
机密和转换方法。
在标记为 C 的节中,客户端随后像往常一样在访问令牌请求中发送授权代码,但包括 code_verifier
A 节中生成的机密。
在标记为 D 的分区中,AD FS 转换 code_verifier
机密并将其与 B 节中的 t(code_verifier)
机密进行比较。如果它们的值不相等,AD FS 将拒绝访问。
如何在 Windows Server 2019 中为同一规则策略选择多个身份验证提供程序
AD FS 已支持基于声明规则策略(RP)触发额外身份验证。 这些策略可以为特定 RP 或全局级别设置这些策略。 可以通过以管理员身份打开 PowerShell 并运行 Set-AdfsRelyingPartyTrust cmdlet 来为特定 RP 设置额外的身份验证策略,方法是传递 AdditionalAuthenticationRules 或 AdditionalAuthenticationRulesFile 参数。 若要全局设置,管理员可以使用 Set-AdfsAdditionalAuthenticationRule cmdlet。 通过设置额外的策略,可以为同一应用程序使用多个身份验证提供程序。
声明规则提供用于选择身份验证提供程序以执行其他身份验证的选项,这在客户在提供程序之间切换或要求特定应用程序的提供程序不同的情况下非常有用。 从 Windows Server 2019 开始,现在可以使用声明规则来决定要调用的其他身份验证提供程序进行额外的身份验证。 此功能适用于两种方案:
用户正在从其他身份验证提供程序转换到另一个身份验证提供程序。 将用户加入到较新的身份验证提供程序时,他们可以使用组来控制服务使用的额外身份验证提供程序。
用户需要特定应用程序的特定额外身份验证提供程序,但还需要对其他应用程序使用不同的方法。
可以通过从其他身份验证策略运行以下命令来配置这些设置:
Set-AdfsAdditionalAuthenticationRule -AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", value = "http://schemas.microsoft.com/claims/multipleauthn" );'
若要设置此规则,必须从其他身份验证策略发出声明 http://schemas.microsoft.com/claims/authnmethodsproviders
。 此声明的值应为身份验证提供程序的名称变量。
还可以修改此规则配置,以帮助用户从一个身份验证提供程序过渡到另一个身份验证提供程序。 例如,假设你想要修改一个组,以便管理使用 Azure AD MFA,另一个组将证书用作额外的身份验证提供程序。
如果要跟踪有多少人注册 Azure AD MFA 和证书身份验证,将运行如下所示的命令,其中的值替换为与组织相关的值:
'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", Value = "http://schemas.microsoft.com/claims/multipleauthn" );
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value == "S-1-5-21-608905689-872870963-3921916988-12345"] => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "AzureMfaAuthentication");
not exists([Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value=="S-1-5-21-608905689-872870963-3921916988-12345"]) => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "CertificateAuthentication");’
接下来,可以通过运行以下命令将第一个调用 AppA
的应用程序设置为使用 Azure AD 多重身份验证作为额外的身份验证提供程序:
Set-AdfsRelyingPartyTrust -TargetName AppA -AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", Value = "http://schemas.microsoft.com/claims/multipleauthn" );
c:[] => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "AzureMfaAuthentication");'
最后,可以通过运行以下命令将第二个应用(称为 AppB
)设置为使用证书作为额外的身份验证提供程序:
Set-AdfsRelyingPartyTrust -TargetName AppB -AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", Value = "http://schemas.microsoft.com/claims/multipleauthn" );
c:[] => issue(Type = "http://schemas.microsoft.com/claims/authnmethodsproviders", Value = "CertificateAuthentication");'
管理员还可以制定规则以允许多个额外身份验证提供程序。 在这种情况下,AD FS 显示已颁发的身份验证方法提供程序,用户可以选择其中任何一个。 若要允许多个额外的身份验证提供程序,应使用该值 http://schemas.microsoft.com/claims/authnmethodsproviders
发出多个声明。
如果声明评估未返回任何身份验证提供程序,AD FS 将回滚并显示一个列表,其中显示了 AD FS 上管理员配置的所有额外身份验证提供程序。 然后,用户必须手动选择相应的身份验证提供程序。
如果首选的身份验证提供程序不在列表中,可以运行以下 cmdlet 以查看所有受支持的提供程序:
(Get-AdfsGlobalAuthenticationPolicy).AdditionalAuthenticationProvider
用于 http://schemas.microsoft.com/claims/authnmethodsproviders
声明的值应该是由返回的提供程序 AD FS 列表返回的提供程序名称之一。
AD FS 不支持在 AD FS Windows Server 2016 中使用访问控制策略时触发特定的额外身份验证提供程序。 将应用程序移出访问控制策略时,AD FS 会将相应的策略从访问控制策略复制到 AdditionalAuthenticationRules 和 IssuanceAuthorizationRules。 如果管理员想要使用特定的身份验证提供程序,则应停止使用访问控制策略并修改 AdditionalAuthenticationRules。
代表流
注意
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中的代理流的详细信息,请参阅 Microsoft 标识平台中的代理流。
OAuth 2.0 代表 (OBO) 流适用于以下用例:应用程序调用服务/Web API,而后者又需要调用另一个服务/Web API。 其思路是通过请求链传播委派的用户标识和权限。 为了使中间层服务向下游服务发出经过身份验证的请求,需要代表用户保护 AD FS 的访问令牌。
协议图
假设已使用上一节中描述的 OAuth 2.0 授权代码授予流在应用程序上对用户进行了身份验证。 此时,应用程序具有 API A 的访问令牌(令牌 A),该令牌具有用户的声明并同意访问中间层 Web API (API A)。 请确保客户端请求令牌中的 user_impersonation 范围。 现在,API A 需要向下游 Web API (API B) 发出经过身份验证的请求。
接下来的步骤构成了 OBO 流,并在下图中进行说明。
- 客户端应用程序使用令牌 A 向 API A 发出请求。注意:在 AD FS 中配置 OBO 流时,请确保选择范围
user_impersonation
,并且客户端在请求中确实请求了user_impersonation
范围。 - API A 向 AD FS 令牌颁发终结点进行身份验证,并请求用于访问 API B 的令牌。注意:在 AD FS 中配置此流时,请确保 API A 也注册为服务器应用程序,其 clientID 与 API A 中的资源 ID 具有相同的值。
- AD FS 令牌颁发终结点使用令牌 A 验证 API A 的凭据,并颁发 API B 的访问令牌(令牌 B)。
- 令牌 B 在针对 API B 的请求授权标头中设置。
- API B 返回受保护资源中的数据。
服务到服务访问令牌请求
要请求访问令牌,请使用以下参数向 AD FS 令牌终结点发出 HTTP POST。
第一种情况:使用共享机密的访问令牌请求
对于共享机密时,服务到服务访问令牌请求包含以下参数:
参数 | 必需/可选 | 说明 |
---|---|---|
grant_type | 必需的 | 令牌请求的类型。 对于使用 JWT 的请求,此值必须为 urn:ietf:params:oauth:grant-type:jwt-bearer。 |
client_id | 必需的 | 将第一个 Web API 注册为服务器应用(中间层应用)时配置的客户端 ID。 这应该与第一个阶段中使用的资源 ID 相同,即第一个 Web API 的 URL。 |
client_secret | 必需 | 在 AD FS 中注册服务器应用时创建的应用程序机密。 |
assertion | 必需 | 请求中使用的令牌的值。 |
requested_token_use | 必需 | 指定应如何处理请求。 在 OBO 流中,该值必须设置为 on_behalf_of |
resource | 必需的 | 将第一个 Web API 注册为服务器应用(中间层应用)时提供的资源 ID。 资源 ID 应为第二个 Web API 中间层应用的 URL,它将代表客户端调用。 |
scope | 可选 | 令牌请求范围的空格分隔列表 |
示例
以下 HTTP POST
请求访问令牌和刷新令牌
//line breaks for legibility only
POST /adfs/oauth2/token HTTP/1.1
Host: adfs.contoso.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=https://webapi.com/
&client_secret=BYyVnAt56JpLwUcyo47XODd
&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIm…
&resource=https://secondwebapi.com/
&requested_token_use=on_behalf_of
&scope=openid
第二种情况:使用证书的访问令牌请求
使用证书的服务到服务访问令牌请求包含以下参数:
参数 | 必需/可选 | 说明 |
---|---|---|
grant_type | 必需的 | 令牌请求的类型。 对于使用 JWT 的请求,此值必须为 urn:ietf:params:oauth:grant-type:jwt-bearer。 |
client_id | 必需的 | 将第一个 Web API 注册为服务器应用(中间层应用)时配置的客户端 ID。 这应该与第一个阶段中使用的资源 ID 相同,即第一个 Web API 的 URL。 |
client_assertion_type | 必需 | 该值必须为 urn:ietf:params:oauth:client-assertion-type:jwt-bearer。 |
client_assertion | 必填 | 需要使用注册为应用程序凭据的证书进行创建和签名的断言(JSON Web 令牌)。 |
assertion | 必需 | 请求中使用的令牌的值。 |
requested_token_use | 必需 | 指定应如何处理请求。 在 OBO 流中,该值必须设置为 on_behalf_of |
resource | 必需的 | 将第一个 Web API 注册为服务器应用(中间层应用)时提供的资源 ID。 资源 ID 应为第二个 Web API 中间层应用的 URL,它将代表客户端调用。 |
scope | 可选 | 令牌请求范围的空格分隔列表 |
请注意,参数几乎相同。 此示例类似于共享机密的请求,只不过 client_secret 参数被替换为两个参数:client_assertion_type 和 client_assertion。
示例
以下 HTTP POST 使用证书请求 Web API 的访问令牌。
// line breaks for legibility only
POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&client_id= https://webapi.com/
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNS…
&resource=https://secondwebapi.com/
&requested_token_use=on_behalf_of
&scope= openid
服务到服务访问令牌响应
成功响应是具有以下参数的 JSON OAuth 2.0 响应。
参数 | 说明 |
---|---|
token_type | 指示令牌类型值。 AD FS 支持的唯一类型是 Bearer。 |
scope | 令牌中授予的访问权限的范围。 |
expires_in | 访问令牌有效的时间长度(以秒为单位)。 |
access_token | 请求的访问令牌。 调用方服务可以使用此令牌向接收方服务进行身份验证。 |
id_token | JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
refresh_token | 所请求的访问令牌的刷新令牌。 在当前访问令牌过期后,调用服务可以使用此令牌请求另一个访问令牌。 |
refresh_token_expires_in | 刷新令牌有效的时间长度(以秒为单位)。 |
成功响应示例
下面的示例演示对请求 Web API 访问令牌的成功响应。
{
"token_type": "Bearer",
"scope": openid,
"expires_in": 3269,
"access_token": "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCbmZpRy1t"
"id_token": "aWRfdG9rZW49ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKU1V6STFOa"
"refresh_token": "OAQABAAAAAABnfiG…"
"refresh_token_expires_in": 28800,
}
使用访问令牌访问受保护的资源,现在中间层服务可以使用上述响应示例中获取的令牌向下游 Web API 发出身份验证请求,方法是在 Authorization 标头中设置令牌。
示例
GET /v1.0/me HTTP/1.1
Host: https://secondwebapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCbmZpRy1tQ…
客户端凭据授予流
注意
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中客户端凭据授权流的详细信息,请参阅 Microsoft 标识平台中的客户端凭据授权流。
你可以使用 RFC 6749 中指定的 OAuth 2.0 客户端凭据授予,以使用应用程序的标识访问 Web 托管的资源。 这种授予通常用于必须在后台运行的服务器间交互,不需要立即与用户交互。 此类应用程序通常称为守护程序或服务帐户。
OAuth 2.0 客户端凭据授权流允许 Web 服务(机密客户端)在调用其他 Web 服务时使用它自己的凭据(而不是模拟用户)进行身份验证。 在此方案中,客户端通常是中间层 Web 服务、守护程序服务或网站。 为了获得更高的保障级别,AD FS 还允许调用服务使用证书(而不是共享机密)作为凭据。
协议图
下图显示了客户端凭据授予流。
请求令牌
要使用客户端凭据授予获取令牌,请将 POST
请求发送到 /token AD FS 终结点:
第一种情况:使用共享机密的访问令牌请求
POST /adfs/oauth2/token HTTP/1.1
//Line breaks for clarity
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&client_secret=qWgdYAmab0YSkuL1qKv5bPX
&grant_type=client_credentials
参数 | 必需/可选 | 说明 |
---|---|---|
client_id | 必需 | AD FS 分配给应用的应用程序(客户端)ID。 |
scope | 可选 | 希望用户同意的作用域的空格分隔列表。 |
client_secret | 必需的 | 在应用注册门户中为应用生成的客户端机密。 在发送客户端机密之前,必须对其进行 URL 编码。 |
grant_type | 必需 | 必须设置为 client_credentials 。 |
第二种情况:使用证书访问令牌请求
POST /adfs/oauth2/token HTTP/1.1
// Line breaks for clarity
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded
&client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNScUtqRlBuZDdSRnd2d1pJMCJ9.eyJ{a lot of characters here}M8U3bSUKKJDEg
&grant_type=client_credentials
参数 | 必需/可选 | 说明 |
---|---|---|
client_assertion_type | 必需 | 该值必须设置为 urn:ietf:params:oauth:client-assertion-type:jwt-bearer。 |
client_assertion | 必填 | 需要使用注册为应用程序凭据的证书进行创建和签名的断言(JSON Web 令牌)。 |
grant_type | 必需 | 必须设置为 client_credentials 。 |
client_id | 可选 | AD FS 分配给应用的应用程序(客户端)ID。 它是 client_assertion 的一部分,因此不需要在此处传递。 |
scope | 可选 | 希望用户同意的作用域的空格分隔列表。 |
使用令牌
获取令牌后,请使用令牌向资源发出请求。 当令牌过期时,对 /token 终结点重复此请求以获取新的访问令牌。
GET /v1.0/me/messages
Host: https://webapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
资源所有者密码凭据授予流(不推荐)
注意
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中资源所有者密码凭据授权流的详细信息,请参阅 Microsoft 标识平台中的资源所有者密码凭据授权流。
资源所有者密码凭据 (ROPC) 授予允许应用程序通过直接处理密码来登录用户。 ROPC 流需要高度的信任和用户暴露,只应在不能使用其他更安全的流时使用此流。
协议图
下图显示了 ROPC 流。
授权请求
ROPC 流是单个请求,它将客户端标识和用户的凭据发送到 IDP,然后接收返回的令牌。 在执行此操作之前,客户端必须请求用户的电子邮件地址 (UPN) 和密码。 在成功进行请求之后,客户端应立即以安全方式释放内存中的用户凭据, 客户端不得保存客户凭据。
// Line breaks and spaces are for legibility only.
POST /adfs/oauth2/token HTTP/1.1
Host: https://adfs.contoso.com
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope= openid
&username=myusername@contoso.com
&password=SuperS3cret
&grant_type=password
参数 | 必需/可选 | 说明 |
---|---|---|
client_id | 必需的 | 客户端 ID |
grant_type | 必需 | 必须设置为 password。 |
username | 必需的 | 用户的电子邮件地址。 |
password | 必需的 | 用户的密码。 |
scope | 可选 | 范围的空格分隔列表。 |
成功的身份验证响应
以下示例显示了一个成功的令牌响应:
{
"token_type": "Bearer",
"scope": "openid",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIn...",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"refresh_token_expires_in": 28800,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDR..."
}
参数 | 说明 |
---|---|
token_type | 始终设置为 Bearer。 |
scope | 如果返回访问令牌,此参数会列出访问令牌有效的范围。 |
expires_in | 包含的访问令牌有效的秒数。 |
access_token | 针对请求的作用域颁发。 |
id_token | JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
refresh_token_expires_in | 包含的刷新令牌有效的秒数。 |
refresh_token | 如果原始作用域参数包含 offline_access,则颁发。 |
你可以使用刷新令牌获取新的访问令牌,并使用本文的“身份验证代码授予流”一节中所述的相同流来刷新令牌。
设备代码流
注意
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中设备代码流的详细信息,请参阅 Microsoft 标识平台中的设备代码流。
设备代码授予允许用户登录到受输入约束的设备,例如智能电视、IoT 设备或打印机。 要启用此流,设备让用户在另一台设备上的浏览器中访问某个网页,以便登录。 用户登录后,设备能够根据需要获取访问令牌和刷新令牌。
协议图
整个设备代码流如下图所示。 本文稍后介绍每个步骤。
设备授权请求
客户端必须先在身份验证服务器中检查用于发起身份验证的设备代码和用户代码。 客户端从 /devicecode
终结点收集此请求。 在此请求中,客户端应包含需要从用户获取的权限。 从发送此请求的那一刻起,用户只有 15 分钟的时间登录(expires_in 的常用值),因此,仅当用户指示他们已准备好登录时才发出此请求。
// Line breaks are for legibility only.
POST https://adfs.contoso.com/adfs/oauth2/devicecode
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
scope=openid
参数 | 条件 | 说明 |
---|---|---|
client_id | 必需 | AD FS 分配给应用的应用程序(客户端)ID。 |
scope | 可选 | 范围的空格分隔列表。 |
设备授权响应
成功的响应是一个 JSON 对象,其中包含允许用户登录所需的信息。
参数 | 说明 |
---|---|
device_code | 用于验证客户端和授权服务器之间的会话的长字符串。 客户端使用此参数来请求授权服务器提供访问令牌。 |
user_code | 向用户显示的短字符串,用于标识辅助设备上的会话。 |
verification_uri | 用户为了登录而应使用 user_code 进入的 URI。 |
verification_uri_complete | 用户为了登录而应使用 user_code 进入的 URI。 已经预先填充了 user_code,因此用户无需输入 user_code |
expires_in | device_code 和 user_code 过期之前的秒数。 |
interval | 客户端在轮询请求之间应等待的秒数。 |
消息 | 用户可读的字符串,其中包含有关用户的说明。 可以通过在 ?mkt=xx-XX 格式的请求中包含查询参数并填写相应的语言区域性代码来进行本地化。 |
对用户进行身份验证
客户端收到 user_code 和 verification_uri 之后,客户端会向用户显示这些详细信息,指示他们使用移动电话或 PC 浏览器登录。 此外,客户端可以使用 QR 码或类似的机制来显示 verfication_uri_complete,这将采取输入用户 user_code 的步骤。
当用户在verification_uri进行身份验证时,客户端应使用device_code轮询 /token
请求的令牌的终结点。
POST https://adfs.contoso.com /adfs/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type: urn:ietf:params:oauth:grant-type:device_code
client_id: 00001111-aaaa-2222-bbbb-3333cccc4444
device_code: GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8
参数 | 必需的 | 说明 |
---|---|---|
grant_type | 必需 | 必须为 urn:ietf:params:oauth:grant-type:device_code |
client_id | 必需 | 必须与初始请求中使用的 client_id 匹配。 |
code | 必填 | 设备授权请求中返回的 device_code。 |
成功的身份验证响应
成功的令牌响应如下所示:
参数 | 说明 |
---|---|
token_type | 始终为 Bearer。 |
scope | 如果返回访问令牌,则会参数会列出访问令牌有效的范围。 |
expires_in | 包含的访问令牌失效之前的秒数。 |
access_token | 针对请求的作用域颁发。 |
id_token | 如果原始作用域参数包含 openid 范围,则颁发。 |
refresh_token | 如果原始作用域参数包含 offline_access,则颁发。 |
refresh_token_expires_in | 包含的刷新令牌失效之前的秒数。 |
相关内容
请参阅 AD FS 开发获取演练文章的完整列表,其中提供了有关如何使用相关流的分步说明。