Note: all original wording is preserved exactly as written. The original ASCII-art diagrams have been recreated below as proper Mermaid diagrams.
Imagine we have:
Architecture:
API needs to know who is calling it. That is authentication.
Authentication → Who are you? Authorization → What can you do?
Authentication is identity; authorization is permission.
Do not send username/password on every API request. User proves identity during login, then gets a token.
Analogy:
A token is proof that the user/application has been authenticated and/or has certain permissions.
JWT = JSON Web Token. It is a token format.
Example:
eyJhbGciOiJIUzI1NiIs...
eyJzdWIiOiIxMjM0NTYi...
SflKxwRJSMeKKF2QT4fwp...
Conceptually a JWT may contain:
{
"userId": "123",
"name": "John",
"role": "User",
"exp": 1791195300
}
JWT is signed, allowing the API to verify it was issued by a trusted signer and not modified.
Important: JWT is not the same thing as OAuth.
Conceptual login:
POST /login
Content-Type: application/json
{
"email": "john@gmail.com",
"password": "password123"
}
After authentication, client can receive:
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "abc123..."
}
In real OAuth/OIDC SPA architecture, login is typically done through the Identity Provider rather than Angular directly handling the user password.
GET /api/orders
Authorization: Bearer eyJhbGciOi...
"Bearer" basically means "I am presenting this token as my credential."
ASP.NET Core:
app.UseAuthentication();
app.UseAuthorization();
401 Unauthorized403 ForbiddenMemory: 401 = Who are you? I don't know you. 403 = I know you, but you can't do this.
Access token:
Authorization: Bearer <token>Refresh token:
Memory:
If access token lasts 30 days and is stolen, attacker may use it for a long time. Short-lived token (e.g. 15 minutes) reduces exposure. But user should not have to log in every 15 minutes. Refresh token solves this.
Usually authorization server / Identity Provider creates and issues it as part of the appropriate OAuth/OIDC flow.
Examples:
Very important:
/api/orders.Correct:
The client/application (Angular or a BFF/server depending on architecture) requests refresh; the authorization server actually validates the refresh credential and issues the new access token.
Example:
10:00 AM User logs in Access Token expires 10:15 Refresh Token has longer lifetime
10:15 Access Token expires Now client needs new access token.
Two common patterns:
expJWT contains:
{
"sub": "123",
"name": "John",
"role": "User",
"exp": 1791195300
}
Angular can decode JWT payload and inspect exp.
Concept:
Angular can refresh before making the API call or shortly before expiration.
Angular sends token:
Angular HTTP interceptor sees 401 and triggers refresh.
Important nuance:
exp.User does not need to type password again.
Wording is confusing:
Mental model:
May check:
If invalid, refresh fails and user may need login again.
Some providers use rotation:
Helps detect suspicious reuse and limits useful lifetime of stolen refresh credential.
Do not memorize "refresh token always goes in localStorage."
Common designs:
HttpOnly means JavaScript cannot directly read cookie via document.cookie.
Correct storage depends on architecture/threat model.
Instead of adding token manually to every API request, interceptor can attach it:
Conceptual code:
intercept(req, next) {
const token = this.authService.getAccessToken();
const request = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(request);
}
Interceptor can also handle 401, trigger refresh, retry original request.
Production implementation should coordinate concurrent 401s so 10 requests do not all trigger refresh separately.
Two patterns:
Option A: ASP.NET Core creates JWT:
Possible for learning/simple architectures; production requires careful password handling, signing key management, validation, refresh handling, account lifecycle/security.
Option B: Dedicated Identity Provider:
OAuth 2.0 is an authorization framework, not "JWT." It defines ways for client applications to obtain access tokens to access protected resources. Access token may be JWT but OAuth does not require JWT.
OAuth vocabulary:
orders.read, orders.writeFor modern browser applications such as Angular:
The browser does not simply send password to Angular code and let Angular decide identity. Identity Provider handles authentication and issues credentials.
Pronounced "pixy."
Simplified:
Before login:
After login:
Analogy:
Scopes describe access:
scope = orders.read
scope = orders.read orders.write
Think of scopes as permissions printed on pass:
OIDC = identity layer built on OAuth 2.0.
OAuth:
"What can this application access?"
OIDC:
"Who is the user who authenticated?"
OIDC standardizes identity info and adds ID token.
ID Token:
Access Token:
Mental model:
Do not use ID token as API access token just because both may look like JWT.
ID token helps client understand authenticated identity; access token is used to call protected API.
Analogy:
API validates:
iss)aud)exp)Security guard analogy:
Conceptual config:
builder.Services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://your-identity-provider";
options.Audience = "orders-api";
});
app.UseAuthentication();
app.UseAuthorization();
Exact config depends on IdP.
JWT has:
If someone changes:
{"role":"User"}
to:
{"role":"Admin"}
the signature no longer matches and token is invalid.
The API must validate signature properly.
JWT contains exp; API can compare current time:
Current: 10:16
exp: 10:15
10:16 > 10:15 → expired
No DB query is needed just to determine whether exp has passed. This is one reason JWTs can be useful in distributed APIs.
When access token expires: