Ahmer Ali Ahsan
Ahmer Ali Ahsan

Reputation: 6136

What is the way in ASP.NET Core 3.0 to get current authenticated User username?

I'm working on the latest ASP.NET Core 3.0 Angular template. What I need to know is how we can get authenticated user, username in ActionFilterAttribute.

The ClaimTypes.NameIdentifier returns the current user id, and ClaimTypes.Name returns the username which is null.

Here is my code:

enter image description here

where getUser has a UserId of d15f997a-6f65-4eb2-aecb-8b525361ae50

I've also register IHttpContextAccessor in the Startup class as follows:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddDefaultIdentity<ApplicationUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

        services.AddAuthentication()
            .AddIdentityServerJwt();

        //Database Setup
        services.AddDbContext<HashooDBContext>(Configuration.GetConnectionString("DefaultConnection"));

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        services.AddControllersWithViews();
        services.AddRazorPages();
        // In production, the Angular files will be served from this directory
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/dist";
        });

        return ContainerSetup.InitializeWeb(Assembly.GetExecutingAssembly(), services);
}

Controller.cs:

[Authorize]
[ApiController]
[Route("api/[controller]/{companycode}")]
public class DashboardTransactionsController : ControllerBase
{
    [CustomAuthorizationFilter("companycode")]
    public string Get()
    {
        return "Welcome";
    }
}

Please let me know what am I doing wrong?

Upvotes: 7

Views: 13688

Answers (3)

Edward
Edward

Reputation: 30046

For this issue, it is caused by that the IdentityServer4 did not add the JwtClaimTypes.Name to the access_token. If you check the access_token from web brower, you will find it miss "name": "Tom", node.

For a workaround, you could implement your own ITokenService like

public class CustomTokenService : DefaultTokenService
{
    public CustomTokenService(IClaimsService claimsProvider
        , IReferenceTokenStore referenceTokenStore
        , ITokenCreationService creationService
        , IHttpContextAccessor contextAccessor
        , ISystemClock clock
        , IKeyMaterialService keyMaterialService
        , ILogger<DefaultTokenService> logger) 
        : base(claimsProvider, referenceTokenStore, creationService, contextAccessor, clock, keyMaterialService, logger)
    {
    }

    public override async Task<Token> CreateAccessTokenAsync(TokenCreationRequest request)
    {

        Logger.LogTrace("Creating access token");
        request.Validate();

        var claims = new List<Claim>();
        claims.AddRange(await ClaimsProvider.GetAccessTokenClaimsAsync(
            request.Subject,
            request.Resources,
            request.ValidatedRequest));

        if (request.ValidatedRequest.Client.IncludeJwtId)
        {
            claims.Add(new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId(16)));
        }
        claims.Add(new Claim(JwtClaimTypes.Name, request.Subject.GetDisplayName()));
        var issuer = Context.HttpContext.GetIdentityServerIssuerUri();
        var token = new Token(OidcConstants.TokenTypes.AccessToken)
        {
            CreationTime = Clock.UtcNow.UtcDateTime,
            Issuer = issuer,
            Lifetime = request.ValidatedRequest.AccessTokenLifetime,
            Claims = claims.Distinct(new ClaimComparer()).ToList(),
            ClientId = request.ValidatedRequest.Client.ClientId,
            AccessTokenType = request.ValidatedRequest.AccessTokenType
        };

        foreach (var api in request.Resources.ApiResources)
        {
            if (!string.IsNullOrWhiteSpace(api.Name))
            {
                token.Audiences.Add(api.Name);
            }
        }

        return token;
    }
}

And then register CustomTokenService before Identity Configuration

    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddTransient<ITokenService, CustomTokenService>();

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddDefaultIdentity<ApplicationUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

        services.AddAuthentication()
            .AddIdentityServerJwt();

    }

For a simple way, you could try to override DefaultClaimsService.GetStandardSubjectClaims like

public class CustomClaimsService : DefaultClaimsService
{
    public CustomClaimsService(IProfileService profile
        , ILogger<DefaultClaimsService> logger) : base(profile, logger)
    {
    }
    protected override IEnumerable<Claim> GetStandardSubjectClaims(ClaimsPrincipal subject)
    {
        var claims = base.GetStandardSubjectClaims(subject);
        var newClaims = new List<Claim>(claims)
        {
            new Claim(JwtClaimTypes.Name, subject.Identity.Name)
        };
        return newClaims;
    }
}

And register like

    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddTransient<IClaimsService, CustomClaimsService>();

        //services.TryAddTransient<ITokenService, CustomTokenService>();

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddDefaultIdentity<ApplicationUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

        services.AddAuthentication()
            .AddIdentityServerJwt();

    }

Upvotes: 3

Nabeel Ilyas
Nabeel Ilyas

Reputation: 29

Try this code:

string userId = context.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;

Upvotes: 0

Tony
Tony

Reputation: 20132

You can use IHttpContextAccessor

private readonly IHttpContextAccessor _httpContextAccessor;

public UserService(
    IHttpContextAccessor httpContextAccessor)
{
  _httpContextAccessor = httpContextAccessor;       
}

And to get user name you can use

_httpContextAccessor?.HttpContext?.User?.Identity?.Name;

Or

 _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);

Then register in your Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

Upvotes: 4

Related Questions