Oleg Sh
Oleg Sh

Reputation: 9013

WebAPI - How to get UserID from token

I have WebApi application and added UserID to token in ApplicationOAuthProvider class:

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }

        context.AdditionalResponseParameters.Add("ID", context.Identity.GetUserId<int>());

        return Task.FromResult<object>(null);
    }

Now how can I get this ID in my controller method?

I try the following:

[Authorize]
public class ApiEditorialController : ApiController
{

    public HttpResponseMessage GetEditorialRequests()
    {
        int id = HttpContext.Current.User.Identity.GetUserId<int>();

        var r = Request.CreateResponse(HttpStatusCode.Accepted);
        r.ReasonPhrase = "Cool!";
        return r;
    }

}

But I get NullReferenceException on

int id = HttpContext.Current.User.Identity.GetUserId<int>(); 

string....

UPDATE: Look at the Response below (from Francis Ducharme) just override OnAuthorization instead on create private contructor :)

public class AuthorizeApiFilter : AuthorizeAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        string token = string.Empty;
        AuthenticationTicket ticket;

        token = (actionContext.Request.Headers.Any(x => x.Key == "Authorization")) ? actionContext.Request.Headers.Where(x => x.Key == "Authorization").FirstOrDefault().Value.SingleOrDefault().Replace("Bearer ", "") : "";

        if (token == string.Empty)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Missing 'Authorization' header. Access denied.");
            return;
        }

        //your OAuth startup class may be called something else...
        ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(token);

        if (ticket == null)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid token decrypted.");
            return;
        }

        // you could perform some logic on the ticket here...

        // you will be able to retrieve the ticket in all controllers by querying properties and looking for "Ticket"... 
        actionContext.Request.Properties.Add(new KeyValuePair<string, object>("Ticket", ticket));
        base.OnAuthorization(actionContext);
    }
}

Thank you, Francis Ducharme

Upvotes: 5

Views: 15945

Answers (2)

Francis Ducharme
Francis Ducharme

Reputation: 4987

You could, in GrantResourceOwnerCredentials of your OAuth startup class, add it there to the dictionary.

ticket.Properties.Dictionary.Add(KeyValuePair<string, string>("UserID", user.Id.ToString())); //the user object from your authentication logic...

Then Implement an AuthorizeAttribute in which you can retrieve the token that was sent in the Authorize header of the request, unprotect it and add it to the request properties that will then be available in all controllers' methods.

public class AuthFilter : AuthorizeAttribute
{
    private void AuthorizeRequest(HttpActionContext actionContext)
    {
        string token = string.Empty;
        AuthenticationTicket ticket;

        token = (actionContext.Request.Headers.Any(x => x.Key == "Authorization")) ? actionContext.Request.Headers.Where(x => x.Key == "Authorization").FirstOrDefault().Value.SingleOrDefault().Replace("Bearer ", "") : "";

        if (token == string.Empty)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Missing 'Authorization' header. Access denied.");
            return;
        }

        //your OAuth startup class may be called something else...
        ticket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(token);

        if (ticket == null)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid token decrypted.");
            return;
        }

        // you could perform some logic on the ticket here...

        // you will be able to retrieve the ticket in all controllers by querying properties and looking for "Ticket"... 
        actionContext.Request.Properties.Add(new KeyValuePair<string, object>("Ticket", ticket));
    }
}

Then in your web methods, Request.Properties will contain Ticket, which itself has a dictionary with the UserID.

You need to register the AuthorizeAttribute in WebApiConfig.cs

config.Filters.Add(new AuthFilter());
// I also have this in my Web API config. Not sure if I had to add this manually or the default project had these lines already...
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

Upvotes: 6

Matti Price
Matti Price

Reputation: 3551

You need to intercept the response message and attach that value back to your user.

The AdditionalResponseParameters will show up in the response, but your application won't assign them to anything unless you tell it to.

Locate the code that you use to assign claim values / name etc to your users (on the redirect back to your site from the OAuth provider), and then look for the parameter on the respones there.

Upvotes: 0

Related Questions