Cannot access JWT claim in WebApi authorization token - c#

I was able to implement the below JWT solution in my MVC WebApi following below link JWT Authentication for Asp.Net Web Api
Now, I want to access claim in the controllers, but all claims are null. I have tried a few things and all of them returns null.
How is claim added in JwtAuthenticationAttribute:
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
if (ValidateToken(token, out username))
{
//Getting user department to add to claim.
eTaskEntities _db = new eTaskEntities();
var _user = (from u in _db.tblUsers join d in _db.tblDepartments on u.DepartmentID equals d.DepartmentID select u).FirstOrDefault();
var department = _user.DepartmentID;
// based on username to get more information from database in order to build local identity
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.SerialNumber, department.ToString())
// Add more claims if needed: Roles, ...
};
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
What I have tried so far
Extension method to get claim:
public static class IPrincipleExtension
{
public static String GetDepartment(this IIdentity principal)
{
var identity = HttpContext.Current.User.Identity as ClaimsIdentity;
if (identity != null)
{
return identity.FindFirst("SerialNumber").Value;
}
else
return null;
}
}
Using the function defined in the post (link above):
TokenManager.GetPrincipal(Request.Headers.Authorization.Parameter).FindFirst("SerialNumber")
Trying to access Claim through thread:
((ClaimsPrincipal)System.Threading.Thread.CurrentPrincipal.Identity).FindFirst("SerialNumber")
For all the above, a claim is always null. What am I doing wrong?

You should try this to get your claim:
if (HttpContext.User.Identity is System.Security.Claims.ClaimsIdentity identity)
{
var serialNum = identity.FindFirst(System.Security.Claims.ClaimTypes.SerialNumber).Value;
}
I reserve the identity.FindFirst("string_here").Value for when I set my claims like this:
var usersClaims = new[]
{
new Claim("string_here", "value_of_string", ClaimValueTypes.String),
...
};
rather than using "prebuilt" values like this:
var usersClaims = new[]
{
new Claim(ClaimTypes.Name, "value_of_name", ClaimValueTypes.String),
...
};

Related

How to get user id from IdentityServer in client app when including access token?

I have implemented an authentication service based on IdentityServer3 and a simple MVC client app and a Shopper API secured by the authentication service. I've implemented a IdentityServer custom UserService so that the authentication service authenticates against our existing user data store. My Shopper API expects a userid in the Shopper Get request. Currently the response from authentication service includes the identity token and the access token, but no user id. I tried adding a user_id claim in the AuthenticationResult from my custom UserService.AuthenticateLocalAsync method, but I'm not seeing it in my client app code.
UserService.AuthenticateLocalAsync looks like this:
try
{
var user = new shopper(_dbConnLib, context.UserName, context.Password);
var claims = new List<Claim> { new Claim("user_id", user.shopperid) };
context.AuthenticateResult = new AuthenticateResult(user.shopperid, user.MemberDetail.billToAddress.FirstName, claims);
}
catch(shopperInitFromException ex)
{
context.AuthenticateResult = null; // Indicates username/password failure
}
return Task.FromResult(0);
And my client app SecurityTokenValidated handler looks like this:
SecurityTokenValidated = async n =>
{
var nid = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role);
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo").ToString());
var userInfo = await userInfoClient.GetAsync(n.ProtocolMessage.AccessToken);
userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Type, ui.Value)));
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
//nid.AddClaim(new Claim("user_id", n.ProtocolMessage.UserId));
nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
}
If I step through that in the debugger, userInfo.Claims always has a count of 0. How can I get back a claim with the unique identifier of the user? Or can I get it from the identity or access token? Or should I just pass the tokens to the Shopper API and let it determine the id from the tokens?
I think I may have the answer. So far, as far as I can tell, the claims I include in the AuthenticateResult constructor in my override of AuthenticateLocalAsync don't seem to go anywhere. But the claims I include in my override of GetProfileDataAsync appear in the token. My GetProfileDataAsync code, which appears to set the claims properly, looks like this:
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = new shopper(_dbConnLib, context.Subject.FindFirst("sub").Value);
var claims = new List<Claim> { new Claim("sub", user.shopperid), new Claim("acr_level", "level 0"), new Claim("amr", "anonymous") };
context.IssuedClaims = claims;
return Task.FromResult(0);
}
My AuthenticateLocalAsync code that sets claims in the AuthenticateResult that I never see in my client app code looks like this:
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
// TODO: Handle AddshopperToBasketException in UserService.AuthenticateLocalAsync
try
{
var user = new shopper(_dbConnLib, context.UserName, context.Password);
var claims = new List<Claim> { new Claim("acr_level", "level 0"), new Claim("amr", "anonymous") };
context.AuthenticateResult = new AuthenticateResult(user.shopperid, user.MemberDetail.billToAddress.FirstName, claims);
}
catch(shopperInitFromException ex)
{
context.AuthenticateResult = null; // Indicates username/password failure
}
return Task.FromResult(0);
}

