Files
igny8/docs/AUTHENTICATION-GUIDE.md

12 KiB

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):

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):

POST /api/v1/auth/login/
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secure_password123"
}

2. Receive Tokens

Response:

{
  "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:

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:

POST /api/v1/auth/refresh/
Content-Type: application/json

{
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response:

{
  "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

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

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

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

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:

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

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

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:

{
  "success": false,
  "error": "Authentication required",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}

Solution: Include valid Authorization: Bearer <token> header.

403 Forbidden:

{
  "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 <your_token>
  4. Click "Authorize"
  5. All requests will include the token

Using cURL

# 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 <token> 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