【问题标题】:Azure AD OAuth2 Access Token Request Error - 400 Bad RequestAzure AD OAuth2 访问令牌请求错误 - 400 错误请求
【发布时间】:2016-12-03 14:09:56
【问题描述】:

我的 WPF 桌面应用程序 (C#) 正在尝试通过 Microsoft Graph API 读取用户的 Outlook 电子邮件。我被困在身份验证过程中;我已经收到了一个身份验证代码,现在我正在尝试从 Azure 获取访问令牌,但在发送 访问令牌 的请求时不断收到 HTTP 400 错误代码:

/**** Auth Code Retrieval ****/
string authCodeUrl = "https://login.microsoftonline.com/common/oauth2/authorize";
authCodeUrl += "?client_id" = clientId;
authCodeUrl += "&redirect_uri=" + redirectUri;
authCodeUrl += "&response_type=code";
authCodeUrl += "&resource=https%3A%2F%2Fgraph.microsoft.com%2F";
Process.start(authUrl); // User logs in, we get the auth code after login
string code = "......"; // Hidden for this post

/**** Access Token Retrieval ****/
string tokenUrl = "https://login.microsoftonline.com/common/oauth2/token"
string content = "grant_type=authorization_code";
content += "&client_id=" + clientId;
content += "&resource=https%3A%2F%2Fgraph.microsoft.com%2F";
content += "&code=" + code;
content += "&redirect_uri=" + redirectUri;
WebRequest request = WebRequest.Create(tokenUrl);
request.ContentType = "application/x-www-form-urlencoded";
byte[] data = Encoding.UTF8.GetBytes(content);
request.ContentLength = data.Length;
request.Method = "POST";
try 
{
  using (Stream stream = request.GetRequestStream())
  {
    stream.Write(data, 0, data.Length);
  }
  WebResponse response = request.GetResponse(); // This throws exception
}
catch (Exception error) // This catches the exception
{
  Console.WriteLine(error.Message); // Outputs 400, bad request
}

以上是用于检索身份验证代码的代码,然后是尝试检索访问令牌的代码。我们没有 client_secret,因为 secret 仅适用于 Web 应用程序,这是一个本机桌面 WPF 应用程序。我读过这不是问题。我在网上关注了很多教程和官方文档,主要是the official Graph authorization doc,但我仍然无法弄清楚我做错了什么。任何帮助将不胜感激,谢谢。

【问题讨论】:

    标签: c# azure active-directory oauth-2.0 microsoft-graph-api


    【解决方案1】:

    如果您不使用客户端密码,则需要配置您的租户以支持隐式授权流。您可以按照this 博客文章中的说明执行/验证配置。这需要使用 Azure 管理门户下载、修改应用清单并上传。

    另外一种可能是更好的策略是将您的代码切换为使用converged v2.0 authentication endpoints。它允许使用new app registration portal 管理您的应用程序,并很好地支持隐式流和动态范围。您可以找到有关实际身份验证流程here 的更多信息。它与您现在所做的相距不远,只需要进行一些小的调整。

    如果您在此之后仍有问题,请再次与我们联系。提琴手/网络跟踪将非常有帮助。此外,异常中的详细消息也将非常有帮助。

    【讨论】:

    • 您能否提供完整的 400 错误消息(或者如罗伯特所说,完整的提琴手跟踪)。您还确定您的应用程序注册为本地客户端(或公共客户端)而不是 Web 应用程序吗?本机/公共客户端不需要秘密来兑换访问令牌的代码。
    • 感谢两位的回复! Fiddler 帮我解决了 400 错误问题——原来我的授权码已经过期。我现在面临一个新问题,其中访问令牌响应不是包含访问令牌的 json 对象(如我所料),而是一个 html 文档。这是提琴手的踪迹:drive.google.com/file/d/0B9w2-YCX6qYvZmxXdERjWDJsamM/…
    • 该调用似乎是对 /authorize 端点的 POST。您应该对 /token 端点进行 POST 以将授权代码更改为访问令牌。
    • 哦,哇,这是一个小错误,谢谢,真正的错误是:drive.google.com/file/d/0B9w2-YCX6qYvSm5jUzZicFJpMmc/…
    • 我一直在关注这个帖子的解决方案:*.com/questions/34775287/… 但我仍然收到 400 错误,我评论了批准的答案。
    【解决方案2】:

    我使用fiddler 调试请求并找到完整的错误消息:用户或管理员未同意使用该应用程序。我用谷歌搜索了这条消息,发现一些堆栈文章和 github 问题线程引导我找到解决方案:我的请求一直在基本 URL 中使用“common”作为租户 ID,而实际上我需要使用我的 Azure 租户我通过这个answer on stack获得的ID。我的身份验证请求的新基本 URL 现在如下所示:

    https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/oauth2/authorize 
    

    其中“xxxx-....xxx”将替换为您的 Azure 租户 ID!

    【讨论】: