Add in Authorization Header to HttpContext in MVC .Net Core Mvc 21

Viewed 3808

I have a Jwt Bearer token that I am storing in Session in my .Net Core 2.1 app. In my middleware I am intercepting the request and attaching the token to the headers by:

httpContext.Request.Headers.Add("Authorization", $"Bearer {token}");

This is currently not working as I am getting an unauthorized on the protected route. If I make the same call using postman and passing in Authorization and Bearer token in the headers, it works.

What do I need to change to the http.Request to make it acceptable?

Portion of startup with Jwt: var signingKey = key;

        services.AddSingleton<IJwtFactory, JwtFactory>();

        var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));

        // Configure JwtIssuerOptions
        services.Configure<JwtIssuerOptions>(options =>
        {
            options.Issuer = Issuer;
            options.Audience = Audience;
            options.SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
        });

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = Issuer,

                    ValidateAudience = true,
                    ValidAudience = Audience,

                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingKey,

                    RequireExpirationTime = false,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                };
            })
            .AddCookie(cfg => cfg.SlidingExpiration = true);

Configure:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
    {
        app.UseAuthentication();
        app.UseStaticFiles();
        app.UseSession();
        app.UseHttpContextLogging();
        app.ConfigureErrorHandling(env);
        app.UseSiteRouteMiddleware(getRoutes); -- Middleware that intercepts the request, adding the token here
        app.UseRequestLocalization();
        app.UseRewriter();
        app.UseMvc(getRoutes);
    }
2 Answers

As posted in the comments, by the time your UseSiteRouteMiddleware is called, Authentication middleware has already been processed.

So its to late to set anything. Change the order in which the middlewares are executed. Put your UseSiteRouteMiddleware closer to the beginning, before UseAuthentcation().

As per official documentation the registration order determines in which order the middlewares are executed.

The order that middleware components are added in the Startup.Configure method defines the order in which the middleware components are invoked on requests and the reverse order for the response. The order is critical for security, performance, and functionality.

app.UseAuthentication();

That is what registers the authentication middleware that will eventually look for the JWT Bearer token inside of the incoming HTTP request, and which will use it for authenticating the user based on that value.

Since that middleware is the first middleware that you register, it will also run as the first middleware, before anything else in your pipeline runs. Since the app.UseSiteRouteMiddleware() call comes later, your custom middleware, that would modify the incoming HTTP request, will also run later. So it will modify the request after it has already been used for authentication purposes.

If you want authentication to happen after having the chance to rewrite the HTTP request, you will have to move the app.UseSiteRouteMiddleware() before the app.UseAuthentication() call.

Note though that since your custom middleware uses the session, the session middleware also has to run before your custom middleware. After all, the way your flow should work is like this:

  1. Retrieve session information (session middleware)
  2. Adjust HTTP request headers (SiteRouteMiddleware)
  3. Authenticate user (authentication middleware)

So you have to set up those three middlewares in that order:

app.UseSession();
app.UseSiteRouteMiddleware(getRoutes);
app.UseAuthentication();

That being said, your approach to storing the JWT is rather unconvential. Authentication using a bearer token is usually supposed to be stateless, in that the client explicitly passes the token necessary to authenticate the user. This is in contrast to the common cookie-based authentication, where data is implicitly passed through a cookie as part of the request.

Ironically, you using the session middleware to store the JWT bearer token is doing exactly that actually: By default, the session middleware uses a session cookie to resume a previous user session. So with your solution, you basically ended up using a different cookie to enable authentication.

Unless you have a really good reason for this design, I would strongly recommend you to switch to a standard cookie authentication, which will just store the identity in a cookie instead of storing it in a JSON Web Token. This will likely also generally reduce the complexity of your authentication setup, while making it a lot better supported. At the same time, you will also lose the requirement to use additional session management, which is often recommended against (again unless you have good reasons).

If you need “normal” JWT bearer authentication, for example if your application is also being accessed by an API client, then note that there is nothing wrong with having both JWT bearer authentication and cookie authentication in your application. That way, you have the freedom to choose whatever authentication style fits best for each case.

Related