Oleg Sh Oleg Sh - 16 days ago 6
C# Question

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

Answer

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));