DEV Community

Alex
Alex

Posted on • Edited on

.NET Learning Notes: How to use Identity + JWT

Reference:
https://learn.microsoft.com/en-us/aspnet/core/security/?view=aspnetcore-9.0
https://blog.csdn.net/ousetuhou/article/details/135392012

  • Authentication

Authentication is a process in which a user provides credentials that are then compared to those stored in an operating system, database, app or resource. If they match, users authenticate successfully, and can then perform actions that they're authorized for, during an authorization process. The authorization refers to the process that determines what a user is allowed to do.

An authentication scheme's authenticate action is responsible for constructing the user's identity based on request context.
An authentication challenge is invoked by Authorization when an unauthenticated user requests an endpoint that requires authentication. A challenge action should let the user know what authentication mechanism to use to access the requested resource.
An authentication scheme's forbid action is called by Authorization when an authenticated user attempts to access a resource they're not permitted to access.

How to use Identity?
1.Add a class named MyUser(any name you want) class that inherits from IdentityUser, T is type of primary key.

public class MyUser : IdentityUser<long>
{
    public string? WechatAccout { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

2.Add a class named MyRole(any name you want) class that inherits from IdentityRole, T is type of Primary key.
3.Add a class named ApplicationDbContext(any name you want) class that inherits from IdentityDbContext, and override the constructor which has a options parameter, makes it possible to configure the database for different environments.
When using Identity with support for roles, an IdentityDbContext class should be used.
It's also possible to use Identity without roles(only claims), in which case an IdentityUserContext class should be used.

public class ApplicationDbContext : IdentityDbContext<MyUser, MyRole, long>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

4.Add configuration to DI framework.

// configure Identity to use a database
builder.Services.AddDbContext<ApplicationDbContext>(options
     =>
{
    var connectionString = builder.Configuration.GetValue("MySQLConnectionString", string.Empty);
    options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
});

builder.Services.AddDataProtection();
builder.Services.AddIdentityCore<MyUser>(
    options =>
    {
        // for test, reduce the strength of password
        options.Password.RequireDigit = false;
        options.Password.RequireLowercase = false;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
        options.Password.RequiredLength = 6;
        options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
        options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
    });
var identityBuilder = new IdentityBuilder(typeof(MyUser), typeof(MyRole), builder.Services);
identityBuilder.AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders().AddRoleManager<RoleManager<MyRole>>()
    .AddUserManager<UserManager<MyUser>>();
Enter fullscreen mode Exit fullscreen mode

5.Add a DbContextFactory implement IDesignTimeDbContextFactory, because EF Core may not be able to migrate.

public class DbContextDesignTimeFactory: IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
        DbContextOptionsBuilder<ApplicationDbContext> builder = new DbContextOptionsBuilder<ApplicationDbContext>();
        var connectionString = arg[0];
        builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
        return new ApplicationDbContext(builder.Options);
    }
}
Enter fullscreen mode Exit fullscreen mode

6.Database migrate

How to use JWT?

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt
Enter fullscreen mode Exit fullscreen mode

1.Configurate JWT

  "JwtTokenOption": {
    "Issuer": "ChenLi",
    "Audience": "EveryOne",
    "IssuerSigningKey": "ChenLi$%%^&%*!&^@*GUH976&^^&T*u34",
    "AccessTokenExpiresMinutes": "30"
  }
Enter fullscreen mode Exit fullscreen mode

2.Create a option

public class JwtTokenOption
{
    public const string JwtKey = "JwtTokenOption";
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public string IssuerSigningKey { get; set; }
    public int AccessTokenExpiresMinutes { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

3.Add JwtTokenOption Configuration

builder.Services.Configure<JwtTokenOption>(builder.Configuration.GetSection(JwtTokenOption.JwtKey));
Enter fullscreen mode Exit fullscreen mode

4.Add JwtBearer to Authentication

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    var jwtTokenOption = builder.Configuration.GetSection(JwtTokenOption.JwtKey).Get<JwtTokenOption>();
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true,
        ValidIssuer = jwtTokenOption.Issuer,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenOption.IssuerSigningKey)),
        ValidateAudience = true,
        ValidAudience = jwtTokenOption.Audience,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.FromMinutes(1)
    };
});
builder.Services.AddAuthorization();

app.UseAuthentication();
app.UseAuthorization();
Enter fullscreen mode Exit fullscreen mode

5.Add Class to create Token


public class TokenHelper(IOptionsSnapshot<JwtTokenOption> jwtTokenOption) : ITokenHelper
{
    public JwtToken CreateToken<T>(T entity) where T : class
    {
        List<Claim> claims = new List<Claim>();
        foreach (var item in entity.GetType().GetProperties())
        {
            object obj = item.GetValue(entity);
            string value = "";
            if (obj != null)
            {
                value = obj.ToString();
            }
            claims.Add(new Claim(item.Name, value));
        }

        return CreateTokenString(claims);
    }

    public JwtToken CreateToken(Dictionary<string, string> KeyValuePairs)
    {
        List<Claim> claims = new List<Claim>();
        foreach (var item in KeyValuePairs)
        {
            claims.Add(new Claim(item.Key, item.Value));
        }

        return CreateTokenString(claims);
    }

    private JwtToken CreateTokenString(IEnumerable<Claim> claims)
    {
        DateTime expires = DateTime.Now.AddHours(jwtTokenOption.Value.AccessTokenExpiresMinutes);
        var token = new JwtSecurityToken(
            issuer: jwtTokenOption.Value.Issuer,
            audience: jwtTokenOption.Value.Audience,
            claims: claims,
            notBefore: DateTime.Now,
            expires: expires,
            signingCredentials: new SigningCredentials(
                new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenOption.Value.IssuerSigningKey)),
                SecurityAlgorithms.HmacSha256
                )
            );
        return new JwtToken()
        {
            TokenStr = new JwtSecurityTokenHandler().WriteToken(token),
            Expires = expires
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

6.return token to client

    [HttpPost]
    public async Task<IActionResult> Login(String username, String password)
    {
        ResponseModel response = new ResponseModel();
        var user = await userManager.FindByNameAsync(username);
        if (user == null)
        {
            response.Code = 401;
            response.Message = "Invalid username or password";
            return Unauthorized(response);
        }

        var checkPassword = await userManager.CheckPasswordAsync(user, password);
        Dictionary<string, string> dir = new Dictionary<string, string>()
        {
            { "username", user.UserName },
        };
        response.Code = 200;
        response.Message = "Success";
        response.TokenInfo = tokenHelper.CreateToken(dir);
        return Ok(response);
    }
Enter fullscreen mode Exit fullscreen mode

7.Add [Authorize] to action or controller, Header must have jwt or will be 401

Headers:
Authorization Bearer <Token>
Enter fullscreen mode Exit fullscreen mode
    [HttpPost]
    [Authorize]
    public async Task<IActionResult> SendRestPasswordToken(string username)
    {
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)