Getting user data without callling GetUserAsync on every request - c#

We currently store a ClientId in the AspnetUsers table. We use this to identify which user the client is logging in against, and for filtering data for by this client.
Is there a more efficient way of getting the current users clientId, in each controller without calling: await _manager.GetUserAsync(_httpContextAccessor.HttpContext.User); In each Controller to get the clientId? Can we use a Singleton service to set this property upon login, and inject this into our controllers?
Thanks,

The best solution would be to store the clientid as a claim so when the user logs in it will get serialized into the claimsprincipal and included in the auth cookie.
You can inherit from the default Identity ClaimsPrincipalFactory and override the CreateAsync method invoke the base class to create and then add custom claims. Then you inject your custom factory and it will be used. Then you can get the clientid from the User.Claims where User is the ClaimsPrincipal and is available in the controller actions.
By doing this you will only need to hit the db at login time to get the clientid to add as a claim. After that you can get it on every request from the claims on the User without hitting the db.
I have an example in my cloudscribe project, you can see my custom ClaimsPrincipalFactory.
Example code:
public override async Task<ClaimsPrincipal> CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
// base class takes care of all the default stuff like roles name id etc
var principal = await base.CreateAsync(user);
if (principal.Identity is ClaimsIdentity)
{
var identity = (ClaimsIdentity)principal.Identity;
// add logic to get the clientid
var clientId = yourmethod();
var clientIdClaim = new Claim("ClientId", clientId);
identity.AddClaim(clientIdClaim);
}
}
Then you could make an extension method to make it easy to get the client id claim
public static string GetClientId(this ClaimsPrincipal principal)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
var claim = principal.FindFirst("ClientId");
return claim != null ? claim.Value : null;
}
Then in the controller you can use var cleintId = User.GetClientId();

Related

C# ASP.NET: How to automatically decode my bearer token for each method?

