# Authentication Guide **Version**: 1.0.0 **Last Updated**: 2025-11-16 Complete guide for authenticating with the IGNY8 API v1.0. --- ## Overview The IGNY8 API uses **JWT (JSON Web Token) Bearer Token** authentication. All endpoints require authentication except: - `POST /api/v1/auth/login/` - User login - `POST /api/v1/auth/register/` - User registration --- ## Authentication Flow ### 1. Register or Login **Register** (if new user): ```http POST /api/v1/auth/register/ Content-Type: application/json { "email": "user@example.com", "username": "user", "password": "secure_password123", "first_name": "John", "last_name": "Doe" } ``` **Login** (existing user): ```http POST /api/v1/auth/login/ Content-Type: application/json { "email": "user@example.com", "password": "secure_password123" } ``` ### 2. Receive Tokens **Response**: ```json { "success": true, "data": { "user": { "id": 1, "email": "user@example.com", "username": "user", "role": "owner", "account": { "id": 1, "name": "My Account", "slug": "my-account" } }, "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE3MDAxMjM0NTZ9...", "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE3MDAxODk0NTZ9..." }, "request_id": "550e8400-e29b-41d4-a716-446655440000" } ``` ### 3. Use Access Token Include the `access` token in all subsequent requests: ```http GET /api/v1/planner/keywords/ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json ``` ### 4. Refresh Token (when expired) When the access token expires (15 minutes), use the refresh token: ```http POST /api/v1/auth/refresh/ Content-Type: application/json { "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Response**: ```json { "success": true, "data": { "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "request_id": "550e8400-e29b-41d4-a716-446655440000" } ``` --- ## Token Expiration - **Access Token**: 15 minutes - **Refresh Token**: 7 days ### Handling Token Expiration **Option 1: Automatic Refresh** ```python def get_access_token(): # Check if token is expired if is_token_expired(current_token): # Refresh token response = requests.post( f"{BASE_URL}/auth/refresh/", json={"refresh": refresh_token} ) data = response.json() if data['success']: return data['data']['access'] return current_token ``` **Option 2: Re-login** ```python def login(): response = requests.post( f"{BASE_URL}/auth/login/", json={"email": email, "password": password} ) data = response.json() if data['success']: return data['data']['access'] ``` --- ## Code Examples ### Python ```python import requests import time from datetime import datetime, timedelta class Igny8API: def __init__(self, base_url="https://api.igny8.com/api/v1"): self.base_url = base_url self.access_token = None self.refresh_token = None self.token_expires_at = None def login(self, email, password): """Login and store tokens""" response = requests.post( f"{self.base_url}/auth/login/", json={"email": email, "password": password} ) data = response.json() if data['success']: self.access_token = data['data']['access'] self.refresh_token = data['data']['refresh'] # Token expires in 15 minutes self.token_expires_at = datetime.now() + timedelta(minutes=14) return True else: print(f"Login failed: {data['error']}") return False def refresh_access_token(self): """Refresh access token using refresh token""" if not self.refresh_token: return False response = requests.post( f"{self.base_url}/auth/refresh/", json={"refresh": self.refresh_token} ) data = response.json() if data['success']: self.access_token = data['data']['access'] self.refresh_token = data['data']['refresh'] self.token_expires_at = datetime.now() + timedelta(minutes=14) return True else: print(f"Token refresh failed: {data['error']}") return False def get_headers(self): """Get headers with valid access token""" # Check if token is expired or about to expire if not self.token_expires_at or datetime.now() >= self.token_expires_at: if not self.refresh_access_token(): raise Exception("Token expired and refresh failed") return { 'Authorization': f'Bearer {self.access_token}', 'Content-Type': 'application/json' } def get(self, endpoint): """Make authenticated GET request""" response = requests.get( f"{self.base_url}{endpoint}", headers=self.get_headers() ) return response.json() def post(self, endpoint, data): """Make authenticated POST request""" response = requests.post( f"{self.base_url}{endpoint}", headers=self.get_headers(), json=data ) return response.json() # Usage api = Igny8API() api.login("user@example.com", "password") # Make authenticated requests keywords = api.get("/planner/keywords/") ``` ### JavaScript ```javascript class Igny8API { constructor(baseUrl = 'https://api.igny8.com/api/v1') { this.baseUrl = baseUrl; this.accessToken = null; this.refreshToken = null; this.tokenExpiresAt = null; } async login(email, password) { const response = await fetch(`${this.baseUrl}/auth/login/`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await response.json(); if (data.success) { this.accessToken = data.data.access; this.refreshToken = data.data.refresh; // Token expires in 15 minutes this.tokenExpiresAt = new Date(Date.now() + 14 * 60 * 1000); return true; } else { console.error('Login failed:', data.error); return false; } } async refreshAccessToken() { if (!this.refreshToken) { return false; } const response = await fetch(`${this.baseUrl}/auth/refresh/`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh: this.refreshToken }) }); const data = await response.json(); if (data.success) { this.accessToken = data.data.access; this.refreshToken = data.data.refresh; this.tokenExpiresAt = new Date(Date.now() + 14 * 60 * 1000); return true; } else { console.error('Token refresh failed:', data.error); return false; } } async getHeaders() { // Check if token is expired or about to expire if (!this.tokenExpiresAt || new Date() >= this.tokenExpiresAt) { if (!await this.refreshAccessToken()) { throw new Error('Token expired and refresh failed'); } } return { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' }; } async get(endpoint) { const response = await fetch( `${this.baseUrl}${endpoint}`, { headers: await this.getHeaders() } ); return await response.json(); } async post(endpoint, data) { const response = await fetch( `${this.baseUrl}${endpoint}`, { method: 'POST', headers: await this.getHeaders(), body: JSON.stringify(data) } ); return await response.json(); } } // Usage const api = new Igny8API(); await api.login('user@example.com', 'password'); // Make authenticated requests const keywords = await api.get('/planner/keywords/'); ``` --- ## Security Best Practices ### 1. Store Tokens Securely **❌ Don't:** - Store tokens in localStorage (XSS risk) - Commit tokens to version control - Log tokens in console/logs - Send tokens in URL parameters **✅ Do:** - Store tokens in httpOnly cookies (server-side) - Use secure storage (encrypted) for client-side - Rotate tokens regularly - Implement token revocation ### 2. Handle Token Expiration Always check token expiration and refresh before making requests: ```python def is_token_valid(token_expires_at): # Refresh 1 minute before expiration return datetime.now() < (token_expires_at - timedelta(minutes=1)) ``` ### 3. Implement Retry Logic ```python def make_request_with_retry(url, headers, max_retries=3): for attempt in range(max_retries): response = requests.get(url, headers=headers) if response.status_code == 401: # Token expired, refresh and retry refresh_token() headers = get_headers() continue return response.json() raise Exception("Max retries exceeded") ``` ### 4. Validate Token Before Use ```python def validate_token(token): try: # Decode token (without verification for structure check) import jwt decoded = jwt.decode(token, options={"verify_signature": False}) exp = decoded.get('exp') if exp and datetime.fromtimestamp(exp) < datetime.now(): return False return True except: return False ``` --- ## Error Handling ### Authentication Errors **401 Unauthorized**: ```json { "success": false, "error": "Authentication required", "request_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Solution**: Include valid `Authorization: Bearer ` header. **403 Forbidden**: ```json { "success": false, "error": "Permission denied", "request_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Solution**: User lacks required permissions. Check user role and resource access. --- ## Testing Authentication ### Using Swagger UI 1. Navigate to `https://api.igny8.com/api/docs/` 2. Click "Authorize" button 3. Enter: `Bearer ` 4. Click "Authorize" 5. All requests will include the token ### Using cURL ```bash # Login curl -X POST https://api.igny8.com/api/v1/auth/login/ \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"password"}' # Use token curl -X GET https://api.igny8.com/api/v1/planner/keywords/ \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" ``` --- ## Troubleshooting ### Issue: "Authentication required" (401) **Causes**: - Missing Authorization header - Invalid token format - Expired token **Solutions**: 1. Verify `Authorization: Bearer ` header is included 2. Check token is not expired 3. Refresh token or re-login ### Issue: "Permission denied" (403) **Causes**: - User lacks required role - Resource belongs to different account - Site/sector access denied **Solutions**: 1. Check user role has required permissions 2. Verify resource belongs to user's account 3. Check site/sector access permissions ### Issue: Token expires frequently **Solution**: Implement automatic token refresh before expiration. --- **Last Updated**: 2025-11-16 **API Version**: 1.0.0