【问题标题】:Office 365 Rest API - Daemon week authenticationOffice 365 Rest API - 守护程序周身份验证
【发布时间】:2015-04-05 18:09:14
【问题描述】:

我正在尝试构建一个 Ruby 守护程序服务来访问 Office 365 REST API。最近可以通过 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

我正在努力生成有效的访问令牌。令牌端点返回给我一个 JWT,但是当使用这个令牌时,我收到了一个 401 和这个消息:

访问令牌是使用一种太弱的身份验证方法获取的,该方法不允许此应用程序访问。提供的身份验证强度为 1,要求为 2

我了解 client_credentials 流程要求您提供 X.509 证书,不幸的是,博客文章中的所有示例都是针对 C# 的。

我在请求令牌时使用生成的自签名证书和私钥来执行客户端断言。我按照博文中的步骤生成证书并更新清单以使用此证书。

这是供参考的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

显然,为了安全起见,我删除了某些信息。即使它是 ruby​​,您也可以看到我正在使用我的证书来验证使用 SSL 连接的客户端。

以下是有关错误的更多信息:

"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"

任何帮助将不胜感激。


编辑

对于希望在 Ruby 中做类似事情的其他人,这里是我使用的代码的要点:https://gist.github.com/NGMarmaduke/a088943edbe4e703129d

该示例使用 Rails 环境,但应该很容易去除 Rails 特定的位。

记得将您的 CLIENT ID、TENANT_ID 和 CERT_THUMBPRINT 替换为正确的值,并将证书路径和客户端密钥方法指向正确的文件路径。

然后你可以这样做:

mailbox = OfficeAPI.new("nick@test.com")
messages = mailbox.request_messages

【问题讨论】:

  • 嘿@Nick 你有没有让这个工作?我想看看你的代码。我正在尝试在 Ruby 中做类似的事情,即使尝试按照 Jason 的回答所说的去做,似乎也无法让它工作。
  • 嘿乔尔,刚刚在问题中添加了更多细节,以及我的代码要点
  • 完美。非常感谢!

标签: ruby rest ssl office365 exchangewebservices


【解决方案1】:

您的请求正文中不需要client_secret,而是需要client_assertion。这有点复杂,但这就是您需要该证书的原因。

基本上,您需要构建一个 JSON Web 令牌并使用您的证书使用 SHA256 哈希对其进行签名。令牌将如下所示:

标题:

{ 
  "alg": "RS256",
  "x5t": "..." // THUMBPRINT of Cert
}

有效载荷:

{
  "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"
}

如果您仍然和我在一起,您现在需要对这两个部分(分别)进行 base64 编码,然后用“.”连接它们。所以现在你应该有:

base64_header.base64_payload

现在您获取该字符串并使用您的证书对其进行签名,使用 SHA256 哈希。然后base64-encode结果,url-encode它,然后附加到字符串,所以现在你有:

base64_header.base64_payload.base64_signature

最后,将其作为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'
  )

希望对您有所帮助!这一切都是基于我对ADAL 是如何做到的的研究,而且我自己还没有在 Ruby 中测试过。

【讨论】:

  • 完美,再次感谢杰森。对于不使用 ADAL 开发的人来说,记录这一点将是一个好主意。可能在此处更新 Azure AD 客户端凭据文档:msdn.microsoft.com/en-us/library/azure/dn645543.aspx
  • @NickMaher 该文档绝对应该更新。我已就此向 Microsoft 提出请求。
  • @Jason,一个问题:为什么调用 Office365 API 时需要此证书,但调用 Graph API 时不需要?我可以在 Graph API 中使用 clientIdclientSecret 使用客户端凭据授予流程进行身份验证,但在 Office365 中则不行。这是为什么呢?
  • Exchange Online 服务需要此流程的证书,所以我的猜测是,即使使用 Graph,如果您尝试使用通过秘密而不是断言获得的令牌访问邮件、日历或联系人会失败。
【解决方案2】:

只是一些补充:断言中的受众声明与您使用令牌请求寻址的端点相同。正如 Jason 正确识别的那样,这是 AAD 的令牌端点:https://login.windows.net/{您想要应用令牌的租户}/oauth2/token。此外,nbf 和 exp 是您在 unix 纪元时间创建断言的时间,例如在 .net 中,您会执行类似“WebConvert.EpocTime(DateTime.UtcNow)”的操作。对于“不早于”(nbf),可能会减去时钟偏差的缓冲区,例如5分钟;并且对于(exp)到期添加一些时间,例如15 分钟(因此断言在这段时间内仍然有效)。

这是令牌请求的提琴手跟踪(原始): 发布https://login.windows.net/0e49ef1f-ca07-45f1-b4c0-ac9409d3e576/oauth2/token HTTP/1.1 内容类型:application/x-www-form-urlencoded 客户端请求 ID:a8108f88-275b-424d-ac28-f675aabe548e 返回客户端请求 ID:真 x-client-SKU:.NET x 客户端版本:2.12.0.0 x-客户端-CPU:x64 x 客户端操作系统:Microsoft Windows NT 6.2.9200.0 主机:login.windows.net 内容长度:983 期望:100-继续 连接:保持活动状态

资源= 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

希望这会有所帮助! 祝你好运!

马蒂亚斯

【讨论】:

  • 另请注意:您可以使用jwt.calebb.net 来查看您的断言是否正确,并将您生成/发送的base64url 编码值粘贴到那里。它应该像上面 Jason 的示例那样成功显示内容。
【解决方案3】:

我在 git 上的 HomeController 中添加了一个函数,以演示如何使用不带 ADAL 的客户端断言手动请求访问令牌。使用它可能更容易移植:https://github.com/mattleib/o365api-as-apponly-webapp/commit/12d5b6dc66055625683020576139f5771e6059e1

【讨论】:

    【解决方案4】:

    我刚刚设法让这个工作正常进行,所以我想我会再提供一条建议。那里的所有说明文章都说您应该将证书添加到清单文件中。我遇到了麻烦,但这是我最终使它起作用的方法:

    • 在 Azure 中,转到设置 > 管理证书
    • 将公钥上传为.cer 文件(如果您不知道如何转换,请自行搜索)。这应该是您的文本编辑器使用的二进制文件。
    • 现在它已上传,Microsoft 将为您提供指纹。它位于“指纹”列中。 但是,它是十六进制的,而不是 base64。所以,像这样转换它:

      # Hint: use your actual thumbprint, not this fake one
      echo '5292850026FADB09700E7D6C1BCB1CD1F3270BCC' | xxd -r -p | base64
      
    • 最后,使用这个 base64 编码的指纹作为 JSON 标头中 x5t 的值。

    【讨论】:

    • Herm,我看到了指纹,但列不够宽,所以它会缩短它并添加椭圆。没有明显的方法来扩展信息...想法?
    • 没关系,我明白了... openssl x509 -in server.cer -fingerprint -noout | tr -d ':' | awk -F"=" '{打印 $2}' | xxd -r -p | base64
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多