I am new to asp.net core and I am currently working in the multi-tenant application.I am now stuck because i have no idea of how the claims are mapped in asp.net core
I am getting the above error when in try to access the claims from MIdAuthorizedBaseApiController.
Getting claims from UserController.
My Doubt is when the claims in jwt token are mapped to "User.Claims".I am getting the claims in "UserController.cs" .But i need those claims in MIdAuthorizedBaseApiController.cs inorder to set the "UserContext".
The claims are available in UserController but the UserController itself is inheriting from MIdAuthorizedBaseApiController,but the Claims are not populated there.When i try to access it throws an exception.So, when and how the claims in jwt token are mapped.Please refer the two screenshots above to get my question correctly.
UserController.cs
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class UsersController : MIdAuthorizedBaseApiController
{
[HttpGet]
[ProducesResponseType(200, Type = typeof(BusinessEntities.User))]
[ProducesResponseType(400)]
[ProducesResponseType(401)]
public IActionResult Users()
{
// Get Tenant Id
var claimsList = User.Claims.ToList();
var tenantId = claimsList[4].Value;
// Set the Claims
// _claimsHelper.SetClaims(User.Claims.AsQueryable());
var users = _userManager.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role);//.Where(n => n.TenantId.ToString() == tenantId);
// Using "LINQ" to Query only the required properties
var result = from User in users
select new
{
Id = User.Id,
UserName = User.UserName,
Email = User.Email,
PhoneNumber = User.PhoneNumber,
// Roles = String.Join(",", (from roles in User.UserRoles select roles.Role.Name))
};
// Return the result set
if(result != null) {
return Ok(result);
}
return BadRequest(_localizer["2006"]);
}
MIdAuthorizedBaseApiController.cs
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class MIdAuthorizedBaseApiController : AuthorizedApiController
{
public MIdAuthorizedBaseApiController(UserContext usercontext):base(usercontext)
{
PopulateUserContext(usercontext);
}
private void PopulateUserContext(UserContext usercontext)
{
// Getting exception here when i try to populate User.Claims
//here
var claim = HttpContext.User.Claims.First(c => c.Type == "UserId");
//To be replaced with details from token
//usercontext.UserId = Guid.Parse("3C5CD705-8DA0-4536-856B-9F39A6ABC0FA");
usercontext.UserId = Guid.Parse("43200003-6972-4849-B80F-81896C3B5505");
usercontext.Username = Guid.NewGuid().ToString();
//usercontext.TenantId = Guid.Parse("28C914CE-C321-4033-BB87-E9C527249503");
usercontext.TenantId = Guid.Parse("21550810-A8E7-438F-BBF5-CB0755087356");
usercontext.ServerName = DateTime.Now.ToLongTimeString();
}
AuthorizedApiController.cs
using Agility.Core;
using Agility.Web.Filters;
namespace Agility.Web
{
[BusinessExceptionFilter]
public class AuthorizedApiController : BaseApiController
{
public AuthorizedApiController(UserContext userContext);
public UserContext UserContext { get; set; }
}
}
You can use HttpContextAccessor to access the claims .
Modify the MIdAuthorizedBaseApiController to perform the dependency injection of IHttpContextAccessor on the controllers :
public class MIdAuthorizedBaseApiController : AuthorizedApiController
{
public MIdAuthorizedBaseApiController(UserContext usercontext, IHttpContextAccessor httpContextAccessor) : base(usercontext)
{
PopulateUserContext(usercontext, httpContextAccessor);
}
private void PopulateUserContext(UserContext usercontext, IHttpContextAccessor httpContextAccessor)
{
// Getting exception here when i try to populate User.Claims
//here
var claim = httpContextAccessor.HttpContext.User.Claims;
//To be replaced with details from token
//usercontext.UserId = Guid.Parse("3C5CD705-8DA0-4536-856B-9F39A6ABC0FA");
.......
}
}
And the users controller:
public class ValuesController : MIdAuthorizedBaseApiController
{
public ValuesController(UserContext usercontext, IHttpContextAccessor httpContextAccessor) : base(usercontext, httpContextAccessor)
{
}
}
In addition , you could refer to below link which shows how to secure your ASP.NET Core using JWT Bearer authentication ,which use AddJwtBearer middlerware to validate/decode the JWT token :
https://jonhilton.net/security/apis/secure-your-asp.net-core-2.0-api-part-2---jwt-bearer-authentication/
Related
My customer want to validate token from query param like this
http://localhost/api/v1/users?token=xxxx
I can do like this:
[CustomAuthorize(Authorities = new[] { Constants.RoleGuest })]
[HTTPGet]
public async Task<IActionResult> Get(string token){
//call validate token function with token provided
//do something
}
Is there a way to implement automatic token authentication that does this for all requests except login and register?
It sucks to have to call the authentication function on every http request. Is this implementable as a custom attribute ?
This question don't mention how to implement authen and authorization. Main popurse is check something when user request to any endpoint. In this situation, it is token. It isn't same access token and refresh token
Thanks for all the help!
You can use action filter and custom attribute to implement it.
public class MyAuth : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
var actionInfo = context.ActionDescriptor as ControllerActionDescriptor;
var token = context.HttpContext.Request.Query.ContainsKey("token")
? Convert.ToString(context.HttpContext.Request.Query["token"])
: string.Empty;
var shouldStop = !IsValidToken(token, actionInfo);
if (shouldStop)
{
context.Result = new UnauthorizedResult();
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
private bool IsValidToken(string token, ControllerActionDescriptor actionInfo)
{
var valid = false;
var controllerName = actionInfo?.ControllerName;
var actionName = actionInfo?.ActionName;
var roles =
(actionInfo?.MethodInfo.GetCustomAttributes(typeof(CustomAuthorize), true)?.FirstOrDefault() as
CustomAuthorize).Roles;
// your token validation logic goes here
return valid;
}
}
public class CustomAuthorize : Attribute
{
public string[] Roles { get; }
public CustomAuthorize(string[] roles)
{
Roles = roles;
}
}
And in the program.cs you can register the Action filter as below
builder.Services.AddControllers(_ =>
{
_.Filters.Add(typeof(MyAuth));
});
Finally, your action method would look like below -
[CustomAuthorize(new string[]{Constants.RoleGuest})]
[HTTPGet]
public async Task<IActionResult> Get(){
// do actual work.
// this method will be only called if your validation logic pass.
}
I am using ORY Kratos for identity and my frontend SPA (React App) is authenticating against the Kratos Login Server and gets a session cookie back.
Now I want to secure my ASP.NET Core Web Api in a way, that a user can only call certain methods protected with the [Authorize] attribute when attaching a valid cookie to the request. For this, I need to validate the cookie from every incoming request. So I am looking for a way to configure Authentication and add custom logic to validate the cookie (I need to make an API call to Kratos to validate it).
The cookie I want to validate has not been issued by the ASP.NET Core App that wants to validate it.
All the samples I found so far, are also issuing the cookie on the same server but I need to validate an external one.
This is what my cookie looks like:
In the Dev Tools, I can validate that the Cookie is attached to the requests header:
This is, what I've tried so far:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.Name = "ory_kratos_session";
options.Cookie.Path = "/";
options.Cookie.Domain = "localhost";
options.Cookie.HttpOnly = true;
options.EventsType = typeof(CustomCookieAuthenticationEvents);
});
services.AddScoped<CustomCookieAuthenticationEvents>();
// ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseAuthentication();
app.UseAuthorization();
// ...
}
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
public CustomCookieAuthenticationEvents() {}
public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
// Never gets called
}
}
Logs:
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
Cookies was not authenticated. Failure message: Unprotect ticket failed
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
AuthenticationScheme: Cookies was challenged.
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
Connection id "0HM6IBAO4PLLL" completed keep alive response.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET https://localhost:5001/weatherforecast - - - 302 0 - 75.3183ms
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET https://localhost:5001/Account/Login?ReturnUrl=%2Fweatherforecast - -
According to the cookie authentication handler's source codes, I found it will read the cookie before goes to the CustomCookieAuthenticationEvents .
Some part of codes as below:
private async Task<AuthenticateResult> ReadCookieTicket()
{
var cookie = Options.CookieManager.GetRequestCookie(Context, Options.Cookie.Name!);
if (string.IsNullOrEmpty(cookie))
{
return AuthenticateResult.NoResult();
}
var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding());
if (ticket == null)
{
return AuthenticateResult.Fail("Unprotect ticket failed");
}
if (Options.SessionStore != null)
{
var claim = ticket.Principal.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim));
if (claim == null)
{
return AuthenticateResult.Fail("SessionId missing");
}
// Only store _sessionKey if it matches an existing session. Otherwise we'll create a new one.
ticket = await Options.SessionStore.RetrieveAsync(claim.Value);
if (ticket == null)
{
return AuthenticateResult.Fail("Identity missing in session store");
}
_sessionKey = claim.Value;
}
var currentUtc = Clock.UtcNow;
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc != null && expiresUtc.Value < currentUtc)
{
if (Options.SessionStore != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey!);
}
return AuthenticateResult.Fail("Ticket expired");
}
CheckForRefresh(ticket);
// Finally we have a valid ticket
return AuthenticateResult.Success(ticket);
}
If you still want to use cookie authentication, you need to rewrite the handler. So I suggest you could write a custom AuthenticationHandler and AuthenticationSchemeOptions class like below and register the class in the startup.cs directly. Then you could use [Authorize(AuthenticationSchemes = "Test")] to set the special AuthenticationSchemes.
Codes:
public class ValidateHashAuthenticationSchemeOptions : AuthenticationSchemeOptions
{
}
public class ValidateHashAuthenticationHandler
: AuthenticationHandler<ValidateHashAuthenticationSchemeOptions>
{
public ValidateHashAuthenticationHandler(
IOptionsMonitor<ValidateHashAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
//TokenModel model;
// validation comes in here
if (!Request.Headers.ContainsKey("X-Base-Token"))
{
return Task.FromResult(AuthenticateResult.Fail("Header Not Found."));
}
var token = Request.Headers["X-Base-Token"].ToString();
try
{
// convert the input string into byte stream
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(token)))
{
// deserialize stream into token model object
//model = Serializer.Deserialize<TokenModel>(stream);
}
}
catch (System.Exception ex)
{
Console.WriteLine("Exception Occured while Deserializing: " + ex);
return Task.FromResult(AuthenticateResult.Fail("TokenParseException"));
}
//if (model != null)
//{
// // success case AuthenticationTicket generation
// // happens from here
// // create claims array from the model
// var claims = new[] {
// new Claim(ClaimTypes.NameIdentifier, model.UserId.ToString()),
// new Claim(ClaimTypes.Email, model.EmailAddress),
// new Claim(ClaimTypes.Name, model.Name) };
// // generate claimsIdentity on the name of the class
// var claimsIdentity = new ClaimsIdentity(claims,
// nameof(ValidateHashAuthenticationHandler));
// // generate AuthenticationTicket from the Identity
// // and current authentication scheme
// var ticket = new AuthenticationTicket(
// new ClaimsPrincipal(claimsIdentity), this.Scheme.Name);
// // pass on the ticket to the middleware
// return Task.FromResult(AuthenticateResult.Success(ticket));
//}
return Task.FromResult(AuthenticateResult.Fail("Model is Empty"));
}
}
public class TokenModel
{
public int UserId { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
}
Startup.cs add below codes into the ConfigureServices method:
services.AddAuthentication(options =>
{
options.DefaultScheme
= "Test";
})
.AddScheme<ValidateHashAuthenticationSchemeOptions, ValidateHashAuthenticationHandler>
("Test", null);
Usage:
Controller:
[Authorize(AuthenticationSchemes = "Test")]
So I now solved it for myself differently by creating a custom Authentication Handler, which takes care of checking the Cookie that gets send to the Web API.
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSingleton(new KratosService("http://localhost:4433"));
services
.AddAuthentication("Kratos")
.AddScheme<KratosAuthenticationOptions, KratosAuthenticationHandler>("Kratos", null);
// ...
}
If you are interested in the implementation that did the job for me, I have attached the additional files below. It is also worth mentioning, that Kratos supports two way of Authenticating: Cookies and Bearer Tokens, depending on if you talk to it via Web App or an API. My implementation supports both. You can find a working Sample with ASP.NET Core and React here: https://github.com/robinmanuelthiel/kratos-demo
KratosAuthenticationHandler.cs:
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace KratosDemo.Server.Kratos
{
public class KratosAuthenticationOptions : AuthenticationSchemeOptions
{
}
public class KratosAuthenticationHandler : AuthenticationHandler<KratosAuthenticationOptions>
{
readonly KratosService _kratosService;
readonly string _sessionCookieName = "ory_kratos_session";
public KratosAuthenticationHandler(
IOptionsMonitor<KratosAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
KratosService kratosService
)
: base(options, logger, encoder, clock)
{
_kratosService = kratosService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// ORY Kratos can authenticate against an API through two different methods:
// Cookie Authentication is for Browser Clients and sends a Session Cookie with each request.
// Bearer Token Authentication is for Native Apps and other APIs and sends an Authentication header with each request.
// We are validating both ways here by sending a /whoami request to ORY Kratos passing the provided authentication
// methods on to Kratos.
try
{
// Check, if Cookie was set
if (Request.Cookies.ContainsKey(_sessionCookieName))
{
var cookie = Request.Cookies[_sessionCookieName];
var id = await _kratosService.GetUserIdByCookie(_sessionCookieName, cookie);
return ValidateToken(id);
}
// Check, if Authorization header was set
if (Request.Headers.ContainsKey("Authorization"))
{
var token = Request.Headers["Authorization"];
var id = await _kratosService.GetUserIdByToken(token);
return ValidateToken(id);
}
// If neither Cookie nor Authorization header was set, the request can't be authenticated.
return AuthenticateResult.NoResult();
}
catch (Exception ex)
{
// If an error occurs while trying to validate the token, the Authentication request fails.
return AuthenticateResult.Fail(ex.Message);
}
}
private AuthenticateResult ValidateToken(string userId)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userId),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new System.Security.Principal.GenericPrincipal(identity, null);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
}
KratosService.cs:
using System.Threading.Tasks;
using System.Net.Http;
using System.Text.Json;
using System;
namespace KratosDemo.Server.Kratos
{
public class KratosService
{
private readonly string _kratosUrl;
private readonly HttpClient _client;
public KratosService(string kratosUrl)
{
_client = new HttpClient();
_kratosUrl = kratosUrl;
}
public async Task<string> GetUserIdByToken(string token)
{
var request = new HttpRequestMessage(HttpMethod.Get, $"{_kratosUrl}/sessions/whoami");
request.Headers.Add("Authorization", token);
return await SendWhoamiRequestAsync(request);
}
public async Task<string> GetUserIdByCookie(string cookieName, string cookieContent)
{
var request = new HttpRequestMessage(HttpMethod.Get, $"{_kratosUrl}/sessions/whoami");
request.Headers.Add("Cookie", $"{cookieName}={cookieContent}");
return await SendWhoamiRequestAsync(request);
}
private async Task<string> SendWhoamiRequestAsync(HttpRequestMessage request)
{
var res = await _client.SendAsync(request);
res.EnsureSuccessStatusCode();
var json = await res.Content.ReadAsStringAsync();
var whoami = JsonSerializer.Deserialize<Whoami>(json);
if (!whoami.Active)
throw new InvalidOperationException("Session is not active.");
return whoami.Identity.Id;
}
}
}
Whoami.cs:
using System;
using System.Text.Json.Serialization;
namespace KratosDemo.Server.Kratos
{
public class Whoami
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("active")]
public bool Active { get; set; }
[JsonPropertyName("expires_at")]
public DateTime ExpiresAt { get; set; }
[JsonPropertyName("authenticated_at")]
public DateTime AuthenticatedAt { get; set; }
[JsonPropertyName("issued_at")]
public DateTime IssuedAt { get; set; }
[JsonPropertyName("identity")]
public Identity Identity { get; set; }
}
public class Identity
{
[JsonPropertyName("id")]
public string Id { get; set; }
}
}
I'm creating a web-api where I need to log people in using Facebook.
I'm following this guide.
Once I provide my credentials to Facebook, it should redirect to an Action but instead it says: "Too many redirects."
This is what I've got in my Startup.cs:
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
FacebookAuthenticationOptions facebookAuthOptions = new FacebookAuthenticationOptions()
{
AppId = "myAppId",
AppSecret = "myAppKey",
Provider = new FacebookAuthProvider()
};
app.UseFacebookAuthentication(facebookAuthOptions);
This is my FacebookAuthProvider: class:
public class FacebookAuthProvider : FacebookAuthenticationProvider
{
public override Task Authenticated(FacebookAuthenticatedContext context)
{
context.Identity.AddClaim(new System.Security.Claims.Claim("ExternalAccessToken", context.AccessToken));
return Task.FromResult<object>(null);
}
}
This is my ChallengeResult class:
public class ChallengeResult : IHttpActionResult
{
public string LoginProvider { get; set; }
public HttpRequestMessage Request { get; set; }
public ChallengeResult(string loginProvider, ApiController controller)
{
LoginProvider = loginProvider;
Request = controller.Request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
response.RequestMessage = Request;
return Task.FromResult<HttpResponseMessage>(response);
}
}
And this is the controller that I'm using to get the token from Facebook after user has logged in:
[HttpGet]
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
//[Route("ExternalLogin", Name = "ExternalLogin")]
public IHttpActionResult GetExternalLogin(string provider)
{
string redirectUri = string.Empty;
AppUserManager manager = new AppUserManager(new AppUserStore(new AppContext()));
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
UserLoginInfo loginInfo = new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey);
IdentityUser user = manager.Find(loginInfo);
bool hasRegistered = user != null;
ValidateRedirectUri(this.Request, ref redirectUri);
redirectUri = String.Format("{0}#external_access_token={1}&provider={2}&haslocalaccount={3}&external_user_name={4}",
redirectUri,
externalLogin.AccessToken,
externalLogin.LoginProvider,
hasRegistered.ToString(),
externalLogin.UserName);
return Redirect(redirectUri);
}
One thing that I'm really curious about, is that, if I uncomment this line:
[Route("ExternalLogin", Name = "ExternalLogin")]
And try to access that controller with that new route, it says that User (The one in the GetExternalLogin's if) is null.
This is the link that I'm using to test:
http://localhost:62887/api/ExternalAuth/GetExternalLogin?provider=Facebook&redirect_uri=http://localhost:62887/api/ExternalAuth/LoggedIn
And after the user has successfully logged in, this is the action that he's supposed to be redirected:
[HttpGet]
public IHttpActionResult LoggedIn()
{
return Ok(new { Message = "You've been successfully logged in! :)" });
}
I'm gonna kill myself, I finally got it working. I just had to update the NuGet Package from 2.1 to 3.1... >:/
I thought I had a pretty simple goal in mind when I set out a day ago to implement a self-contained bearer auth webapi on .NET core 2.0, but I have yet to get anything remotely working. Here's a list of what I'm trying to do:
Implement a bearer token protected webapi
Issue tokens & refresh tokens from an endpoint in the same project
Use the [Authorize] attribute to control access to api surface
Not use ASP.Net Identity (I have much lighter weight user/membership reqs)
I'm totally fine with building identity/claims/principal in login and adding that to request context, but I've not seen a single example on how to issue and consume auth/refresh tokens in a Core 2.0 webapi without Identity. I've seen the 1.x MSDN example of cookies without Identity, but that didn't get me far enough in understanding to meet the requirements above.
I feel like this might be a common scenario and it shouldn't be this hard (maybe it's not, maybe just lack of documentation/examples?). As far as I can tell, IdentityServer4 is not compatible with Core 2.0 Auth, opendiddict seems to require Identity. I also don't want to host the token endpoint in a separate process, but within the same webapi instance.
Can anyone point me to a concrete example, or at least give some guidance as to what best steps/options are?
Did an edit to make it compatible with ASP.NET Core 2.0.
Firstly, some Nuget packages:
Microsoft.AspNetCore.Authentication.JwtBearer
Microsoft.AspNetCore.Identity
System.IdentityModel.Tokens.Jwt
System.Security.Cryptography.Csp
Then some basic data transfer objects.
// Presumably you will have an equivalent user account class with a user name.
public class User
{
public string UserName { get; set; }
}
public class JsonWebToken
{
public string access_token { get; set; }
public string token_type { get; set; } = "bearer";
public int expires_in { get; set; }
public string refresh_token { get; set; }
}
Getting into the proper functionality, you'll need a login/token web method to actually send the authorization token to the user.
[Route("api/token")]
public class TokenController : Controller
{
private ITokenProvider _tokenProvider;
public TokenController(ITokenProvider tokenProvider) // We'll create this later, don't worry.
{
_tokenProvider = tokenProvider;
}
public JsonWebToken Get([FromQuery] string grant_type, [FromQuery] string username, [FromQuery] string password, [FromQuery] string refresh_token)
{
// Authenticate depending on the grant type.
User user = grant_type == "refresh_token" ? GetUserByToken(refresh_token) : GetUserByCredentials(username, password);
if (user == null)
throw new UnauthorizedAccessException("No!");
int ageInMinutes = 20; // However long you want...
DateTime expiry = DateTime.UtcNow.AddMinutes(ageInMinutes);
var token = new JsonWebToken {
access_token = _tokenProvider.CreateToken(user, expiry),
expires_in = ageInMinutes * 60
};
if (grant_type != "refresh_token")
token.refresh_token = GenerateRefreshToken(user);
return token;
}
private User GetUserByToken(string refreshToken)
{
// TODO: Check token against your database.
if (refreshToken == "test")
return new User { UserName = "test" };
return null;
}
private User GetUserByCredentials(string username, string password)
{
// TODO: Check username/password against your database.
if (username == password)
return new User { UserName = username };
return null;
}
private string GenerateRefreshToken(User user)
{
// TODO: Create and persist a refresh token.
return "test";
}
}
You probably noticed the token creation is still just "magic" passed through by some imaginary ITokenProvider. Define the token provider interface.
public interface ITokenProvider
{
string CreateToken(User user, DateTime expiry);
// TokenValidationParameters is from Microsoft.IdentityModel.Tokens
TokenValidationParameters GetValidationParameters();
}
I implemented the token creation with an RSA security key on a JWT. So...
public class RsaJwtTokenProvider : ITokenProvider
{
private RsaSecurityKey _key;
private string _algorithm;
private string _issuer;
private string _audience;
public RsaJwtTokenProvider(string issuer, string audience, string keyName)
{
var parameters = new CspParameters { KeyContainerName = keyName };
var provider = new RSACryptoServiceProvider(2048, parameters);
_key = new RsaSecurityKey(provider);
_algorithm = SecurityAlgorithms.RsaSha256Signature;
_issuer = issuer;
_audience = audience;
}
public string CreateToken(User user, DateTime expiry)
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "jwt"));
// TODO: Add whatever claims the user may have...
SecurityToken token = tokenHandler.CreateJwtSecurityToken(new SecurityTokenDescriptor
{
Audience = _audience,
Issuer = _issuer,
SigningCredentials = new SigningCredentials(_key, _algorithm),
Expires = expiry.ToUniversalTime(),
Subject = identity
});
return tokenHandler.WriteToken(token);
}
public TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters
{
IssuerSigningKey = _key,
ValidAudience = _audience,
ValidIssuer = _issuer,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(0) // Identity and resource servers are the same.
};
}
}
So you're now generating tokens. Time to actually validate them and wire it up. Go to your Startup.cs.
In ConfigureServices()
var tokenProvider = new RsaJwtTokenProvider("issuer", "audience", "mykeyname");
services.AddSingleton<ITokenProvider>(tokenProvider);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = tokenProvider.GetValidationParameters();
});
// This is for the [Authorize] attributes.
services.AddAuthorization(auth => {
auth.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
Then Configure()
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
// Whatever else you're putting in here...
app.UseMvc();
}
That should be about all you need. Hopefully I haven't missed anything.
The happy result is...
[Authorize] // Yay!
[Route("api/values")]
public class ValuesController : Controller
{
// ...
}
Following on #Mitch answer: Auth stack changed quite a bit moving to .NET Core 2.0. Answer below is just using the new implementation.
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
namespace JwtWithoutIdentity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = "me",
ValidAudience = "you",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")) //Secret
};
});
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
}
Token Controller
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using JwtWithoutIdentity.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
namespace JwtWithoutIdentity.Controllers
{
public class TokenController : Controller
{
[AllowAnonymous]
[Route("api/token")]
[HttpPost]
public async Task<IActionResult> Token(LoginViewModel model)
{
if (!ModelState.IsValid) return BadRequest("Token failed to generate");
var user = (model.Password == "password" && model.Username == "username");
if (!user) return Unauthorized();
//Add Claims
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.UniqueName, "data"),
new Claim(JwtRegisteredClaimNames.Sub, "data"),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rlyaKithdrYVl6Z80ODU350md")); //Secret
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken("me",
"you",
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new JsonWebToken()
{
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_in = 600000,
token_type = "bearer"
});
}
}
}
Values Controller
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace JwtWithoutIdentity.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[Authorize]
[HttpGet]
public IEnumerable<string> Get()
{
var name = User.Identity.Name;
var claims = User.Claims;
return new string[] { "value1", "value2" };
}
}
}
Hope this helps!
I've done this before with MVC5 using User.Identity.GetUserId() but that doesn't seem to work here.
The User.Identity doesn't have the GetUserId() method.
I am using Microsoft.AspNet.Identity.
Update in ASP.NET Core Version >= 2.0
In the Controller:
public class YourControllerNameController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public YourControllerNameController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> YourMethodName()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
var userName = User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
// For ASP.NET Core <= 3.1
ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
string userEmail = applicationUser?.Email; // will give the user's Email
// For ASP.NET Core >= 5.0
var userEmail = User.FindFirstValue(ClaimTypes.Email) // will give the user's Email
}
}
In some other class:
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
}
Then you should register IHttpContextAccessor in the Startup class as follows:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Or you can also register as follows
services.AddHttpContextAccessor();
}
For more readability write extension methods as follows:
public static class ClaimsPrincipalExtensions
{
public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(loggedInUserId, typeof(T));
}
else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
{
return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
}
else
{
throw new Exception("Invalid type provided");
}
}
public static string GetLoggedInUserName(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Name);
}
public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Email);
}
}
Then use as follows:
public class YourControllerNameController : Controller
{
public IActionResult YourMethodName()
{
var userId = User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
var userName = User.GetLoggedInUserName();
var userEmail = User.GetLoggedInUserEmail();
}
}
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
}
}
Until ASP.NET Core 1.0 RC1 :
It's User.GetUserId() from System.Security.Claims namespace.
Since ASP.NET Core 1.0 RC2 :
You now have to use UserManager.
You can create a method to get the current user :
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
And get user information with the object :
var user = await GetCurrentUserAsync();
var userId = user?.Id;
string mail = user?.Email;
Note :
You can do it without using a method writing single lines like this string mail = (await _userManager.GetUserAsync(HttpContext.User))?.Email, but it doesn't respect the single responsibility principle. It's better to isolate the way you get the user because if someday you decide to change your user management system, like use another solution than Identity, it will get painful since you have to review your entire code.
you can get it in your controller:
using System.Security.Claims;
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or write an extension method like before .Core v1.0
using System;
using System.Security.Claims;
namespace Shared.Web.MvcExtensions
{
public static class ClaimsPrincipalExtensions
{
public static string GetUserId(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
}
}
and get wherever user ClaimsPrincipal is available :
using Microsoft.AspNetCore.Mvc;
using Shared.Web.MvcExtensions;
namespace Web.Site.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return Content(this.User.GetUserId());
}
}
}
I included using System.Security.Claims and I could access the GetUserId() extension method
NB: I had the using Microsoft.AspNet.Identity already but couldn't get the extension method. So I guess both of them have to be used in conjunction with one another
using Microsoft.AspNet.Identity;
using System.Security.Claims;
EDIT:
This answer is now outdated. Look at Soren's or Adrien's answer for a dated way of achieving this in CORE 1.0
For .NET Core 2.0 Only The following is required to fetch the UserID of the logged-in User in a Controller class:
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or
var userId = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
e.g.
contact.OwnerID = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
As stated somewhere in this post, the GetUserId() method has been moved to the UserManager.
private readonly UserManager<ApplicationUser> _userManager;
public YourController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public IActionResult MyAction()
{
var userId = _userManager.GetUserId(HttpContext.User);
var model = GetSomeModelByUserId(userId);
return View(model);
}
If you started an empty project you might need to add the UserManger to your services in startup.cs. Otherwise this should already be the case.
you have to import Microsoft.AspNetCore.Identity & System.Security.Claims
// to get current user ID
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
// to get current user info
var user = await _userManager.FindByIdAsync(userId);
For ASP.NET Core 2.0, Entity Framework Core 2.0, AspNetCore.Identity 2.0 api (https://github.com/kkagill/ContosoUniversity-Backend):
The Id was changed to User.Identity.Name
[Authorize, HttpGet("Profile")]
public async Task<IActionResult> GetProfile()
{
var user = await _userManager.FindByIdAsync(User.Identity.Name);
return Json(new
{
IsAuthenticated = User.Identity.IsAuthenticated,
Id = User.Identity.Name,
Name = $"{user.FirstName} {user.LastName}",
Type = User.Identity.AuthenticationType,
});
}
Response:
In .net core 3.1 (and other more recent versions), you can use:
private readonly UserManager<IdentityUser> _userManager;
public ExampleController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
Then:
string userId = _userManager.GetUserId(User);
Or async:
var user = await _userManager.GetUserAsync(User);
var userId = user.Id;
At this point, I'm trying to figure out why you'd use one over the other. I know the general benefits of async, but see both of these used frequently. Please post some comments if anyone knows.
For ASP.NET 5.0, I have an extension method as follow:
using System;
using System.ComponentModel;
using System.Security.Claims;
namespace YOUR_PROJECT.Presentation.WebUI.Extensions
{
public static class ClaimsPrincipalExtensions
{
public static TId GetId<TId>(this ClaimsPrincipal principal)
{
if (principal == null || principal.Identity == null ||
!principal.Identity.IsAuthenticated)
{
throw new ArgumentNullException(nameof(principal));
}
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(TId) == typeof(string) ||
typeof(TId) == typeof(int) ||
typeof(TId) == typeof(long) ||
typeof(TId) == typeof(Guid))
{
var converter = TypeDescriptor.GetConverter(typeof(TId));
return (TId)converter.ConvertFromInvariantString(loggedInUserId);
}
throw new InvalidOperationException("The user id type is invalid.");
}
public static Guid GetId(this ClaimsPrincipal principal)
{
return principal.GetId<Guid>();
}
}
}
So you can use it like:
using Microsoft.AspNetCore.Mvc;
using YOUR_PROJECT.Presentation.WebUI.Extensions;
namespace YOUR_PROJECT.Presentation.WebUI.Controllers
{
public class YourController :Controller
{
public IActionResult YourMethod()
{
// If it's Guid
var userId = User.GetId();
// Or
// var userId = User.GetId<int>();
return View();
}
}
}
in the APiController
User.FindFirst(ClaimTypes.NameIdentifier).Value
Something like this you will get the claims
Although Adrien's answer is correct, you can do this all in single line. No need for extra function or mess.
It works I checked it in ASP.NET Core 1.0
var user = await _userManager.GetUserAsync(HttpContext.User);
then you can get other properties of the variable like user.Email. I hope this helps someone.
For getting current user id in razor views, we can inject UserManager in the view like this:
#inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> _userManager
#{ string userId = _userManager.GetUserId(User); }
I hope you find it useful.
User.Identity.GetUserId();
does not exist in asp.net identity core 2.0. in this regard, i have managed in different way. i have created a common class for use whole application, because of getting user information.
create a common class PCommon & interface IPCommon
adding reference using System.Security.Claims
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Common.Web.Helper
{
public class PCommon: IPCommon
{
private readonly IHttpContextAccessor _context;
public PayraCommon(IHttpContextAccessor context)
{
_context = context;
}
public int GetUserId()
{
return Convert.ToInt16(_context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
}
public string GetUserName()
{
return _context.HttpContext.User.Identity.Name;
}
}
public interface IPCommon
{
int GetUserId();
string GetUserName();
}
}
Here the implementation of common class
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Pay.DataManager.Concreate;
using Pay.DataManager.Helper;
using Pay.DataManager.Models;
using Pay.Web.Helper;
using Pay.Web.Models.GeneralViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Pay.Controllers
{
[Authorize]
public class BankController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger _logger;
private readonly IPCommon _iPCommon;
public BankController(IUnitOfWork unitOfWork, IPCommon IPCommon, ILogger logger = null)
{
_unitOfWork = unitOfWork;
_iPCommon = IPCommon;
if (logger != null) { _logger = logger; }
}
public ActionResult Create()
{
BankViewModel _bank = new BankViewModel();
CountryLoad(_bank);
return View();
}
[HttpPost, ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Insert(BankViewModel bankVM)
{
if (!ModelState.IsValid)
{
CountryLoad(bankVM);
//TempData["show-message"] = Notification.Show(CommonMessage.RequiredFieldError("bank"), "Warning", type: ToastType.Warning);
return View(bankVM);
}
try
{
bankVM.EntryBy = _iPCommon.GetUserId();
var userName = _iPCommon.GetUserName()();
//_unitOfWork.BankRepo.Add(ModelAdapter.ModelMap(new Bank(), bankVM));
//_unitOfWork.Save();
// TempData["show-message"] = Notification.Show(CommonMessage.SaveMessage(), "Success", type: ToastType.Success);
}
catch (Exception ex)
{
// TempData["show-message"] = Notification.Show(CommonMessage.SaveErrorMessage("bank"), "Error", type: ToastType.Error);
}
return RedirectToAction(nameof(Index));
}
}
}
get userId and name in insert action
_iPCommon.GetUserId();
Thanks,
Maksud
TLDR:
In the Controler add:
using System.Security.Claims;
and then you can use:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
endof TLDR;
Just an easy way in dot net 6 to test how to get the userID and test it in the default Blazor WebAssembly Core Hosted:
I added a String in WeatherForecast class named userId
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string userId { get; set; } = "nope";
}
Then in the WeatherForecastController
I add
using System.Security.Claims;
In the GET method I set WeatherForecast.userId to User.FindFirstValue(ClaimTypes.NameIdentifier):
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)],
userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
})
.ToArray();
}
And finally in the FetchData.razor I modify the table to:
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
<th>User Id</th>
</tr>
</thead>
<tbody>
#foreach (var forecast in forecasts)
{
<tr>
<td>#forecast.Date.ToShortDateString()</td>
<td>#forecast.TemperatureC</td>
<td>#forecast.TemperatureF</td>
<td>#forecast.Summary</td>
<td>#forecast.userId</td>
</tr>
}
</tbody>
</table>
And then finally I get:
I hope it helps because in net core 6 sometimes it's quite difficult to find the answers
If you are using JWT tokens this code works:
User.FindFirstValue("sub");
use can use
string userid = User.FindFirst("id").Value;
for some reason NameIdentifier now retrieve the username (.net core 2.2)
Make sure that you have enable windows authentication. If you have anonymous authentication enabled you may be getting a null string.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio
I know there are many answers posted already, but maybe it will help someone as it did for me.
I mixed two solutions into one, and I am able to get the logged-in User and its Data.
I was using DotNet 5.
Following code, help to get the logged-in User.
var user = await _userManager.FindByNameAsync(HttpContext.User.Identity.Name);
I used the following package for _userManager
using Microsoft.AspNetCore.Identity;
And for HttpContext, I inherit my Controller from ControllerBase, and for ControllerBase Class I was using the following package
using Microsoft.AspNetCore.Mvc;
As an administrator working on other people's profile and you need to get the Id of the profile you are working on, you can use a ViewBag to capture the Id e.g ViewBag.UserId = userId; while userId is the string Parameter of the method you are working on.
[HttpGet]
public async Task<IActionResult> ManageUserRoles(string userId)
{
ViewBag.UserId = userId;
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {userId} cannot be found";
return View("NotFound");
}
var model = new List<UserRolesViewModel>();
foreach (var role in roleManager.Roles)
{
var userRolesViewModel = new UserRolesViewModel
{
RoleId = role.Id,
RoleName = role.Name
};
if (await userManager.IsInRoleAsync(user, role.Name))
{
userRolesViewModel.IsSelected = true;
}
else
{
userRolesViewModel.IsSelected = false;
}
model.Add(userRolesViewModel);
}
return View(model);
}
If you want this in ASP.NET MVC Controller, use
using Microsoft.AspNet.Identity;
User.Identity.GetUserId();
You need to add using statement because GetUserId() won't be there without it.