Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set Principal/User Context to a User Object

My WebAPI 2 application has a custom authorization filter which checks for an access token. If the token is present, and the API has the attribute, then I check if there exists a user which maps to that token.

Due to the nature of the API, most methods run in the context of a particular user (i.e. "POST api/profile" to update a user's profile). In order to do this, I need information on the target user which I get from the access token.

[Current Implementation, happens within attribute of type AuthorizeAttribute]

if( myDBContext.MyUsers.Count( x => x.TheAccessToken == clientProvidedToken ) ){
    IPrincipal principal = new GenericPrincipal( new GenericIdentity( myAccessToken ), new string[] { "myRole" } );
    Thread.CurrentPrincipal = principal;
    HttpContext.Current.User = principal;
    return true;
}

This works fine, and I'm able to then use the access token to do a second lookup in the Method. But since I already do a lookup at auth time, I don't want to waste another DB call.

[What I'd like to do (but obviously doesn't work)]

MyUser user = myDBContext.MyUsers.FirstOrDefault( x => x.TheAccessToken == clientProvidedToken );
if( user != null ){
    // Set *SOME* property to the User object, such that it can be
    // access in the body of my controller method
    // (e.g. /api/profile uses this object to load data)
    HttpContext.Current.User = user;
    return true;
}
like image 896
ShaneC Avatar asked Oct 28 '13 19:10

ShaneC


2 Answers

You could use your own principal class. Maybe something like:

public class MyPrincipal : GenericPrincipal
{
    public MyPrincipal(IIdentity identity, string[] roles)
        : base(identity, roles)
    {
    }
    public MyUser UserDetails {get; set;}
}

Then your action filter could do:

MyUser user = myDBContext.MyUsers.FirstOrDefault( x => x.TheAccessToken == clientProvidedToken );
if(user != null)
{
    MyPrincipal principal = new MyPrincipal( new GenericIdentity( myAccessToken ), new string[] { "myRole" } );
    principal.UserDetails = user;
    Thread.CurrentPrincipal = principal;
    HttpContext.Current.User = principal;
    return true;
}
return false;

And subsequently in your actual method, you can take the current user, check if it's of type MyPrincipal and if so cast it and then access the UserDetails:

...
MyUser currentUser = null;
MyPrincipal curPrincipal = HttpContext.Current.User as MyPrincipal;
if (curPrincipal != null)
{
    currentUser = curPrincipal.UserDetails;
}
...

I haven't actaully tried this code, so there might be typos...

like image 82
user1429080 Avatar answered Oct 28 '22 16:10

user1429080


You could use a ClaimsIdentity/ClaimsPrincipal and add the Claims you need later in your controller, for example the actors ID or other values you need.

I've made an example which sets claims to the Actor but if it better suits you you could also at Claims directly to the current User.

var identity = new ClaimsIdentity(HttpContext.Current.User.Identity);
identity.Actor = new ClaimsIdentity();
identity.Actor.AddClaim(new Claim("Your", "Values"));

var principal = new ClaimsPrincipal(identity);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = Thread.CurrentPrincipal;
like image 35
Jos Vinke Avatar answered Oct 28 '22 17:10

Jos Vinke