168 lines
4.5 KiB
Markdown
168 lines
4.5 KiB
Markdown
# Authentication Setup - Token Persistence
|
|
|
|
## ✅ Fixed: Token persists across page refreshes
|
|
|
|
### The Problem
|
|
After login, refreshing the page redirected to `/login` because the auth state wasn't being restored from the saved token.
|
|
|
|
### The Solution
|
|
**No plugin needed!** The token is already saved in localStorage. I updated the router guard to check for the token on page load.
|
|
|
|
---
|
|
|
|
## How it works
|
|
|
|
### 1. **Login Flow**
|
|
```
|
|
User submits login form
|
|
↓
|
|
POST /api/auth/login
|
|
↓
|
|
Backend returns: { success: true, data: { user, token } }
|
|
↓
|
|
Token saved to localStorage.setItem("auth_token", token)
|
|
↓
|
|
User saved to Pinia store
|
|
↓
|
|
Redirect to dashboard
|
|
```
|
|
|
|
### 2. **Page Refresh Flow**
|
|
```
|
|
User refreshes page
|
|
↓
|
|
Router guard runs (beforeEach)
|
|
↓
|
|
Check: auth.checked? No
|
|
↓
|
|
Check: localStorage.getItem("auth_token")? Yes
|
|
↓
|
|
Set auth.token = token (from localStorage)
|
|
↓
|
|
Fetch user: GET /api/auth/user (with Bearer token)
|
|
↓
|
|
Backend returns user data
|
|
↓
|
|
auth.user = userData
|
|
auth.checked = true
|
|
↓
|
|
User stays on protected route
|
|
```
|
|
|
|
### 3. **Logout Flow**
|
|
```
|
|
User clicks logout
|
|
↓
|
|
POST /api/auth/logout
|
|
↓
|
|
localStorage.removeItem("auth_token")
|
|
↓
|
|
auth.user = null
|
|
auth.token = null
|
|
↓
|
|
Redirect to /login
|
|
```
|
|
|
|
---
|
|
|
|
## Files Modified
|
|
|
|
### `src/services/auth.ts`
|
|
- `login()` saves token to localStorage
|
|
- `logout()` removes token from localStorage
|
|
- `me()` fetches user from `/api/auth/user`
|
|
- `getToken()` retrieves token from localStorage
|
|
|
|
### `src/services/http.ts`
|
|
- Automatically adds `Authorization: Bearer {token}` header to all requests
|
|
- Reads token from localStorage on every request
|
|
|
|
### `src/stores/auth.ts`
|
|
- Stores both `user` and `token` in state
|
|
- `isAuthenticated` checks for both user AND token
|
|
- `login()` extracts user and token from API response
|
|
|
|
### `src/router/index.js`
|
|
- On every navigation, checks if token exists in localStorage
|
|
- If token exists and auth not checked yet:
|
|
1. Sets `auth.token = token`
|
|
2. Fetches user data with `auth.fetchMe()`
|
|
3. If successful, user stays authenticated
|
|
4. If fails (invalid token), clears token and redirects to login
|
|
|
|
### `src/views/pages/Login.vue`
|
|
- Form submits to `authStore.login()`
|
|
- Shows loading state and error messages
|
|
- Redirects to dashboard on success
|
|
|
|
---
|
|
|
|
## API Endpoints Used
|
|
|
|
| Endpoint | Method | Purpose | Auth Required |
|
|
|----------|--------|---------|---------------|
|
|
| `/api/auth/login` | POST | Login with email/password | No |
|
|
| `/api/auth/user` | GET | Get current user | Yes (Bearer token) |
|
|
| `/api/auth/logout` | POST | Logout | Yes (Bearer token) |
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Test 1: Fresh Login
|
|
1. Go to `/login`
|
|
2. Enter credentials: `admin@admin.com`
|
|
3. Click "Se connecter"
|
|
4. Should redirect to `/dashboards/dashboard-default`
|
|
5. Check localStorage: `auth_token` should be present
|
|
|
|
### Test 2: Page Refresh
|
|
1. After login, refresh the page
|
|
2. Should stay on dashboard (not redirect to login)
|
|
3. Check console: should see "Fetching user from /api/auth/user"
|
|
|
|
### Test 3: Invalid Token
|
|
1. Manually edit token in localStorage to garbage value
|
|
2. Refresh page
|
|
3. Should clear token and redirect to login
|
|
4. Check console: "Invalid token, clearing auth"
|
|
|
|
### Test 4: Logout
|
|
1. After login, click logout
|
|
2. Should redirect to `/login`
|
|
3. Check localStorage: `auth_token` should be removed
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### "Still redirects to /login after refresh"
|
|
- Open browser DevTools → Network tab
|
|
- Refresh page
|
|
- Check if `GET /api/auth/user` is called with `Authorization: Bearer ...` header
|
|
- Check response:
|
|
- **200 OK**: Token is valid, check if user data is correct format
|
|
- **401 Unauthorized**: Token is invalid or backend isn't verifying correctly
|
|
|
|
### "Token is saved but still logged out"
|
|
- Check `auth.isAuthenticated` in Vue DevTools
|
|
- Verify both `auth.user` and `auth.token` are set
|
|
- If only token is set, check if `/api/auth/user` endpoint returns user data
|
|
|
|
### "Backend doesn't accept token"
|
|
- Verify backend expects: `Authorization: Bearer {token}`
|
|
- Check if backend route has auth middleware
|
|
- Test token manually: `curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8000/api/auth/user`
|
|
|
|
---
|
|
|
|
## No Plugin Required!
|
|
|
|
You mentioned Pinia Persisted or Vuex Persisted - you don't need them because:
|
|
1. Token is already saved in `localStorage` directly
|
|
2. Router guard reads from `localStorage` on app load
|
|
3. Token is automatically added to all API requests
|
|
4. This is simpler and more explicit than a plugin
|
|
|
|
The only state that needs to persist is the token (string), and we're handling that manually.
|