304 lines
6.3 KiB
Markdown
304 lines
6.3 KiB
Markdown
|
||
|
||
|
||
Great — now we’re connecting both ends of your application. Let’s walk through **how authentication and authorization flow works on the frontend side (React)** when using **JWT-based authentication with a backend API**.
|
||
|
||
---
|
||
|
||
## 🔁 Overall Frontend Authentication Flow (React + JWT)
|
||
|
||
1. **User logs in**:
|
||
|
||
- They submit credentials (username/password) via a login form.
|
||
|
||
- Frontend sends a POST request to `/api/auth/login`.
|
||
|
||
- Backend validates and responds with a **JWT** (and optionally a **refresh token**).
|
||
|
||
2. **Frontend stores the token**:
|
||
|
||
- This is usually saved in:
|
||
|
||
- `localStorage` ✅ simple, persistent
|
||
|
||
- or an **HttpOnly cookie** ✅ safer, but needs server support
|
||
|
||
3. **Frontend sends the token on future API calls**:
|
||
|
||
- Automatically attaches the token as a header:
|
||
|
||
```js
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
4. **Frontend restricts access to protected pages**:
|
||
|
||
- You read the token from storage.
|
||
|
||
- Decode it to extract role/claims.
|
||
|
||
- Use React Router + `PrivateRoute` (or similar) to guard access.
|
||
|
||
|
||
---
|
||
|
||
## 🧱 How to Restrict Pages in React (Role-Based Routing)
|
||
|
||
### ✅ Step 1: Decode and Check the Token
|
||
|
||
```bash
|
||
npm install jwt-decode
|
||
```
|
||
|
||
```js
|
||
import jwtDecode from "jwt-decode";
|
||
|
||
function getUserFromToken() {
|
||
const token = localStorage.getItem("token");
|
||
if (!token) return null;
|
||
|
||
try {
|
||
const decoded = jwtDecode(token);
|
||
return decoded; // contains roles, exp, username, etc.
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### ✅ Step 2: Create a Protected Route Component
|
||
|
||
```jsx
|
||
import { Navigate } from "react-router-dom";
|
||
|
||
function PrivateRoute({ children, requiredRole }) {
|
||
const user = getUserFromToken();
|
||
|
||
if (!user) return <Navigate to="/login" />;
|
||
if (requiredRole && !user.role?.includes(requiredRole)) {
|
||
return <Navigate to="/unauthorized" />;
|
||
}
|
||
|
||
return children;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### ✅ Step 3: Use It in Routing
|
||
|
||
```jsx
|
||
<Route
|
||
path="/admin-dashboard"
|
||
element={
|
||
<PrivateRoute requiredRole="Admin">
|
||
<AdminDashboard />
|
||
</PrivateRoute>
|
||
}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## 🛡️ Where to Store the JWT?
|
||
|
||
|Method|Persistent?|Secure?|Notes|
|
||
|---|---|---|---|
|
||
|`localStorage`|✅ Yes|❌ No (accessible to JS)|Good for simple apps|
|
||
|`sessionStorage`|❌ No (clears on tab close)|❌ No||
|
||
|`HttpOnly cookie`|✅ Yes|✅ Yes|Needs backend to set cookie|
|
||
|
||
> 🔐 **If your app is vulnerable to XSS, use `HttpOnly` cookies instead of localStorage**.
|
||
|
||
---
|
||
|
||
## 🧷 Automatically Adding JWT to Requests
|
||
|
||
You can use **Axios interceptors**:
|
||
|
||
```js
|
||
// axios.js
|
||
import axios from "axios";
|
||
|
||
const api = axios.create({
|
||
baseURL: "http://your-api-url.com/api",
|
||
});
|
||
|
||
api.interceptors.request.use(config => {
|
||
const token = localStorage.getItem("token");
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`;
|
||
}
|
||
return config;
|
||
});
|
||
|
||
export default api;
|
||
```
|
||
|
||
Then use `api.get(...)` or `api.post(...)` in your services.
|
||
|
||
---
|
||
|
||
## 🧠 Summary
|
||
|
||
|Task|Solution|
|
||
|---|---|
|
||
|Protect route to logged-in users|Use custom `<PrivateRoute />` with token check|
|
||
|Check if user has a role|Decode token and inspect `role` claim|
|
||
|Send token with API requests|Use Axios interceptors|
|
||
|Save token|LocalStorage (easy) or HttpOnly cookie (secure)|
|
||
|Keep user logged in|Token stays in localStorage or cookie|
|
||
|Handle logout|Clear the token from storage|
|
||
|
||
---
|
||
|
||
Let me know if you want:
|
||
|
||
- A sample React project with protected routes and JWT integration
|
||
|
||
- How to implement auto-logout when token expires
|
||
|
||
- How to use **refresh tokens**
|
||
|
||
|
||
Happy to walk you through any of it!
|
||
|
||
That flow is **excellent** and matches exactly how modern applications with **JWT-based authentication** typically work. You're thinking about both UX (popup forms) and proper backend coordination — that's a solid start.
|
||
|
||
---
|
||
|
||
## ✅ Your Flow — Reviewed & Annotated:
|
||
|
||
### **1. Account Sign-Up (Register)**
|
||
|
||
> **Frontend (React)**: Sign-up modal with:
|
||
|
||
- Phone number
|
||
|
||
- Password (+ confirm password)
|
||
|
||
- Basic validations (empty fields, password length, etc.)
|
||
|
||
|
||
> **Backend (ASP.NET Core API)**:
|
||
|
||
- Validate: Check if phone already exists
|
||
|
||
- Validate password strength (you can set rules)
|
||
|
||
- Hash the password securely (use `PasswordHasher<T>` or Identity's helper)
|
||
|
||
- Store the user in DB (with phone number as username or unique field)
|
||
|
||
- Return success/failure
|
||
|
||
|
||
✅ **Note**: Use `[ApiController]`, `[FromBody]`, and model validation on the backend for clean error reporting.
|
||
|
||
---
|
||
|
||
### **2. Login Page (Popup)**
|
||
|
||
> **Frontend (React)**:
|
||
|
||
- Login modal with:
|
||
|
||
- Phone number
|
||
|
||
- Password
|
||
|
||
|
||
> **Backend**:
|
||
|
||
- Validate credentials
|
||
|
||
- If valid, generate:
|
||
|
||
- **JWT** (access token) — includes user ID and roles
|
||
|
||
- (Optional) Refresh token
|
||
|
||
- Return the JWT token to the frontend
|
||
|
||
|
||
✅ **Token Payload Example**:
|
||
|
||
```json
|
||
{
|
||
"sub": "user_id_here",
|
||
"phone": "09xxxxxxxxx",
|
||
"role": "Customer",
|
||
"exp": 1234567890
|
||
}
|
||
```
|
||
|
||
✅ You’ll send this token to the frontend in the response.
|
||
|
||
---
|
||
|
||
### **3. Store the Token + Access Protected Page**
|
||
|
||
> **Frontend**:
|
||
|
||
- Store the token (in `localStorage` for now)
|
||
|
||
- Use Axios to attach it on every request
|
||
|
||
- Use a `PrivateRoute` component to guard protected pages
|
||
|
||
- Access `/profile` page (protected by `[Authorize]` in backend)
|
||
|
||
|
||
> **Backend**:
|
||
|
||
- Secure `/profile` with `[Authorize(Roles = "Customer")]` or similar
|
||
|
||
- Parse token from `Authorization: Bearer <token>` header
|
||
|
||
- Allow or reject based on claims
|
||
|
||
|
||
---
|
||
|
||
## 🛠️ What You'll Need
|
||
|
||
### Frontend:
|
||
|
||
- `react-router-dom`
|
||
|
||
- `axios`
|
||
|
||
- `jwt-decode`
|
||
|
||
- Modals (e.g., using Tailwind or a component lib)
|
||
|
||
|
||
### Backend:
|
||
|
||
- ASP.NET Core API (you already have it)
|
||
|
||
- JWT configuration in `Program.cs`
|
||
|
||
- Custom authentication controller (`/api/auth/register`, `/api/auth/login`)
|
||
|
||
- Role support
|
||
|
||
- Token generation utility
|
||
|
||
|
||
---
|
||
|
||
## 🚀 Ready to Begin?
|
||
|
||
Let's start with **Step 1**: Sign-up flow
|
||
|
||
Would you like to begin with:
|
||
|
||
1. **Backend first** — setting up `/api/auth/register` endpoint and phone+password validation
|
||
|
||
2. **Frontend first** — creating the sign-up modal form in React
|
||
|
||
|
||
Let me know your preference and we’ll go step by step. |