Prepare / read authenticated user claims from HttpContext.User - asp.net-core

var authenticateResult = await HttpContext.AuthenticateAsync("External");
if (!authenticateResult.Succeeded) return BadRequest();
var email = authenticateResult.Principal.FindFirst(ClaimTypes.Email);
var id = _usersService.Create(email.Value);
var claimsIdentity = new ClaimsIdentity("Application");
claimsIdentity.AddClaim(new Claim("id", id.ToString()));
await HttpContext.SignInAsync("Application", new ClaimsPrincipal(claimsIdentity));
In controllers the following line throws Sequence contains no elements in controllers:
var idClaim = HttpContext.User?.Claims.Where(x => x.Type == "id").Single();
I call UseAuthentication and UseAuthorization in Startup and I thought that the first snippet would set cookies and then User would provide access to it on every client request but it doesn't work.
If I put a breakpoint right after SiginInAsync then HttpContext.User contains the expected claim but not in other calls.
HttpContext.Request.Cookies is empty. In browser I see several cookies among them .AspNetCore.External and .AspNetCore.Application plus some antiforgery cookies.
How do I achieve having claims in User for all requests after authentication was done?
Can it be the problem that my front-end runs on a separate port? Probably not.

You could follow the steps below to add updated claims to HttpContext.User:
https://stackoverflow.com/a/65319557/11398810
Then you can get claims in other request:
var idClaim = HttpContext.User?.Claims.Where(x => x.Type == "id").Single();

Related

check if graph.user manager exists without try catch

I have all my Active Directory users in a list and am iterating through the users to get their manager, using the below code.
foreach (User u in userResult)
{
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var directoryObject = await graphClient2.Users[u.UserPrincipalName].Manager
.Request()
.GetAsync();
}
This works fine if the user has a manger, but if not, the code fails.
The only way i can currently see around this is to use a try catch, but that feels crude, and when iterating over a lot of users, its not very efficient.
is there a better way of doing this?
You can use OData query expand parameter here to query manager alone with users.
Query url looks like this:
https://graph.microsoft.com/v1.0/users?$expand=manager($select=id,displayName)
And when using Graph SDK in asp.net core, since you need to query all users, you should use client credential flow, and it should look like this, please note, if the use doesn't have an manager, the property Manager will be null:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_clientid";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var users = await graphClient.Users.Request().Expand("manager($select=id,displayName)").GetAsync();
Get the user, not the manager. The user has a manager field which can be checked for null.
You still need to do try/catch in the case that the user is not a part of the org anymore or if the server has an internal error. 4xx and 5xx error responses throw exceptions in .NET code.

Asp.net core Identity Password reset "invalid token" even though tokens generated are identical

I am on Asp.Net core with .Net 5.0 In startup I have added;
services.AddIdentity<ApplicationUser, ApplicationRole>(SetupIdentityOptions)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<DataProtectionTokenProviderOptions>(opt =>
{
opt.TokenLifespan = TimeSpan.FromDays(1);
}
);
I generate the code as below(encoding done as per the MS docs);
var code = await CommonServices.UserManager.GeneratePasswordResetTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = $"{request.RequestData.ReturnUrl}?code={code}";
Now if I save the code generated at this point and check the code received to controller after user clicks the link they are identical. However when I do;
var result = await CS.UserManager.ResetPasswordAsync(user, model.Code, model.Password);
it gives me "Invalid Token" however tokens are identical.
Then I tried this and still says invalid token;
var isValidToken = await _userManager.VerifyUserTokenAsync(
user,
_userManager.Options.Tokens.PasswordResetTokenProvider,
UserManager<TUser>.ResetPasswordTokenPurpose,
code);
I am not sure whether the Identity framework saves the generated tokens in [AspNetUserTokens] table. I can't find any saved tokens after a generating the token.
What may be the problem?
My bad, I had to convert the encoded string back which I haven't done.
So, since I used
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
After user clicks the link, I have to;
var bytes = WebEncoders.Base64UrlDecode(model.Code);
var code = Encoding.UTF8.GetString(bytes);
and then call
ResetPasswordAsync(user, code, model.Password);
which worked

How to attach needed claims to tokens with policy-based authorization?

I am trying to implement policy-based authorization in my Web API. What I am trying to figure out is how to determine which claims should be added to the token when generating it for the user on his/her log-in operation. Should I store information about claims for each user in the database, or I am misunderstanding some concepts?
Here is the method I use to generate JWT/refresh-token pair:
public async Task<AuthenticationResponse> GenerateTokenPairForUserAsync(User user)
{
var jwtTokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwtConfig.Secret);
var guid = Guid.NewGuid().ToString();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(ClaimTypes.Sid, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(ClaimTypes.Role, user.RoleId.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, guid)
}),
Expires = DateTime.UtcNow.Add(_jwtConfig.TokenLifetime),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
};
var token = jwtTokenHandler.CreateToken(tokenDescriptor);
var jwtToken = jwtTokenHandler.WriteToken(token);
var refreshToken = new RefreshToken
{
JwtId = token.Id,
IsUsed = false,
IsRevoked = false,
UserId = user.Id,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.Add(_refreshTokenConfig.TokenLifetime),
Token = RandomString(25) + Guid.NewGuid()
};
await _refreshTokenRepository.CreateAsync(refreshToken);
return new AuthenticationResponse
{
Token = jwtToken,
Success = true,
RefreshToken = refreshToken.Token
};
}
You need a few things to achieve that:
You should have a mapping of which clients can receive which claims. Clients, meaning the apps which call your authorization endpoint. If you only have one, then this is not a problem, but if you have many, you should keep somewhere in a database a list of all the claims that the given client should receive in the token.
It's convenient to have a mapping of scopes to claims. In an authorization request you can then request scopes, which are essentially groups of claims. You will need that if your clients (or client) can actually request tokens with different scopes. E.g. you might want to request a token which can be used to perform some more sensitive operations, maybe change user's email. Then you can ask the server to issue the token maybe with a scope "admin", which translates to, among others, a claim can_change_email: true. This claim can then be used to perform authorization decisions.
Finally you need to know, for every claim, what is the source of data. So, once you know that your tokens must contain claims claim1, claim2 and claim3, then you must know where to take the data from. This can be hardcoded - e.g. you implement a getValueForClaim2() method which knows that it should read data from a database (e.g. it's a user's phone number). Or you can create some more sophisticated solutions, where you keep some mappings to claimProviders, then implement those providers. In the end, where do you get the data from it's totally up to you - this can be a database, a file, maybe an API call, or the value is calculated based on some input.
Have a look at these resources about claims that we wrote at Curity: https://curity.io/resources/claims/ if you want to learn more about this topic.

