How to inject a ClaimsPrincipal in a Blazor Server application - c#

Here are some artifacts to help understand the issue:
Sample Code - Github repo
Deployed Application - no longer available
Update: I have followed this YouTube video which I now believe to be the correct way of accessing information about the authenticated user in dependent services for a Blazor Server application: https://www.youtube.com/watch?v=Eh4xPgP5PsM.
I've updated the Github code to reflect that solution.
I have the following classes that I register using dependency injection in my ASP.NET MVC Core application.
public class UserContext
{
ClaimsPrincipal _principal;
public UserContext(ClaimsPrincipal principal) => _principal = principal;
public bool IsAuthenticated => _principal.Identity.IsAuthenticated;
}
public class WrapperService
{
UserContext _userContext;
public WrapperService(UserContext context) => _userContext = context;
public bool UserHasSpecialAccess()
{
return _userContext.IsAuthenticated;
}
}
The IoC dependency registrations are configured in Startup.cs
services.AddScoped<ClaimsPrincipal>(x =>
{
var context = x.GetRequiredService<IHttpContextAccessor>();
return context.HttpContext.User;
});
services.AddScoped<UserContext>();
services.AddScoped<WrapperService>();
I recently enabled Blazor in the MVC application and wanted to use my DI registered services from within my Blazor components.
I injected the service in a Blazor component in order to use it like so:
#inject WrapperService _Wrapper
However, when I attempt to use the service from a server side handler, the request fails with an exception complaining that the services could not be constructed - due to IHttpContext not existing on subsequent calls to the server.
<button #onclick="HandleClick">Check Access</button>
async Task HandleClick()
{
var hasPermission = _Wrapper.UserHasSpecialAccess(); // fails 😔
}
I think I understand why the use of IHttpContextAccessor is not working/recommended in Blazor Server apps. My question is, how can I access the claims I need in my services without it?
The odd thing to me is that this all works when I run it under IIS Express in my development environment, but fails when I deploy and attempt to run it from within an Azure AppService.

This is what work for me, writing a derived class for AuthenticationStateProvider.
public class AppAuthenticationStateProvider : AuthenticationStateProvider
{
private ClaimsPrincipal principal;
// Constructor, only needed when injections required
public AppAuthenticationStateProvider(/* INJECTIONS HERE */)
: base()
{
principal ??= new();
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(new AuthenticationState(principal));
}
// Method called from login form view
public async Task LogIn(/* USER AND PASSWORD */)
{
// Create session
principal = new ClaimsPrincipal(...);
var task = Task.FromResult(new AuthenticationState(principal));
NotifyAuthenticationStateChanged(task);
}
// Method called from logout form view
public async Task LogOut()
{
// Close session
principal = new();
var task = Task.FromResult(new AuthenticationState(principal));
NotifyAuthenticationStateChanged(task);
}
Then, at program/startup you add these lines:
// Example for .Net 6
builder.Services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
builder.Services.AddScoped<ClaimsPrincipal>(s =>
{
var stateprovider = s.GetRequiredService<AuthenticationStateProvider>();
var state = stateprovider.GetAuthenticationStateAsync().Result;
return state.User;
});
That's it. Now you can inject ClaimsPrincipal wherever you want.

You can inject AuthenticationStateProvider into your Service constructor and then use
var principal = await _authenticationStateProvider.GetAuthenticationStateAsync();
AuthenticationStateProvider is a Scoped service so yours has to be too.

Use CascadingAuthenticationState to access the claims principal
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-5.0#expose-the-authentication-state-as-a-cascading-parameter-1
If you need to use your own logic, you will need to implement your own authentication state provider.
If you want to use a service to use ClaimsPrincipal you can do the following:
ClaimsPrincipalUserService.cs
ClaimsPrincipal claimsPrincipal;
void SetClaimsPrincipal(ClaimsPrincipal cp)
{
claimsPrincipal = cp;
// any logic + notifications which need to be raised when
// ClaimsPrincipal has changes
}
Inject this service as scoped in the startup.
In the layout
MainLayout.razor
#inject ClaimsPrincipalUserService cpus;
[CascadingParameter]
public Task<AuthenticationState> State {get;set;}
protected override async Task OnInitializedAsync()
{
var state = await State;
var user = state.User; // Get claims principal.
cpus.SetClaimsPrincipal(user);
}

Related

Inject custom created object based on claims

I'm using claims based authentication in my Blazor Server app. When the user logs in to my app, I define a claim which contains a specific ID to identify the user within the database.
Now I want to get an object based on its value which I can use within my app.
For example: let's say the value from my claim is 1. Now I need a way to get the data for user 1 from the database and inject the object into my razor components/pages to make all properties accessible at any time within my app. I think this can be achieved with some sort of middleware but I'm not sure about this.
My current approach is to access the HttpContext within the _Host.cshtml file which loads the appropriate data to the page on a page reload but not when changing pages using a NavLink or the NavigationManager.
How can I get the relevant data to load each time the active page is changed?
I tried to adjust #Hans code but by using AuthenticationStateProvider
using System.Security.Claims
using Microsoft.AspNetCore.Components.Authorization
public class ClaimsPrincipalDataService
{
private readonly AuthenticationStateProvider AuthenticationStateProvider;
private readonly DbContext DbContext;
public ClaimsPrincipalDataService(AuthenticationStateProvider AuthenticationStateProvider , DbContext DbContext)
{
this.AuthenticationStateProvider = AuthenticationStateProvider;
this.DbContext = DbContext;
}
private async Task<User> GetUserAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
var userId = user.FindFirst(ClaimTypes.NameIdentifier).Value;
return await DbContext.Users.FindAsync(userId);
}
else
{
//do something
}
}
}
Add scope
services.AddScoped<ClaimsPrincipalDataService>();
Inject the service in your component
#inject ClaimsPrincipalDataService ClaimService
#code {
private User _user;
protected override async Task OnInitializedAsync()
{
_user = await ClaimService.GetUserAsync();
}
}

