Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing dotnetcore middleware AFTER a JWT Token is validated

I am using JWT bearer authentication, configured as follows.

My problem is that the middleware is executing before the token is validated.
How do I configure the middleware to run afterwards?

services.AddAuthentication()
    .AddCookie(_ => _.SlidingExpiration = true)
    .AddJwtBearer(
        _ =>
        {
            _.Events = new JwtBearerEvents
            {
                // THIS CODE EXECUTES AFTER THE MIDDLEWARE????
                OnTokenValidated = context =>
                {
                    context.Principal = new ClaimsPrincipal(
                        new ClaimsIdentity(context.Principal.Claims, "local"));
                    return Task.CompletedTask;
                }
            };
            _.RequireHttpsMetadata = false;
            _.SaveToken = false;
            _.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidIssuer = this.Configuration["Tokens:Issuer"],
                ValidAudience = this.Configuration["Tokens:Issuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.Configuration["Tokens:Key"])),
            };
        });

I am attempting to add middleware into the pipeline that accesses the current user. This code unfortunately executes BEFORE the token is validated. How do I make it execute afterwards?

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();
    app.UseIdentityServer();
    app.UseAuthentication();

    app.Use(async (httpContext, next) =>
       {
           // THIS CODE EXECUTES BEFORE THE TOKEN IS VALIDATED IN OnTokenValidated.
           var userName = httpContext.User.Identity.IsAuthenticated 
             ? httpContext.User.GetClaim("email")
             : "(unknown)";
           LogContext.PushProperty("ActiveUser", !string.IsNullOrWhiteSpace(userName) ? userName : "(unknown)");
           await next.Invoke();
       });
like image 501
Jim Avatar asked Sep 19 '17 09:09

Jim


People also ask

How do you check if a JWT token is valid or not in net core?

Validate JWT Token using Custom Middleware and Custom Authorize Attribute. Below is the custom JWT middleware that validates the token in the request "Authorization" header if it exists. On successful validation, the middleware retrieves that associated user from the database and assigns it to its context.

What is JWT middleware?

JWT provides a JSON Web Token (JWT) authentication middleware. For valid token, it sets the user in context and calls next handler. For invalid token, it sends “401 - Unauthorized” response. For missing or invalid Authorization header, it sends “400 - Bad Request”.

What is ValidateIssuer in JWT?

ValidateIssuer, validates that the iss claim inside the access token matches the issuer(authority) that the API trusts (Ie, your token service). Verifies that the issuer of the token is what this API expects.


Video Answer


2 Answers

It looks like you've found a good solution to your problem but I thought I'd add an answer to explain the behavior you're seeing.

Since you have multiple authentication schemes registered and none is the default, authentication does not happen automatically as the request goes through the pipeline. That's why the HttpContext.User was empty/unauthenticated when it went through your custom middleware. In this "passive" mode, the authentication scheme won't be invoked until it is requested. In your example, this happens when the request passes through your AuthorizeFilter. This triggers the JWT authentication handler, which validates the token, authenticates and sets the Identity, etc. That's why (as in your other question) the User is populated correctly by the time it gets to your controller action.

It probably doesn't make sense for your scenario (since you're using both cookies and jwt)... however, if you did want the Jwt authentication to happen automatically, setting HttpContext.User for other middleware in the pipeline, you just need to register it as the default scheme when configuring authentication:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
like image 148
Peter Avatar answered Oct 18 '22 06:10

Peter


based on @leppie's comment, here is a solution that works.

public class ActiveUserFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        var userName = context.HttpContext.User.Identity.IsAuthenticated
        ? context.HttpContext.User.GetClaim("email")
        : "(unknown)";
        using (LogContext.PushProperty("ActiveUser", !string.IsNullOrWhiteSpace(userName) ? userName : "(unknown)"))
            await next();
    }
}

Inserted as follows...

services.AddMvc(
    _ =>
    {
        _.Filters.Add(
           new AuthorizeFilter(
               new AuthorizationPolicyBuilder(
                 JwtBearerDefaults.AuthenticationScheme,
                 IdentityConstants.ApplicationScheme)
               .RequireAuthenticatedUser()
                 .Build()));
        _.Filters.Add(new ActiveUserFilter());

        ...
like image 20
Jim Avatar answered Oct 18 '22 06:10

Jim