Aller au contenu principal

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

  1. Look up user in database
  2. Verify password
  3. If correct → generate a JWT
  4. 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:

  1. Extract token from header
  2. Decode header + payload
  3. Recalculate signature using secret
  4. Compare its signature with your secret
  5. Check expiration exp
  6. If valid → user is authenticated
  7. Spring populates SecurityContext with the user’s details
  8. 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

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 Authorization header
  • 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.