Sergey
Sergey

Reputation: 28

WebApi serialize object from HttpActionContext

EDIT: I changed my approach. Now I am using MessageHandlers. Thanks to Raciel, who pointed the direction where to dig.

These links where very useful for me: MessageHandlers overview Using own Principal classes


I have a WebApi project and need to provide custom authorization to it. Special Token object is added to each request from the frontend. Something like that:

SendApiRequest: function (controller, action, data, successCallback, failureCallback) {
    var url = ROUTER.GetApiUrl(controller, action);

    data.Token = TOKEN;

    jQuery.ajax({
        type: 'POST',
        dataType: 'json',
        url: url,
        data: data,
        success: function (result) {
            if (typeof successCallback == 'function') {
                successCallback(result);
            }
        },
        error: function (result) {
            if (typeof failureCallback == 'function') {
                failureCallback(result);
            }
        }
    });}

I have an AuthorizationAttribute and I somehow need to serialize Token object from the request. But cannot find any automatic way to do that.

public class CustomAuthorizationAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(HttpActionContext context)
    {
        UserToken token = null;

        //HOW TO SERIALIZE token object from context?

    }
}

UserToken class looks like that:

public class UserToken
{
    public Int64 TokenID;
    public int UserID;
    public string TokenValue;
    public string IP;
    public string Hash;

    public DateTime CreatedOn;
    public DateTime ActionOn;
}

So the question is: how to serialize custom Object from HttpActionContext?

Thank you.

Upvotes: 0

Views: 2657

Answers (1)

Raciel R.
Raciel R.

Reputation: 2156

This is what I have done when I'm dealing with cases similar to yours.

Instead of creating your own Authorize attribute, you can create a MessageHandler which checks for the token and validates it on every request. This message handler is the responsible for populating the Principal in the current thread, so the Authorize attribute can work as expected allowing authorized clients to access the controller/action in question.

This is how my Authorization message handler looks like:

public class AuthMessageHandler : DelegatingHandler
{
    protected ITokenProvider TokenProvider { get; private set; }
    protected IPrincipalProvider PrincipalProvider { get; private set; }

    public AuthMessageHandler(ITokenProvider tokenProvider, IPrincipalProvider principalProvider)
    {
        TokenProvider = tokenProvider;
        PrincipalProvider = principalProvider;
    }


    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Identity identity = null;
        string token = ExtractToken(request);

        if (token != null && TokenProvider.Verify(token, out identity))
        {
            request.Properties.Add(Constants.IdentityKey, identity);
            var principal = PrincipalProvider.CreatePrincipal(identity);
            Thread.CurrentPrincipal = principal;
            HttpContext.Current.User = principal;
        }

        return base.SendAsync(request, cancellationToken);
    }


    private string ExtractToken(HttpRequestMessage request)
    {
        IEnumerable<string> tokenValues = null;
        if (request.Headers.TryGetValues(Constants.TokenHeaderKey, out tokenValues))
            return tokenValues.First();

        return null;
    }
}

Please note:

  1. Both TokenProvider and PrincipalProvider are injected here. The first is in charge of validating the token and if valid, return the identity data so it is available across the request.
  2. The IPrincipal provider just creates a GenericPrincipal that is then assigned to the Thread.CurrentPrincipal and to the context user (HttpContext.Current.User). This ensures that the Authorize attribute works later, when it checks for IsAuthenticated in the current Principal.
  3. In this case, I'm passing the token information in the headers, which I prefer. (You might need to authorize GET requests too)
  4. If you need to pass data from a message handler to a controller, you use the request.Properties, this is why I'm putting the Identity information there with request.Properties.Add(Constants.IdentityKey, identity); so it is available to the controller. There are several ways you can achieve this, but this dictionary is pretty much a cheap transport for this type of data.

Please let me know if you have any questions. This is actually simpler that it looks.

Upvotes: 2

Related Questions