12

We are using the OAuthAuthorizationServerProvider class to do authorization in our ASP.NET Web Api app.

If the provided username and password is invalid in GrantResourceOwnerCredentials, the call

context.SetError( "invalid_grant", "The user name or password is incorrect." );

Produces the following Json result:

{
    "error": "invalid_grant",
    "error_description": "The user name or password is incorrect."
}

Is there any way to customize this error result?
I would like to make it consistent with default error message format used in other parts of the API:

{
    "message": "Some error occurred."
}

Is this possible to achieve with the OAuthAuthorizationServerProvider?

Mark Vincze
  • 7,297
  • 8
  • 39
  • 77
  • 1
    Whilst the answers below demonstrate how to do this, I would caution against doing this, as the default response returned conforms to the OAuth 2.0 specification, whereas your modified response would not. This might be acceptable for internal-only APIs. However, if this API is publicly accessible, you probably want to be following the specification and not inventing new conventions. – Chris Nov 06 '16 at 18:16
  • I found that these answers do not work and it seems being the OAuth response to look more like a standard Web API response is not as easy as one might think. – trees_are_great Jul 24 '17 at 14:30

3 Answers3

11

This is how I did it.

string jsonString = "{\"message\": \"Some error occurred.\"}";

// This is just a work around to overcome an unknown internal bug. 
// In future releases of Owin, you may remove this.
context.SetError(new string(' ',jsonString.Length-12)); 

context.Response.StatusCode = 400;
context.Response.Write(jsonString);
Dasun
  • 3,078
  • 1
  • 27
  • 39
  • 1
    As a note I had tried creating a OwinMiddleware class to override the context.Response.Body, but that didn't work either if anyone tries. This worked perfectly. – DDiVita Sep 02 '15 at 19:54
  • 4
    This did not work for me on web api 2. The response was not proper json, because of that internal bug that you mentioned. – trees_are_great Jul 24 '17 at 14:30
7

+1 for Dasun's answer. Here is how I extended it a bit further.

public class ErrorMessage
{
    public ErrorMessage(string message)
    {
        Message = message;
    }

    public string Message { get; private set; }
}

public static class ContextHelper
{
    public static void SetCustomError(this OAuthGrantResourceOwnerCredentialsContext context, string errorMessage)
    {
        var json = new ErrorMessage(errorMessage).ToJsonString();

        context.SetError(json);
        context.Response.Write(json);
    }
}

The .ToJsonString() is another extension method that uses the Newtonsoft.Json library.

public static string ToJsonString(this object obj)
    {
        return JsonConvert.SerializeObject(obj);
    }

Usage:

context.SetCustomError("something went wrong");
ajpetersen
  • 639
  • 8
  • 17
2

1+ again for "user2325333" and "Dasun's" answer his solution, your answers are good but still there is an issue . The Josn Tag still return {error:""}, thus I replace the context.Response.Body with empty MemoryStream and here the work example

public static class ContextHelper
{
    public static void SetCustomError(this OAuthGrantResourceOwnerCredentialsContext context,string error, string errorMessage)
    {
        var json = new ResponseMessage
        { Data = errorMessage, Message = error, IsError = true }.ToJsonString();
        context.SetError(json);
        context.Response.Write(json);
        Invoke(context);
    }
    public static string ToJsonString(this object obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
    static async Task Invoke(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var owinResponseStream = new MemoryStream();
        var customResponseBody = new System.Net.Http.StringContent(JsonConvert.SerializeObject(new ResponseMessage()));
        var customResponseStream = await customResponseBody.ReadAsStreamAsync();
        await customResponseStream.CopyToAsync(owinResponseStream);
        context.Response.ContentType = "application/json";
        context.Response.ContentLength = customResponseStream.Length;
        context.Response.Body = owinResponseStream;
    }
}
public class ResponseMessage
{
    public bool IsError { get; set; }
    public string Data { get; set; }
    public string Message { get; set; }
}

for usage of this context

 public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        if (!context.Match.Passcode)
        {
            context.SetCustomError("invalid_grant", "Passcode is invalid.");
            return;
        }
    }

The Result will be as enter image description here

Ahmad Hindash
  • 1,369
  • 14
  • 15