How to keep track of consumed JWT by my API

my controller method consumes a JWT token which was enabled in ConfigureServices method in Startup.cs
.AddJwtBearer(options => { // some code }; });
The CreateUser() action in UserController consumes this token
[HttpPost, Authorize("JWT")]
public SaveResponse CreateUser(IUnitOfWork uow, UserRequest request) {
return new UserRepository().Create(uow, request);
}
The problem is as follows: A few methods deeper upon creating a new user, there's a method HasPermission() that checks logged in user's Administration permissions. However, in this particular case using JWT, there's no logged in user. The presence of valid JWT suffices. I am going to modify this HasPermission() in a way, that it also accepts JWT.
At CreateUser method level, the JWT is present inside HttpRequest's 'Authorization' header.
The question is - How can I deliver this JWT token to like a 8th method in a chain of methods executed by UserRepository().Create(uow, request) ? Is there a way to pull this off without modifying parameters of these methods?
thank you
If you use DI to instantiate service dependecies you can register IHttpContextAccessor via services.AddHttpContextAccessor() and use it to get information about request:
public SomeService(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public void SomeServiceMethod()
{
var auth = _contextAccessor.HttpContext.Request.Headers[HeaderNames.Authorization].ToString(); // possibly will need to remove scheme from the header
}
This particular case using JWT, there's no logged in user. The presence of valid JWT suffices.
Assuming you have the auth middleware enabled, if the request is able to reach CreateUser action, then [Authorize] attribute makes sure that the token is valid. So you don't need to do another validation.
Second, you shouldn't flow the token down to the repository. Keep HTTP and data retrieval concerns separate.
The solution to not "passing a parameter down 8 level" is to use dependency injection throughout your application and let it keep track of dependencies.
To access the current user inside your repo, create an interface that exposes the user:
interface IPrincipalAccessor {
ClaimsPrincipal? Principal { get; }
}
then implement this with IHttpContextAccessor
private class HttpPrincipalAccessor : IPrincipalAccessor
{
private IHttpContextAccessor _httpContextAccessor;
public HttpPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal? Principal => _httpContextAccessor?.HttpContext?.User;
}
You need to enable IHttpAccessor and register this class in DI:
services.AddHttpContextAccessor();
services.AddScoped<IPrincipalAccessor, HttpPrincipalAccessor>();
Now you can inject this interface in your repo and use the user claims. The repo isn't aware, and doesn't care where the user comes from, it just needs to know the current user.
class MyRepo
{
private IPrincipalAccessor _principalAccessor;
public MyRepo(IPrincipalAccessor principalAccessor)
{
_principalAccessor = principalAccessor;
}
Task Create(/* some parameters */)
{
var user = _principalAccessor.Principal;
if (user.HasClaim("eyes", "2"))
{
// user has two eyes
}
// ...
}
}
But the problem with your code is that you're not using dependency injection, so you need to inject your repo, instead of newing it up.

Handle SubDomain in Asp.net Core 3.1 application

I'm working on an asp.net core 3.1 application (MVC), and as a requirement, every account should have its subdomain (ex : mystore.domain.com) and its data. So I'm trying to figure out how to add the subdomain part in the routing pattern, and catch it in my controller in order to get the user data, and return it in a view.
I've done some research and found solutions for asp.net core version 2, unfortunettly, it does not work on version 3 (so much have changed) this article for example.
Summary :
User types : mystore.domain.com or mystore.domain.com\store
I catch the subsomain "mystore", search the database for the user data, and render a view.
You could use a filter, specifically, an action filter, which could:
Run code immediately before and after an action method is called.
Can change the arguments passed into an action.
Can change the result returned from the action.
Are not supported in Razor Pages.
An example is
public class MySampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Do something before the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do something after the action executes.
MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
}
}
Here you could prepare a scoped service, load the user based on the service and then reuse it in any service that requires that data.
Even without the filter, you could simply create a UserService with a scoped lifetime, load the user there and use it anywhere in your services.
In our system we are doing something similar:
A service to load the session data:
public class ClientTokenService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ClientTokenService(
IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Profile LoadProfile()
{
if (_httpContextAccessor.HttpContext.User == null)
{
throw new Exception("No user claims found to load Profile");
}
var user = _httpContextAccessor.HttpContext.User;
var numberType = (NumberType)int.Parse(user.FindFirst("numberType").Value);
var profileType = (PackagePlan)int.Parse(user.FindFirst("profileType").Value);
var lineOfBusiness = (LineOfBusiness)int.Parse(user.FindFirst("lineOfBusiness").Value);
// More stuff
// Prepare the profile data
return new Profile(
user.FindFirst("number").Value,
user.FindFirst("contractId").Value,
numberType,
profileType,
user.FindFirst("cc")?.Value,
user.FindFirst("app").Value,
user.FindFirst("clickId")?.Value,
user.FindFirst("wifi") != null,
lineOfBusiness
);
}
}
This service can be transient, and then a scoped service which saves the data
public class ClientSessionContext
{
public Profile Profile { get; }
public ClientSessionContext(
ClientTokenService sessionService)
{
Profile = sessionService.LoadProfile();
}
}
Declare this service as scoped, so this class is initialized just once per request
Statup.cs
services.AddScoped<ClientSessionContext>();
Then just inject this service anywhere where you need access to the user data.

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

DB-First authentication confusion with ASP.NET Web API 2 + EF6

I need to create a Web API C# application for an existing MySQL database. I've managed to use Entity Framework 6 to bind every database table to a RESTful API (that allows CRUD operations).
I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests).
The MySQL database I have to use has a table for users (called user) that has the following self-explanatory columns:
id
email
username
password_hash
It seems that the de-facto standard for authentication is ASP.Net Identity. I have spent the last hour trying to figure out how to make Identity work with an existing DB-First Entity Framework setup.
If I try to construct ApplicationUser instances storing user instances (entities from the MySQL database) to retrieve user data, I get the following error:
The entity type ApplicationUser is not part of the model for the current context.
I assume I need to store Identity data in my MySQL database, but couldn't find any resource on how to do that. I've tried completely removing the ApplicationUser class and making my user entity class derive from IdentityUser, but calling UserManager.CreateAsync resulted in LINQ to Entities conversion errors.
How do I setup authentication in a Web API 2 application, having an existing user entity?
You say:
I want to implement a login/registration system (so that I can
implement roles and permissions in the future, and restrict certain
API requests).
How do I setup authentication in a Web API 2 application, having an
existing user entity?
It definitely means that you DO NOT need ASP.NET Identity. ASP.NET Identity is a technology to handle all users stuffs. It actually does not "make" the authentication mechanism. ASP.NET Identity uses OWIN Authentication mechanism, which is another thing.
What you are looking for is not "how to use ASP.NET Identity with my existing Users table", but "How to configure OWIN Authentication using my existing Users table"
To use OWIN Auth follow these steps:
Install the packages:
Owin
Microsoft.AspNet.Cors
Microsoft.AspNet.WebApi.Client
Microsoft.AspNet.WebApi.Core
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.WebHost
Microsoft.Owin
Microsoft.Owin.Cors
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security
Microsoft.Owin.Security.OAuth
Create Startup.cs file inside the root folder (example):
make sure that [assembly: OwinStartup] is correctly configured
[assembly: OwinStartup(typeof(YourProject.Startup))]
namespace YourProject
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
//other configurations
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/security/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
Provider = new AuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
try
{
//retrieve your user from database. ex:
var user = await userService.Authenticate(context.UserName, context.Password);
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
//roles example
var rolesTechnicalNamesUser = new List<string>();
if (user.Roles != null)
{
rolesTechnicalNamesUser = user.Roles.Select(x => x.TechnicalName).ToList();
foreach (var role in user.Roles)
identity.AddClaim(new Claim(ClaimTypes.Role, role.TechnicalName));
}
var principal = new GenericPrincipal(identity, rolesTechnicalNamesUser.ToArray());
Thread.CurrentPrincipal = principal;
context.Validated(identity);
}
catch (Exception ex)
{
context.SetError("invalid_grant", "message");
}
}
}
}
Use the [Authorize] attribute to authorize the actions.
Call api/security/token with GrantType, UserName, and Password to get the bearer token. Like this:
"grant_type=password&username=" + username + "&password=" password;
Send the token within the HttpHeader Authorization as Bearer "YOURTOKENHERE". Like this:
headers: { 'Authorization': 'Bearer ' + token }
Hope it helps!
Since your DB schema are not compatible with default UserStore You must implement your own UserStore and UserPasswordStore classes then inject them to UserManager. Consider this simple example:
First write your custom user class and implement IUser interface:
class User:IUser<int>
{
public int ID {get;set;}
public string Username{get;set;}
public string Password_hash {get;set;}
// some other properties
}
Now author your custom UserStore and IUserPasswordStore class like this:
public class MyUserStore : IUserStore<User>, IUserPasswordStore<User>
{
private readonly MyDbContext _context;
public MyUserStore(MyDbContext context)
{
_context=context;
}
public Task CreateAsync(AppUser user)
{
// implement your desired logic such as
// _context.Users.Add(user);
}
public Task DeleteAsync(AppUser user)
{
// implement your desired logic
}
public Task<AppUser> FindByIdAsync(string userId)
{
// implement your desired logic
}
public Task<AppUser> FindByNameAsync(string userName)
{
// implement your desired logic
}
public Task UpdateAsync(AppUser user)
{
// implement your desired logic
}
public void Dispose()
{
// implement your desired logic
}
// Following 3 methods are needed for IUserPasswordStore
public Task<string> GetPasswordHashAsync(AppUser user)
{
// something like this:
return Task.FromResult(user.Password_hash);
}
public Task<bool> HasPasswordAsync(AppUser user)
{
return Task.FromResult(user.Password_hash != null);
}
public Task SetPasswordHashAsync(AppUser user, string passwordHash)
{
user.Password_hash = passwordHash;
return Task.FromResult(0);
}
}
Now you have very own user store simply inject it to the user manager:
public class ApplicationUserManager: UserManager<User, int>
{
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new MyUserStore(context.Get<MyDbContext>()));
// rest of code
}
}
Also please note you must directly inherit your DB Context class from DbContext not IdentityDbContext since you have implemented own user store.

Categories