# Authentication & Authorization
# Authentication and Authorization
SkySpy provides a comprehensive, enterprise-ready authentication and authorization system supporting multiple authentication methods, role-based access control (RBAC), and fine-grained feature permissions.
***
## Quick Reference
> **TL;DR** - Everything you need to get started in 30 seconds
| What | Where | Example |
| ----------------- | --------------------------------- | ---------------------------------------- |
| **Login** | `POST /api/v1/auth/login` | `{"username": "...", "password": "..."}` |
| **Refresh Token** | `POST /api/v1/auth/refresh` | `{"refresh": "eyJ..."}` |
| **Use Token** | `Authorization` header | `Bearer eyJ0eXAiOiJKV1Q...` |
| **API Key** | `X-API-Key` header | `sk_a1B2c3D4e5F6g7H8...` |
| **SSO/OIDC** | `GET /api/v1/auth/oidc/authorize` | Redirects to IdP |
| **Current User** | `GET /api/v1/auth/profile` | Returns user + permissions |
| Auth Method | Best For | Token Lifetime |
| ----------- | ---------------------------- | ---------------------------------- |
| JWT | Web apps, SPAs | 60 min (access) / 2 days (refresh) |
| API Key | Scripts, integrations, CI/CD | Custom (up to years) |
| OIDC/SSO | Enterprise, corporate IdP | Follows IdP settings |
***
## Authentication Overview
SkySpy's authentication system is designed with flexibility and security in mind. It supports three operational modes and multiple authentication methods to accommodate various deployment scenarios.
### Architecture Diagram
```mermaid
flowchart TB
subgraph Frontend["Frontend"]
WEB[Web App
React]
AUTH_CTX[AuthContext
JWT + OIDC]
end
subgraph API["Backend API"]
REST[REST API
Django REST]
WS[Socket.IO
Real-time]
end
subgraph AuthLayer["Auth Layer"]
JWT_AUTH[JWT Auth]
API_KEY[API Key Auth]
OIDC[OIDC/SSO]
end
subgraph Permissions["Authorization"]
RBAC[Role-Based
Access Control]
FEATURE[Feature-Based
Permissions]
end
WEB --> AUTH_CTX
AUTH_CTX --> REST
AUTH_CTX --> WS
REST --> JWT_AUTH
REST --> API_KEY
REST --> OIDC
WS --> JWT_AUTH
WS --> API_KEY
JWT_AUTH --> RBAC
API_KEY --> RBAC
OIDC --> RBAC
RBAC --> FEATURE
```
### Authentication Modes
SkySpy operates in one of three authentication modes, configured via the `AUTH_MODE` environment variable:
| Mode | Description | Use Case | Security Level |
| --------- | ----------------------------------------- | ------------------------------------------ | -------------- |
| `public` | No authentication required | Development, demos, public kiosks | Low |
| `private` | Authentication required for all endpoints | Enterprise, security-sensitive deployments | High |
| `hybrid` | Per-feature configuration **(default)** | Most production deployments | Flexible |
```bash
# Environment variable configuration
AUTH_MODE=hybrid # Options: public, private, hybrid
```
***
## Authentication Methods
### Method Comparison
> **Which method should I use?**
>
> **Web Applications** - JWT tokens with automatic refresh
> **Scripts & Automation** - API keys with scoped access
> **Enterprise/Corporate** - OIDC/SSO with your identity provider
| Feature | JWT | API Key | OIDC |
| --------------------- | --- | ------- | ---- |
| **Stateless** | Yes | Yes | Yes |
| **Auto Refresh** | Yes | No | Yes |
| **Scoped Access** | No | Yes | Yes |
| **User Context** | Yes | Yes | Yes |
| **External IdP** | No | No | Yes |
| **Socket.IO Support** | Yes | Yes | Yes |
***
### 1. JWT Token Authentication
SkySpy uses JSON Web Tokens (JWT) for stateless authentication. The implementation is built on `djangorestframework-simplejwt`.
#### Token Structure Visual
```
+-----------------------------------------------------------------------------+
| JWT ACCESS TOKEN |
+-----------------------------------------------------------------------------+
| HEADER | PAYLOAD | SIGNATURE |
| ------- | -------- | ---------- |
| { | { | |
| "typ": "JWT", | "token_type": "access", | HMACSHA256( |
| "alg": "HS256" | "exp": 1704067200, | base64(header) + |
| } | "user_id": 42, | base64(payload), |
| | "jti": "unique-id" | secret |
| | } | ) |
+-----------------------------------------------------------------------------+
| eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIi4uLn0 |
| --------------------------------------------------------------------------- |
| Header (Base64) Payload (Base64) Signature |
+-----------------------------------------------------------------------------+
```
#### Configuration
```bash
# JWT Settings (environment variables)
JWT_SECRET_KEY=your-secret-key # Separate from Django SECRET_KEY recommended
JWT_ACCESS_TOKEN_LIFETIME_MINUTES=60 # Access token validity (default: 60 min)
JWT_REFRESH_TOKEN_LIFETIME_DAYS=2 # Refresh token validity (default: 2 days)
JWT_AUTH_COOKIE=false # Enable httpOnly cookie storage
```
#### Token Endpoints
| Endpoint | Method | Description |
| ---------------------- | ------ | -------------------------------- |
| `/api/v1/auth/login` | POST | Obtain access and refresh tokens |
| `/api/v1/auth/refresh` | POST | Refresh access token |
| `/api/v1/auth/logout` | POST | Blacklist refresh token |
#### JWT Authentication Flow
```mermaid
sequenceDiagram
autonumber
participant User as User
participant App as Frontend
participant API as SkySpy API
participant DB as Database
User->>App: Enter credentials
App->>API: POST /auth/login
{username, password}
API->>DB: Validate credentials
DB-->>API: User verified
API-->>App: {access_token, refresh_token, user}
App->>App: Store tokens in localStorage
Note over User,DB: Making Authenticated Requests
App->>API: GET /aircraft
Authorization: Bearer {token}
API->>API: Validate JWT signature
API-->>App: Aircraft data
Note over User,DB: Token Refresh (before expiry)
App->>API: POST /auth/refresh
{refresh_token}
API->>DB: Validate & rotate token
DB-->>API: New tokens generated
API-->>App: {new_access_token, new_refresh_token}
```
#### Code Examples
**cURL**
```bash
# Login Request
curl -X POST https://your-skyspy-instance/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "operator",
"password": "secure-password"
}'
```
**JavaScript**
```javascript
// Login Request
const response = await fetch('https://your-skyspy-instance/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'operator',
password: 'secure-password'
})
});
const { access, refresh, user } = await response.json();
localStorage.setItem('skyspy_access_token', access);
localStorage.setItem('skyspy_refresh_token', refresh);
```
**Python**
```python
import requests
# Login Request
response = requests.post(
'https://your-skyspy-instance/api/v1/auth/login',
json={
'username': 'operator',
'password': 'secure-password'
}
)
tokens = response.json()
access_token = tokens['access']
refresh_token = tokens['refresh']
```
#### Login Response
```json
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"user": {
"id": 42,
"username": "operator",
"email": "operator@example.com",
"display_name": "John Operator",
"permissions": ["aircraft.view", "alerts.create", "alerts.edit"],
"roles": ["operator"]
}
}
```
#### Using JWT Tokens
**cURL**
```bash
# Include the access token in the Authorization header
curl https://your-skyspy-instance/api/v1/aircraft \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
```
**JavaScript**
```javascript
// Using the token in fetch requests
const token = localStorage.getItem('skyspy_access_token');
const response = await fetch('https://your-skyspy-instance/api/v1/aircraft', {
headers: {
'Authorization': `Bearer ${token}`
}
});
```
**Python**
```python
import requests
headers = {
'Authorization': f'Bearer {access_token}'
}
response = requests.get(
'https://your-skyspy-instance/api/v1/aircraft',
headers=headers
)
```
> **Security Note**
>
> Refresh tokens are **rotated on use** and the old token is blacklisted. This provides protection against token theft and replay attacks.
***
### 2. API Key Authentication
API keys provide programmatic access for integrations, scripts, and third-party applications.
#### Key Format
```
sk_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0
| |
| +-- Random characters
|
+-- Prefix identifier
```
> **Warning**
>
> The full API key is **only returned once** at creation time. Store it securely immediately - you cannot retrieve it later!
#### Creating API Keys
**cURL**
```bash
curl -X POST https://your-skyspy-instance/api/v1/auth/api-keys \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline",
"scopes": ["aircraft", "alerts"],
"expires_at": "2025-12-31T23:59:59Z"
}'
```
**JavaScript**
```javascript
const response = await authFetch('/api/v1/auth/api-keys', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'CI/CD Pipeline',
scopes: ['aircraft', 'alerts'],
expires_at: '2025-12-31T23:59:59Z'
})
});
const { key } = await response.json();
// Store this immediately - shown only once!
console.log('API Key:', key);
```
**Django**
```python
from skyspy.models import APIKey
from django.utils import timezone
from datetime import timedelta
api_key = APIKey.objects.create(
user=user,
name="Read-only Aircraft Data",
scopes=["aircraft", "history"],
expires_at=timezone.now() + timedelta(days=90)
)
```
#### Using API Keys
**cURL**
```bash
# Using Authorization header (recommended)
curl https://your-skyspy-instance/api/v1/aircraft \
-H "Authorization: ApiKey sk_a1B2c3D4e5F6g7H8i9J0..."
# Using X-API-Key header
curl https://your-skyspy-instance/api/v1/aircraft \
-H "X-API-Key: sk_a1B2c3D4e5F6g7H8i9J0..."
```
**JavaScript**
```javascript
const API_KEY = 'sk_a1B2c3D4e5F6g7H8i9J0...';
// Using Authorization header
const response = await fetch('https://your-skyspy-instance/api/v1/aircraft', {
headers: {
'Authorization': `ApiKey ${API_KEY}`
}
});
// Or using X-API-Key header
const response2 = await fetch('https://your-skyspy-instance/api/v1/aircraft', {
headers: {
'X-API-Key': API_KEY
}
});
```
#### Scope-Based Access
| Scope | Access Granted | Example Endpoints |
| ---------- | ------------------------- | -------------------- |
| `aircraft` | Aircraft tracking data | `/api/v1/aircraft/*` |
| `alerts` | Alert rules and history | `/api/v1/alerts/*` |
| `safety` | Safety event data | `/api/v1/safety/*` |
| `audio` | Audio transmissions | `/api/v1/audio/*` |
| `acars` | ACARS messages | `/api/v1/acars/*` |
| `history` | Historical data | `/api/v1/history/*` |
| `system` | System status and metrics | `/api/v1/system/*` |
***
### 3. OIDC/SSO Authentication
SkySpy supports OpenID Connect (OIDC) for enterprise single sign-on integration with identity providers like Okta, Auth0, Azure AD, and Keycloak.
#### Configuration
```bash
# OIDC Settings
OIDC_ENABLED=true
OIDC_PROVIDER_URL=https://your-idp.example.com
OIDC_CLIENT_ID=skyspy-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_PROVIDER_NAME=Corporate SSO # Display name for UI
OIDC_SCOPES=openid profile email groups
OIDC_DEFAULT_ROLE=viewer # Role for new OIDC users
```
#### OIDC Authentication Flow
```mermaid
sequenceDiagram
autonumber
participant User as User
participant App as SkySpy Frontend
participant API as SkySpy Backend
participant IdP as Identity Provider
User->>App: Click "Login with SSO"
App->>API: GET /auth/oidc/authorize
API-->>App: Authorization URL + state
App->>IdP: Redirect to IdP login
Note over IdP: User authenticates
with corporate credentials
IdP->>User: Login form
User->>IdP: Enter credentials
IdP-->>App: Redirect with auth code
App->>API: GET /auth/oidc/callback?code=xxx
API->>IdP: Exchange code for tokens
IdP-->>API: ID token + access token
API->>IdP: Fetch user info
IdP-->>API: User claims (email, groups, etc.)
API->>API: Create/update user
Map claims to roles
API-->>App: SkySpy JWT tokens + user
App->>App: Store tokens, redirect to dashboard
```
#### Claim-Based Role Mapping
Map your IdP groups to SkySpy roles automatically:
```json
{
"name": "Admin Group Mapping",
"claim_name": "groups",
"match_type": "exact",
"claim_value": "skyspy-admins",
"role": "admin",
"priority": 10,
"is_active": true
}
```
| Match Type | Description | Example |
| ---------- | ------------------------------ | ------------------------------------------- |
| `exact` | Claim value must match exactly | `"skyspy-admins"` matches `"skyspy-admins"` |
| `contains` | Claim must contain the string | `"admin"` matches `"skyspy-admins"` |
| `regex` | Claim must match the pattern | `"skyspy-.*"` matches `"skyspy-operators"` |
> **Security Warning**
>
> **Email linking** (`OIDC_ALLOW_EMAIL_LINKING`) is disabled by default. Enabling it allows existing accounts to be linked to OIDC based on matching email addresses.
>
> **Risk**: An attacker who controls an OIDC provider with matching email addresses could gain access to existing accounts.
***
### 4. Session-Based Authentication (Admin Only)
Django admin uses session-based authentication. This is separate from the API authentication system.
```python
# settings.py - Session configuration
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SECURE = True # In production
```
***
## User Roles and Permissions
### Role-Based Access Control (RBAC)
SkySpy implements RBAC with the following default roles:
| Role | Priority | Description | Typical User |
| ------------ | -------- | --------------------------------------------------- | -------------------------- |
| `viewer` | 10 | Read-only access to allowed features | Public dashboards, guests |
| `operator` | 20 | Create/manage own alerts, acknowledge safety events | Daily users, shift workers |
| `analyst` | 30 | Extended access with export and transcription | Data analysts, researchers |
| `admin` | 40 | Full feature access with limited user management | Team leads, managers |
| `superadmin` | 100 | Full access including user and role management | System administrators |
### Permission Matrix
Permissions follow the format `feature.action`:
```
aircraft.view # View aircraft data
aircraft.view_military # View military aircraft
alerts.create # Create alert rules
alerts.delete # Delete alert rules
alerts.manage_all # Manage all users' alerts
safety.acknowledge # Acknowledge safety events
users.create # Create new users
roles.edit # Modify roles
```
### Role Permission Comparison
| Permission | Viewer | Operator | Analyst | Admin | Super |
| --------------------------- | ------ | -------- | ------- | ----- | ----- |
| **aircraft.view** | Yes | Yes | Yes | Yes | Yes |
| **aircraft.view\_military** | No | No | Yes | Yes | Yes |
| **alerts.view** | Yes | Yes | Yes | Yes | Yes |
| **alerts.create** | No | Yes | Yes | Yes | Yes |
| **alerts.manage\_all** | No | No | No | Yes | Yes |
| **safety.acknowledge** | No | Yes | Yes | Yes | Yes |
| **audio.transcribe** | No | No | Yes | Yes | Yes |
| **history.export** | No | No | Yes | Yes | Yes |
| **users.view** | No | No | No | Yes | Yes |
| **users.create** | No | No | No | No | Yes |
| **roles.edit** | No | No | No | No | Yes |
### Creating Custom Roles
**cURL**
```bash
curl -X POST https://your-skyspy-instance/api/v1/roles \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"name": "shift_supervisor",
"display_name": "Shift Supervisor",
"description": "Can manage alerts and acknowledge safety events",
"permissions": [
"aircraft.view",
"aircraft.view_details",
"alerts.view",
"alerts.create",
"alerts.edit",
"alerts.delete",
"safety.view",
"safety.acknowledge",
"safety.manage"
],
"priority": 25
}'
```
**JavaScript**
```javascript
const newRole = await authFetch('/api/v1/roles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'shift_supervisor',
display_name: 'Shift Supervisor',
description: 'Can manage alerts and acknowledge safety events',
permissions: [
'aircraft.view',
'aircraft.view_details',
'alerts.view',
'alerts.create',
'alerts.edit',
'alerts.delete',
'safety.view',
'safety.acknowledge',
'safety.manage'
],
priority: 25
})
});
```
### Role Assignment with Expiration
Roles can be assigned with optional expiration for temporary access:
```bash
curl -X POST https://your-skyspy-instance/api/v1/user-roles \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"user": 42,
"role": 3,
"expires_at": "2024-02-01T00:00:00Z"
}'
```
> **Tip: Temporary Access**
>
> Use role expiration for:
>
> * **Contractors** with limited engagement periods
> * **Trainees** who need elevated access during onboarding
> * **Incident response** requiring temporary admin privileges
***
## Feature-Based Access Control
Each feature can be configured with independent access levels:
### Access Levels
| Level | Description |
| --------------- | ---------------------------- |
| `public` | No authentication required |
| `authenticated` | Any logged-in user |
| `permission` | Specific permission required |
### Configuration Example
```bash
curl -X PATCH https://your-skyspy-instance/api/v1/feature-access/aircraft \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"read_access": "public",
"write_access": "permission",
"is_enabled": true
}'
```
### Hybrid Mode Setup Example
```json
{
"aircraft": {
"read_access": "public",
"write_access": "permission",
"is_enabled": true
},
"alerts": {
"read_access": "authenticated",
"write_access": "permission",
"is_enabled": true
},
"safety": {
"read_access": "authenticated",
"write_access": "permission",
"is_enabled": true
},
"users": {
"read_access": "permission",
"write_access": "permission",
"is_enabled": true
}
}
```
***
## Socket.IO Authentication
Socket.IO connections support both JWT tokens and API keys for real-time data streaming.
### Socket.IO Authentication Flow
```mermaid
sequenceDiagram
autonumber
participant Client as Client
participant SIO as Socket.IO Server
participant Auth as Auth Middleware
participant Handler as Event Handler
Client->>SIO: Connect with token
(auth parameter)
SIO->>Auth: Validate token
alt Token Valid
Auth-->>SIO: User authenticated
SIO->>Handler: Accept connection
Handler-->>Client: Connection accepted
loop Real-time Updates
Handler->>Client: Aircraft data
Handler->>Client: Alert notifications
end
else Token Invalid
Auth-->>SIO: Authentication failed
SIO-->>Client: Disconnect (4001)
end
```
### Authentication Methods
> **Recommended: Auth Parameter**
>
> ```javascript
> import { io } from 'socket.io-client';
> const socket = io('https://your-skyspy', {
> path: '/socket.io/',
> auth: {
> token: accessToken
> }
> });
> ```
> **Warning: Not Recommended - Query String**
>
> ```javascript
> const socket = io('https://your-skyspy?token=eyJ...', {
> path: '/socket.io/'
> });
> ```
>
> **Why?** Tokens may appear in server logs and browser history.
### Topic-Based Permissions
| Topic | Required Permission | Description |
| ---------- | ------------------------ | -------------------------- |
| `aircraft` | `aircraft.view` | Live aircraft positions |
| `military` | `aircraft.view_military` | Military aircraft data |
| `alerts` | `alerts.view` | Alert notifications |
| `safety` | `safety.view` | Safety events |
| `acars` | `acars.view` | ACARS messages |
| `audio` | `audio.view` | Audio stream notifications |
| `system` | `system.view_status` | System status updates |
### Handling Connection Rejection
```javascript
import { io } from 'socket.io-client';
const socket = io('https://your-skyspy', {
path: '/socket.io/',
auth: {
token: token
}
});
socket.on('connect_error', (error) => {
if (error.message === 'Authentication failed') {
console.error('Authentication failed - invalid or expired token');
// Trigger re-authentication
} else if (error.message === 'Permission denied') {
console.error('Permission denied - insufficient access');
} else {
console.error('Connection error:', error.message);
}
});
```
***
## Frontend Authentication Flow
The React frontend uses the `AuthContext` provider for authentication state management.
### AuthContext API
```jsx
import { useAuth } from '../contexts/AuthContext';
function MyComponent() {
const {
// State
status, // 'loading' | 'anonymous' | 'authenticated'
user, // Current user object
config, // Auth configuration
error, // Last error message
isLoading, // Boolean shorthand
isAuthenticated, // Boolean shorthand
isAnonymous, // Boolean shorthand
// Actions
login, // (username, password) => Promise
logout, // () => Promise
loginWithOIDC, // () => Promise
refreshAccessToken, // () => Promise
authFetch, // Authenticated fetch wrapper
// Permission checks
hasPermission, // (permission) => boolean
hasAnyPermission, // ([permissions]) => boolean
hasAllPermissions,// ([permissions]) => boolean
canAccessFeature, // (feature, action?) => boolean
// Token access
getAccessToken, // () => string | null
// Error handling
clearError, // () => void
} = useAuth();
}
```
### Frontend Login Flow
```mermaid
flowchart LR
subgraph Login["Login"]
A[User enters credentials] --> B[Call login]
B --> C{Success?}
C -->|Yes| D[Store tokens]
C -->|No| E[Show error]
D --> F[Redirect to dashboard]
end
```
**Standard Login**
```javascript
const { login, error } = useAuth();
async function handleLogin(username, password) {
const result = await login(username, password);
if (result.success) {
navigate('/dashboard');
} else {
console.error(result.error);
}
}
```
**OIDC/SSO Login**
```javascript
const { loginWithOIDC, config } = useAuth();
async function handleOIDCLogin() {
try {
const result = await loginWithOIDC();
if (result.success) {
navigate('/dashboard');
}
} catch (err) {
// User closed popup or login timed out
console.error(err.message);
}
}
// Show OIDC button if enabled
{config.oidcEnabled && (
)}
```
### Permission Checking
```jsx
const { hasPermission, canAccessFeature } = useAuth();
// Check specific permission
if (hasPermission('alerts.create')) {
return ;
}
// Check feature access
if (canAccessFeature('safety', 'write')) {
return ;
}
// Check multiple permissions
if (hasAllPermissions(['alerts.view', 'alerts.edit'])) {
return ;
}
```
### Automatic Token Refresh
```
+--------------------------------------------------------------------+
| TOKEN REFRESH TIMELINE |
+--------------------------------------------------------------------+
| |
| Token Created Refresh Scheduled Token Expires |
| | | | |
| v v v |
| ------------------------------------------------------> Time |
| | | | |
| +-------- 59 min 30s ------+ | |
| +------ 30s buffer ----+ |
| |
| New token obtained before expiry = seamless user experience |
+--------------------------------------------------------------------+
```
### Token Storage
| Key | Value | Description |
| ---------------------- | ---------------------- | -------------------------------- |
| `skyspy_access_token` | JWT access token | Used for API requests |
| `skyspy_refresh_token` | JWT refresh token | Used to obtain new access tokens |
| `skyspy_user` | Serialized user object | Cached user info |
***
## Security Best Practices
### Security Checklist
> **Production Security Checklist**
>
> Before going to production, verify these settings:
>
> **Environment & Secrets**
>
> * `DEBUG=false`
> * Strong, unique `DJANGO_SECRET_KEY`
> * Separate `JWT_SECRET_KEY` from Django secret
> * Secrets not committed to version control
>
> **Authentication**
>
> * `AUTH_MODE=hybrid` or `private`
> * Rate limiting enabled on auth endpoints
> * JWT token lifetimes appropriate for use case
>
> **Cookies & Transport**
>
> * `SESSION_COOKIE_SECURE=true`
> * `CSRF_COOKIE_SECURE=true`
> * HTTPS enforced
> * CORS restricted to known origins
>
> **API Keys**
>
> * Scoped to minimum required permissions
> * Expiration dates set
> * Query parameter auth disabled
### Environment Variables
```bash
# Production settings
DEBUG=false
DJANGO_SECRET_KEY=
JWT_SECRET_KEY=
AUTH_MODE=hybrid
# Enable secure cookies
SESSION_COOKIE_SECURE=true
CSRF_COOKIE_SECURE=true
JWT_AUTH_COOKIE=true
```
### Rate Limiting
| Endpoint | Rate Limit | Purpose |
| ---------------------- | ----------- | --------------------------- |
| `/api/v1/auth/login` | 5/minute | Prevent brute force attacks |
| `/api/v1/auth/refresh` | 5/minute | Prevent token abuse |
| Anonymous requests | 100/minute | General protection |
| Authenticated requests | 1000/minute | Fair usage |
### Token Security Best Practices
| Practice | Implementation | Why It Matters |
| ----------------------- | ------------------------------------------------------- | --------------------------------------------- |
| **Separate JWT Secret** | Use different `JWT_SECRET_KEY` than `DJANGO_SECRET_KEY` | Limits blast radius if one key is compromised |
| **Short Access Tokens** | Default 60 minutes, adjust as needed | Limits window for stolen tokens |
| **Token Rotation** | Refresh tokens blacklisted on use | Prevents replay attacks |
| **Secure Storage** | Use httpOnly cookies (`JWT_AUTH_COOKIE=true`) | Prevents XSS token theft |
### API Key Security
> **Warning: API Key Best Practices**
>
> 1. **No Query Parameters** - API keys cannot be passed in URLs (prevents logging/leakage)
> 2. **Hashed Storage** - Only SHA-256 hash stored in database
> 3. **Scoped Access** - Limit API keys to required features only
> 4. **Expiration** - Always set expiration dates on API keys
> 5. **Rotation** - Rotate keys periodically and after team changes
### CORS Configuration
```bash
CORS_ALLOW_ALL_ORIGINS=false
CORS_ALLOW_CREDENTIALS=true
CORS_ALLOWED_ORIGINS=https://your-frontend-domain.com
```
***
## Configuration Reference
### Authentication Settings
| Setting | Default | Description |
| ----------------------------------- | ------------ | ------------------------------------------- |
| `AUTH_MODE` | `hybrid` | Authentication mode (public/private/hybrid) |
| `LOCAL_AUTH_ENABLED` | `true` | Enable username/password login |
| `API_KEY_ENABLED` | `true` | Enable API key authentication |
| `JWT_SECRET_KEY` | `SECRET_KEY` | Key for signing JWTs |
| `JWT_ACCESS_TOKEN_LIFETIME_MINUTES` | `60` | Access token validity |
| `JWT_REFRESH_TOKEN_LIFETIME_DAYS` | `2` | Refresh token validity |
| `JWT_AUTH_COOKIE` | `false` | Store tokens in httpOnly cookies |
### OIDC Settings
| Setting | Default | Description |
| -------------------------- | ----------------------------- | -------------------------------------- |
| `OIDC_ENABLED` | `false` | Enable OIDC authentication |
| `OIDC_PROVIDER_URL` | - | Base URL of the OIDC provider |
| `OIDC_CLIENT_ID` | - | OAuth client ID |
| `OIDC_CLIENT_SECRET` | - | OAuth client secret |
| `OIDC_PROVIDER_NAME` | `SSO` | Display name for UI |
| `OIDC_SCOPES` | `openid profile email groups` | OAuth scopes to request |
| `OIDC_DEFAULT_ROLE` | `viewer` | Default role for new OIDC users |
| `OIDC_ALLOW_EMAIL_LINKING` | `false` | Allow linking by email (security risk) |
### Rate Limiting Settings
| Setting | Default | Description |
| ----------------------------- | ------------- | ----------------------------- |
| `DEFAULT_THROTTLE_RATES.anon` | `100/minute` | Anonymous user rate limit |
| `DEFAULT_THROTTLE_RATES.user` | `1000/minute` | Authenticated user rate limit |
| Auth endpoints | `5/minute` | Login/refresh rate limit |
***
## API Endpoints Reference
### Authentication Endpoints
| Endpoint | Method | Auth | Description |
| ----------------------------- | ------ | ---- | ---------------------------- |
| `/api/v1/auth/config` | GET | No | Get auth configuration |
| `/api/v1/auth/login` | POST | No | Login with credentials |
| `/api/v1/auth/logout` | POST | Yes | Logout and blacklist token |
| `/api/v1/auth/refresh` | POST | No | Refresh access token |
| `/api/v1/auth/profile` | GET | Yes | Get current user profile |
| `/api/v1/auth/profile` | PATCH | Yes | Update current user profile |
| `/api/v1/auth/password` | POST | Yes | Change password |
| `/api/v1/auth/oidc/authorize` | GET | No | Get OIDC authorization URL |
| `/api/v1/auth/oidc/callback` | GET | No | OIDC callback handler |
| `/api/v1/auth/permissions` | GET | No | List all permissions |
| `/api/v1/auth/my-permissions` | GET | Yes | Get current user permissions |
### User Management Endpoints
| Endpoint | Method | Permission | Description |
| -------------------- | ------ | -------------- | ---------------- |
| `/api/v1/users` | GET | `users.view` | List users |
| `/api/v1/users` | POST | `users.create` | Create user |
| `/api/v1/users/{id}` | GET | `users.view` | Get user details |
| `/api/v1/users/{id}` | PATCH | `users.edit` | Update user |
| `/api/v1/users/{id}` | DELETE | `users.delete` | Delete user |
### Role Management Endpoints
| Endpoint | Method | Permission | Description |
| -------------------- | ------ | -------------- | ---------------- |
| `/api/v1/roles` | GET | `roles.view` | List roles |
| `/api/v1/roles` | POST | `roles.create` | Create role |
| `/api/v1/roles/{id}` | GET | `roles.view` | Get role details |
| `/api/v1/roles/{id}` | PATCH | `roles.edit` | Update role |
| `/api/v1/roles/{id}` | DELETE | `roles.delete` | Delete role |
### API Key Management Endpoints
| Endpoint | Method | Auth | Description |
| ----------------------- | ------ | ---- | -------------------- |
| `/api/v1/api-keys` | GET | Yes | List user's API keys |
| `/api/v1/api-keys` | POST | Yes | Create API key |
| `/api/v1/api-keys/{id}` | DELETE | Yes | Delete API key |
***
## Troubleshooting
### Common Issues
> **401 Unauthorized**
>
> **Possible causes:**
>
> * Token is expired
> * Invalid Authorization header format (should be `Bearer `)
> * User account is deactivated
>
> **Solutions:**
>
> 1. Check token expiration with a JWT decoder
> 2. Verify header format: `Authorization: Bearer eyJ...`
> 3. Confirm user `is_active=True` in database
> **Warning: 403 Forbidden**
>
> **Possible causes:**
>
> * User lacks required permission
> * Feature is disabled
> * API key scope doesn't include the feature
>
> **Solutions:**
>
> 1. Check user permissions via `/api/v1/auth/my-permissions`
> 2. Verify feature is enabled in admin
> 3. Check API key scopes if using API key auth
> **OIDC Login Failed**
>
> **Possible causes:**
>
> * Incorrect `OIDC_CLIENT_SECRET`
> * Redirect URIs not configured in IdP
> * Scopes not allowed by IdP
>
> **Solutions:**
>
> 1. Double-check client secret in environment
> 2. Add callback URL to IdP allowed redirects: `https://your-domain/api/v1/auth/oidc/callback`
> 3. Verify scopes are enabled in IdP application settings
> **Socket.IO Connection Rejected**
>
> **Possible causes:**
>
> * Token is invalid or expired
> * Using query string instead of auth parameter
> * Token format incorrect
>
> **Solutions:**
>
> 1. Refresh token before connecting
> 2. Use `auth` parameter: `io(url, { auth: { token: token } })`
> 3. Check Socket.IO authentication middleware configuration
### Debug Logging
Enable debug logging for authentication issues:
```python
LOGGING = {
'loggers': {
'skyspy.auth': {
'level': 'DEBUG',
'handlers': ['console'],
},
},
}
```
***
## Next Steps
> **Ready to integrate?**
>
> * **Quick Start**: Try the `/api/v1/auth/login` endpoint with your credentials
> * **API Keys**: Create a scoped API key for your integration
> * **Enterprise**: Configure OIDC with your identity provider
> * **Support**: Check our GitHub issues or contact support