Update authentication Cookie in ASP.net CORE 3 - asp.net-core

In ASP.net CORE 3 app, I use authentication cookie without Identity. Here is the code when a user log in.
string securityStamp = Guid.NewGuid().ToString();
var userClaims = new List<Claim>()
{
new Claim("id", id_user.ToString()),
new Claim("securityStamp", securityStamp),
}
var grantMyIdentity = new ClaimsIdentity(userClaims, "User Identity");
var userPrincipal = new ClaimsPrincipal(new[] { grantMyIdentity });
await HttpContext.SignInAsync(userPrincipal);
At some point, I would like to update the securityStamp of the cookie without making the user log out and log in again.
How can I do this transparently for the user?

Related

Custom claim not refreshed

Custom claims set is not updating. I have two API and an MVC project API1 validates the user and set claims, API2 is supposed to consume the custom claims, MVC is the UI.
The below code is from API1 that runs successfully. The claims are added. I can see the same in API2, (but it's now visible anywhere else including the MVC. maybe I don't know how to access it).
But the main problem is not that, the main problem is, the claims are not updated when it's changed the next time through API1. So, even if another user login in the claim still shows the first users' claims.
public async Task<AuthenticationModel> GetTokenAsync(LoginRequestModel model)
{
//...
if (await _userManager.CheckPasswordAsync(user, model.Password))
{
JwtSecurityToken jwtSecurityToken = await CreateJwtToken(user, model.Database);
authenticationModel.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}
}
private async Task<JwtSecurityToken> CreateJwtToken(ApplicationUser user, string myClaimValue)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var roles = await _userManager.GetRolesAsync(user);
var roleClaims = new List<Claim>();
for (int i = 0; i < roles.Count; i++)
{
roleClaims.Add(new Claim("roles", roles[i]));
}
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim("uid", user.Id),
new Claim("MyCustomClaim", myClaimValue)
}
.Union(userClaims)
.Union(roleClaims);
var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwt.Key));
var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
var jwtSecurityToken = new JwtSecurityToken(
issuer: _jwt.Issuer,
audience: _jwt.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(_jwt.DurationInMinutes),
signingCredentials: signingCredentials);
return jwtSecurityToken;
}
This data is used in the following code. It works for the first time, but when the values are changed in API1, the same is not reflected in the claims. The claim always shows the first value assigned to it. I tried getting details of the claim in the MVC for debug and initialization, but everywhere else the claims are empty.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
var identity = _httpContextAccessor.HttpContext.User.Identity as ClaimsIdentity;
if (identity != null)
{
IEnumerable<Claim> claims = identity.Claims;
var myclaim= identity.FindFirst("MyCustomClaim").Value;
}
optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
}

Access Current User in ASP.NET Core Startup.cs

I'm authenticated users using Azure AD and I'm trying to add roles to the authenticated user using the middleware below. The problem is that I can't find anything that tells me how I can access the current User in the Startup class to be able to add the roles.
Everything talks about in the controller or in repositories further down.
Does anyone know how I can get access to the User in the Startup class?
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = ctx =>
{
// claimsIdentity we want to add our roles to...
ClaimsIdentity claimsIdentity = HttpContext.User.Identity as ClaimsIdentity;
// List of claims
var appRoles = new System.Collections.Generic.List<Claim>();
foreach (Claim claim in ClaimsPrincipal.Current.FindAll("groups"))
{
// use the OID and get a friendly name to use as the role (if it exists)
var groupStringValue = Configuration[$"AcceptedRoles:{claim.Value}"];
if (groupStringValue != null)
{
// build the list
appRoles.Add(new Claim(claimsIdentity.RoleClaimType, groupStringValue));
}
}
if (appRoles.Count > 0)
{
// if anything in the list, add these claims to the current identity
claimsIdentity.AddClaims(appRoles);
}
return Task.CompletedTask;
},
};
});
The current user (the ClaimsPrincipal) is available from TokenValidatedContext.HttpContext.User:
OnTokenValidated = ctx =>
{
var user = ctx.HttpContext.User;
...
}
However, because you are modifying the user that has already been authenticated by Open ID Connect, you should access the ClaimsPrincipal via TokenValidatedContext.Principal instead of TokenValidatedContext.HttpContext.User. Depending on your scenario, you can either add the additional claims directly on the default ClaimsIdentity (which contains the Open ID Connect claims), or create a separate ClaimsIdentity for your own purposes:
OnTokenValidated = ctx =>
{
// This is the ClaimsIdentity created by OpenID Connect, you can add claims to it directly
ClaimsIdentity claimsIdentity = ctx.Principal.Identities.FirstOrDefault();
claimsIdentity.AddClaim(new Claim(...));
// You can also add a new ClaimsIdentity to hold the claims that you'll add
ctx.Principal.AddIdentity(new ClaimsIdentity(...))
}
You can use this in your project:
httpContextAccessor.HttpContext.User.Identity.Name;
You can also reference below link
How to Get the Current User in ASP.NET Core

How to validate if a user logged in with Google is still valid?