Store and retrieve facebook access token in ASP.NET MVC 5 app using OWIN

I'm a newbie with OWIN. Currently i'm using visual studio 2015 to create ASP.NET MVC project, and visual studio wizard handle almost of basic functions like register new user, login with external accounts...
In Startup.Auth.cs i implemented the login with facebook function as following:
var option = new FacebookAuthenticationOptions
{
AppId = appId,
AppSecret = appSecret,
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (ctx) =>
{
ctx.Identity.AddClaim(new System.Security.Claims.Claim("fb_access_token", ctx.AccessToken, ClaimValueTypes.String, "Facebook"));
return Task.FromResult(0);
}
},
AuthenticationType = DefaultAuthenticationTypes.ExternalCookie
};
app.UseFacebookAuthentication(option);
In AccountController.cs i want to get the access token to fetch some user's information, but i always receive a null reference exception of claim object:
public ActionResult Test()
{
ClaimsIdentity claimIdenties = HttpContext.User.Identity as ClaimsIdentity;
var claim = claimIdenties.FindFirst("fb_access_token");
return View(claim.Value);
}
Can you guys suggest me any solution?
Sorry i'm not good at English
during sign up with facebook..lets store access token in our database to retrieve them later..so in your Account controller
var claimsIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
if (claimsIdentity != null)
{
// Retrieve the existing claims for the user and add the FacebookAccessTokenClaim
var currentClaims = await UserManager.GetClaimsAsync(user.Id);
var facebookAccessToken = claimsIdentity.FindAll("FacebookAccessToken").First();
if (currentClaims.Count() <= 0)
{
await UserManager.AddClaimAsync(user.Id, facebookAccessToken);
}
now you can get the access token to get some data like this
var fb = new FacebookClient(loginInfo.ExternalIdentity.Claims.FirstOrDefault(x => x.Type == "FacebookAccessToken").Value);
dynamic userfbData = fb.Get("/me?fields=email,name");
picurl = String.Format("https://graph.facebook.com/{0}/picture?type=large", userfbData.id);
dispname = userfbData.name;
this worked for me perfectly

ASP.NET Identity and Claim-based

How to use claims? For example, I want to set access to each page (resource) for each user. I understand, I can do it using roles, but as I understand, claim-based is more effectively. But when I try to create a claim, I see the following method:
userIdentity.AddClaim(new Claim(ClaimTypes.Role, "test role"));
first parameter of constructor of Claim class get ClaimTypes enum, which has many "strange" members like Email, Phone etc. I want to set that this claim and then check this claim to have access to certain resource. I'm on wrong way? How to do it?
From the code above, I am assuming you have already added the claim in startup class on authenticated of your provider as below.
context.Identity.AddClaim(new Claim("urn:google:name", context.Identity.FindFirstValue(ClaimTypes.Name))); // added claim for reading google name
context.Identity.AddClaim(new Claim("urn:google:email", context.Identity.FindFirstValue(ClaimTypes.Email))); // and email too
Once you have added the claims in startup, when the request is actually processed check if its a callback and if yes, read the claims as below(in IHttpHandler).
public void ProcessRequest(HttpContext context)
{
IAuthenticationManager authManager = context.GetOwinContext().Authentication;
if (string.IsNullOrEmpty(context.Request.QueryString[CallBackKey]))
{
string providerName = context.Request.QueryString["provider"] ?? "Google";//I have multiple providers so checking if its google
RedirectToProvider(context, authManager, providerName);
}
else
{
ExternalLoginCallback(context, authManager);
}
}
If its 1st call redirect to provider
private static void RedirectToProvider(HttpContext context, IAuthenticationManager authManager, string providerName)
{
var loginProviders = authManager.GetExternalAuthenticationTypes();
var LoginProvider = loginProviders.Single(x => x.Caption == providerName);
var properties = new AuthenticationProperties()
{
RedirectUri = String.Format("{0}&{1}=true", context.Request.Url, CallBackKey)
};
//string[] authTypes = { LoginProvider.AuthenticationType, DefaultAuthenticationTypes.ExternalCookie };
authManager.Challenge(properties, LoginProvider.AuthenticationType);
//without this it redirect to forms login page
context.Response.SuppressFormsAuthenticationRedirect = true;
}
And finally read the claims you get back
public void ExternalLoginCallback(HttpContext context, IAuthenticationManager authManager)
{
var loginInfo = authManager.GetExternalLoginInfo();
if (loginInfo == null)
{
throw new System.Security.SecurityException("Failed to login");
}
var LoginProvider = loginInfo.Login.LoginProvider;
var ExternalLoginConfirmation = loginInfo.DefaultUserName;
var externalIdentity = authManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
var pictureClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type.Equals("picture"));
var pictureUrl = pictureClaim.Value;
LogInByEmail(context, email, LoginProvider); //redirects to my method of adding claimed user as logged in, you will use yours.
}
Claim doesn't set permission. It's used to verify you that "you are who you claim to be you are". These claims are identified by issuer, usually a 3rd party. See for example this article for description.
So, you should define which claims are necessary (who user should be) in order to access a certain page. Otherwise, using claim-based authorization will be same as using identity based or role based.

