title: Azure Devops Oauth authentication: Cannot get access token (BadRequest Failed to deserialize the JsonWebToken object)
body: <p>I'm trying to implement an OAUth 2.0 flow for custom webapplication for Azure Devops. I'm following this <a href="https://docs.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops" rel="nofollow noreferrer">https://docs.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops</a> documentation as well as this <a href="https://github.com/microsoft/azure-devops-auth-samples/tree/master/OAuthWebSample" rel="nofollow noreferrer">https://github.com/microsoft/azure-devops-auth-samples/tree/master/OAuthWebSample</a> OauthWebSample but using ASP.NET Core (I also read one issue on SO that looked similar but is not: <a href="https://stackoverflow.com/questions/56527208/access-azure-devops-rest-api-with-oauth">Access Azure DevOps REST API with oAuth</a>)</p>
  
  <h1>Reproduction</h1>
  
  <p>I have registered an azdo app at <a href="https://app.vsaex.visualstudio.com/app/register" rel="nofollow noreferrer">https://app.vsaex.visualstudio.com/app/register</a> and the <strong>authorize step</strong> seems to work fine, i.e. the user can authorize the app and the redirect to my app returns something that looks like a valid jwt token: </p>
  
  <pre><code>header: {
    "typ": "JWT",
    "alg": "RS256",
    "x5t": "oOvcz5M_7p-HjIKlFXz93u_V0Zo"
  }
  payload: {
    "aui": "b3426a71-1c05-497c-ab76-259161dbcb9e",
    "nameid": "7e8ce1ba-1e70-4c21-9b51-35f91deb6d14",
    "scp": "vso.identity vso.work_write vso.authorization_grant",
    "iss": "app.vstoken.visualstudio.com",
    "aud": "app.vstoken.visualstudio.com",
    "nbf": 1587294992,
    "exp": 1587295892
  }
  
  </code></pre>
  
  <p>The next step is to get an <strong>access token</strong> which fails with a <strong>BadReqest: invalid_client, Failed to deserialize the JsonWebToken object</strong>.</p>
  
  <p>Here is the full example:</p>
  
  <pre class="lang-cs prettyprint-override"><code>public class Config
  {
      public string ClientId { get; set; } = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
      public string Secret { get; set; } = "....";
      public string Scope { get; set; } = "vso.identity vso.work_write";
      public string RedirectUri { get; set; } = "https://....ngrok.io/azdoaccount/callback";
  }
  
  /// &lt;summary&gt;
  /// Create azdo application at https://app.vsaex.visualstudio.com/
  /// Use configured values in above 'Config' (using ngrok to have a public url that proxies to localhost)
  /// navigating to localhost:5001/azdoaccount/signin 
  /// =&gt; redirect to https://app.vssps.visualstudio.com/oauth2/authorize and let user authorize (seems to work)
  /// =&gt; redirect back to localhost:5001/azdoaccount/callback with auth code
  /// =&gt; post to https://app.vssps.visualstudio.com/oauth2/token =&gt; BadReqest: invalid_client, Failed to deserialize the JsonWebToken object
  /// &lt;/summary&gt;
  [Route("[controller]/[action]")]
  public class AzdoAccountController : Controller
  {
      private readonly Config config = new Config();
      [HttpGet]
      public ActionResult SignIn()
      {
          Guid state = Guid.NewGuid();
  
          UriBuilder uriBuilder = new UriBuilder("https://app.vssps.visualstudio.com/oauth2/authorize");
          NameValueCollection queryParams = HttpUtility.ParseQueryString(uriBuilder.Query ?? string.Empty);
  
          queryParams["client_id"] = config.ClientId;
          queryParams["response_type"] = "Assertion";
          queryParams["state"] = state.ToString();
          queryParams["scope"] = config.Scope;
          queryParams["redirect_uri"] = config.RedirectUri;
  
          uriBuilder.Query = queryParams.ToString();
  
          return Redirect(uriBuilder.ToString());
      }
  
      [HttpGet]
      public async Task&lt;ActionResult&gt; Callback(string code, Guid state)
      {
          string token = await GetAccessToken(code, state);
          return Ok();
      }
  
      public async Task&lt;string&gt; GetAccessToken(string code, Guid state)
      {
          Dictionary&lt;string, string&gt; form = new Dictionary&lt;string, string&gt;()
                  {
                      { "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
                      { "client_assertion", config.Secret },
                      { "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
                      { "assertion", code },
                      { "redirect_uri", config.RedirectUri }
                  };
  
          HttpClient httpClient = new HttpClient();
  
          HttpResponseMessage responseMessage = await httpClient.PostAsync(
              "https://app.vssps.visualstudio.com/oauth2/token",
              new FormUrlEncodedContent(form)
          );
          if (responseMessage.IsSuccessStatusCode) // is always false for me
          {
              string body = await responseMessage.Content.ReadAsStringAsync();
              // TODO parse body and return access token
              return "";
          }
          else
          {
              // Bad Request ({"Error":"invalid_client","ErrorDescription":"Failed to deserialize the JsonWebToken object."})
              string content = await responseMessage.Content.ReadAsStringAsync();
              throw new Exception($"{responseMessage.ReasonPhrase} {(string.IsNullOrEmpty(content) ? "" : $"({content})")}");
          }
      }
  }
  </code></pre>