Full, Detailed Explanation of JWT Authentication, User Login Flow, and File Upload Exposure
A complete guide for understanding how your backend + frontend work together.
1. What is JWT?
A JWT (JSON Web Token) is a compact, digitally signed string used to authenticate users.
A real JWT looks like:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiamF5In0
.
OoQHzkzRr5ZLfDTpljLE4-yIRvNZ0iJYqvTtlzsmQoo
It has 3 parts:
1. Header (metadata)
Contains algorithm + token type.
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload (claims)
Contains user data.
⚠️ Not encrypted — only encoded (base64).
Anyone can read payload if they decode the JWT.
Example:
{
"userId": 42,
"username": "jay",
"roles": ["USER"],
"exp": 1735689600
}
3. Signature
This is the important part.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
SECRET_KEY
)
The backend uses your application.yml secret:
jwt:
secret: myverylongsecretstring
The signature ensures:
- the token was created by your backend
- the token was not modified
- the secret never leaves the backend
2. JWT Login Process (Step-by-Step)
Step 1 — User sends login request
Frontend sends POST:
POST /api/auth/login
{
"username": "jay",
"password": "******"
}
Step 2 — Backend verifies credentials
- Look up user in database
- Verify password
- If correct → generate a JWT
- Return JWT to the frontend:
{
"token": "<the_jwt_string_here>",
"user": {
"id": 42,
"username": "jay"
}
}
3. Frontend stores the token
Your React code does this:
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
Now the user stays logged in until the token expires or is removed.
4. Every API call uses the JWT
Your axios interceptor automatically adds it:
Authorization: Bearer <token>
Example full request:
GET /api/user/profile
Authorization: Bearer eyJhbGciOi...
5. Backend receives request and validates token
What Spring Security does:
Step-by-step:
- Extract token from header
- Decode header + payload
- Recalculate signature using secret
- Compare its signature with your secret
- Check expiration
exp - If valid → user is authenticated
- Spring populates
SecurityContextwith the user’s details - Controller is allowed to execute
If invalid/expired → return:
401 Unauthorized
Your frontend then:
localStorage.removeItem('token');
window.location.href = "/login";
6. Login vs Session?
JWTs are stateless.
Backend does not store sessions.
Authentication relies entirely on:
- presence of a valid token
- signature validation
- expiration check
7. Uploading Files Securely with JWT
✔️ Recommended Flow
1. User logs in (gets token)
2. User uploads file from React
React:
const formData = new FormData();
formData.append("file", selectedFile);
api.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
});
Axios automatically adds the Bearer <token> header.
8. Backend receives the upload with authentication
Spring Boot endpoint:
@PostMapping("/upload")
public ResponseEntity<?> upload(
@RequestParam("file") MultipartFile file,
Authentication auth
) {
String username = auth.getName(); // Extracted from JWT
...
}
What happens:
- The interceptor reads the
Authorizationheader - Token is validated
- Spring injects the authenticated user into
auth
This ensures:
❌ Unauthenticated users cannot upload
✔️ Only logged-in users can upload
9. Where to store uploaded files?
Three common approaches:
Option A — Store files on disk (simple)
Example directory:
/uploads/{userId}/{fileName}
Pros:
- Fast
- Easy
- Good for small apps
Cons:
- Needs server storage
Option B — Store in Cloud (best for real apps)
Example: AWS S3
You upload using AWS SDK and store metadata in database.
Pros:
- Scalable
- High availability
- Cheap
Cons:
- Requires account + setup
Option C — Store in database as BLOB (not great)
Pros:
- Simple
Cons:
- Bad performance
- DB grows fast
10. Downloading or Showing Files to Users
When user requests a file:
GET /api/files/mycv.docx
Authorization: Bearer <token>
Backend checks:
- Does the JWT authenticate the user?
- Is this file owned by the authenticated user?
If yes → return the file stream.
11. Securing File Exposure
✔️ Always check the JWT
Never allow public file access.
✔️ Never expose your file directory
No /uploads/*.docx public folder!
Always serve files through the backend, which checks permissions.
12. Summary: Full Flow Diagram
[React login form]
│
▼
POST /auth/login
│
▼
[Spring validates → generates JWT]
│
▼
React stores JWT in localStorage
│
▼
User uploads file → React sends:
Authorization: Bearer <token>
│
▼
Spring validates token
│
▼
Spring saves file with user ID
│
▼
User requests file
Authorization: Bearer <token>
│
▼
Spring verifies + returns file
Final Advice
- The JWT secret must always stay in the backend only.
- Frontend stores only the JWT token, never the secret.
- All protected endpoints must require the JWT.
- Files should be served only after validating JWT.
- User-specific permissions must be enforced on upload/download.