How to add and Persist a new ClaimsIdentity with custom claims to a ClaimsPrincipal after authentication by an OpenId Connect provider

I'm using OpenId Connect with .NetCore to send users to an external OpenId Connect authentication provider (OP) which returns relevant access tokens and an authenticated ClaimsPrinciple which has claims from the OP in such as Name, Email Address, Customer Id, etc. What I want to be able to do is once the User has been authenticated and the ClaimsPrinciple is returned from the OP, I would like to add a ClaimsIdentities with custom claims for each licence the user holds on there account to the ClaimsPrinciple. So when a user switches between their account licences I can access the correct identity for that licence and provide access to features based on the custom claims. Currently, I can add the custom claims to a ClaimsIdentity then add the ClaimsIdentity to the ClaimsPrinciple but the new identities are not persisted and added into the Cookie.
So A User can have multiple licences.
For each licence I want to add a ClaimsIdentity.
Then I want to persist the changes to the ClaimsPrinciple by using a cookie.
Here is a code snippet which will hopefully add some context. This is for the Login method in my application which is hit once the OP has authenticated the user.
public async Task<IActionResult> Login()
{
string token = HttpContext.Authentication.GetTokenAsync("access_token").Result;
string refreshToken = HttpContext.Authentication.GetTokenAsync("refresh_token").Result;
// ControllerBase User class this is the User I want to add the identites to, I think.
string userGuid = User.Claims.Where(c => c.Type == "Guid").FirstOrDefault().Value;
UserTokens userTokens = new UserTokens
{
LastUpdatedDate = DateTime.Now,
UserGuid = userGuid,
UserAccessToken = token,
RefreshToken = refreshToken
};
await _busClient.PublishAsync<UpdateUserTokens>(new UpdateUserTokens(userTokens));
// Domain Model User, a different User to the controllerBase User
// this is how a user is represented in my application but this has
// no control over authentication and claims
User user = await _requestClient.RequestAsync<UserGuidRequest, User>(new UserGuidRequest(userGuid));
var userLicences = await _requestClient.RequestAsync<UserLicenceRequest, List<UserLicence>>(new UserLicenceRequest(userGuid));
var identityServerUserClaims = User.Claims.ToList();
foreach(UserLicence userLicence in userLicences)
{
List<Claim> userLicenceIdentityClaims = new List<Claim>();
foreach (Claim claim in identityServerUserClaims)
{
userLicenceIdentityClaims.Add(claim);
}
userLicenceIdentityClaims.Add(new Claim(userLicence.RoleType.ToString(), ""));
var userLicenceIdentity = new ClaimsIdentity(userLicenceIdentityClaims, User.Identity.AuthenticationType);
userLicenceIdentity.Label = userLicence.Id.ToString();
User.AddIdentity(userLicenceIdentity);
}
//TODO: either need to sign out then back in or save new user claims to cookies somehow?
return View("Index", user);
}
If anyone could help it would be greatly appreciated. If you need any more information please ask and I'll try to provide whatever I can, hopefully, this is a good start.

Adding extra details to a webapi bearer token

I am trying to learn the new webapi2.1 authentication pieces.
I have got the bearer token wired up and working with my webapi. My next thing I would like to do is be able to store some additional information within the token (if possible) so when the client sends back the token I can retrieve the details without the need of them sending multiple values.
Can the token be extended to contain custom data?
Sorry if the question is a little vague but I have had a big hunt around and can't seem to find any further information
Thank you
Since the token is signed with a "secret" key - only the issuer can add data to it.
You can amend something to the claim set after receiving the token in your Web API - this is called claims transformation.
I have a sample of it here:
https://github.com/thinktecture/Thinktecture.IdentityModel/tree/master/samples/OWIN/AuthenticationTansformation
In essence you are writing some code that inspects the incoming token and add application specific claims to the resulting principal.
// Transform claims to application identity
app.UseClaimsTransformation(TransformClaims);
private Task<ClaimsPrincipal> TransformClaims(ClaimsPrincipal incoming)
{
if (!incoming.Identity.IsAuthenticated)
{
return Task.FromResult<ClaimsPrincipal>(incoming);
}
// Parse incoming claims - create new principal with app claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "foo"),
new Claim(ClaimTypes.Role, "bar")
};
var nameId = incoming.FindFirst(ClaimTypes.NameIdentifier);
if (nameId != null)
{
claims.Add(nameId);
}
var thumbprint = incoming.FindFirst(ClaimTypes.Thumbprint);
if (thumbprint != null)
{
claims.Add(thumbprint);
}
var id = new ClaimsIdentity("Application");
id.AddClaims(claims);
return Task.FromResult<ClaimsPrincipal>(new ClaimsPrincipal(id));
}