How to add claims during user registration

I'm using ASP.NET MVC 5 project with identity 2.1.0 and VS2013 U4. I want to add claims to user during registration in order to be stored in db. These claims represent user custom properties.
As I created a web page for administrator to create/edit/delete users, I'm still using create method from AccountController to create a user, but I don't want to login that user. How can I add those claims to the user ?
You probably already have a UserManager class. You can use that one to create users and to add claims.
As an example in a controller:
// gather some context stuff
var context = this.Request.GetContext();
// gather the user manager
var usermanager = context.Get<ApplicationUserManager>();
// add a country claim (given you have the userId)
usermanager.AddClaim("userid", new Claim(ClaimTypes.Country, "Germany"));
In order for this to work you need to implement your own UserManager and link it with the OWIN context (in the example it's ApplicationUserManager which basically is class ApplicationUserManager : UserManager<ApplicationUser> { } with only a small amount of configuration added). A bit of reading is available here: https://msdn.microsoft.com/en-us/library/dn613290%28v=vs.108%29.aspx
you can use Like
private void SignInAsync(User User)
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, User.Employee.Name));
claims.Add(new Claim(ClaimTypes.Email, User.Employee.EmailId));
claims.Add(new Claim(ClaimTypes.Role, User.RoleId.ToString()));
var id = new ClaimsIdentity(claims,
DefaultAuthenticationTypes.ApplicationCookie);
var claimsPrincipal = new ClaimsPrincipal(id);
// Set current principal
Thread.CurrentPrincipal = claimsPrincipal;
var ctx = Request.GetOwinContext();
var authenticationManager = ctx.Authentication;
authenticationManager.SignIn(id);
}
after login pass the User table value in this function
SignInAsync(result);
you can get clam value like
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
// Get the claims values
string UserRoleValue = identity.Claims.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value).SingleOrDefault();
You can, in fact, create claims at the same time you create the user account.
Just add the claims to the user object before you call CreateAsync on the user manager.
var identityUser = new IdentityUser
{
UserName = username,
Email = email,
// etc...
Claims = { new IdentityUserClaim { ClaimType = "SomeClaimType", ClaimValue = "SomeClaimValue"} }
};
var identityResult = await _userManager.CreateAsync(identityUser, password);
This will create the user and associate the claims with the user as one logical operation with persistence.

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;
}
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...
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;

Categories