在Azure AD中,当我们调用AuthenticationContext.AcquireTokenAsync(resource, new ClientAssertionCertificate(_clientId, _cert))
时,不清楚究竟发生了什么。
如果有的话,交换证书的哪个部分?
是否有挑战/回应发生?
客户端需要私钥作为其中的一部分吗?
有两种方法可以找到问题的答案。一种方法是查看GitHub上的。net源代码的Microsoft Active Directory Authentication Library (ADAL),因为它是开源的。另一个(我们将在这里做的)是查看AcquireTokenAsync(String, ClientAssertion)
生成的网络请求,并从那里向后工作。
使用Fiddler(或任何其他流量分析器),我们可以看到如下内容(为了可读性而格式化):
POST https://login.microsoftonline.com/{tenant-id}/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&resource=https%3A%2F%2Fgraph.windows.net
&client_id={app-id}
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1N...VE8wHSf-HZvGQ
分解:
-
grant_type=client_credentials
告诉我们这是一个使用OAuth 2.0客户端凭证授予流的令牌请求。 -
resource=https%3A%2F%2Fgraph.windows.net
给出了客户端请求访问令牌的资源的URI。在本例中,它用于Azure AD Graph API。 -
client_id={app-id}
为客户端标识符。在Azure AD中,这是已注册应用程序的应用ID。 -
client_assertion_type
和client_assertion
的存在表明客户端正在使用断言进行身份验证: -
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
表示正在使用的客户端断言是一个签名的JSON Web令牌(JWT)。 -
client_assertion=eyJhbGciOiJSUzI1N...VE8wHSf-HZvGQ
是前面提到的带签名的JWT令牌。授权服务器(例如Azure AD)将验证内容,并检查令牌是否确实由有关客户端授权的证书签名。
那么,ADAL做的是:
- 用一组关于客户端(你的应用程序)的声明构造一个令牌
- 使用您的证书私钥生成这些声明的加密签名
- 把它打包成一个带签名的JWT
- 向授权机构 发出适当形式的令牌请求
在AcquireTokenAsync
期间,只提供证书的指纹(它包含在JWT头中,以帮助授权服务器查找相应的公钥)。JWT的签名证明了客户端拥有私钥。但是,在 AcquireTokenAsync(String, ClientAssertion)
可以成功使用之前,客户端所有者(即您)需要向Azure AD提供证书的公钥。
这里没有挑战/响应发生。令牌是在客户端发起的单个请求中获得的。
要了解更多细节,您可以查看所有这些实现的标准:
- RFC 6749: OAuth 2.0授权框架
- RFC 7521: OAuth 2.0客户端认证和授权授权断言框架
- RFC 7523:用于OAuth 2.0客户端身份验证和授权授予的JSON Web令牌(JWT)配置文件
- RFC 7519: JSON Web Token (JWT)
(注意,ADAL有一个缓存。只有当ADAL在令牌缓存中没有找到有效的访问令牌时,我上面描述的一切才会发生。您可以使用AuthenticationContext.TokenCache.Clear()
来清除缓存以进行实验。