I have a Web API that uses bearer tokens for authentication and authorization. The presence of a token signifies that the user is authenticated; the claims within that token specify the authorizations the user has.
I wrote an OAuth class that queries the database upon user login to determine the user's permissions. All of this works fine.
Now, for each method in each controller, I need to extract information from the bearer token's claims. What I have done now is to define a class that contains the entities I need from the token and write a static method to take a User object from within a controller method and produce an instance of my class containing the token data:
// Bearer token class
class TokenData
{
public string UserId { get; set; }
public int GroupId { get; set; }
public string UserTag { get; set; }
// ... more properties as needed
}
// Method to get bearer token data from user object
internal static TokenData getTokenDataFromUserPrincipal(System.Security.Principal.IPrincipal p)
{
var identity = (ClaimsIdentity)p.Identity;
IEnumerable<Claim> claims = identity.Claims;
var enumerable = claims as Claim[] ?? claims.ToArray();
var claimTypes = from x in enumerable select x.Type;
var types = claimTypes as string[] ?? claimTypes.ToArray();
if (!types.Contains("userId")) return null; // if a token was given but it has no UserID it's not valid.
var claimsByType = enumerable.ToDictionary(x => x.Type, x => x.Value);
TokenData td = new TokenData
{
UserId = claimsByType["userId"],
GroupId = types.Contains("groupId") ? int.Parse(claimsByType["groupId"]) : 0,
UserTag = types.Contains("userTag") ? claimsByType["userTag"]) : null,
// more properies as needed
};
return td;
}
// A controller method that uses the claims
public HttpResponseMessage DoSomething()
{
TokenData td = getTokenDataFromUserPrincipal(User);
// Now I can access the token data in the td variable.
return Request.CreateResponse(td.UserId);
}
Ok, so this all works perfectly. The thing I'm looking for though is if there's a way to automate pulling the claims from the User object whenever a controller method is invoked.
The Web API architecture already basically does this with the User object itself - it contains any information that is part of the request related to the user. (Similar to the Request object, which contains all of the HTTP request data.)
What I'd like to be able to do is, instead of calling getTokenDataFromUserPrincipal at the start of each controller, to instead just have a static variable available similar to how Web API does it - e.g. TokenData. For example:
// A controller method that uses the claims and doesn't have to explicitly retrieve them.
public HttpResponseMessage DoSomething()
{
return Request.CreateResponse(TokenData.UserId);
}
I do know how to use attributes - I actually wrote custom attributes which pull the same token data and use it to determine if a user can access a certain class of function - so for example I can just include [MinimumUserLevel(2)] in front of my controller methods. But having to add an attribute to each method simply moves the problem outside of the method.
To sum: Is it possible to have another static variable, scoped at the request level, that user code can populate per request without having to copy code to the beginning of each controller method?
Ideally, there would be a way to insert a function in the pipeline, so that prior to the controller method being run, I can run the code to get the token data from the principal, so it will be ready when the controller method runs. (Note that the method to pull token data simply returns null if the data doesn't exist - this is the expected behavior for this static variable in the instance of a call with no token.)
You can either manually verify the jwt token. Or add the JwtBearerAuthentication to the Owin stack.
Here is one of the found samples. This would parse the token for each request and set the User properties of each request.
You would have to set it up for the secret you were using when you created the jwt token.
The provided sample validates and converts the JWT token to a ClaimsIdentity which is set on each request and is accessible through the User.Identity property within each controller. No custom code is necessary for that.
And since this package is provided by Microsoft, it might be better then some self made JWT parser.
(User.Identity as ClaimsIdentity).FindFirst("userId")?.Value to access your user ID.
You could also extend the IIdentity with the following code.
public static class IIdentityExtensions
{
public static string UserId(this IIdentity identity)
{
return identity.GetClaimValue("userId");
}
public static string GetClaimValue(this IIdentity identity, string claimType)
{
return identity
.AsClaimsIdentity()
.Claims.FirstOrDefault(c => c.Type == claimType)?.Value;
}
private static ClaimsIdentity AsClaimsIdentity(this IIdentity identity)
{
if (!(identity?.IsAuthenticated ?? false))
throw new UnauthorizedAccessException("User not logged-in");
return identity as ClaimsIdentity;
}
}
And then accessing the user id like User.Identity.UserId()
The solution to this turned out to be simpler than I was making it: define a subclass of ApiController.
So I simply wrote a class that looks like this:
public class MyApiController : ApiController
{
// Makes token data available to any method on a class deriving from MyApiController
public TokenData Token { get; set; }
// Override constructor - parse token whenever instance is created
public MyApiController()
{
// Extract the token data from the User variable (User is pulled in from the base ApiController)
Token = getTokenDataFromUserPrincipal(User);
}
private static TokenData getTokenDataFromUserPrincipal(System.Security.Principal.IPrincipal p)
{
...
}
}
Now I have all of my controllers derive from MyApiController rather than from ApiController. From that point, I'm able to access the Token variable in any controller's logic, which will contain the parsed token data.

How to apply role base authorization to Web API 2 application that use token base authentication?

I created a web API 2 application that used token-based authentication system. I used this tutorial to implement the authentication to the application.
Then I added the roles to the system by using seed method.
protected override void Seed(TBA.Models.AuthContext context)
{
if (!context.Roles.Any(r => r.Name == "SuperAdmin"))
{
var store = new RoleStore<IdentityRole>(context);
var manager = new RoleManager<IdentityRole>(store);
var role = new IdentityRole { Name = "SuperAdmin" };
manager.Create(role);
}
}
Then I added the user to the user role.
public async Task<IdentityResult> RegisterUser(UserModel userModel)
{
IdentityUser user = new IdentityUser
{
UserName = userModel.UserName
};
var result = await _userManager.CreateAsync(user, userModel.Password);
await _userManager.AddToRoleAsync(user.Id, userModel.UserRole);
return result;
}
Then I try to access to below end-point.
[Authorize(Roles = "SuperAdmin")]
[Route("GetBySuperAdmin")]
public IHttpActionResult GetBySuperAdmin()
{
return Ok("Get By Super Admin");
}
It gives me below error message.
"message": "Authorization has been denied for this request."
What should I change to make this correct?
How to check role before accessing the end-point in Web API 2 application?
After adding user to role, You have to log out and then log in again (generate new token).
Tokens have information about user's role, so after changing roles You have to refresh token, to have information about new roles.

Handle the Role paramater of the Authorize tag in the AuthenticationHandler

So I'm trying to create a custom authorisation system which supports my custom token in my ASP.NET core web API. I know how to create custom handlers and all but I don't know one thing, I even don't know if it's even possible, how can I read the Role attribute from:
[Authorize(Roles="role")]
this is my custom authorisation handler:
public class Auth : AuthenticationHandler<AuthOptions>
{
//private static Models.flowerpowerContext context = new Models.flowerpowerContext
private Token.AuthToken authToken;
private readonly RequestDelegate _next;
private Models.flowerpowerContext _context;
public Auth(IOptionsMonitor<AuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
DbContextOptions<Models.flowerpowerContext> _options = new DbContextOptions<Models.flowerpowerContext>();
_context = new Models.flowerpowerContext(_options);
string test = options.ToString();
authToken = new Token.AuthToken(_context);
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
Models.Token _token = new Models.Token();
Token.Hasher hasher = new Token.Hasher();
var bearer = Request.Headers["Authorization"];
string token = bearer.ToString();
_token = JsonConvert.DeserializeObject<Models.Token>(hasher.DecodeHash(token));
string id = "";
return AuthenticateResult.NoResult();
}
}
The above handler works fine and it get's the token from the Authorization header. And the AuthOptions only have an empty constructor.
I couldn't find anything that would seem, at least to me, to retrieve the value of the Roles parameter. Does anybody know a better approach/work-around or a direct solution to this, and also if this is even possible?
EDIT:
Forgot to mention why I want to do this; I want to do this because the role of the user has been defined within my custom token. And I need to compare it to the required role.
You mix up two concepts here, authentication and authorization.
Authentication is the process of verifying who the user is.
Authorization is the process of verifying that the user has the access to requested resource.
Your Auth class is responsible for authentication, not authorization. If request is successfully authenticated, HandleAuthenticateAsync method should return instance of AuthenticateResult with filled Principal property of ClaimsPrincipal type. ClaimsPrincipal implements IPrincipal interface that has method bool IsInRole(string role).
Thus after the authentication step, it's known for the principal associated with the request, whether hi is in specific role. And the list of roles authorized for specific controller or action is specified in Roles property of AuthorizeAttribute. In your example if request principal is in "role", request will be successfully authorized. If he is not, request will fail with 401 or 403 error. This code of checking principal role is already implemented in AuthorizeAttribute filter. You don't need to reinvent it until you implement your custom authorization filter (I remind you that your Auth class is an authentication filter).
So answering your question about handling the user role: you should just return proper principal object that returns correct value for IsInRole(string role) method. All the rest will be handled by default authorization filter.
So after I followed CodeFuller's responses I came up with a solution.
I basically I had to create a Claim collection like this:
IList<Claim> claimCollection = new List<Claim>
{
new Claim(ClaimTypes.Email, _token.TokenEmail),
new Claim(ClaimTypes.Role, _token.TokenRole)
};
Then I had to appoint a ClaimsIdentity and a ClaimsPrinciple. After this I had to create an AuthenticationTicket like this:
AuthenticationTicket ticket = new AuthenticationTicket(principal, "Custom scheme");
And all I had to do now was to return this:
return AuthenticateResult.Success(ticket);
This allowed me to verify roles from my custom token. This basically allowed me to do this:
[Authorize(Roles = "Admin")]
To get some more explanation about this matter you should check out CodeFuller's response.

How to get users from a existing database for identityServer4

i try to understand how i can bind users (email, password, firstname, lastname and os on) which are stored in an existing database (located: localhost:3306) into my identityserver4 project so that i can use these information to login a user or register a new user into that database?
I read some tutorials (specially http://docs.identityserver.io/en/release/quickstarts/8_entity_framework.html) but i think this is always for db in the same project. my db isn´t in the same project.
In this context i read about asp.net-core Identity. but i don´t understand completely how that´s related.
Can someone tell me how can i bind a db in my project and what´s the role of identity with application User and so on?
thanks in advance
This article is more relevant to your situation. The one you linked is for configuration data and not for user data:
http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
In short, you want to access your user data through Asp.Net Core Identity.
You need to:
Make a user class containing the relevant fields as your database
Create an EntityFramework DbContext class to map your database to your class
Register your user class and dbcontext with aspnet core identity
Tell IdentityServer to use AspNetIdentity
This is what your Startup ConfigureServices method might look like once implemented. Not pictured here is the DbContext and User classes you need to make.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<YourUserStoreDbContextHere>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<YourUserClassHere, YourRoleClassHereIfAny>()
.AddEntityFrameworkStores<YourUserStoreDbContextHere>()
.AddDefaultTokenProviders();
services.AddIdentityServer()
// Other config here
.AddAspNetIdentity<YourUserClassHere>();
}
Refer to the docs on AspNet Identity for details on configuring your user class and dbcontext: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity
You need to implement your own UserStore (example)
public async Task<TapkeyUser> ValidateCredentialsAsync(string username, string password)
{
//This is pseudo-code implement your DB logic here
if (database.query("select id from users where username = username and password = password")
{
return new User(); //return User from Database here
} else {
return null;
}
}
And use this in your AccountController:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
// use our custom UserStore here
--------> if (_users.ValidateCredentials(model.Username, model.Password))
{
AuthenticationProperties props = null;
// only set explicit expiration here if persistent.
// otherwise we reply upon expiration configured in cookie middleware.
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
// issue authentication cookie with subject ID and username
var user = _users.FindByUsername(model.Username);
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username));
await HttpContext.Authentication.SignInAsync(user.SubjectId, user.Username, props);
// make sure the returnUrl is still valid, and if yes - redirect back to authorize endpoint or a local page
if (_interaction.IsValidReturnUrl(model.ReturnUrl) || Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return Redirect("~/");
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await _account.BuildLoginViewModelAsync(model);
return View(vm);
}

How to get current user in asp.net core

I want to get the current user, so I can access fields like their email address.
But I can't do that in asp.net core.
This is my code:
HttpContext almost is null in constructor of controller.
It's not good to get a user in each action. I want to get the user's information once and save it to ViewData;
public DashboardController()
{
var user = HttpContext.User.GetUserId();
}
User.FindFirst(ClaimTypes.NameIdentifier).Value
EDIT for constructor
Below code works:
public Controller(IHttpContextAccessor httpContextAccessor)
{
var userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value
}
Edit for RTM
You should register IHttpContextAccessor:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
Simple way that works and I checked.
private readonly UserManager<IdentityUser> _userManager;
public CompetitionsController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
var user = await _userManager.GetUserAsync(HttpContext.User);
then you can all the properties of this variables like user.Email. I hope this would help someone.
Edit:
It's an apparently simple thing but bit complicated cause of different types of authentication systems in ASP.NET Core. I update cause some people are getting null.
For JWT Authentication (Tested on ASP.NET Core v3.0.0-preview7):
var email = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
var user = await _userManager.FindByEmailAsync(email);
I have to say I was quite surprised that HttpContext is null inside the constructor. I'm sure it's for performance reasons. Have confirmed that using IPrincipal as described below does get it injected into the constructor. Its essentially doing the same as the accepted answer, but in a more interfacey-way.
For anyone finding this question looking for an answer to the generic "How to get current user?" you can just access User directly from Controller.User. But you can only do this inside action methods (I assume because controllers don't only run with HttpContexts and for performance reasons).
However - if you need it in the constructor (as OP did) or need to create other injectable objects that need the current user then the below is a better approach:
Inject IPrincipal to get user
First meet IPrincipal and IIdentity
public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IPrincipal and IIdentity represents the user and username. Wikipedia will comfort you if 'Principal' sounds odd.
Important to realize that whether you get it from IHttpContextAccessor.HttpContext.User, ControllerBase.User or ControllerBase.HttpContext.User you're getting an object that is guaranteed to be a ClaimsPrincipal object which implements IPrincipal.
There's no other type of User that ASP.NET uses for User right now, (but that's not to say other something else couldn't implement IPrincipal).
So if you have something which has a dependency of 'the current user name' that you want injected you should be injecting IPrincipal and definitely not IHttpContextAccessor.
Important: Don't waste time injecting IPrincipal directly to your controller, or action method - it's pointless since User is available to you there already.
In startup.cs:
// Inject IPrincipal
services.AddHttpContextAccessor();
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Then in your DI object that needs the user you just inject IPrincipal to get the current user.
The most important thing here is if you're doing unit tests you don't need to send in an HttpContext, but only need to mock something that represents IPrincipal which can just be ClaimsPrincipal.
One extra important thing that I'm not 100% sure about. If you need to access the actual claims from ClaimsPrincipal you need to cast IPrincipal to ClaimsPrincipal. This is fine since we know 100% that at runtime it's of that type (since that's what HttpContext.User is). I actually like to just do this in the constructor since I already know for sure any IPrincipal will be a ClaimsPrincipal.
If you're doing mocking, just create a ClaimsPrincipal directly and pass it to whatever takes IPrincipal.
Exactly why there is no interface for IClaimsPrincipal I'm not sure. I assume MS decided that ClaimsPrincipal was just a specialized 'collection' that didn't warrant an interface.
Have another way of getting current user in Asp.NET Core - and I think I saw it somewhere here, on SO ^^
// Stores UserManager
private readonly UserManager<ApplicationUser> _manager;
// Inject UserManager using dependency injection.
// Works only if you choose "Individual user accounts" during project creation.
public DemoController(UserManager<ApplicationUser> manager)
{
_manager = manager;
}
// You can also just take part after return and use it in async methods.
private async Task<ApplicationUser> GetCurrentUser()
{
return await _manager.GetUserAsync(HttpContext.User);
}
// Generic demo method.
public async Task DemoMethod()
{
var user = await GetCurrentUser();
string userEmail = user.Email; // Here you gets user email
string userId = user.Id;
}
That code goes to controller named DemoController. Won't work without both await (won't compile) ;)
It would appear that as of now (April of 2017) that the following works:
public string LoggedInUser => User.Identity.Name;
At least while within a Controller
Perhaps I didn't see the answer, but this is how I do it.
.Net Core --> Properties --> launchSettings.json
You need to have change these values
"windowsAuthentication": true, // needs to be true
"anonymousAuthentication": false, // needs to be false
Startup.cs --> ConfigureServices(...)
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
MVC or Web Api Controller
private readonly IHttpContextAccessor _httpContextAccessor;
//constructor then
_httpContextAccessor = httpContextAccessor;
Controller method:
string userName = _httpContextAccessor.HttpContext.User.Identity.Name;
Result is userName e.g. = Domain\username
I know there area lot of correct answers here, with respect to all of them I introduce this hack :
In StartUp.cs
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
and then everywhere you need HttpContext you can use :
var httpContext = new HttpContextAccessor().HttpContext;
Hope it helps ;)
My problem was to access the logged in User as an object in the cshtml file. Considering you wanted the user in ViewData, this approach might be helpful:
In the cshtml file
#using Microsoft.AspNetCore.Identity
#inject UserManager<ApplicationUser> UserManager
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
#UserManager.FindByNameAsync(UserManager.GetUserName(User)).Result.Email
</title>
</head>
<body>
</body>
</html>
In addition to existing answers I'd like to add that you can also have a class instance available app-wide which holds user-related data like UserID etc.
It may be useful for refactoring e.g. you don't want to fetch UserID in every controller action and declare an extra UserID parameter in every method related to Service Layer.
I've done a research and here's my post.
You just extend your class which you derive from DbContext by adding UserId property (or implement a custom Session class which has this property).
At filter level you can fetch your class instance and set UserId value.
After that wherever you inject your instance - it will have the necessary data (lifetime must be per request, so you register it using AddScoped method).
Working example:
public class AppInitializationFilter : IAsyncActionFilter
{
private DBContextWithUserAuditing _dbContext;
public AppInitializationFilter(
DBContextWithUserAuditing dbContext
)
{
_dbContext = dbContext;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next
)
{
string userId = null;
int? tenantId = null;
var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;
var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
if (userIdClaim != null)
{
userId = userIdClaim.Value;
}
var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
if (tenantIdClaim != null)
{
tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
}
_dbContext.UserId = userId;
_dbContext.TenantId = tenantId;
var resultContext = await next();
}
}
For more information see my answer.
This is old question but my case shows that my case wasn't discussed here.
I like the most the answer of Simon_Weaver (https://stackoverflow.com/a/54411397/2903893). He explains in details how to get user name using IPrincipal and IIdentity. This answer is absolutely correct and I recommend to use this approach. However, during debugging I encountered with the problem when ASP.NET can NOT populate service principle properly. (or in other words, IPrincipal.Identity.Name is null)
It's obvious that to get user name MVC framework should take it from somewhere. In the .NET world, ASP.NET or ASP.NET Core is using Open ID Connect middleware.
In the simple scenario web apps authenticate a user in a web browser. In this scenario, the web application directs the user’s browser to sign them in to Azure AD. Azure AD returns a sign-in response through the user’s browser, which contains claims about the user in a security token.
To make it work in the code for your application, you'll need to provide the authority to which you web app delegates sign-in.
When you deploy your web app to Azure Service the common scenario to meet this requirements is to configure web app: "App Services" -> YourApp -> "Authentication / Authorization" blade -> "App Service Authenticatio" = "On" and so on (https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md). I beliebe (this is my educated guess) that under the hood of this process the wizard adjusts "parent" web config of this web app by adding the same settings that I show in following paragraphs.
Basically, the issue why this approach does NOT work in ASP.NET Core is because "parent" machine config is ignored by webconfig. (this is not 100% sure, I just give the best explanation that I have). So, to meke it work you need to setup this manually in your app.
Here is an article that explains how to manyally setup your app to use Azure AD.
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2
Step 1: Register the sample with your Azure AD tenant.
(it's obvious, don't want to spend my time of explanations).
Step 2: In the appsettings.json file:
replace the ClientID value with the Application ID from the application you registered in Application Registration portal on Step 1.
replace the TenantId value with common
Step 3: Open the Startup.cs file and in the ConfigureServices method, after the line containing .AddAzureAD insert the following code, which enables your application to sign in users with the Azure AD v2.0 endpoint, that is both Work and School and Microsoft Personal accounts.
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
Summary: I've showed one more possible issue that could leed to an error that topic starter is explained. The reason of this issue is missing configurations for Azure AD (Open ID middleware). In order to solve this issue I propose manually setup "Authentication / Authorization". The short overview of how to setup this is added.
Taking IdentityUser would also work. This is a current user object and all values of user can be retrieved.
private readonly UserManager<IdentityUser> _userManager;
public yourController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
var user = await _userManager.GetUserAsync(HttpContext.User);
If you are using the scafolded Identity and using Asp.net Core 2.2+ you can access the current user from a view like this:
#using Microsoft.AspNetCore.Identity
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
<p>Hello #User.Identity.Name!</p>
}
else
{
<p>You're not signed in!</p>
}
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio
Most of the answers show how to best handle HttpContext from the documentation, which is also what I went with.
I did want to mention that you'll want to check you project settings when debugging, the default is Enable Anonymous Authentication = true.
if (access token in header or query parameter)
{
// Set the claims like in the Account/Login action from the interactive login form
var claims = ...;
// Local helper method, is used in other places, too
var claimsIdentity = await SignInAsync(httpContext, claims, false);
// Set user for the current request
// This works in that it's in User.Identity, but the auth events won't fire
httpContext.User = new ClaimsPrincipal(claimsIdentity);
}
And
var userEmail = HttpContext.User.FindFirst(ClaimTypes.Email).Value;
After exploring many solutions, here is what worked for me with ASP.NET core 5.
var claims = new List<Claim>(){
new Claim("Id", _user.Id)
};
As shown in the above snippet, add custom "Id" type and set it to user id while preparing list of claims to be included in the Jwt Token generation.
Then simply use that claim to access the user(This method uniquely identifies the user by its Id).
var userEmail = User.FindFirstValue("Id");
var user = await _userManager.FindByIdAsync(userEmail);
Here is complete solution:
->Token generation helper method
public async Task<string> CreateToken()
{
var signingCredentials = GetSigningCredentials();
var claims = await GetClaims();
var tokenOptions = GenerateTokenOptions(signingCredentials, claims);
return new JwtSecurityTokenHandler().WriteToken(tokenOptions);
}
private SigningCredentials GetSigningCredentials()
{
var key = Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWT_SECRET"));
var secret = new SymmetricSecurityKey(key);
return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
}
private async Task<List<Claim>> GetClaims()
{
var claims = new List<Claim>(){
new Claim("Id", _user.Id)
};
return claims;
}
private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, List<Claim> claims)
{
var jwtSettings = _configuration.GetSection("JwtSettings");
var tokenOptions = new JwtSecurityToken(
issuer: jwtSettings.GetSection("ValidIssuer").Value,
audience: jwtSettings.GetSection("ValidAudience").Value,
expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings.GetSection("ExpiresIn").Value)),
signingCredentials: signingCredentials,
claims: claims
);
return tokenOptions;
}
Here is code for Getting LoggedIn User:
[HttpGet("user")]
public async Task<ActionResult<User>> GetUser()
{
var userId = User.FindFirstValue("Id");
var user = await _userManager.FindByIdAsync(userId);
return Ok(new { User = User });
}
I use answer provided by #Ahmed for Identity
For getting the current user id, I use the following
var currentuserid = userManager.GetUserId(User);
For getting other fields related to logged user in AspNetUsers table, I use the following
var userorg = context.Users.Where(l=>l.Id== currentuserid).FirstOrDefaultAsync().Result.OrganizationId;
Hi if you want you can get id on claim like here
var userId = User.Claims.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Sub).Value;
I got my solution
var claim = HttpContext.User.CurrentUserID();
public static class XYZ
{
public static int CurrentUserID(this ClaimsPrincipal claim)
{
var userID = claimsPrincipal.Claims.ToList().Find(r => r.Type ==
"UserID").Value;
return Convert.ToInt32(userID);
}
public static string CurrentUserRole(this ClaimsPrincipal claim)
{
var role = claimsPrincipal.Claims.ToList().Find(r => r.Type ==
"Role").Value;
return role;
}
}

Categories