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 viaAuthorization
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:
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 user’s 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:
{
"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 hasn’t 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 user’s browser (with a valid session/cookie) into making an unwanted request to your site without the user’s 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 (doesn’t 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)
-
Login request: The user submits a form with username/password.
-
Server validates the credentials using ASP.NET Identity.
-
If valid, the server issues a cookie (with a session token).
-
The browser stores this authentication cookie.
-
For future requests, the browser automatically sends the cookie.
-
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:
-
User logs in, and if credentials are valid…
-
Server creates a JWT containing user info (e.g., roles).
-
Server signs the token and sends it to the client.
-
Client stores it (e.g., in localStorage or cookies).
-
On every request, the client sends the JWT in the
Authorization
header. -
Server verifies the JWT signature using a secret or key.
-
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 Identity’s user management
-
JWT’s 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:
string? maybeNull = null;
string definitelyNotNull = null!; // ← This tells the compiler: “Trust me, it’s not null.”
You’re telling the compiler:
“I know this is technically null, but treat it as non-null and don’t 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:
string name = null; // Warning: assigning null to non-nullable reference
To silence the warning, you can use null!
:
string name = null!; // No warning
But this is your responsibility: the compiler assumes you’re guaranteeing that it won’t cause a NullReferenceException
.
⚠️ When to use null!
?
Use it sparingly and carefully, only when:
-
You know an object will be initialized later.
-
You’re 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:
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:
-
Generates a signed JWT (JSON Web Token) when a user logs in.
-
Encodes the user’s identity, roles, and other claims.
-
Signs the token with a secret or private key so it can be validated later.
🔧 Structure of a JWT
A JWT has 3 parts:
xxxxx.yyyyy.zzzzz
-
Header (Base64-encoded JSON):
{ "alg": "HS256", "typ": "JWT" }
-
Payload (Base64-encoded JSON):
Contains user data and claims (e.g., user ID, role, expiry).{ "sub": "userId123", "phone": "0930xxx", "role": "Admin", "exp": 1717502800 }
-
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
public interface IJwtGenerator
{
string GenerateToken(Account account);
}
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
"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 — they’re just base64-encoded.
-
Don’t 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"
orClaimTypes.Role
. -
You add multiple claims with the same key — one for each role.
Example:
{
"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
-
The frontend receives the JWT (usually after login).
-
It decodes the JWT payload (using a library like
jwt-decode
). -
It extracts the roles as an array of strings.
Example in React using jwt-decode
:
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"]
-
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 |