JWT কী?
JSON Web Token — header.payload.signature, server stateless থাকে।
Setup
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
appsettings.json
{
"Jwt": {
"Key": "your-very-long-secret-min-32-characters",
"Issuer": "MyApi",
"Audience": "MyApiUsers",
"ExpiryMinutes": 60
}
}
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var jwt = builder.Configuration.GetSection("Jwt");
var key = Encoding.UTF8.GetBytes(jwt["Key"]!);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt => {
opt.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidIssuer = jwt["Issuer"],
ValidateAudience = true,
ValidAudience = jwt["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization();
// ...
app.UseAuthentication();
app.UseAuthorization();
Token Service
public interface ITokenService {
string GenerateAccessToken(User user);
string GenerateRefreshToken();
}
public class TokenService : ITokenService {
private readonly JwtOptions _opt;
public TokenService(IOptions<JwtOptions> opt) => _opt = opt.Value;
public string GenerateAccessToken(User user) {
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.Role, user.Role)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_opt.Key));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _opt.Issuer,
audience: _opt.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(_opt.ExpiryMinutes),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public string GenerateRefreshToken() {
var bytes = new byte[64];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
}
Auth Controller
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase {
private readonly AppDbContext _db;
private readonly ITokenService _tokens;
public AuthController(AppDbContext db, ITokenService tokens) {
_db = db; _tokens = tokens;
}
[HttpPost("register")]
public async Task<IActionResult> Register(RegisterDto dto) {
if (await _db.Users.AnyAsync(u => u.Email == dto.Email))
return Conflict("Email already registered");
var user = new User {
Email = dto.Email,
Name = dto.Name,
PasswordHash = BCrypt.Net.BCrypt.HashPassword(dto.Password),
Role = "User"
};
_db.Users.Add(user);
await _db.SaveChangesAsync();
return Ok(new { token = _tokens.GenerateAccessToken(user) });
}
[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto dto) {
var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == dto.Email);
if (user == null || !BCrypt.Net.BCrypt.Verify(dto.Password, user.PasswordHash))
return Unauthorized("Invalid credentials");
var refresh = _tokens.GenerateRefreshToken();
user.RefreshToken = refresh;
user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
await _db.SaveChangesAsync();
return Ok(new {
accessToken = _tokens.GenerateAccessToken(user),
refreshToken = refresh
});
}
[HttpPost("refresh")]
public async Task<IActionResult> Refresh(RefreshDto dto) {
var user = await _db.Users.FirstOrDefaultAsync(u =>
u.RefreshToken == dto.RefreshToken &&
u.RefreshTokenExpiry > DateTime.UtcNow);
if (user == null) return Unauthorized();
return Ok(new { accessToken = _tokens.GenerateAccessToken(user) });
}
}
Protected Endpoint
[ApiController]
[Route("api/[controller]")]
[Authorize] // ← সব action protected
public class ProductsController : ControllerBase {
[HttpGet]
[AllowAnonymous] // ← public
public IActionResult GetAll() => Ok();
[HttpPost]
[Authorize(Roles = "Admin")] // ← role check
public IActionResult Create() => Ok();
[HttpGet("me")]
public IActionResult Me() {
var userId = User.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
var email = User.FindFirst(JwtRegisteredClaimNames.Email)?.Value;
return Ok(new { userId, email });
}
}
Frontend Use
// Save token
localStorage.setItem("token", res.accessToken);
// Use
fetch("/api/products", {
headers: { "Authorization": `Bearer ${token}` }
});