Sync current dev state
This commit is contained in:
421
docs/API_AUTHENTICATION_GUIDE.md
Normal file
421
docs/API_AUTHENTICATION_GUIDE.md
Normal file
@@ -0,0 +1,421 @@
|
||||
# EasyStream API Authentication Guide
|
||||
|
||||
## Overview
|
||||
|
||||
EasyStream now supports modern JWT token-based authentication for API clients, alongside traditional session-based authentication for web pages.
|
||||
|
||||
## Authentication Systems
|
||||
|
||||
### 1. **Session-Based Authentication** (Traditional Web Pages)
|
||||
- Uses PHP sessions with cookies
|
||||
- Managed by `VAuth::login()` and `VAuth::logout()`
|
||||
- Stored in `db_sessions` table
|
||||
- Best for: Traditional server-rendered pages
|
||||
|
||||
### 2. **JWT Token Authentication** (Modern APIs)
|
||||
- Uses Bearer tokens in Authorization header
|
||||
- Managed by `VAuth::loginWithToken()` and `VAuth::validateJWTToken()`
|
||||
- Stateless (no server-side session storage)
|
||||
- Best for: SPAs, mobile apps, API clients
|
||||
|
||||
---
|
||||
|
||||
## Backend API Endpoints
|
||||
|
||||
### Base URL
|
||||
```
|
||||
http://localhost:8083/api
|
||||
```
|
||||
|
||||
### Authentication Endpoints
|
||||
|
||||
#### 1. **Login with Token** (New)
|
||||
Get a JWT token for API authentication.
|
||||
|
||||
**Endpoint:** `POST /auth.php?action=login_token`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"identifier": "username or email",
|
||||
"password": "your_password",
|
||||
"expires_in": 86400 // Optional: token expiry in seconds (default: 24 hours)
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Success):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Login successful",
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 86400,
|
||||
"user": {
|
||||
"user_id": 1,
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"role": "member"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Error):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Invalid credentials"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2. **Verify Token**
|
||||
Validate a JWT token and get user information.
|
||||
|
||||
**Endpoint:** `GET /auth.php?action=verify_token` or `POST /auth.php?action=verify_token`
|
||||
|
||||
**Authorization Header:**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
**Alternative (POST body):**
|
||||
```json
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Success):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"valid": true,
|
||||
"user": {
|
||||
"user_id": 1,
|
||||
"username": "john_doe",
|
||||
"email": "john@example.com",
|
||||
"role": "member"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. **Traditional Login** (Session-based)
|
||||
For web pages that need PHP session authentication.
|
||||
|
||||
**Endpoint:** `POST /auth.php?action=login`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"identifier": "username or email",
|
||||
"password": "your_password",
|
||||
"remember_me": false
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Login successful",
|
||||
"user": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
*Note: This sets a PHP session cookie, not a JWT token.*
|
||||
|
||||
---
|
||||
|
||||
## Frontend API Helper Usage
|
||||
|
||||
### Include the API Helper
|
||||
|
||||
Add to your HTML:
|
||||
```html
|
||||
<script src="/f_scripts/fe/js/api-helper.js"></script>
|
||||
```
|
||||
|
||||
### Initialize API Client
|
||||
|
||||
```javascript
|
||||
// API client is automatically initialized as window.api
|
||||
const api = window.api; // or new EasyStreamAPI()
|
||||
```
|
||||
|
||||
### Authentication Examples
|
||||
|
||||
#### Login and Get Token
|
||||
```javascript
|
||||
try {
|
||||
const result = await api.login('john_doe', 'password123');
|
||||
|
||||
if (result.success) {
|
||||
console.log('Logged in!', result.user);
|
||||
console.log('Token:', result.token);
|
||||
// Token is automatically stored in localStorage
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error.message);
|
||||
}
|
||||
```
|
||||
|
||||
#### Check Authentication Status
|
||||
```javascript
|
||||
if (api.isAuthenticated()) {
|
||||
console.log('User is authenticated');
|
||||
} else {
|
||||
console.log('User is not authenticated');
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Current User Info
|
||||
```javascript
|
||||
try {
|
||||
const userData = await api.getCurrentUser();
|
||||
console.log('Current user:', userData.user);
|
||||
} catch (error) {
|
||||
console.error('Not authenticated:', error);
|
||||
}
|
||||
```
|
||||
|
||||
#### Logout
|
||||
```javascript
|
||||
try {
|
||||
await api.logout();
|
||||
console.log('Logged out successfully');
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Making Authenticated API Requests
|
||||
|
||||
The API helper automatically includes the Bearer token in all requests.
|
||||
|
||||
#### GET Request
|
||||
```javascript
|
||||
try {
|
||||
const videos = await api.get('/videos.php', {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
sort: 'newest'
|
||||
});
|
||||
|
||||
console.log('Videos:', videos);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
```
|
||||
|
||||
#### POST Request
|
||||
```javascript
|
||||
try {
|
||||
const newVideo = await api.post('/videos.php', {
|
||||
title: 'My Awesome Video',
|
||||
description: 'Check this out!',
|
||||
privacy: 'public'
|
||||
});
|
||||
|
||||
console.log('Video created:', newVideo);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
```
|
||||
|
||||
#### PUT Request
|
||||
```javascript
|
||||
try {
|
||||
const updated = await api.put('/videos.php?id=123', {
|
||||
title: 'Updated Title'
|
||||
});
|
||||
|
||||
console.log('Video updated:', updated);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
```
|
||||
|
||||
#### DELETE Request
|
||||
```javascript
|
||||
try {
|
||||
const result = await api.delete('/videos.php?id=123');
|
||||
console.log('Video deleted:', result);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manual Fetch Example (Without Helper)
|
||||
|
||||
If you prefer to use fetch() directly:
|
||||
|
||||
```javascript
|
||||
// Login and get token
|
||||
const loginResponse = await fetch('/api/auth.php?action=login_token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identifier: 'john_doe',
|
||||
password: 'password123'
|
||||
})
|
||||
});
|
||||
|
||||
const loginData = await loginResponse.json();
|
||||
const token = loginData.token;
|
||||
|
||||
// Store token
|
||||
localStorage.setItem('jwt_token', token);
|
||||
|
||||
// Make authenticated request
|
||||
const response = await fetch('/api/videos.php', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const videos = await response.json();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## cURL Examples
|
||||
|
||||
### Login with Token
|
||||
```bash
|
||||
curl -X POST http://localhost:8083/api/auth.php?action=login_token \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"identifier": "john_doe",
|
||||
"password": "password123"
|
||||
}'
|
||||
```
|
||||
|
||||
### Verify Token
|
||||
```bash
|
||||
curl -X GET http://localhost:8083/api/auth.php?action=verify_token \
|
||||
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE"
|
||||
```
|
||||
|
||||
### Authenticated API Request
|
||||
```bash
|
||||
curl -X GET http://localhost:8083/api/videos.php?page=1&limit=10 \
|
||||
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Token Management
|
||||
|
||||
### Token Storage
|
||||
- Frontend: Stored in `localStorage` with expiry time
|
||||
- Backend: JWT is stateless (no server-side storage)
|
||||
|
||||
### Token Expiry
|
||||
- Default: 24 hours (86400 seconds)
|
||||
- Configurable via `JWT_EXPIRY` in `.env`
|
||||
- Client automatically clears expired tokens
|
||||
|
||||
### Token Security
|
||||
- Secret: Stored in `JWT_SECRET` environment variable
|
||||
- Algorithm: HS256 (HMAC-SHA256)
|
||||
- Validation: Signature verified on every request
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### For Existing AJAX Code
|
||||
|
||||
**Before (jQuery with sessions):**
|
||||
```javascript
|
||||
$.post('/some_endpoint.php', { action: 'do_something' }, function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
**After (Fetch with JWT):**
|
||||
```javascript
|
||||
api.post('/some_endpoint.php', { action: 'do_something' })
|
||||
.then(data => console.log(data))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Update your `.env` file:
|
||||
|
||||
```env
|
||||
# JWT Configuration
|
||||
JWT_SECRET=9a652ee880c41bafb0a81d38d54b029d63903eeaafccaa8c12880a913931f63b
|
||||
JWT_EXPIRY=86400
|
||||
|
||||
# CORS Configuration (for separate frontends)
|
||||
CORS_ORIGIN=http://localhost:3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Errors
|
||||
|
||||
#### 401 Unauthorized
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Invalid or expired token"
|
||||
}
|
||||
```
|
||||
**Solution:** Re-login to get a new token.
|
||||
|
||||
#### 403 Forbidden
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Access denied"
|
||||
}
|
||||
```
|
||||
**Solution:** User doesn't have permission for this resource.
|
||||
|
||||
#### 429 Rate Limited
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Too many requests"
|
||||
}
|
||||
```
|
||||
**Solution:** Wait before retrying.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use HTTPS in production** - JWT tokens should be transmitted over secure connections
|
||||
2. **Store tokens securely** - Use localStorage for web, secure storage for mobile
|
||||
3. **Handle token expiry** - Implement token refresh or re-login flow
|
||||
4. **Validate on every request** - Backend validates tokens on all protected endpoints
|
||||
5. **Clear tokens on logout** - Remove tokens from storage when user logs out
|
||||
6. **Use CORS properly** - Configure `Access-Control-Allow-Origin` for your frontend domain
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions, check:
|
||||
- EasyStream documentation: `/docs/`
|
||||
- API logs: `f_data/logs/`
|
||||
- Error handling: `VLogger` and `VErrorHandler` classes
|
||||
Reference in New Issue
Block a user