[英]Office 365 Rest API - Daemon week authentication
I am trying to build a Ruby Daemon service to access the Office 365 rest API.我正在尝试构建一个Ruby Daemon 服务来访问 Office 365 rest API。 It was recently made possible to do this via the OAuth 'client_credentials' flow, as detailed in this blog post: https://docs.microsoft.com/en-us/archive/blogs/exchangedev/building-daemon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow
最近可以通过 OAuth 'client_credentials' 流程执行此操作,如本博客文章所述: https : //docs.microsoft.com/en-us/archive/blogs/exchangedev/building-daemon-or-service -apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow
I am struggling to generate a valid access token.我正在努力生成有效的访问令牌。 The token endpoint returns me a JWT however when using this token I received a 401 with this message:
令牌端点返回一个 JWT,但是在使用此令牌时,我收到了带有此消息的 401:
The access token is acquired using an authentication method that is too weak to allow access for this application.
访问令牌是使用太弱而无法访问此应用程序的身份验证方法获取的。 Presented auth strength was 1, required is 2
提供的身份验证强度为 1,要求为 2
I understand that the client_credentials flow requires you to present a X.509 cert, unfortunately all the examples in the blog post are for C#.我知道 client_credentials 流程要求您提供 X.509 证书,不幸的是,博客文章中的所有示例都是针对 C# 的。
I am using a generated self signed cert and private key to do a client assertion when requesting the token.我正在使用生成的自签名证书和私钥在请求令牌时进行客户端断言。 I followed the steps in the blog post to generate the cert and update the manifest to use this cert.
我按照博客文章中的步骤生成证书并更新清单以使用此证书。
This is the ruby code for reference:这是参考的ruby代码:
def request_token
uri = URI.parse("https://login.windows.net/== TENANT-ID ==/oauth2/token?api-version=1.0")
https = Net::HTTP.new(uri.host, uri.port)
req = Net::HTTP::Post.new(uri.request_uri)
req.set_form_data(
:grant_type => 'client_credentials',
:redirect_uri => 'http://spready.dev',
:resource => 'https://outlook.office365.com/',
:client_id => '== Client ID ==',
:client_secret => '== Client secret =='
)
https.use_ssl = true
https.cert = client_cert
https.key = client_key
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
resp = https.start { |cx| cx.request(req) }
@access_token = JSON.parse(resp.body)
end
Obviously I have removed certain bits of information for security.显然,为了安全起见,我删除了某些信息。 Even though it is ruby you can see I am using my cert to validate the client using an SSL connection.
即使它是 ruby,您也可以看到我正在使用我的证书使用 SSL 连接验证客户端。
Here's some more infomation on the error:以下是有关错误的更多信息:
"x-ms-diagnostics" => "2000010;
reason=\"The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2.\";
error_category=\"insufficient_auth_strength\"",
"x-diaginfo"=>"AM3PR01MB0662",
"x-beserver"=>"AM3PR01MB0662"
Any help would be appreciate.任何帮助将不胜感激。
Edit编辑
For others looking to do something similar in Ruby here's a Gist of the code I use: https://gist.github.com/NGMarmaduke/a088943edbe4e703129d对于希望在 Ruby 中做类似事情的其他人,这是我使用的代码要点: https : //gist.github.com/NGMarmaduke/a088943edbe4e703129d
The example uses a Rails environment but it should be fairly easy to strip out the Rails specific bits.该示例使用 Rails 环境,但剥离 Rails 特定位应该相当容易。
Remember to replace YOUR CLIENT ID, TENANT_ID and CERT_THUMBPRINT with the correct values and point the cert path and client key methods to the right file path.请记住用正确的值替换您的客户端 ID、TENANT_ID 和 CERT_THUMBPRINT,并将证书路径和客户端密钥方法指向正确的文件路径。
Then you can do something like this:然后你可以做这样的事情:
mailbox = OfficeAPI.new("nick@test.com")
messages = mailbox.request_messages
Instead of a client_secret
in your request body, you need a client_assertion
.您需要一个
client_assertion
,而不是请求正文中的client_secret
。 This is a bit more complex, but it's the reason you need that certificate.这有点复杂,但这就是您需要该证书的原因。
Basically you need to build a JSON Web Token and sign it with your certificate using a SHA256 hash.基本上,您需要构建一个 JSON Web 令牌并使用 SHA256 哈希用您的证书对其进行签名。 The token is going to look something like this:
令牌看起来像这样:
Header:标题:
{
"alg": "RS256",
"x5t": "..." // THUMBPRINT of Cert
}
Payload:有效载荷:
{
"aud": "https:\\/\\/login.windows.net\\/<The logged in user's tenant ID>\\/oauth2\\/token",
"exp": 1423168488,
"iss": "YOUR CLIENT ID",
"jti": "SOME GUID YOU ASSIGN",
"nbf": 1423167888,
"sub": "YOUR CLIENT ID"
}
If you're still with me, you now need to base64-encode both pieces (separately), then concatenate them with a '.'.如果你还在我身边,你现在需要对两部分进行 base64 编码(单独),然后用“.”将它们连接起来。 So now you should have:
所以现在你应该有:
base64_header.base64_payload
Now you take that string and sign it with your certificate, using a SHA256 hash.现在您获取该字符串并使用您的证书对其进行签名,使用 SHA256 哈希。 Then base64-encode the result of that, url-encode it, then append to the string, so now you have:
然后对结果进行 base64 编码,对其进行 url 编码,然后附加到字符串,所以现在您有:
base64_header.base64_payload.base64_signature
Finally, include this in your POST to the token endpoint as the client_assertion
parameter, and also include a client_assertion_type
parameter set to "urn:ietf:params:oauth:client-assertion-type:jwt-bearer":最后,将此作为
client_assertion
参数包含在您对令牌端点的 POST 中,并且还包含一个设置为“urn:ietf:params:oauth:client-assertion-type:jwt-bearer”的client_assertion_type
参数:
req.set_form_data(
:grant_type => 'client_credentials',
:redirect_uri => 'http://spready.dev',
:resource => 'https://outlook.office365.com/',
:client_id => '== Client ID ==',
:client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
:client_assertion => 'base64_header.base64_payload.base64_signature'
)
I hope that helps!我希望这有帮助! This is all based on my research into how ADAL does it, and I haven't tested it myself in Ruby.
这都是基于我对ADAL如何做到这一点的研究,我自己还没有在 Ruby 中进行过测试。
I just managed to get this working, so I thought I'd throw one more piece of advice into the mix.我只是设法让这个工作,所以我想我会再提出一条建议。 All the instruction articles out there say that you should add your certificate to the manifest file.
那里的所有说明文章都说您应该将证书添加到清单文件中。 I had trouble with that, but here is what I did that finally made it work:
我遇到了麻烦,但这是我所做的最终使它起作用的方法:
.cer
file (google around if you don't know how to convert it)..cer
文件(如果您不知道如何转换它,请谷歌一下)。 This should be a binary file that your text editor barfs on. Now that it's uploaded, Microsoft will give you the thumbprint.现在它已上传,Microsoft 将为您提供指纹。 It's in the "Thumbprint" column.
它位于“指纹”列中。 But , it's in hex, not base64.
但是,它是十六进制的,而不是 base64。 So, convert it like this:
所以,像这样转换它:
# Hint: use your actual thumbprint, not this fake one echo '5292850026FADB09700E7D6C1BCB1CD1F3270BCC' | xxd -r -p | base64
Finally, use this base64 encoded thumbprint as the value for x5t
in the JSON header.最后,使用这个 base64 编码的指纹作为 JSON 标头中
x5t
的值。
I added a function in HomeController on the git to demo how to request an access token by hand using client assertion w/o ADAL.我在 git 上的 HomeController 中添加了一个函数来演示如何使用没有 ADAL 的客户端断言手动请求访问令牌。 It might be easier to port using this: https://github.com/mattleib/o365api-as-apponly-webapp/commit/12d5b6dc66055625683020576139f5771e6059e1
使用它可能更容易移植: https : //github.com/mattleib/o365api-as-apponly-webapp/commit/12d5b6dc66055625683020576139f5771e6059e1
Just some additions: The audience claim in the assertion is the same as the endpoint you address with the token request.只是一些补充:断言中的受众声明与您使用令牌请求寻址的端点相同。 As Jason correctly identified, this is the token endpoint of AAD: https://login.windows.net/ {the tenant you want an app token for}/oauth2/token.
正如 Jason 正确识别的那样,这是 AAD 的令牌端点: https : //login.windows.net/ {您想要应用令牌的租户}/oauth2/token。 Also the nbf and exp are the time you created the assertion in unix epoche time, eg in .net you would do something like "WebConvert.EpocTime(DateTime.UtcNow)".
此外,nbf 和 exp 是您在 unix 纪元时间中创建断言的时间,例如在 .net 中,您将执行类似“WebConvert.EpocTime(DateTime.UtcNow)”的操作。 For "not before" (nbf) maybe subtract a buffer for clock skew, eg 5 minutes;
对于“not before” (nbf) 可能会减去时钟偏差的缓冲区,例如 5 分钟; and for expires in (exp) add some time, eg 15 minutes (so the assertion remains valid for that time).
并为 (exp) 中的到期添加一些时间,例如 15 分钟(因此断言在该时间内仍然有效)。
Here is a fiddler trace of a token request (raw): POST https://login.windows.net/0e49ef1f-ca07-45f1-b4c0-ac9409d3e576/oauth2/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded client-request-id: a8108f88-275b-424d-ac28-f675aabe548e return-client-request-id: true x-client-SKU: .NET x-client-Ver: 2.12.0.0 x-client-CPU: x64 x-client-OS: Microsoft Windows NT 6.2.9200.0 Host: login.windows.net Content-Length: 983 Expect: 100-continue Connection: Keep-Alive这是令牌请求(原始)的提琴手跟踪:POST https://login.windows.net/0e49ef1f-ca07-45f1-b4c0-ac9409d3e576/oauth2/token HTTP/1.1 Content-Type: application/x-www- form-urlencoded client-request-id: a8108f88-275b-424d-ac28-f675aabe548e return-client-request-id: true x-client-SKU: .NET x-client-Ver: 2.12.0.0 x-client-CPU: x64 x-client-OS: Microsoft Windows NT 6.2.9200.0 Host: login.windows.net Content-Length: 983 Expect: 100-continue Connection: Keep-Alive
resource=https%3A%2F%2Fgraph.windows.net%2F&client_id=f17bb8a5-2bef-4ad5-a83f-cd7113449fc2&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6ImY4S2JVY0xtMnItS2s4b1Z3ZVZYTFU0NzhJcyJ9.eyJhdWQiOiJodHRwczpcL1wvbG9naW4ud2luZG93cy5uZXRcLzBlNDllZjFmLWNhMDctNDVmMS1iNGMwLWFjOTQwOWQzZTU3Nlwvb2F1dGgyXC90b2tlbiIsImV4cCI6MTQyMjk4NDMzNSwiaXNzIjoiZjE3YmI4YTUtMmJlZi00YWQ1LWE4M2YtY2Q3MTEzNDQ5ZmMyIiwianRpIjoiZTI3OTA5YTctZGYwMC00NjBhLTlmZjctOGZkNDExOWVmNTYzIiwibmJmIjoxNDIyOTgzNzM1LCJzdWIiOiJmMTdiYjhhNS0yYmVmLTRhZDUtYTgzZi1jZDcxMTM0NDlmYzIifQ.g9bo4-lxpNJ4kEOMuQxODU-5iakwSVIzyRQEPLdbpuNn_XD4lcvt2yBIWT12EQaUVKkMyqFrDiIh4Oav565-Po7HfhmSPF3URXVj8Kx5lx17Zh0nWiaNkRXEi1vhwswsfjm1o-8B8LGUJTtT6JXTognrueuSL1aEE_-4qSG1y74aoc949Un1pQCjwuBtao4vs4CPJLu9Y9mVbirVRRtiIfxkUMmzf6yfMtuhugoGmrvUYntUo4x6N2fu4LxGjuIs7czyrMMAmDRo-XK4sAhDo5uof10HKb8ETEU8mhObwNZcz86MYHWbZm3Z_HDOwzC9kA_tp6hWqmlJ3c-gLg5VXA&grant_type=client_credentials资源= HTTPS%3A%2F%2Fgraph.windows.net%2F&CLIENT_ID = f17bb8a5-2bef-4ad5-a83f-cd7113449fc2&client_assertion_type =瓮%3Aietf%3Aparams%3Aoauth%3Aclient断言型%3Ajwt承载&client_assertion = eyJhbGciOiJSUzI1NiIsIng1dCI6ImY4S2JVY0xtMnItS2s4b1Z3ZVZYTFU0NzhJcyJ9.eyJhdWQiOiJodHRwczpcL1wvbG9naW4ud2luZG93cy5uZXRcLzBlNDllZjFmLWNhMDctNDVmMS1iNGMwLWFjOTQwOWQzZTU3Nlwvb2F1dGgyXC90b2tlbiIsImV4cCI6MTQyMjk4NDMzNSwiaXNzIjoiZjE3YmI4YTUtMmJlZi00YWQ1LWE4M2YtY2Q3MTEzNDQ5ZmMyIiwianRpIjoiZTI3OTA5YTctZGYwMC00NjBhLTlmZjctOGZkNDExOWVmNTYzIiwibmJmIjoxNDIyOTgzNzM1LCJzdWIiOiJmMTdiYjhhNS0yYmVmLTRhZDUtYTgzZi1jZDcxMTM0NDlmYzIifQ.g9bo4- lxpNJ4kEOMuQxODU-5iakwSVIzyRQEPLdbpuNn_XD4lcvt2yBIWT12EQaUVKkMyqFrDiIh4Oav565-Po7HfhmSPF3URXVj8Kx5lx17Zh0nWiaNkRXEi1vhwswsfjm1o-8B8LGUJTtT6JXTognrueuSL1aEE_-4qSG1y74aoc949Un1pQCjwuBtao4vs4CPJLu9Y9mVbirVRRtiIfxkUMmzf6yfMtuhugoGmrvUYntUo4x6N2fu4LxGjuIs7czyrMMAmDRo-XK4sAhDo5uof10HKb8ETEU8mhObwNZcz86MYHWbZm3Z_HDOwzC9kA_tp6hWqmlJ3c-gLg5VXA&grant_type = client_credentials
Hope this helps!希望这可以帮助! Good luck!
祝你好运!
Matthias马蒂亚斯
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.