I'm running .NET Core v3.1 and Blazor and have implemented authorization using Google limited to our domain in Google G Suite as mentioned here: https://www.jerriepelser.com/blog/forcing-users-sign-in-gsuite-domain-account/
Login/logout is working fine, but when the user who logged in is blocked or removed in Google G Suite the user stays logged in into my application until he logs out from the application. When he doesn't logs out he can keep using the application.
I'm looking for a refresh every hour.
This is my login.cshtml.cs:
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
// Get the information about the user from the external login provider
var GoogleUser = User.Identities.FirstOrDefault();
if (GoogleUser.IsAuthenticated)
{
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
RedirectUri = Request.Host.Value,
IssuedUtc = System.DateTime.UtcNow,
ExpiresUtc = System.DateTime.UtcNow.AddHours(1)
};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(GoogleUser), authProperties);
}
return LocalRedirect("/");
}
I already added IssuedUtc and ExpiresUtc but that didn't change anything.
You have to enable the ability to call Google APIs (https://www.googleapis.com/auth/admin.directory.user, https://www.googleapis.com/auth/admin.directory.group) to get this information, but, before you can do that, the G-Suite Domain Admin has to authorize that access using https://developers.google.com/admin-sdk/directory/v1/guides/authorizing
This explains the process:
https://developers.google.com/admin-sdk/directory/v1/guides/delegation
You will want to see this GitHub repo for code samples:
https://github.com/googleapis/google-api-dotnet-client
Here is some psudo code:
string[] Scopes = {
DirectoryService.Scope.AdminDirectoryGroup,
DirectoryService.Scope.AdminDirectoryUser
};
GoogleCredential credential;
//redirectUrl = this.Request.Host.Value;
string keyfilepath = "yourKeyFile.json";
using (var stream = new FileStream(keyfilepath, FileMode.Open, FileAccess.Read))
{
// As we are using admin SDK, we need to still impersonate user who has admin access
// https://developers.google.com/admin-sdk/directory/v1/guides/delegation
credential = GoogleCredential.FromStream(stream)
.CreateScoped(Scopes).CreateWithUser(EmailOfGoogleDomainAdmin);
}
// Create Directory API service.
var service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "ApplicationName",
});
// G Suite User to get information about
// This test user should be suspended
var gs_email = UserToCHeck;
var request = service.Users.Get(gs_email);
var result = request.Execute();
Console.WriteLine("Full Name: {0}", result.Name.FullName);
Console.WriteLine("Email: {0}", result.PrimaryEmail);
Console.WriteLine("ID: {0}", result.Id);
Console.WriteLine("Is Admin: {0}", result.IsAdmin);
Console.WriteLine("Is Suspended: {0}", result.Suspended);

User.Claims is null after deploying application while users are still logged in

I have a aspnet core web application that uses Identity claims (w/ JWT token authorization). I have the expiration for the token set for 2 days.
The normal process of logging in, authenticating credentials, and receiving / sending tokens works well.
The problem I'm having is when I deploy the application to IIS AND users are still logged in, the User.Claims property is null. The User object has no value. It only gets refreshed if they log out and then log back in.
The only idea I can think of is detecting if the claims are null and redirecting to login, but this isn't ideal as they may be continuing to update information from the day before, only to have it fail on submission.
EDIT (code sample):
ApplicationUser appUser = _userManager.FindByNameAsync(login).Result;
var displayName = appUser.ToDisplayName();
var claimsIdentity = new ClaimsIdentity(userClaims, "Bearer");
claimsIdentity.AddClaim(new Claim("displayName", displayName));
await _userManager.AddClaimAsync(appUser, new Claim("id", appUser.Id)); // server claim
await _userManager.AddClaimAsync(appUser, new Claim("displayName", displayName)); // server claim
await _userManager.UpdateAsync(appUser);
// return token to client.
var expires = DateTime.UtcNow.AddMinutes(2880);
var token = GetJwtToken(expires, claimsIdentity);
return new TokenDTO()
{
id_token = token,
tokenExpires = expires,
displayName = displayName,
authenticated = true,
username = appUser.UserName,
roles = applicationRoles // roles is for client-side nav authorization
};
GetJwtToken
private string GetJwtToken(DateTime? expires, ClaimsIdentity identity)
{
var handler = new JwtSecurityTokenHandler();
var securityToken = handler.CreateToken(new
SecurityTokenDescriptor()
{
Issuer = _tokenOptions.Issuer,
Audience = _tokenOptions.Audience,
SigningCredentials = _tokenOptions.SigningCredentials,
Subject = identity,
Expires = expires,
NotBefore = DateTime.UtcNow.AddMinutes(-1)
});
return handler.WriteToken(securityToken);
}
Calling User.Claims:
[HttpPut]
public IActionResult Put([FromBody]OrderProcessUpdateDTO
orderProcessUpdateDTO)
{
// claims is sometimes null here
var currentUserId = User.Claims.Single(x => x.Type ==
nameof(CustomClaimType.id)).Value;
...
}

Custom claims with Jwt Bearer Authentication

I have an application that uses JwtBearerAuthentication. I am trying to add my application claims to the User(ClaimsPrincipal) at the beginning of each request. I managed to do that using ClaimsTransformationOptions:
app.UseClaimsTransformation(new ClaimsTransformationOptions
{
Transformer = new ClaimsTransformer<TUser, TRole>()
});
and in my TransformAsync:
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
var services = context.Context.RequestServices;
var userManager = services.GetRequiredService<UserManager<TUser>>();
var roleManager = services.GetRequiredService<RoleManager<TRole>>();
var userId = 1; // Get the UserId from my store, let say its 1 for now
if (userId != 0)
{
var user = await userManager.FindByIdAsync(userId);
var claimsPrincipal = await new UserClaimsPrincipalFactory<TUser, TRole>(userManager, roleManager, _optionsAccessor)
.CreateAsync(user);
context.Principal.AddIdentities(claimsPrincipal.Identities);
}
return context.Principal;
}
So far so good and the claims are being loaded from the database and added to the context.Principal. My problem is once I reach the controller, the identities are being overwritten !!
So I solved this problem by putting the app.UseClaimsTransformation after app.UseJwtBearerAuthentication which made sure that whenever JWT is going to amend the ClaimsPrincipal the ClaimsTransformation will be called afterwards to add my own claims.