简体   繁体   English

Office 365 Rest API - 守护进程周身份验证

[英]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:我遇到了麻烦,但这是我所做的最终使它起作用的方法:

  • In Azure, go to Settings > Management Certificates在 Azure 中,转到设置 > 管理证书
  • Upload the public key as a .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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM