Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cgwire/zou/llms.txt

Use this file to discover all available pages before exploring further.

Refresh Access Token

curl -X GET https://zou.example.com/api/auth/refresh-token \
  -H "Authorization: Bearer <refresh_token>"

GET /api/auth/refresh-token

Tokens are considered outdated every two weeks. This route allows you to extend their lifetime before they get outdated. Use the refresh token to obtain a new access token.

Headers

Authorization
string
required
Bearer refresh token for authenticationFormat: Bearer <refresh_token>

Response

refresh
boolean
Present for browser requests, indicates refresh success
access_token
string
New JWT access token (present for non-browser requests)

Status Codes

  • 200 - Token refreshed successfully
  • 401 - Invalid or expired refresh token
  • 403 - User does not have required permissions
For browser requests, the new access token is automatically set as an HTTP-only cookie. For API clients, the access token is returned in the response body.
If 2FA enforcement is enabled and the user hasn’t configured 2FA, the new access token will have a requires_2fa_setup claim that restricts access until 2FA is configured.

Token Lifecycle

Access Tokens

Access tokens are short-lived JWT tokens used to authenticate API requests. They are valid for a limited time (typically a few hours) and should be included in the Authorization header for protected endpoints. Format:
Authorization: Bearer <access_token>
Token Claims:
  • sub: User ID (subject)
  • identity_type: Always “person” for user tokens
  • exp: Expiration timestamp
  • jti: JWT ID for token revocation
  • requires_2fa_setup: Present when 2FA setup is required (optional)

Refresh Tokens

Refresh tokens are long-lived tokens used to obtain new access tokens. They should be stored securely and only used with the /api/auth/refresh-token endpoint. Validity:
  • Access tokens: Several hours
  • Refresh tokens: 2 weeks
Best Practices:
  1. Store refresh tokens securely (encrypted storage, HTTP-only cookies)
  2. Never expose refresh tokens in URLs or logs
  3. Refresh access tokens proactively before they expire
  4. Implement token rotation for enhanced security

Token Revocation

Tokens are automatically revoked when:
  1. User logs out - The logout endpoint revokes the current token
  2. Token expires - Expired tokens are no longer valid
  3. User changes password - All existing tokens are invalidated
  4. Account is deactivated - All tokens for the account become invalid

Manual Token Revocation

To manually revoke a token, use the logout endpoint:
curl -X GET https://zou.example.com/api/auth/logout \
  -H "Authorization: Bearer <access_token>"
See the Login documentation for more details.

Token Storage

Browser Applications

For browser-based applications, Zou automatically manages token storage using HTTP-only cookies when the request comes from a browser user agent:
  • Access token cookie: access_token_cookie
  • Refresh token cookie: refresh_token_cookie
  • Cookies are HTTP-only and secure
  • Cookies include CSRF protection
Login Example:
// Tokens are automatically stored in cookies
const response = await fetch('https://zou.example.com/api/auth/login', {
  method: 'POST',
  credentials: 'include', // Important: include cookies
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'password123'
  })
});

// Subsequent requests automatically include the cookie
const protectedResponse = await fetch('https://zou.example.com/api/data', {
  credentials: 'include'
});

Native/Desktop Applications

For non-browser applications, tokens are returned in the response body:
import requests

# Login and extract tokens
response = requests.post(
    "https://zou.example.com/api/auth/login",
    json={
        "email": "user@example.com",
        "password": "password123"
    }
)

data = response.json()
access_token = data["access_token"]
refresh_token = data["refresh_token"]

# Store tokens securely (use encrypted storage in production)
# ...

# Use access token for API requests
headers = {"Authorization": f"Bearer {access_token}"}
api_response = requests.get(
    "https://zou.example.com/api/data",
    headers=headers
)
Storage Recommendations:
  • Desktop apps: Use OS-provided secure storage (Keychain, Credential Manager)
  • Mobile apps: Use platform-specific secure storage (Keychain, KeyStore)
  • Never store tokens in plain text files or unencrypted databases

Token Validation

To validate if a token is still valid, use the authentication check endpoint:
curl -X GET https://zou.example.com/api/auth/authenticated \
  -H "Authorization: Bearer <access_token>"
See the Login documentation for response details.

Error Handling

Common Token Errors

401 Unauthorized

The token is invalid, expired, or missing:
{
  "msg": "Token has expired"
}
Solution: Use the refresh token to obtain a new access token.

403 Forbidden

The token is valid but the user doesn’t have permission:
{
  "error": true,
  "message": "Permission denied"
}
Solution: Ensure the user has the required role or permissions.

422 Unprocessable Entity

The token format is invalid:
{
  "msg": "Bad Authorization header. Expected value 'Bearer <JWT>'"
}
Solution: Verify the Authorization header format is correct.

Refresh Token Flow

Implement automatic token refresh to handle expired access tokens:
import requests
from datetime import datetime, timedelta

class TokenManager:
    def __init__(self):
        self.access_token = None
        self.refresh_token = None
        self.token_expiry = None
    
    def login(self, email, password):
        response = requests.post(
            "https://zou.example.com/api/auth/login",
            json={"email": email, "password": password}
        )
        data = response.json()
        self.access_token = data["access_token"]
        self.refresh_token = data["refresh_token"]
        # Tokens typically expire in a few hours
        self.token_expiry = datetime.now() + timedelta(hours=2)
    
    def refresh(self):
        response = requests.get(
            "https://zou.example.com/api/auth/refresh-token",
            headers={"Authorization": f"Bearer {self.refresh_token}"}
        )
        data = response.json()
        self.access_token = data["access_token"]
        self.token_expiry = datetime.now() + timedelta(hours=2)
    
    def get_valid_token(self):
        # Refresh if token expires in next 5 minutes
        if datetime.now() >= self.token_expiry - timedelta(minutes=5):
            self.refresh()
        return self.access_token
    
    def api_request(self, url):
        headers = {"Authorization": f"Bearer {self.get_valid_token()}"}
        response = requests.get(url, headers=headers)
        
        # Handle expired token
        if response.status_code == 401:
            self.refresh()
            headers = {"Authorization": f"Bearer {self.access_token}"}
            response = requests.get(url, headers=headers)
        
        return response

# Usage
tm = TokenManager()
tm.login("user@example.com", "password123")
response = tm.api_request("https://zou.example.com/api/data")

Security Best Practices

Always use HTTPS to prevent token interception. Never send tokens over unencrypted HTTP connections.
  • Browser: Use HTTP-only cookies (automatic with Zou)
  • Desktop: Use OS credential managers (Keychain, Windows Credential Manager)
  • Mobile: Use platform secure storage (iOS Keychain, Android KeyStore)
  • Never store tokens in localStorage or plain text
Refresh tokens regularly before expiration. Consider implementing refresh token rotation where each refresh returns a new refresh token.
Implement proper error handling for 401 responses and automatically refresh tokens when needed.
Always call the logout endpoint when the user logs out to revoke tokens server-side.
Log token usage and watch for suspicious patterns like:
  • Multiple simultaneous sessions from different IPs
  • Rapid token refresh attempts
  • Access from unusual locations

Build docs developers (and LLMs) love