简体   繁体   中英

ASP.NET Core Sign In with JWT

im using JWT to Authentication, and I'm storing it in Cookies. I want to use HttpContext.SignInAsync to login, I saw a lot of example with basic Cookie Auth, but not with JWT.

Here is my Startup.cs

 services.AddTransient<IUserRepository, UserRepository>();
        services.AddTransient<ITokenService, TokenService>();
        IdentityModelEventSource.ShowPII = true;
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
        {

            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidAudience = Configuration["Jwt:Issuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
            };
            options.SaveToken = true;
            options.Events = new JwtBearerEvents();
            options.Events.OnMessageReceived = context =>
            {

                if (context.Request.Cookies.ContainsKey("X-Access-Token"))
                {
                    context.Token = context.Request.Cookies["X-Access-Token"];
                }

                return Task.CompletedTask;
            };
        })
          .AddCookie(options =>
          {
              options.Cookie.SameSite = SameSiteMode.Strict;
              options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
              options.Cookie.IsEssential = true;

          });

Here is my Login (Its currently working, but it would be bettern with SignInAsync

public IActionResult Login(LoginModel loginModel, string returnUrl)
        {
            if (string.IsNullOrEmpty(loginModel.UserName) || string.IsNullOrEmpty(loginModel.Password))
            {
            ViewBag.Message = "Nem lehet üres";
            return View("Index",loginModel);
            }

            IActionResult response = Unauthorized();
            var validUser = GetUser(loginModel);

            if (validUser != null)
            {
                generatedToken = _tokenService.BuildToken(_config["Jwt:Key"].ToString(), _config["Jwt:Issuer"].ToString(),
                validUser);

                if (generatedToken != null)
                {
                    Response.Cookies.Append("X-Access-Token", generatedToken, new CookieOptions() { HttpOnly = true, SameSite = SameSiteMode.Strict, Secure=true });
                    Response.Cookies.Append("X-Username", loginModel.UserName, new CookieOptions() { HttpOnly = true, SameSite = SameSiteMode.Strict, Secure=true });
                    //Response.Cookies.Append("X-Refresh-Token", user.RefreshToken, new CookieOptions() { HttpOnly = true, SameSite = SameSiteMode.Strict });
                   // HttpContext.Session.SetString("Token", generatedToken);
                if (returnUrl != null)
                {
                    return Redirect(returnUrl);
                }
                else
                {
                    return RedirectToAction("MainWindow");
                }
            }
                else
                {
                ViewBag.Message = "Nem jo token";
                return View("Index", loginModel);
            }
            }
            else
            {
            ViewBag.Message = "Nem jó user";
            return View("Index", loginModel);
        }
        }

And here is my Token service:

public class TokenService : ITokenService
{
    private const double EXPIRY_DURATION_MINUTES = 30;

    public string BuildToken(string key, string issuer, User user)
    {
        var claims = new[] {
        new Claim(ClaimTypes.Name, user.Name),
        new Claim(ClaimTypes.Role, user.Role),
        new Claim(ClaimTypes.NameIdentifier,
        Guid.NewGuid().ToString())
        };

        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
        var tokenDescriptor = new JwtSecurityToken(issuer, issuer, claims,
            expires: DateTime.Now.AddMinutes(EXPIRY_DURATION_MINUTES), signingCredentials: credentials);
        return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
    }
    public bool IsTokenValid(string key, string issuer, string token)
    {
        var mySecret = Encoding.UTF8.GetBytes(key);
        var mySecurityKey = new SymmetricSecurityKey(mySecret);

        var tokenHandler = new JwtSecurityTokenHandler();
        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidIssuer = issuer,
                ValidAudience = issuer,
                IssuerSigningKey = mySecurityKey,
            }, out SecurityToken validatedToken);
        }
        catch
        {
            return false;
        }
        return true;
    }
}

Edit 1 - Added the question

How can I implement HttpContext.SignInAsync in this situation?

My startup like below:

 services.AddAuthentication(options =>
            {
                // custom scheme defined in .AddPolicyScheme() below
                options.DefaultScheme = "JWT_OR_COOKIE";
                options.DefaultChallengeScheme = "JWT_OR_COOKIE";
            })
           // Adding Jwt Bearer  
           .AddJwtBearer(options =>
           {
              ....//do your staff
           }).AddCookie("Cookies", options =>
           {
               options.LoginPath = "/account/login";
               options.ExpireTimeSpan = TimeSpan.FromDays(1);
           }).AddPolicyScheme("JWT_OR_COOKIE", "JWT_OR_COOKIE", options =>
           {
               // runs on each request
               options.ForwardDefaultSelector = context =>
               {
                   // filter by auth type
                   string authorization = context.Request.Headers[HeaderNames.Authorization];
                   if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
                       return "Bearer";

                   // otherwise always check for cookie auth
                   return "Cookies";
               };
               });

Add more code:

Some code in this answer

In Login post method I add below into the claims

new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),

And my token generated as below, when you login , you will get token.

var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));

                    var token = new JwtSecurityToken(
                        issuer: _configuration["JWT:ValidIssuer"],
                        audience: _configuration["JWT:ValidAudience"],
                        expires: DateTime.Now.AddHours(3),
                        claims: claims,
                        signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
                        );

                    return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token),
                    expiration = token.ValidTo
                });

Result:

在此处输入图像描述

Do you mean you want read claims from jwt and ceate a new ticket for cookie authentication as below:

var jwtsecuritytoken = new JwtSecurityTokenHandler().ReadToken(token) as JwtSecurityToken;
                    var username = jwtsecuritytoken.Claims.FirstOrDefault(m => m.Type == ClaimTypes.Name).Value;
                     .......
                     //add other logic
                    .........
                        var claims = new Claim[]
                        {
                        new Claim(ClaimTypes.Name, username),
                         ..........
                         };
                        var claimsIdentity = new ClaimsIdentity(claims);
                        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
                    }

just change authentication like below

 services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = "JwtBearerDefaults.AuthenticationScheme";
                options.DefaultChallengeScheme = "JwtBearerDefaults.AuthenticationScheme";
                options.DefaultSignInScheme = "JwtBearerDefaults.AuthenticationScheme";
         
            })

authentication middleware do signin context automaticly

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM