适用于 Active Directory 联合身份验证服务 (AD FS) 2019 及更高版本
Scenario | 包含示例的方案演练 | OAuth 2.0 流/授予 | 客户端类型 |
---|---|---|---|
单页应用 | 使用 MSAL 的示例 | Implicit | Public |
用于登录用户的 Web 应用 | 授权代码 | 公用、机密 | |
本机应用调用 Web API | 使用 MSAL 的示例 | 授权代码 | Public |
Web 应用调用 Web API | 使用 MSAL 的示例 | 授权代码 | Confidential |
PKCE 实现 | 授权代码 | Public | |
Web API 代表 (OBO) 用户调用另一个 Web API | 使用 MSAL 的示例 | On-behalf-of | Web 应用充当机密 |
守护程序应用调用 Web API | 客户端凭据 | Confidential | |
Web 应用使用用户凭据调用 Web API | 资源所有者密码凭据 | 公用、机密 | |
无浏览器应用调用 Web API | 设备代码 | 公用、机密 |
隐式授予流
Note
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中的隐式授权流的详细信息,请参阅 Microsoft 标识平台中的隐式授权流。
对于单页应用程序(AngularJS、Ember.js、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
Parameter | Required/optional | Description |
---|---|---|
client_id | required | AD FS 分配给应用的应用程序(客户端)ID。 |
response_type | required | 必须包含 OpenID Connect 登录的 id_token 。 它还可以包括response_type token 。 在此处使用 token ,应用可以立即从授权终结点接收访问令牌,而无需向令牌终结点发出第二个请求。 |
redirect_uri | required | 应用的 redirect_uri,你的应用可在其中发送和接收身份验证响应。 它必须与你在 AD FS 中配置的其中一个 redirect_uris 完全匹配。 |
nonce | required | 包含在请求中的值,由应用生成,这些值将作为声明包含在生成的 id_token 中。 应用程序接着便可确认此值,以减轻令牌重放攻击的风险。 此值通常是随机的唯一字符串,可用以识别请求的来源。 仅在请求 id_token 时是必需的。 |
作用域 | optional | 范围的空格分隔列表。 对于 OpenID Connect,它必须包含作用域 openid 。 |
资源 | optional | Web API 的 URL。 注意 – 如果使用 MSAL 客户端库,则不会发送资源参数。 而是将资源 URL 作为范围参数的一部分发送: scope = [resource url]//[scope values, for example, openid] 如果未在此处或在此范围内传递资源,AD FS 将使用默认资源 urn:microsoft:userinfo。 无法自定义用户信息资源策略,例如 MFA、颁发或授权策略。 |
response_mode | optional | 指定应该用于将生成的令牌发送回应用的方法。 默认为 fragment 。 |
状态 | optional | 请求中包含的一个值,该值也将在令牌响应中返回。 可以是所需的任何内容的字符串。 随机生成的唯一值通常用于防止跨站点请求伪造攻击。 该状态还用于在身份验证请求发生之前,于应用中编码关于用户状态的信息,例如他们之前访问的页面或视图。 |
提示 | optional | 指示所需的用户交互类型。 目前唯一有效的值为登录和无。 - prompt=login 强制用户在该请求上输入其凭据,从而取消单一登录。 - prompt=none 相反 - 它确保用户未显示任何交互式提示。 如果请求无法通过单一登录静默完成,AD FS 将返回 interaction_required 错误。 |
login_hint | optional | 如果事先知道用户名,可用于预先填充用户登录页的用户名/电子邮件地址字段。 通常,应用会在重新身份验证期间使用此参数,之前已通过使用 upn 声明从 id_token 的登录信息中提取出用户名。 |
domain_hint | optional | 如果包含此值,则会跳过用户在登录页上经历的基于域的发现过程,从而导致略微简化的用户体验。 |
此时,系统会要求用户输入凭据并完成身份验证。 用户进行身份验证后,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
Parameter | Description |
---|---|
access_token | 如果 response_type 包含 token ,则包含此参数。 |
token_type | 如果 response_type 包含 token ,则包含此参数。 始终为 Bearer 。 |
expires_in | 如果 response_type 包含 token ,则包含此参数。 指示令牌有效(以缓存为目的)的秒数。 |
作用域 | 指示 access_token 有效使用的范围。 |
id_token | 如果 response_type 包含 id_token ,则包含此参数。 有符号 JSON Web 令牌 (JWT)。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
状态 | 如果请求中包含 state 参数,响应中就应该出现相同的值。 应用应验证请求和响应中的状态值是否相同。 |
刷新令牌
隐式授予不提供刷新令牌。 id_tokens和access_tokens都在短时间内过期,因此应用程序必须准备好定期刷新这些令牌。 若要刷新任何类型的令牌,可以执行与前面部分相同的隐藏 iframe 请求,并使用 prompt=none
参数来控制标识平台的行为。 如果想要接收新的id_token
,请务必使用response_type=id_token
。
授权码授权流程
Note
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 应用和本机安装的应用。 流程使应用程序能够安全地获取 access_token,以访问信任 AD FS 的资源。
协议关系图
在高级别上,本机应用程序的身份验证流看起来有点类似于:
请求授权代码
授权代码流始于客户端将用户定向到 /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
Parameter | Required/optional | Description |
---|---|---|
client_id | required | AD FS 分配给应用的应用程序(客户端)ID。 |
response_type | required | 必须包含授权代码流的代码。 |
redirect_uri | required | 应用的 redirect_uri ,应用可以在此发送和接收身份验证响应。 它必须与在 AD FS 中为客户端注册的其中一个重定向 URI 完全匹配。 |
资源 | optional | Web API 的 URL。 注意 – 如果使用 MSAL 客户端库,则不会发送资源参数。 而是将资源 URL 作为范围参数的一部分发送: scope = [resource url]//[scope values, for example, openid] 如果未在此处或在此范围内传递资源,AD FS 将使用默认资源 urn:microsoft:userinfo。 无法自定义用户信息资源策略,例如 MFA、颁发或授权策略。 |
作用域 | optional | 范围的空格分隔列表。 |
response_mode | optional | 指定应该用于将生成的令牌发送回应用的方法。 可以是以下方法之一: - query - fragment - form_post query 会将代码作为查询字符串参数提供到你的重定向 URI。 如果你正在请求代码,则可以使用 query、fragment 或 form_post。 form_post 对重定向 URI 执行包含代码的 POST。 |
状态 | optional | 请求中包含的一个值,该值也将在令牌响应中返回。 可以是所需的任何内容的字符串。 随机生成的唯一值通常用于防止跨站点请求伪造攻击。 该值还可以用于在身份验证请求出现之前,在应用中编码有关用户状态的信息,例如他们所在的页面或视图。 |
提示 | optional | 指示所需的用户交互类型。 目前唯一有效的值为登录和无。 - prompt=login 强制用户在该请求上输入其凭据,从而取消单一登录。 - prompt=none 相反 - 它确保用户未显示任何交互式提示。 如果请求无法通过单一登录以无提示方式完成,AD FS 将返回“interaction_required”错误。 |
login_hint | optional | 如果事先知道用户名,可用于预先填充用户登录页的用户名/电子邮件地址字段。 通常,应用在重新身份验证期间使用此参数,并且已经使用来自 id_token 的 upn 声明从前次登录提取用户名。 |
domain_hint | optional | 如果包含此值,则会跳过用户在登录页上经历的基于域的发现过程,从而导致略微简化的用户体验。 |
code_challenge_method | optional | 用于对 code_challenge 参数的 code_verifier 进行编码的方法。 可以是下列值之一: - 纯 - S256 如果排除此值,则假定code_challenge为纯文本(如果 code_challenge 包含)。 AD FS 支持纯文本和 S256。 有关详细信息,请参阅 PKCE RFC。 |
code_challenge | optional | 用于通过 Proof Key for Code Exchange (PKCE) 从本地客户端安全地获取授权代码授权。 如果包含 code_challenge_method ,则需要。 有关详细信息,请参阅 PKCE RFC。 |
此时,系统会要求用户输入凭据并完成身份验证。 用户验证身份后,AD FS 将按照参数中指定的方法在指示的redirect_uri
处返回对应用程序的响应。
成功的响应
使用 response_mode=query 时,成功的响应如下所示:
GET https://adfs.contoso.com/common/oauth2/nativeclient?
code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
&state=12345
Parameter | Description |
---|---|
代码 | 应用请求的 authorization_code 。 应用可以使用授权代码请求目标资源的访问令牌。 authorization_code 生存期较短。 它们通常在大约 10 分钟后过期。 |
状态 | 如果请求中包含 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)
Parameter | Required/optional | Description |
---|---|---|
client_id | required | AD FS 分配给应用的应用程序(客户端)ID。 |
grant_type | required | 必须是授权代码流的 authorization_code 。 |
代码 | required | 你在流的第一个步骤中获取的 authorization_code 。 |
redirect_uri | required | 用于获取 redirect_uri 的同一 authorization_code 值。 |
client_secret | 对于 Web 应用是必需的 | 在 AD FS 中注册应用时创建的应用程序机密。 不应在本机应用中使用应用程序机密,因为 client_secrets 无法可靠地存储在设备上。 Web 应用和 Web API 需要此参数,这些 API 能够安全地将client_secret存储在服务器端。 在发送客户端机密之前,必须对其进行 URL 编码。 这些应用还可以通过对 JWT 进行签名并将其添加为client_assertion参数来使用基于密钥的身份验证。 |
code_verifier | optional | 用于获取 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...",
}
Parameter | Description |
---|---|
access_token | 请求的访问令牌。 应用可以使用此令牌向安全资源(Web API)进行身份验证。 |
token_type | 指示令牌类型值。 AD FS 支持的唯一类型是 Bearer。 |
expires_in | 访问令牌有效的时间长度(以秒为单位)。 |
refresh_token | OAuth 2.0 刷新令牌。 应用可以使用此令牌,在当前访问令牌过期之后获取更多访问令牌。 refresh_token 的生存期较长,可用于长时间保留对资源的访问权限。 |
refresh_token_expires_in | 刷新令牌有效的时间长度(以秒为单位)。 |
id_token | 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)
Parameter | Required/optional | Description |
---|---|---|
client_id | required | AD FS 分配给应用的应用程序(客户端)ID。 |
grant_type | required | 必须是授权代码流的此阶段的 refresh_token 。 |
资源 | optional | Web API 的 URL。 注意 – 如果使用 MSAL 客户端库,则不会发送资源参数。 而是将资源 URL 作为范围参数的一部分发送: scope = [resource url]//[scope values, for example, openid] 如果未在此处或在此范围内传递资源,AD FS 将使用默认资源 urn:microsoft:userinfo。 无法自定义用户信息资源策略,例如 MFA、颁发或授权策略。 |
作用域 | optional | 范围的空格分隔列表。 |
refresh_token | required | 在流的第二个阶段获取的 refresh_token。 |
client_secret | 对于 Web 应用是必需的 | 在应用注册门户中为应用程序创建的应用程序机密。 不应在本机应用中使用它,因为 client_secrets 无法可靠地存储在设备上。 Web 应用和 Web API 需要此参数,这些 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...",
}
Parameter | Description |
---|---|
access_token | 请求的访问令牌。 应用可以使用此令牌对受保护的资源(如 Web API)进行身份验证。 |
token_type | 指示令牌类型值。 AD FS 支持的唯一类型是 Bearer。 |
expires_in | 访问令牌有效的时间长度(以秒为单位)。 |
作用域 | access_token 的有效范围。 |
refresh_token | OAuth 2.0 刷新令牌。 应用可以使用此令牌,在当前访问令牌过期之后获取更多访问令牌。 Refresh_tokens寿命较长,可用于长时间保留访问资源的权限。 |
refresh_token_expires_in | 刷新令牌有效的时间长度(以秒为单位)。 |
id_token | JWT。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
OAuth 代码交换证明密钥(PKCE)支持
使用授权代码授予的 OAuth 公共客户端容易受到授权代码拦截攻击,如 RFC 7636 中所述。 为了缓解这些攻击,从 Windows Server 2019 开始,AD FS 支持 OAuth 授权码授予流程中的 Proof Key for Code Exchange (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,传递 AdditionalAuthenticationRules 或 AdditionalAuthenticationRulesFile 参数来为特定 RP 设置额外的身份验证策略。 若要全局设置,管理员可以使用 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
。 此声明的值应为身份验证提供程序的名称变量。
还可以修改此规则配置,以帮助用户从一个身份验证提供程序过渡到另一个身份验证提供程序。 例如,假设你想要修改你管理的一个组以使用 Microsoft Entra 多重身份验证(MFA),以及另一个组以将证书用作额外的身份验证提供程序。
如果要跟踪注册 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
的应用程序设置为使用 MFA 作为额外的身份验证提供程序:
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 不支持在 RP 使用 AD FS Windows Server 2016 中的访问控制策略时触发特定的额外身份验证提供程序。 将应用程序移出访问控制策略时,AD FS 会将相应的策略从访问控制策略复制到 AdditionalAuthenticationRules 和 IssuanceAuthorizationRules。 如果管理员想要使用特定的身份验证提供程序,则应停止使用访问控制策略,并改为修改 AdditionalAuthenticationRules。
代理流
Note
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 也注册为具有与 API A 中的资源 ID 相同的值的客户端应用程序。
- AD FS 令牌颁发终结点使用令牌 A 验证 API A 的凭据,并颁发 API B 的访问令牌(令牌 B)。
- 令牌 B 在针对 API B 的请求授权标头中设置。
- API B 返回受保护资源中的数据。
服务到服务访问令牌请求
若要请求访问令牌,请使用以下部分所述的参数向 AD FS 令牌终结点发出 HTTP POST。
第一种情况:使用共享机密访问令牌请求
对于共享密钥,服务间访问令牌请求包含以下参数:
Parameter | Required/optional | Description |
---|---|---|
grant_type | required | 令牌请求的类型。 对于使用 JWT 的请求,此值必须为 urn:ietf:params:oauth:grant-type:jwt-bearer。 |
client_id | required | 将第一个 Web API 注册为服务器应用(中间层应用)时配置的客户端 ID。 它应与第一回合中使用的资源 ID 相同,即第一个 Web API 的 URL。 |
client_secret | required | 在 AD FS 中注册服务器应用时创建的应用程序机密。 |
断言 | required | 请求中使用的令牌的值。 |
requested_token_use | required | 指定应如何处理请求。 在 OBO 流中,该值必须设置为 on_behalf_of。 |
资源 | required | 将第一个 Web API 注册为服务器应用(中间层应用)时提供的资源 ID。 资源 ID 应是代表客户端进行第二个 Web API 的中间层应用调用的 URL。 |
作用域 | optional | 空格分隔的令牌请求范围的列表。 |
Example
下面的 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
第二种情况:使用证书的访问令牌请求
使用证书的服务到服务访问令牌请求包含以下参数:
Parameter | Required/optional | Description |
---|---|---|
grant_type | required | 令牌请求的类型。 对于使用 JWT 的请求,此值必须为 urn:ietf:params:oauth:grant-type:jwt-bearer。 |
client_id | required | 将第一个 Web API 注册为服务器应用(中间层应用)时配置的客户端 ID。 它应与第一回合中使用的资源 ID 相同,即第一个 Web API 的 URL。 |
client_assertion_type | required | 该值必须为 urn:ietf:params:oauth:client-assertion-type:jwt-bearer。 |
client_assertion | required | 需要使用注册为应用程序凭据的证书进行创建和签名的断言 (JWT)。 |
断言 | required | 请求中使用的令牌的值。 |
requested_token_use | required | 指定应如何处理请求。 在 OBO 流程中,该值必须设置为 on_behalf_of。 |
资源 | required | 将第一个 Web API 注册为服务器应用(中间层应用)时提供的资源 ID。 资源 ID 应是代表客户端进行第二个 Web API 的中间层应用调用的 URL。 |
作用域 | optional | 空格分隔的令牌请求范围的列表。 |
请注意,参数几乎相同。 此示例类似于共享机密的请求,只不过 client_secret 参数被替换为两个参数:client_assertion_type 和 client_assertion。
Example
以下 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 响应。
Parameter | Description |
---|---|
token_type | 指示令牌类型值。 AD FS 支持的唯一类型是 Bearer。 |
作用域 | 令牌中授予的访问权限的范围。 |
expires_in | 访问令牌有效的时间长度(以秒为单位)。 |
access_token | 请求的访问令牌。 调用方服务可以使用此令牌向接收方服务进行身份验证。 |
id_token | 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 标头中设置令牌。
Example
GET /v1.0/me HTTP/1.1
Host: https://secondwebapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCbmZpRy1tQ…
客户端凭据授予流
Note
Microsoft 强烈建议迁移到 Microsoft Entra ID,而不是升级到较新的 AD FS 版本。 有关 Microsoft Entra ID 中客户端凭据授权流的详细信息,请参阅 Microsoft 标识平台中的客户端凭据授权流。
你可以使用 RFC 6749 中指定的 OAuth 2.0 客户端凭据授予,以使用应用程序的标识访问 Web 托管的资源。 这种授予通常用于必须在后台运行的服务器间交互,不需要立即与用户交互。 此类应用程序通常称为守护程序或服务帐户。
OAuth 2.0 客户端凭据授权流允许 Web 服务(机密客户端)在调用其他 Web 服务时使用它自己的凭据(而不是模拟用户)进行身份验证。 在此方案中,客户端通常是中间层 Web 服务、守护程序服务或网站。 为了提高保证级别,AD FS 还允许调用服务使用证书(而不是共享机密)作为凭据。
协议关系图
下图显示了客户端凭据授予流。
请求令牌
若要使用客户端凭据授予获取令牌,请向 /token AD FS 终结点发送 POST
请求,如以下部分所示。
第一种情况:使用共享机密访问令牌请求
POST /adfs/oauth2/token HTTP/1.1
// Line breaks for legibility only
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
Parameter | Required/optional | Description |
---|---|---|
client_id | required | AD FS 分配给应用的应用程序(客户端)ID。 |
作用域 | optional | 希望用户同意的作用域的空格分隔列表。 |
client_secret | required | 在应用注册门户中为应用生成的客户端机密。 在发送客户端机密之前,必须对其进行 URL 编码。 |
grant_type | required | 必须设置为 client_credentials 。 |
第二种情况:使用证书的访问令牌请求
POST /adfs/oauth2/token HTTP/1.1
// Line breaks for legibility only
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
Parameter | Required/optional | Description |
---|---|---|
client_assertion_type | required | 该值必须设置为 urn:ietf:params:oauth:client-assertion-type:jwt-bearer。 |
client_assertion | required | 需要使用注册为应用程序凭据的证书进行创建和签名的断言 (JWT)。 |
grant_type | required | 必须设置为 client_credentials 。 |
client_id | optional | AD FS 分配给应用的应用程序(客户端)ID。 这是client_assertion的一部分,所以这里不需要。 |
作用域 | optional | 希望用户同意的作用域的空格分隔列表。 |
使用令牌
获取令牌后,请使用令牌向资源发出请求。 当令牌过期时,对 /token 终结点重复此请求以获取新的访问令牌。
GET /v1.0/me/messages
Host: https://webapi.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
资源所有者密码凭据认证流程(不推荐)
Note
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
Parameter | Required/optional | Description |
---|---|---|
client_id | required | 客户端 ID。 |
grant_type | required | 必须设置为 password。 |
用户名 | required | 用户的电子邮件地址。 |
密码 | required | 用户的密码。 |
作用域 | optional | 范围的空格分隔列表。 |
成功的身份验证响应
以下示例显示了一个成功的令牌响应:
{
"token_type": "Bearer",
"scope": "openid",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIn...",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"refresh_token_expires_in": 28800,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDR..."
}
Parameter | Description |
---|---|
token_type | 始终设置为 Bearer。 |
作用域 | 如果返回访问令牌,此参数会列出访问令牌有效的范围。 |
expires_in | 包含的访问令牌有效的秒数。 |
access_token | 针对请求的作用域颁发。 |
id_token | JWT。 应用可以解码此令牌的段,以请求有关登录用户的信息。 应用可以缓存并显示这些值,但不应依赖它们来获取任何授权或安全边界。 |
refresh_token_expires_in | 包含的刷新令牌有效的秒数。 |
refresh_token | 如果原始作用域参数包含 offline_access,则颁发。 |
可以使用刷新令牌,通过本文授权码授予流程部分中描述的相同流程来获取新的访问令牌和刷新令牌。
设备代码流
Note
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
Parameter | Condition | Description |
---|---|---|
client_id | required | AD FS 分配给应用的应用程序(客户端)ID。 |
作用域 | optional | 范围的空格分隔列表。 |
设备授权响应
成功的响应是一个 JSON 对象,其中包含允许用户登录所需的信息。
Parameter | Description |
---|---|
device_code | 用于验证客户端和授权服务器之间的会话的长字符串。 客户端使用此参数来请求授权服务器提供访问令牌。 |
user_code | 向用户显示的短字符串,用于标识辅助设备上的会话。 |
verification_uri | 用户为了登录而应使用 user_code 进入的 URI。 |
verification_uri_complete | 用户为了登录而应使用 user_code 进入的 URI。 它预先填充了user_code,以便用户无需输入该值。 |
expires_in | 设备码和用户码过期前的秒数。 |
间隔 | 客户端在轮询请求之间应等待的秒数。 |
消息 | 用户可读的字符串,其中包含有关用户的说明。 可以通过在请求中包含查询参数(格式为 ?mkt=xx-XX)对其进行本地化,并填写相应的语言区域性代码。 |
对用户进行身份验证
客户端收到user_code并verification_uri后,向用户显示这些详细信息,指示他们使用移动电话或电脑浏览器登录。 此外,客户端可以使用 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
Parameter | Required/optional | Description |
---|---|---|
grant_type | required | 必须为 urn:ietf:params:oauth:grant-type:device_code。 |
client_id | required | 必须与初始请求中使用的 client_id 匹配。 |
代码 | required | 设备授权请求中返回的 device_code。 |
成功的身份验证响应
成功的令牌响应可以包含以下参数:
Parameter | Description |
---|---|
token_type | 始终为 Bearer 。 |
作用域 | 如果返回访问令牌,则会参数会列出访问令牌有效的范围。 |
expires_in | 包含的访问令牌有效的秒数。 |
access_token | 针对请求的作用域颁发。 |
id_token | 如果原始作用域参数包含 openid 范围,则颁发。 |
refresh_token | 如果原始作用域参数包含 offline_access,则颁发。 |
refresh_token_expires_in | 包含的刷新令牌有效的秒数。 |
相关内容
请参阅 AD FS 开发,以获取指南文章的完整列表,其中提供了有关如何使用相关流程的分步说明。