Azure AD OAuth2 Access Token Request Error - 400 Bad Request

17,196

Solution 1

I used fiddler to debug the request and I found the full error message: The user or administrator has not consented to use the application. I googled this message for a bit and found some stack articles and github issue threads that lead me to the solution: my request had been using "common", in the base URL, as the tenant ID when actually I needed to use my Azure tenant ID which I acquired through this answer on stack. My new base URL for the authentication requests now looks like:

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

where "xxxx-....xxx" would be replaced by your Azure tenant id!

Solution 2

If you don't use a client secret, then you need to configure your tenant to support implicit grant flow. You can follow the directions from this blog post to perform / validate the configuration. This requires using the Azure management portal to download, modify the app manifest, and upload it.

Alternatively, and possibly a better strategy, is to switch your code over to using the converged v2.0 authentication endpoints. It allows management of your application using the new app registration portal and nicely supports implicit flow and dynamic scopes. You can find more information about the actual authentication flow here. It isn't far from what you are doing now and requires only a few small tweaks.

If you are still having issues after this, please reach out again. A fiddler / network trace would be very helpful. Also, the detailed message inside the exception would also be very helpful.

Share:
17,196
mattyb
Author by

mattyb

Updated on June 18, 2022

Comments

  • mattyb
    mattyb almost 2 years

    My WPF desktop application (C#) is attempting to read the user's Outlook emails through the Microsoft Graph API. I am stuck in the authentication process; I've already received an authentication code and now I'm trying to get an access token from Azure but keep getting a HTTP 400 error code when sending out the request for the access token:

    /**** 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
    }
    

    The above is the code used to retrieve the auth code followed by the attempt to retrieve the access token. We do not have a client_secret because secrets are only for Web applications and this is a native desktop WPF application. I have read that this isn't an issue though. I have followed many tutorials and official docs online, mainly the official Graph authorization doc and I still cannot figure out what I am doing wrong. Any help would be greatly appreciated, thank you.

  • Dan Kershaw - MSFT
    Dan Kershaw - MSFT almost 8 years
    Can you provide the full 400 error message that you get back please (or as Robert says, a full fiddler trace). Also are you sure that your app is registered as a native client (or public client) rather than a web app? Native/public clients to not require a secret to redeem the code for an access token.
  • mattyb
    mattyb almost 8 years
    Thank you both for the responses! Fiddler helped me resolve the 400 error issue - it turned out that my authorization code had expired. I am now facing a new issue where the access token response isn't a json object containing the access token (like I expected) but is an html document. Here is the fiddler trace: drive.google.com/file/d/0B9w2-YCX6qYvZmxXdERjWDJsamM/…
  • Robert Anderson - Microsoft
    Robert Anderson - Microsoft almost 8 years
    That call appears to be a POST to the /authorize endpoint. You should be doing a POST to the /token endpoint to change an authorization code into an access token.
  • mattyb
    mattyb almost 8 years
    Oh wow yeah that is trivial mistake thanks, the real error is this: drive.google.com/file/d/0B9w2-YCX6qYvSm5jUzZicFJpMmc/…
  • mattyb
    mattyb almost 8 years
    I've been following the solution to this post: stackoverflow.com/questions/34775287/… But I'm still getting the 400 error, I commented on the approved answer.
  • mattyb
    mattyb almost 8 years
    Okay so I fixed the issue, I was sending my requests with "common" as the tenant ID when I really needed to substitute that with my actual tenant id!