You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
749 lines
20 KiB
Markdown
749 lines
20 KiB
Markdown
---
|
|
marp: true
|
|
paginate: true
|
|
math: mathjax
|
|
theme: buutti
|
|
title: 7. Authentication and Authorization
|
|
---
|
|
|
|
# Authentication and Authorization
|
|
|
|
<!-- headingDivider: 3 -->
|
|
<!-- class: invert -->
|
|
|
|
## Contents
|
|
|
|
- [Introduction to Authentication & Authorization](#introduction-to-authentication--authorization)
|
|
- [JWT Tokens](#jwt-tokens)
|
|
- [Setting up Authentication in ASP.NET Core](#setting-up-authentication-in-aspnet-core)
|
|
- [User Registration and Login](#user-registration-and-login)
|
|
- [Protected Endpoints](#protected-endpoints)
|
|
- [Role-based Authorization](#role-based-authorization)
|
|
- [Testing Authentication](#testing-authentication)
|
|
|
|
## Introduction to Authentication & Authorization
|
|
|
|
### What is authentication?
|
|
|
|
* ***Authentication*** is the process of verifying who a user is
|
|
* *"Who are you?"*
|
|
* Confirms the identity of a user trying to access the system
|
|
* Common authentication methods:
|
|
* Username/email and password
|
|
* Social login (Google, Facebook, etc.)
|
|
* Multi-factor authentication (MFA)
|
|
* Biometric authentication
|
|
|
|
### What is authorization?
|
|
|
|
* ***Authorization*** is the process of determining what a user can access
|
|
* *"What are you allowed to do?"*
|
|
* Controls access to resources based on user permissions
|
|
* Example: Only admins can delete users
|
|
* Different authorization levels:
|
|
* ***Public***: Anyone can access
|
|
* ***Authenticated***: Only logged-in users can access
|
|
* ***Role-based***: Only users with specific roles can access
|
|
* ***Resource-based***: Users can only access their own resources
|
|
|
|
### Why do we need authentication & authorization?
|
|
|
|
* ***Security***: Protect sensitive data and operations
|
|
* ***User experience***: Personalized content and features
|
|
* ***Compliance***: Meet legal and regulatory requirements
|
|
* ***Audit Trail***: Track who did what and when
|
|
* ***Resource management***: Control access to limited resources
|
|
|
|
## JWT Tokens
|
|
|
|
### What is JWT?
|
|
|
|
* [JWT](https://auth0.com/docs/secure/tokens/json-web-tokens) (JSON Web Token) is an open standard for a compact, URL-safe means of representing claims between two parties
|
|
* Self-contained tokens that include all necessary information
|
|
* It is ***stateless***: no need to store session data on the server
|
|
* ***Notes:***
|
|
* Tokens have a set expiration time, and cannot be invalidated beforehand
|
|
* Client must store tokens securely!
|
|
|
|
### JWT structure
|
|
|
|
```
|
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
|
```
|
|
|
|
* JWTs consist of three parts separated by dots `.`
|
|
* ***Header*** contains token type and signing algorithm
|
|
* ***Payload*** contains claims (user data, permissions, etc.)
|
|
* ***Signature*** verifies the token hasn't been tampered with
|
|
|
|
|
|
### JWT claims
|
|
|
|
* There are two types of [JWT claims](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims), ***registered*** (defined in the JWT specification) and ***custom*** (which can be either public or private)
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
#### Some registered claims (standard)
|
|
* `iss` (issuer): Who issued the token
|
|
* `sub` (subject): Who the token is for
|
|
* `exp` (expiration): When the token expires
|
|
* `iat` (issued at): When the token was issued
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
#### Custom claim examples
|
|
|
|
* `userId`: User's unique identifier
|
|
* `email`: User's email address
|
|
* `role`: User's role (admin, user, etc.)
|
|
* `permissions`: Specific permissions
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
## Setting up Authentication in ASP.NET Core
|
|
|
|
### Required NuGet Packages
|
|
|
|
* Install the following packages from the NuGet Package manager:
|
|
```bash
|
|
Microsoft.AspNetCore.Authentication.JwtBearer
|
|
Microsoft.IdentityModel.Tokens
|
|
System.IdentityModel.Tokens.Jwt
|
|
```
|
|
|
|
### Configuration in appsettings.json
|
|
|
|
```json
|
|
{
|
|
"JwtSettings": {
|
|
"SecretKey": "your-super-secret-key-with-at-least-32-characters",
|
|
"Issuer": "your-app-name",
|
|
"Audience": "your-app-users",
|
|
"ExpirationInMinutes": 60
|
|
},
|
|
"ConnectionStrings": {
|
|
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AuthDemoDb;Trusted_Connection=true;MultipleActiveResultSets=true"
|
|
}
|
|
}
|
|
```
|
|
* To generate the `SecretKey`, you can use [random.org](https://www.random.org/strings)
|
|
|
|
### Program.cs Configuration
|
|
|
|
```csharp
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using System.Text;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add services to the container
|
|
builder.Services.AddControllers();
|
|
|
|
// Configure JWT Authentication
|
|
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
|
|
var secretKey = Encoding.ASCII.GetBytes(jwtSettings["SecretKey"]);
|
|
|
|
builder.Services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
})
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuer = true,
|
|
ValidateAudience = true,
|
|
ValidateLifetime = true,
|
|
ValidateIssuerSigningKey = true,
|
|
ValidIssuer = jwtSettings["Issuer"],
|
|
ValidAudience = jwtSettings["Audience"],
|
|
IssuerSigningKey = new SymmetricSecurityKey(secretKey)
|
|
};
|
|
});
|
|
|
|
builder.Services.AddAuthorization();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Configure the HTTP request pipeline
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
app.MapControllers();
|
|
|
|
app.Run();
|
|
```
|
|
|
|
### Exercise 1: Basic JWT Setup
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create a new ASP.NET Core Web API project
|
|
2) Install the required NuGet packages
|
|
3) Configure JWT authentication in `Program.cs`
|
|
4) Add JWT settings to `appsettings.json`
|
|
5) Test that the application starts without errors
|
|
|
|
## User Registration and Login
|
|
|
|
### User Model
|
|
|
|
```csharp
|
|
public class User
|
|
{
|
|
public int Id { get; set; }
|
|
public string Username { get; set; }
|
|
public string Email { get; set; }
|
|
public string PasswordHash { get; set; }
|
|
public string Role { get; set; } = "User";
|
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
}
|
|
```
|
|
|
|
### DTOs (Data Transfer Objects)
|
|
|
|
```csharp
|
|
public class RegisterRequest
|
|
{
|
|
[Required]
|
|
[StringLength(50)]
|
|
public string Username { get; set; }
|
|
|
|
[Required]
|
|
[EmailAddress]
|
|
public string Email { get; set; }
|
|
|
|
[Required]
|
|
[StringLength(100, MinimumLength = 6)]
|
|
public string Password { get; set; }
|
|
}
|
|
|
|
public class LoginRequest
|
|
{
|
|
[Required]
|
|
public string Username { get; set; }
|
|
|
|
[Required]
|
|
public string Password { get; set; }
|
|
}
|
|
|
|
public class AuthResponse
|
|
{
|
|
public string Token { get; set; }
|
|
public string Username { get; set; }
|
|
public string Role { get; set; }
|
|
public DateTime ExpiresAt { get; set; }
|
|
}
|
|
```
|
|
|
|
### JWT Service
|
|
|
|
```csharp
|
|
public interface IJwtService
|
|
{
|
|
string GenerateToken(User user);
|
|
bool ValidateToken(string token);
|
|
}
|
|
|
|
public class JwtService : IJwtService
|
|
{
|
|
private readonly IConfiguration _configuration;
|
|
|
|
public JwtService(IConfiguration configuration)
|
|
{
|
|
_configuration = configuration;
|
|
}
|
|
|
|
public string GenerateToken(User user)
|
|
{
|
|
var jwtSettings = _configuration.GetSection("JwtSettings");
|
|
var secretKey = Encoding.ASCII.GetBytes(jwtSettings["SecretKey"]);
|
|
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
var tokenDescriptor = new SecurityTokenDescriptor
|
|
{
|
|
Subject = new ClaimsIdentity(new[]
|
|
{
|
|
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
|
new Claim(ClaimTypes.Name, user.Username),
|
|
new Claim(ClaimTypes.Email, user.Email),
|
|
new Claim(ClaimTypes.Role, user.Role)
|
|
}),
|
|
Expires = DateTime.UtcNow.AddMinutes(
|
|
Convert.ToDouble(jwtSettings["ExpirationInMinutes"])
|
|
),
|
|
Issuer = jwtSettings["Issuer"],
|
|
Audience = jwtSettings["Audience"],
|
|
SigningCredentials = new SigningCredentials(
|
|
new SymmetricSecurityKey(secretKey),
|
|
SecurityAlgorithms.HmacSha256Signature
|
|
)
|
|
};
|
|
|
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
|
return tokenHandler.WriteToken(token);
|
|
}
|
|
|
|
public bool ValidateToken(string token)
|
|
{
|
|
var jwtSettings = _configuration.GetSection("JwtSettings");
|
|
var secretKey = Encoding.ASCII.GetBytes(jwtSettings["SecretKey"]);
|
|
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
try
|
|
{
|
|
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(secretKey),
|
|
ValidateIssuer = true,
|
|
ValidIssuer = jwtSettings["Issuer"],
|
|
ValidateAudience = true,
|
|
ValidAudience = jwtSettings["Audience"],
|
|
ValidateLifetime = true,
|
|
ClockSkew = TimeSpan.Zero
|
|
}, out SecurityToken validatedToken);
|
|
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Auth Controller
|
|
|
|
```csharp
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class AuthController : ControllerBase
|
|
{
|
|
private readonly IUserService _userService;
|
|
private readonly IJwtService _jwtService;
|
|
|
|
public AuthController(IUserService userService, IJwtService jwtService)
|
|
{
|
|
_userService = userService;
|
|
_jwtService = jwtService;
|
|
}
|
|
|
|
[HttpPost("register")]
|
|
public async Task<ActionResult<AuthResponse>> Register(RegisterRequest request)
|
|
{
|
|
try
|
|
{
|
|
var user = await _userService.RegisterUser(request);
|
|
var token = _jwtService.GenerateToken(user);
|
|
|
|
return Ok(new AuthResponse
|
|
{
|
|
Token = token,
|
|
Username = user.Username,
|
|
Role = user.Role,
|
|
ExpiresAt = DateTime.UtcNow.AddMinutes(60)
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new { message = ex.Message });
|
|
}
|
|
}
|
|
|
|
[HttpPost("login")]
|
|
public async Task<ActionResult<AuthResponse>> Login(LoginRequest request)
|
|
{
|
|
try
|
|
{
|
|
var user = await _userService.ValidateUser(request);
|
|
var token = _jwtService.GenerateToken(user);
|
|
|
|
return Ok(new AuthResponse
|
|
{
|
|
Token = token,
|
|
Username = user.Username,
|
|
Role = user.Role,
|
|
ExpiresAt = DateTime.UtcNow.AddMinutes(60)
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Unauthorized(new { message = ex.Message });
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Exercise 2: User Authentication Implementation
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create the `User` model and DTOs
|
|
2) Implement the `JwtService` class
|
|
3) Create the `AuthController` with register and login endpoints
|
|
4) Implement password hashing using `BCrypt.Net.BCrypt`
|
|
5) Test the registration and login endpoints using Postman
|
|
|
|
## Protected Endpoints
|
|
|
|
### Using [Authorize] Attribute
|
|
|
|
```csharp
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class UserController : ControllerBase
|
|
{
|
|
[HttpGet("profile")]
|
|
[Authorize] // Requires authentication
|
|
public ActionResult<UserProfile> GetProfile()
|
|
{
|
|
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
|
var username = User.FindFirst(ClaimTypes.Name)?.Value;
|
|
var email = User.FindFirst(ClaimTypes.Email)?.Value;
|
|
|
|
return Ok(new UserProfile
|
|
{
|
|
Id = int.Parse(userId),
|
|
Username = username,
|
|
Email = email
|
|
});
|
|
}
|
|
|
|
[HttpPut("profile")]
|
|
[Authorize] // Requires authentication
|
|
public ActionResult UpdateProfile(UpdateProfileRequest request)
|
|
{
|
|
// Update user profile logic
|
|
return Ok(new { message = "Profile updated successfully" });
|
|
}
|
|
}
|
|
```
|
|
|
|
### Accessing User Claims
|
|
|
|
```csharp
|
|
[HttpGet("me")]
|
|
[Authorize]
|
|
public ActionResult GetCurrentUser()
|
|
{
|
|
var claims = new
|
|
{
|
|
UserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value,
|
|
Username = User.FindFirst(ClaimTypes.Name)?.Value,
|
|
Email = User.FindFirst(ClaimTypes.Email)?.Value,
|
|
Role = User.FindFirst(ClaimTypes.Role)?.Value,
|
|
IsAuthenticated = User.Identity.IsAuthenticated
|
|
};
|
|
|
|
return Ok(claims);
|
|
}
|
|
```
|
|
|
|
### Custom Authorization Attributes
|
|
|
|
```csharp
|
|
public class RequireAdminAttribute : AuthorizeAttribute
|
|
{
|
|
public RequireAdminAttribute()
|
|
{
|
|
Roles = "Admin";
|
|
}
|
|
}
|
|
|
|
[HttpGet("admin-only")]
|
|
[RequireAdmin]
|
|
public ActionResult AdminOnly()
|
|
{
|
|
return Ok(new { message = "This endpoint is only for admins" });
|
|
}
|
|
```
|
|
|
|
### Exercise 3: Protected Endpoints
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create a `UserController` with protected endpoints
|
|
2) Implement an endpoint that returns the current user's profile
|
|
3) Create a custom authorization attribute for admin users
|
|
4) Test the endpoints with and without valid JWT tokens
|
|
5) Verify that unauthorized requests return 401 status codes
|
|
|
|
## Role-based Authorization
|
|
|
|
### Role-based Access Control (RBAC)
|
|
|
|
* ***Roles*** define sets of permissions
|
|
* Users are assigned one or more roles
|
|
* Endpoints are protected based on required roles
|
|
* Common roles: `Admin`, `User`, `Moderator`, `Guest`
|
|
|
|
### Using Role-based Authorization
|
|
|
|
```csharp
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class AdminController : ControllerBase
|
|
{
|
|
[HttpGet("users")]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult<List<User>> GetAllUsers()
|
|
{
|
|
// Only admins can access this endpoint
|
|
return Ok(/* user list */);
|
|
}
|
|
|
|
[HttpDelete("users/{id}")]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult DeleteUser(int id)
|
|
{
|
|
// Only admins can delete users
|
|
return Ok(new { message = "User deleted successfully" });
|
|
}
|
|
|
|
[HttpGet("stats")]
|
|
[Authorize(Roles = "Admin,Moderator")]
|
|
public ActionResult GetStats()
|
|
{
|
|
// Both admins and moderators can access this
|
|
return Ok(/* statistics */);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Multiple Roles
|
|
|
|
```csharp
|
|
[HttpGet("moderate")]
|
|
[Authorize(Roles = "Admin,Moderator")]
|
|
public ActionResult ModerateContent()
|
|
{
|
|
// Users with Admin OR Moderator role can access
|
|
return Ok(new { message = "Content moderated" });
|
|
}
|
|
|
|
[HttpGet("super-admin")]
|
|
[Authorize(Roles = "Admin")]
|
|
[Authorize(Roles = "SuperAdmin")]
|
|
public ActionResult SuperAdminOnly()
|
|
{
|
|
// Users must have BOTH Admin AND SuperAdmin roles
|
|
return Ok(new { message = "Super admin access granted" });
|
|
}
|
|
```
|
|
|
|
### Policy-based Authorization
|
|
|
|
```csharp
|
|
// In Program.cs
|
|
builder.Services.AddAuthorization(options =>
|
|
{
|
|
options.AddPolicy("MinimumAge", policy =>
|
|
policy.RequireAssertion(context =>
|
|
{
|
|
var ageClaim = context.User.FindFirst("Age");
|
|
if (ageClaim != null && int.TryParse(ageClaim.Value, out int age))
|
|
{
|
|
return age >= 18;
|
|
}
|
|
return false;
|
|
}));
|
|
|
|
options.AddPolicy("CanEditProfile", policy =>
|
|
policy.RequireAssertion(context =>
|
|
{
|
|
var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
|
var requestedUserId = context.Resource as string;
|
|
return userId == requestedUserId || context.User.IsInRole("Admin");
|
|
}));
|
|
});
|
|
|
|
// In controller
|
|
[HttpPut("profile/{userId}")]
|
|
[Authorize(Policy = "CanEditProfile")]
|
|
public ActionResult UpdateProfile(string userId, UpdateProfileRequest request)
|
|
{
|
|
// Users can only edit their own profile, unless they're admin
|
|
return Ok(new { message = "Profile updated" });
|
|
}
|
|
```
|
|
|
|
### Exercise 4: Role-based Authorization
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create an `AdminController` with role-protected endpoints
|
|
2) Implement endpoints that require different roles
|
|
3) Create a policy that allows users to edit only their own profiles
|
|
4) Test role-based access with different user roles
|
|
5) Verify that users without required roles get 403 Forbidden responses
|
|
|
|
## Testing Authentication
|
|
|
|
### Testing with Postman
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
* Register a new user:
|
|
```
|
|
POST /api/auth/register
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"username": "testuser",
|
|
"email": "test@example.com",
|
|
"password": "password123"
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
* Login to get JWT token:
|
|
```
|
|
POST /api/auth/login
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"username": "testuser",
|
|
"password": "password123"
|
|
}
|
|
```
|
|
* Use JWT token in protected requests:
|
|
```
|
|
GET /api/user/profile
|
|
Authorization: Bearer <your-jwt-token>
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
### JWT Token Structure Analysis
|
|
|
|
* Decode JWT tokens at [jwt.io](https://jwt.io)
|
|
* Examine the payload to see user claims
|
|
* Verify token expiration and issuer information
|
|
|
|
### Testing Different Scenarios
|
|
|
|
* **Valid token**: Should return 200 OK
|
|
* **Invalid token**: Should return 401 Unauthorized
|
|
* **Expired token**: Should return 401 Unauthorized
|
|
* **Missing token**: Should return 401 Unauthorized
|
|
* **Wrong role**: Should return 403 Forbidden
|
|
|
|
### Exercise 5: Testing Authentication
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create a Postman collection for testing authentication
|
|
2) Test user registration with valid and invalid data
|
|
3) Test login with correct and incorrect credentials
|
|
4) Test protected endpoints with valid JWT tokens
|
|
5) Test role-based endpoints with different user roles
|
|
6) Verify error responses for unauthorized access
|
|
|
|
## Security Best Practices
|
|
|
|
### Password Security
|
|
|
|
* Use strong password requirements
|
|
* Hash passwords with bcrypt or similar
|
|
* Never store plain-text passwords
|
|
* Implement password reset functionality
|
|
|
|
```csharp
|
|
// Password hashing
|
|
public string HashPassword(string password)
|
|
{
|
|
return BCrypt.Net.BCrypt.HashPassword(password);
|
|
}
|
|
|
|
public bool VerifyPassword(string password, string hash)
|
|
{
|
|
return BCrypt.Net.BCrypt.Verify(password, hash);
|
|
}
|
|
```
|
|
|
|
### JWT Security
|
|
|
|
* Use strong secret keys (at least 32 characters)
|
|
* Set appropriate token expiration times
|
|
* Use HTTPS in production
|
|
* Implement token refresh mechanism
|
|
* Consider token blacklisting for logout
|
|
|
|
### Input Validation
|
|
|
|
```csharp
|
|
public class RegisterRequest
|
|
{
|
|
[Required]
|
|
[StringLength(50, MinimumLength = 3)]
|
|
[RegularExpression(@"^[a-zA-Z0-9_]+$")]
|
|
public string Username { get; set; }
|
|
|
|
[Required]
|
|
[EmailAddress]
|
|
public string Email { get; set; }
|
|
|
|
[Required]
|
|
[StringLength(100, MinimumLength = 8)]
|
|
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]")]
|
|
public string Password { get; set; }
|
|
}
|
|
```
|
|
|
|
### Rate Limiting
|
|
|
|
```csharp
|
|
// In Program.cs
|
|
builder.Services.AddRateLimiter(options =>
|
|
{
|
|
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
|
|
RateLimitPartition.GetFixedWindowLimiter(
|
|
partitionKey: context.User.Identity?.Name ?? context.Request.Headers.Host.ToString(),
|
|
factory: partition => new FixedWindowRateLimiterOptions
|
|
{
|
|
AutoReplenishment = true,
|
|
PermitLimit = 100,
|
|
Window = TimeSpan.FromMinutes(1)
|
|
}));
|
|
});
|
|
|
|
// In controller
|
|
[EnableRateLimiting("fixed")]
|
|
[HttpPost("login")]
|
|
public async Task<ActionResult<AuthResponse>> Login(LoginRequest request)
|
|
{
|
|
// Login logic
|
|
}
|
|
```
|
|
|
|
## Advanced authentication
|
|
|
|
* Refresh tokens: You can have long-lived refresh tokens for automatic re-authentication, and short-lived access tokens for API calls
|
|
* Secure token storage and rotation
|
|
* OAuth 2.0 Integration
|
|
* Social login with Google, Facebook, etc.
|
|
* Third-party application authorization
|
|
* Multi-factor authentication (MFA)
|
|
* SMS/email-based verification, Authenticator app integration, backup codes...
|
|
* API Key authentication
|
|
* Alternative to JWT for API access
|
|
* Key-based authentication for services with rate limiting per API key
|
|
|
|
## Resources
|
|
|
|
* [JWT.io](https://jwt.io): JWT token decoder and debugger
|
|
* [ASP.NET Core Security Documentation](https://docs.microsoft.com/en-us/aspnet/core/security/)
|
|
* [OAuth 2.0 Specification](https://tools.ietf.org/html/rfc6749)
|
|
* [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
|
|
* [C# corner: JWT Token creation tutorial](https://www.c-sharpcorner.com/article/jwt-token-creation-authentication-and-authorization-in-asp-net-core-6-0-with-po/)
|
|
* [ASP.NET Core: Configure JWT bearer authentication](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-jwt-bearer-authentication?view=aspnetcore-9.0)
|
|
* Remember, if you don't restrict access to your endpoints, something like
|
|
[this](https://mastodon.gamedev.place/@badlogic/) [might happen](https://firesky.tv/) |