Files
2025-07-18 14:47:22 +03:30

786 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AUTHENTICATION METHODS FOR ASP.NET CORE WEB API
### 1. **JWT (JSON Web Token) Authentication**
**Most common for SPAs (like your React frontend)**
-**Stateless**: No session stored on the server.
- 🔐 **Token structure**: Header + Payload (including roles) + Signature.
- 🎯 Good for: SPAs, mobile apps, APIs.
- 📦 Libraries: `Microsoft.AspNetCore.Authentication.JwtBearer`.
**Setup Highlights:**
- Client sends credentials → API generates token with user roles → Client stores token → Client sends token in `Authorization` header (`Bearer <token>`).
- Server verifies signature and extracts user identity and roles from the token.
**Role support**:
- You embed roles inside the JWT (`"roles": ["Admin", "User"]`).
- Use `[Authorize(Roles = "Admin")]`.
---
### 2. **Cookie-Based Authentication**
**Traditional approach for server-rendered apps (not ideal for APIs)**
- 🛑 **Not recommended for APIs** due to CSRF vulnerability and session overhead.
- 🗂 Stores session identifier in browser cookie.
- Useful when using ASP.NET Core MVC or Razor Pages (not Web API).
---
### 3. **OAuth2 + OpenID Connect (OIDC)**
**Best for federated login, single sign-on (SSO), or external providers**
- 🔗 Integrates with Identity Providers (IDPs) like:
- Azure AD
- Google, Facebook, GitHub
- Auth0, Okta, Duende IdentityServer
- 📦 Library: `Microsoft.AspNetCore.Authentication.OpenIdConnect`
- Uses **access tokens** (JWT) issued by an authority.
**Role support**:
- Roles/claims provided by the Identity Provider.
- You map these claims to roles in the API.
- `[Authorize(Roles = "Admin")]` still works.
---
### 4. **API Key Authentication**
**Lightweight alternative (not ideal for user-based roles)**
- Client includes a static API key in headers or query string.
- 🔐 No user context → ❌ no role-based support unless you map API keys to roles in a custom way.
- 🔧 Implemented manually in middleware or filters.
---
### 5. **Basic Authentication**
- User provides `username:password` in Base64 via `Authorization` header.
- ❌ Insecure unless used with HTTPS.
- ⚠️ Rarely used anymore — not good for role-based systems or production apps.
---
### 6. **ASP.NET Core Identity**
**Full-featured user management system (often combined with JWT)**
- ✅ Provides login, registration, role management, password hashing, etc.
- 🎯 Good choice if you want to **own the user system** and **manage roles** yourself.
- Can be used with:
- JWT tokens (custom token generation)
- Cookie auth (not for APIs)
- 🔧 Use `UserManager`, `RoleManager`.
**Example**: Use Identity for creating users and roles, then issue JWTs on login.
---
## Summary Table
|Method|Stateless|Token-Based|Role Support|Ideal For|
|---|---|---|---|---|
|JWT Authentication|✅|✅|✅|APIs, SPAs (React, etc.)|
|Cookie Authentication|❌|❌|✅|Server-side apps only|
|OAuth2 + OpenID Connect|✅|✅|✅|External login, SSO, enterprise|
|API Key|✅|❌|❌ (manual)|Simple apps, service-to-service|
|Basic Auth|✅|❌|❌ (manual)|Very basic use, not recommended|
|ASP.NET Core Identity|❌|Optional|✅|User/Role management|
---
# Web Security and JWT Terms
### 🧨 **CSP (Content Security Policy)**
**Definition:**
A **browser security mechanism** that helps prevent **XSS attacks** by controlling which sources the browser can load content from.
**Example Usage:**
It can prevent JavaScript from running unless it's from a trusted source.
**Example CSP header:**
```http
Content-Security-Policy: default-src 'self'; script-src 'self' https://trustedscripts.example.com
```
---
### 💥 **XSS (Cross-Site Scripting)**
**Definition:**
A vulnerability where an attacker **injects malicious JavaScript** into a web page that gets executed in a users browser.
**Why It Matters for JWT:**
If you store JWTs in `localStorage`, and your app is vulnerable to XSS, the attacker can steal the token and impersonate the user.
**Prevention:**
- Never trust user input (sanitize!)
- Use **CSP headers**
- Avoid inline scripts
- Use frameworks that auto-escape HTML (like React, Razor)
---
### 📦 **Payload (in JWT)**
**Definition:**
The **middle part** of a JWT. It contains the **claims** (user data, permissions, roles, etc.) in a **Base64-encoded JSON** format.
**Example:**
```json
{
"sub": "1234567890",
"name": "John Doe",
"role": "Admin",
"exp": 1716850984
}
```
⚠️ **Note:** Payload is **not encrypted**, just encoded — anyone can read it, but not modify it without invalidating the signature.
---
### ✍️ **Signature (in JWT)**
**Definition:**
The **third part** of the token. It's a cryptographic hash (HMAC or RSA/ECDSA) of the header and payload, signed with a **secret** or private key.
**Purpose:**
To verify that the token **hasnt been tampered with**.
**Structure:**
```
JWT = base64(header) + '.' + base64(payload) + '.' + signature
```
Only the server (with the secret key) can validate the signature.
---
### 🦠 **CSRF (Cross-Site Request Forgery)**
**Definition:**
A vulnerability where an attacker tricks a users **browser** (with a valid session/cookie) into making an unwanted request to your site **without the users knowledge**.
**Example:**
If you're logged in and visit a malicious site, that site may submit a POST request using your cookies to perform actions on your behalf.
**Why It Matters:**
- If you store JWTs in **HttpOnly cookies**, you must guard against CSRF.
**Defense:**
- Use `SameSite=Strict` cookies
- Use anti-CSRF tokens
- Prefer `Authorization` header with tokens (doesnt auto-send like cookies)
---
### 🧠 **Session Overhead**
**Definition:**
The **memory and server resource cost** of maintaining a session for each logged-in user on the server.
**Why It Matters:**
- Traditional **cookie-based auth** stores session info on the server.
- With JWT, the session is **stateless** (no server memory), reducing overhead and scaling better.
---
## 🔑 **Authentication Ecosystem Concepts**
---
### 🌐 **Federated Login**
**Definition:**
Users log in to your application using **another trusted identity provider** (IdP), such as:
- Google
- Facebook
- Microsoft
- GitHub
You delegate authentication to the third party and just receive user info (often as a JWT or OpenID Connect token).
**Protocol Examples:**
- OAuth2
- OpenID Connect
---
### 🔁 **SSO (Single Sign-On)**
**Definition:**
A user logs in **once** and gains access to **multiple systems or applications** without logging in again.
**Common in Enterprises**:
- Logging into one dashboard gives you access to HR system, email, file storage, etc.
**How It Works:**
- A **central identity provider (IdP)** issues tokens
- Applications **trust the token** and skip login
- Often uses **OAuth2**, **OpenID Connect**, or **SAML**
---
## 🔄 Summary Table
|Term|Meaning|
|---|---|
|**CSP**|Restricts sources of scripts/styles to prevent XSS|
|**XSS**|Injected JavaScript that steals data like JWT tokens|
|**JWT Payload**|JSON with user claims (readable but not secure on its own)|
|**JWT Signature**|Proves the token is untampered (signed by server)|
|**CSRF**|Unauthorized actions using authenticated user's browser/session|
|**Session Overhead**|Cost of storing user sessions in memory on the server|
|**Federated Login**|Login using a third-party identity provider (e.g., Google)|
|**SSO**|One login grants access to multiple trusted systems|
# ASP.NET Identity vs JWT Authentication
---
### ✅ **What is ASP.NET Identity?**
**ASP.NET Identity** is a full **membership system** that:
- Manages users, roles, passwords, claims, tokens, and external logins.
- Stores user data in a **database** (usually using Entity Framework).
- Works well with **cookie-based authentication** by default.
- Handles login, logout, registration, password hashing, email confirmation, two-factor auth, etc.
**Default storage:** SQL Server (via Entity Framework)
---
### 🔑 **How Authentication Works in ASP.NET Identity (Cookie-Based)**
1. **Login request**: The user submits a form with username/password.
2. **Server validates** the credentials using ASP.NET Identity.
3. If valid, the server issues a **cookie** (with a session token).
4. The browser stores this **authentication cookie**.
5. For future requests, the browser **automatically sends the cookie**.
6. The server validates the cookie (and reads the user session from memory or database).
**Stateful**
❌ Doesn't scale well for large APIs unless you add external session storage (like Redis).
---
### 🔐 What is JWT Authentication?
JWT Authentication is:
- **Stateless**.
- Based on tokens — not sessions.
- Works well for APIs and SPAs/mobile apps.
#### Flow:
1. **User logs in**, and if credentials are valid...
2. Server **creates a JWT** containing user info (e.g., roles).
3. Server **signs the token** and sends it to the client.
4. Client stores it (e.g., in localStorage or cookies).
5. On every request, the client sends the JWT in the `Authorization` header.
6. Server **verifies the JWT signature** using a secret or key.
7. If valid → allow access (no server-side session needed).
**Stateless**
✅ Scales easily
✅ Good for distributed APIs
---
### 📊 Comparison Table
|Feature|ASP.NET Identity (Cookie)|JWT Authentication (Token-Based)|
|---|---|---|
|**Stateful/Stateless**|Stateful|Stateless|
|**Storage**|Cookie on client, session on server|Token on client only|
|**Default Transport**|Cookie (auto-sent by browser)|Authorization header (manual send)|
|**Built-in Support**|ASP.NET Identity (UI + EF Core)|ASP.NET Core + Manual JWT setup|
|**Scalability**|Limited (server stores session)|High (no session to manage)|
|**Security**|Cookie CSRF risk|XSS risk if stored in JS-accessible storage|
|**Use Case**|Web apps with UI (MVC, Razor)|APIs, SPAs, mobile apps|
|**External login support**|Built-in|Needs integration|
|**Token expiration**|Server-controlled session|Token has expiration embedded|
---
### 🚨 Statelessness — What Does It Mean?
- **Stateful Authentication**: Server **stores a session** (usually in memory or a database) for each user. The client just stores a cookie with a session ID.
- When user logs in, server keeps a record of that.
- Logout → delete session.
- **Stateless Authentication** (JWT):
- Server **does not remember anything**.
- JWT has all info about the user in itself (claims, roles, expiry).
- Logout = just delete token on client side (server has no "memory").
⚠️ **You can't "force logout" someone server-side in pure JWT unless you blacklist tokens manually** (or rotate secrets).
---
### 💡 Can You Use ASP.NET Identity with JWT?
Yes — ASP.NET Identity can be configured to:
- Authenticate user credentials
- Then issue a **JWT** instead of using a cookie
- This way, you get:
- ASP.NET Identitys user management
- JWTs **stateless** API authentication
This is often used in **hybrid apps**:
- Use Identity for registration/login
- Use JWT for frontend and mobile API calls
# What does `null!` mean?
It's a **null-forgiving operator**.
When you write:
```csharp
string? maybeNull = null;
string definitelyNotNull = null!; // ← This tells the compiler: “Trust me, its not null.”
```
You're telling the compiler:
> _“I know this is technically null, but treat it as non-null and dont warn me.”_
---
### 🧩 Why is this needed?
C# 8 introduced **nullable reference types**, where the compiler gives warnings if you assign or use `null` where it's not safe.
For example:
```csharp
string name = null; // Warning: assigning null to non-nullable reference
```
To silence the warning, you can use `null!`:
```csharp
string name = null!; // No warning
```
But this is **your responsibility**: the compiler **assumes you're guaranteeing** that it wont cause a `NullReferenceException`.
---
### ⚠️ When to use `null!`?
Use it **sparingly and carefully**, only when:
- You know an object **will be initialized later**.
- Youre working with **ORMs like EF Core** where properties are set **after construction**.
- You're dealing with **dependency injection** and the value is assigned **automatically later**.
#### Example:
```csharp
public class Account
{
public string PhoneNumber { get; set; } = null!; // EF will populate it
}
```
---
### ✅ Summary
|Expression|Meaning|
|---|---|
|`null!`|"This is null, but treat it as if it's non-null (trust me)."|
|Use case|Avoiding compiler warnings for nullable reference types|
|Risk|Can lead to runtime `NullReferenceException` if used incorrectly|
---
Let me know if you want help turning on/off nullable reference types or dealing with EF Core model warnings.
# What is a JWT Generator?
A **JWT Generator** is a class or service in your backend that:
1. **Generates** a signed JWT (JSON Web Token) when a user logs in.
2. **Encodes** the user's identity, roles, and other claims.
3. **Signs** the token with a secret or private key so it can be validated later.
---
## 🔧 Structure of a JWT
A JWT has 3 parts:
```plaintext
xxxxx.yyyyy.zzzzz
```
1. **Header** (Base64-encoded JSON):
```json
{
"alg": "HS256",
"typ": "JWT"
}
```
2. **Payload** (Base64-encoded JSON):
Contains user data and claims (e.g., user ID, role, expiry).
```json
{
"sub": "userId123",
"phone": "0930xxx",
"role": "Admin",
"exp": 1717502800
}
```
3. **Signature**:
HMACSHA256(header + "." + payload, secret key)
---
## ✅ What Goes into the Payload?
Include things you want to check _without querying the DB every time_:
- `sub` (Subject usually user ID)
- `phone` or username
- `role` (e.g., "Admin", "User")
- `exp` (expiration timestamp)
- Any custom claim, like `companyId`, `verified`, etc.
---
## 🔐 Signature
The **signature ensures** that the token hasn't been tampered with. If the signature doesn't match (due to modification or incorrect secret), the token is invalid.
---
## 🧱 Example: JWT Generator in ASP.NET Core
```csharp
public interface IJwtGenerator
{
string GenerateToken(Account account);
}
```
```csharp
public class JwtGenerator : IJwtGenerator
{
private readonly IConfiguration _config;
public JwtGenerator(IConfiguration config)
{
_config = config;
}
public string GenerateToken(Account account)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
new Claim(JwtRegisteredClaimNames.PhoneNumber, account.PhoneNumber),
new Claim(ClaimTypes.Role, account.Role),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddHours(2),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
```
---
## 🛠 Configuration in `appsettings.json`
```json
"Jwt": {
"Key": "YourSuperSecureSecretKey123!",
"Issuer": "YourAppName",
"Audience": "YourFrontendApp"
}
```
---
## 🧪 Token Validation
When the frontend sends this token on requests (via `Authorization: Bearer <token>` header), ASP.NET Core automatically validates:
- Signature (using the same secret)
- Expiry (`exp`)
- Audience, Issuer
- Claims (like role)
This is done via `JwtBearer` middleware.
---
## 🔐 Roles in the Token?
Yes, you can (and should) include user roles. The backend will **automatically enforce** `[Authorize(Roles = "Admin")]` using that claim.
But:
- Don't rely **only** on frontend logic — always **protect endpoints** on the backend too.
- Frontend can use roles to **hide UI elements**, but not for enforcing access.
---
## 🤔 Should You Encrypt the Token?
No, standard practice is:
- Don't encrypt JWTs — theyre just base64-encoded.
- **Dont put sensitive data** inside.
- Sign them to prevent tampering.
- Secure the token in frontend (e.g., HttpOnly cookies or `localStorage` with care).
---
## ✅ Summary of Responsibilities
|Responsibility|Backend (JWT Generator)|Frontend|
|---|---|---|
|Token generation|✅|❌|
|Storing token|❌|✅ (localStorage / cookie)|
|Sending token|❌|✅ (Authorization header)|
|Token validation|✅ (`JwtBearer`)|❌|
|Role enforcement|✅ (`[Authorize]`)|✅ (UI-level only)|
---
# How multiple roles in JWT claims work
- The JWT claims are basically a list of key-value pairs.
- For roles, the key is usually `"role"` or `ClaimTypes.Role`.
- You add **multiple claims with the same key** — one for each role.
**Example:**
```json
{
"sub": "1234567890",
"phone_number": "123456789",
"role": "Admin",
"role": "Editor",
"role": "User",
"exp": 1711600000
}
```
- When the token is created, it contains multiple `"role"` entries.
- On the backend, ASP.NET Core `ClaimsPrincipal` reads all of them, so `[Authorize(Roles="Admin,Editor")]` works by checking if **any** of the roles match.
---
### How frontend handles multiple roles in the JWT
1. The frontend receives the JWT (usually after login).
2. It **decodes** the JWT payload (using a library like `jwt-decode`).
3. It extracts the roles as an **array of strings**.
Example in React using `jwt-decode`:
```js
import jwtDecode from 'jwt-decode';
const token = localStorage.getItem('token');
const decoded = jwtDecode(token);
const roles = decoded.role; // roles is usually an array if multiple roles exist
console.log(roles); // ["Admin", "Editor", "User"]
```
4. The frontend can then use these roles to:
- Conditionally render UI components or routes.
- Show/hide buttons, pages, or features.
---
### Important security note for frontend roles
- The frontend can **only do UI-level checks** based on roles.
- **Never trust frontend role checks to secure data or APIs.**
- The backend must always verify the token and roles via `[Authorize]` attributes or middleware.
---
### Summary
| Step | How it works |
| --------------------- | ----------------------------------------------------- |
| JWT creation | Multiple `"role"` claims added to token |
| Backend authorization | Validates token and checks if user has required roles |
| Frontend decoding | Extracts `role` claim(s) as array of strings |
| Frontend UI control | Shows or hides content based on roles |