dotnet / AspNetCore.Docs

Documentation for ASP.NET Core
https://docs.microsoft.com/aspnet/core
Creative Commons Attribution 4.0 International
12.64k stars 25.29k forks source link

Support for AuthN with JWT stored in session http-only cookies #27902

Open MadL1me opened 1 year ago

MadL1me commented 1 year ago

Summary

Create in-box support for Authn with JWT stored in http-only cookies

Motivation and goals

Then we develop SPA application with ASP.NET Core (for ex. with React at frontend) we very often think about how we implement authentication in our application. There is currently two ways to do it: with session cookie, and with JWT. As devs, we prefer to use jwt, because it does not require for a DB lookup.

If we decided to use jwt, there is 3 ways to do request with it:

The third option is currently the most secure way to pass and store jwt tokens, because we become immutable to XSS attack, because attacker cannot read our token from cookie. Still, we became vulnerable to CSRF, but this we can fix this by providing XSRF token on a client side. By the end of a day, we can see why JWT with http-only secure cookie is preferred as authn solution using jwt's.

However this way is most secure, ASP.NET Core does not provide in-box solution for this, so we need to add custom middleware to any project which uses this technique. Currently, we implement this logic by this sort of middleware:

app.UseCookiePolicy(cookiePolicyOptions); // making cookie http-only, strict, e.t.c
app.Use(async (context, next) =>
{
        var token = context.Request.Cookies[".AspNetCore.Application.Id"];
        if (!string.IsNullOrEmpty(token))
                context.Request.Headers.Add("Authorization", "Bearer " + token);

        await next();
});
app.UseAuthentication();

Futhermore, a lot of other tech solutions (such as NextAuth) already have support for this feature.

Goals:

In scope

A list of major scenarios, perhaps in priority order.

Out of scope

Scenarios you explicitly want to exclude.

Risks / unknowns

There is security concern, and we need to be careful about reviewing PR for this. Also,

Examples

Design proposal No.1: New middleware

We can add new middleware, which will

app.UseCookiePolicy(cookiePolicyOptions);
app.UseJwtInCookie(jwtInCookiePolicyOptions);

Design proposal No.2: Different authentication scheme?

I'm not sure, if that can be considered as a different authn scheme. If so, we would be able to add jwt in cookie alongside with default jwt bearer and cookies:

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        // some cookie settings
    })
    .AddJwtBearer(options => 
    {
         // some jwt settings
    })
    .AddJwtInCookie(options => 
    {
         // There is an options for both cookie and jwt configuration for this?
    });

Associated WorkItem - 55240

davidfowl commented 1 year ago

It's unclear to me in the above proposal what the middleware would do, and what the end-to-end flow looks like.

  1. Where is the token being acquired and written to the cookie?
  2. Why not just use cookie authentication?

It's possible today to customize where the JWT comes from:

builder.Services.AddAuthentication().AddJwtBearer(o =>
{
    o.Events = new()
    {
        OnMessageReceived = context =>
        {
            // Get the token from a cookie
            context.Token = context.Request.Cookies[".AspNetCore.Application.Id"];

            return Task.CompletedTask;
        }
    };
});

PS:

This is already doable with what ASP.NET Core ships today. You can see it here:

Add the cookie authentication scheme:

https://github.com/davidfowl/TodoApi/blob/56674554626ff9bb4b3442f8a4710b906ae8c6e6/Todo.Web/Server/Authentication/AuthenticationExtensions.cs#L19-L23

Acquire JWT from API server and store it in a cookie:

https://github.com/davidfowl/TodoApi/blob/56674554626ff9bb4b3442f8a4710b906ae8c6e6/Todo.Web/Server/AuthApi.cs#L13-L24

Store the token in a cookie:

https://github.com/davidfowl/TodoApi/blob/56674554626ff9bb4b3442f8a4710b906ae8c6e6/Todo.Web/Server/AuthApi.cs#L95-L112

Getting the token from the cookie

https://github.com/davidfowl/TodoApi/blob/56674554626ff9bb4b3442f8a4710b906ae8c6e6/Todo.Web/Server/TodoApi.cs#L16-L17

The JWT token is being acquired from another server in this situation, but you can see that all of the pieces are there.

MadL1me commented 1 year ago

@davidfowl thank you. I tried to search, but didn't found the solution you provided anywhere in the microsoft docs. Again, thanks for help, and sorry for issue, I thought it wasn't possible currently

captainsafia commented 1 year ago

@Rick-Anderson Do we document this particular scenario for OnMessageReceived to incorporate JWT token derived from a cookie?

There are some docs on this pattern in https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-7.0#built-in-jwt-authentication.

Rick-Anderson commented 1 year ago

@Rick-Anderson Do we document this particular scenario for OnMessageReceived to incorporate JWT token derived from a cookie?

No.

There are some docs on this pattern in https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-7.0#built-in-jwt-authentication.

Where should this information go? I don't see a doc where it could be added.

captainsafia commented 1 year ago

I think adding it to the same heading linked above should be sufficient.

Rick-Anderson commented 1 year ago

I think adding it to the same heading linked above should be sufficient.

But that's SignalR, as you asked

@Rick-Anderson Do we document this particular scenario for OnMessageReceived to incorporate JWT token derived from a cookie?

How will non-SignalR folks find it?

@adityamandaleeka can you assign someone to draft how to use OnMessageReceivedto incorporate JWT token derived from a cookie?

adityamandaleeka commented 1 year ago

@HaoK Can you help with that?

FYI @Rick-Anderson The auth space is now owned by @rafikiassumani-msft's team.

Rick-Anderson commented 1 year ago

FYI @Rick-Anderson The auth space is now owned by @rafikiassumani-msft's team.

Can you update Team Ownership? That's what I use.

adityamandaleeka commented 1 year ago

Will do.

captainsafia commented 1 year ago

cc: @JeremyLikness

There's questions here about where to document a scenario that users inquired about. Bringing it to your radar because I'm not sure where the best place in the docs to place it is.

JeremyLikness commented 1 year ago

Will address this as part of a broader effort to improve auth-related docs and will cross-link when the other issue is available.