Morvael Morvael - 5 months ago 32
JSON Question

WebAPI2.0 OWIN Token request using JSON

I have created a new WebAPI solution in visual studio and am playing around with the code to try and understand whats going on.

I have a test API thats all up and running with an Authorization controller and another controller that implements all the actual functionality.

The controllers (API) all work by receiving JSON and replying with JSON, with the exception of the /Token request.This has to be:

Content-Type: application/x-www-form-urlencoded


otherwise I just get an error back.

The section of code that creates this endpoint appears to be this:

OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = false
};


Calling it like this results in a 200 Success response, with a Bearer token:

$("#token_button").click(function ()
{
var username = $("#token_email").val();
var password = $("#token_password").val();

postData("Token", "grant_type=password&username=" + username + "&password=" + password, "application/x-www-form-urlencoded", function (data)
{
user = data;
$("#feedback_display").html(user.access_token);
}, function ()
{
user = null;
});
});


Calling it like this results in a 400 Response:

$("#token_button").click(function ()
{
var username = $("#token_email").val();
var password = $("#token_password").val();

var data = {
"grant_type": "password",
"username": username,
"password": password
}

postData("Token", JSON.stringify(data), "application/json", function (data)
{
user = data;
$("#feedback_display").html(user.access_token);
}, function ()
{
user = null;
});
});


The response body is:

{"error":"unsupported_grant_type"}


The only difference here is the encoding used to transmit the request.
Every where I look all the examples are using form encoding to request this token.

Placing a breakpoint on the code under /api/Account/ExternalLogin, never gets hit.

Is there a reason for this only accepting form encoding? and if not how can I change the controller to accept JSON?

Alternatively have I just done something stupid?

Answer

The reason behind the use of application/x-www-form-urlencoded as Content-Type is simple: the OAuth2 specification (RFC 6749) requires this content type for token requests.

Any other content-type will break OAuth2 compliant clients compatibility. I advice you to not change this standard behavior.

Note
Please note that this:

postData("Token", data, "application/json", function (data)
{
    //...
}

works just because you are not sending JSON at all! Even if you added application/json as Content-Type header your request body is serialized as form key-value pairs (the jQuery default object serialization in AJAX calls).

The default implementation of OAuthAuthorizationServerMiddleware (more precisely the internal used OAuthAuthorizationServerHandler) from Microsoft.Owin.Security.OAuth just ignores the Content-Type header and tries to read the request body as form anyway.

Comments