admin管理员组

文章数量:1356597

Let me give you some context.

I am currently trying to implement a Refresh/Access token setup for my authentication and authorization. I think I understand the idea and the purpose of each token. But the issue comes with I am not sure about how to proceed with the implementation.

Before what I would do was just have the Access Token be stored through localStorage in the frontend. And then attached to the relevant API calls through the Authentication header.

And it did worked. But lately I've been trying to implement it a different way. This time I want both the Access and Refresh token be HTTP-Only tokens.

Now this complicated things a bit. As I believe or I feel like ASP.NET Core is wired so the default token is the Bearer one.

Meaning for the HTTP-Only to serve its purpose I had to setup the token manually through the JWT's events. Like so:

  services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = true,
                ValidateLifetime = true,
                ValidIssuer = config["JWT:Issuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["JWT:Key"]!)),
            };

            options.Events = new JwtBearerEvents
            {
                OnMessageReceived = ctx =>
                {
                    ctx.Request.Cookies.TryGetValue("access-token", out var accessToken);
                    if (!string.IsNullOrEmpty(accessToken))
                    {
                        ctx.Token = accessToken;
                    }
                    return Task.CompletedTask;
                }
            };
        });

The idea is to have a HTTP-Only be used as the Bearer on ASP.NET Core. So I can use the [Authorize] attribute on the endpoints as well as the rest of the functionality that this brings.

But it is not working.

I've done some testing and the token is correctly generated and also correctly grabbed from the HttpContext.

I've created a validate method in a Token Service class in order to test if my Token is correctly generated. And it is.

This is the method I used to check my tokens.

  public ClaimsPrincipal? ValidateToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = true,
            ValidateLifetime = true,
            ValidIssuer = _config["JWT:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:Key"]!)),
        };
        try
        {
            return tokenHandler.ValidateToken(token, validationParameters, out var result);

        }
        catch (SecurityTokenException)
        {
            return null;
        }
    }

Now I think the issue comes from the fact I don't yet understand fully how [Authorize] works and I believe the is still something more to do in order to make it work.

Any feedback, guidance or advice into how to solve this issue would be appreciated!

Thank you for your time!

Let me give you some context.

I am currently trying to implement a Refresh/Access token setup for my authentication and authorization. I think I understand the idea and the purpose of each token. But the issue comes with I am not sure about how to proceed with the implementation.

Before what I would do was just have the Access Token be stored through localStorage in the frontend. And then attached to the relevant API calls through the Authentication header.

And it did worked. But lately I've been trying to implement it a different way. This time I want both the Access and Refresh token be HTTP-Only tokens.

Now this complicated things a bit. As I believe or I feel like ASP.NET Core is wired so the default token is the Bearer one.

Meaning for the HTTP-Only to serve its purpose I had to setup the token manually through the JWT's events. Like so:

  services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = true,
                ValidateLifetime = true,
                ValidIssuer = config["JWT:Issuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["JWT:Key"]!)),
            };

            options.Events = new JwtBearerEvents
            {
                OnMessageReceived = ctx =>
                {
                    ctx.Request.Cookies.TryGetValue("access-token", out var accessToken);
                    if (!string.IsNullOrEmpty(accessToken))
                    {
                        ctx.Token = accessToken;
                    }
                    return Task.CompletedTask;
                }
            };
        });

The idea is to have a HTTP-Only be used as the Bearer on ASP.NET Core. So I can use the [Authorize] attribute on the endpoints as well as the rest of the functionality that this brings.

But it is not working.

I've done some testing and the token is correctly generated and also correctly grabbed from the HttpContext.

I've created a validate method in a Token Service class in order to test if my Token is correctly generated. And it is.

This is the method I used to check my tokens.

  public ClaimsPrincipal? ValidateToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var validationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = true,
            ValidateLifetime = true,
            ValidIssuer = _config["JWT:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:Key"]!)),
        };
        try
        {
            return tokenHandler.ValidateToken(token, validationParameters, out var result);

        }
        catch (SecurityTokenException)
        {
            return null;
        }
    }

Now I think the issue comes from the fact I don't yet understand fully how [Authorize] works and I believe the is still something more to do in order to make it work.

Any feedback, guidance or advice into how to solve this issue would be appreciated!

Thank you for your time!

Share Improve this question edited Mar 30 at 10:08 marc_s 756k184 gold badges1.4k silver badges1.5k bronze badges asked Mar 30 at 8:38 yzkaelyzkael 3312 silver badges9 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

First, ensure UseAuthentication is called before UseAuthorization in Startup.cs/Program.cs:

app.UseAuthentication();
app.UseAuthorization();

Then, when use the [Authorize] attribute, try to specify the JWT Bearer Scheme:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Besides, when setting the access token cookie, include necessary attributes, to make sure the cookie should be set with the correct HttpOnly, Secure, SameSite attributes.

Response.Cookies.Append("access-token", token, new CookieOptions
{
    HttpOnly = true,
    Secure = true, // Use in HTTPS environments
    SameSite = SameSiteMode.Strict,
    Expires = // Set expiration based on token validity
});

Finally, here are some resources about using access token and refresh token in asp core, you can refer to them:

Using Refresh Tokens in ASP.NET Core Authentication

Implementing JWT Refresh Token in ASP.NET Core MVC

本文标签: cHow do I set up a different cookie for authentication in an ASPNET Core Web APIStack Overflow