Auth API Reference
Overview
The Tronexus Auth API is a standalone authentication service. It provides Google OAuth for human users, JWT-based access tokens, rotating refresh tokens, and API keys for service-to-service authentication.
It is designed to serve multiple apps from a single instance. Each app gets its own user namespace and role system.
GET /health returns {"status":"ok"} with no authentication required.
OAuth Flow
Tronexus uses the authorization code flow with PKCE. The flow is:
- Your app redirects the user to
GET /oauth/google?app_id=<your_app_id> - The Auth API generates a PKCE code challenge and redirects to Google
- After Google authentication, the user is redirected to
/oauth/google/callback - The Auth API exchanges the code, fetches user info, upserts the user, and returns tokens
# Initiate OAuth — redirect user to this URL
GET https://auth-api.yourdomain.com/oauth/google?app_id=43d8b05c-2eaf-4077-9ee1-3602ba8901de
# Response: 302 redirect to Google's OAuth consent screen
# Callback — handled automatically by the Auth API
# On success returns:
{
"access_token": "eyJhbGciOiJIUzI1NiJ9...",
"token_type": "bearer",
"expires_in": 900,
"refresh_token": "550e8400-e29b-41d4-a716-446655440000.Abc123..."
}
Tokens
Access Token
JWT signed with HS256. Expires in 15 minutes by default. Contains:
{
"sub": "user-uuid",
"app_id": "app-uuid",
"roles": ["admin", "user"],
"exp": 1234567890,
"iat": 1234567890
}
Refresh Token
Format: token-row-uuid.secret. Stored as a bcrypt hash in Postgres. Rotates on every use. If a used token is presented again, the entire token family is revoked (theft detection).
POST /auth/refresh
Content-Type: application/json
{
"refresh_token": "550e8400-e29b-41d4-a716-446655440000.Abc123..."
}
# Response: new access_token + new refresh_token
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health | None | Liveness check |
| GET | /oauth/google | None | Initiate Google OAuth. Requires app_id query param. |
| GET | /oauth/google/callback | None | OAuth callback. Handled by Auth API — do not call directly. |
| POST | /auth/refresh | Refresh token | Rotate refresh token and issue new access token. |
| POST | /auth/logout | Access token | Revoke all refresh tokens for the current user. |
| GET | /auth/me | Access token | Current user profile and roles. |
| GET | /apps | Admin API key | List all registered apps. |
| POST | /apps | Admin API key | Register a new app. |
| GET | /apps/{id}/roles | Access token | List roles for an app. |
| POST | /apps/{id}/roles | Admin API key | Create a role for an app. |
| POST | /apps/{id}/users/{user_id}/roles | Admin API key | Assign a role to a user. |
| POST | /apikeys | Admin API key | Create a new API key. |
| DELETE | /apikeys/{id} | Admin API key | Revoke an API key. |
| POST | /apikeys/verify | None | Verify an API key and return its claims. |
Authentication Header
Pass the access token or API key as a Bearer token:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Apps & Roles
Apps are namespaces. Each app has its own users and roles. You can run multiple apps from one Auth API instance.
# Create an app
POST /apps
Authorization: Bearer ak_your_admin_key
Content-Type: application/json
{
"name": "my-app",
"description": "My application"
}
# Create a role
POST /apps/{app_id}/roles
{
"name": "admin",
"description": "Full access"
}
# Assign role to user
POST /apps/{app_id}/users/{user_id}/roles
{
"role_id": "role-uuid"
}
API Keys
API keys are for service-to-service authentication. They are bcrypt hashed on creation — the plain key is returned once and never stored.
# Create an API key
POST /apikeys
Authorization: Bearer ak_your_admin_key
Content-Type: application/json
{
"app_id": "app-uuid",
"name": "my-service",
"scopes": ["admin"],
"expires_in_days": 365
}
# Response includes the plain key — store it immediately:
{
"id": "key-uuid",
"key": "ak_Abc123...", // shown ONCE
"scopes": ["admin"],
"expires_at": "2027-01-01T00:00:00Z"
}
# Verify an API key (for service-to-service)
POST /apikeys/verify
Content-Type: application/json
{
"key": "ak_Abc123..."
}
Rate Limiting
Rate limiting uses a Redis sliding window per IP per endpoint. Defaults:
- 20 requests per 60-second window
- After 10 violations: 15-minute IP lockout
- Permanent bans stored in Postgres for repeat offenders
/healthis exempt from rate limiting
Adjust thresholds via .env variables. See Configuration → Auth API.
Security Notes
- PKCE is enforced on all OAuth flows — authorization code interception is mitigated
- Refresh token family detection revokes entire session families on replay
- API keys are bcrypt hashed — compromise of the database does not expose plain keys
- API keys have mandatory expiry — maximum 365 days from creation
- Security headers middleware applies HSTS, CSP, X-Frame-Options, and X-Content-Type-Options to all responses
- Redis is bound to the internal Docker network — no external exposure