Skip to main content

Overview

Torn uses JWT (JSON Web Token) authentication for securing API endpoints. The authentication flow involves:
  1. Login with email and password
  2. Receive a JWT access token
  3. Include the token in subsequent requests
  4. Optionally validate the session

Token Configuration

Algorithm
string
default:"HS256"
JWT signing algorithm
Token Expiration
duration
default:"12 hours"
Access tokens expire after 12 hours (720 minutes)
Token Type
string
default:"bearer"
OAuth2 bearer token format

Authentication Endpoints

POST /auth/login

JSON-based login endpoint that accepts email and password credentials.
email
string
required
User’s email address
password
string
required
User’s password
Request Example:
curl -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "securepassword123"
  }'
Response:
access_token
string
JWT access token for authentication
token_type
string
Token type (always “bearer”)
user
object
User information object
id
integer
User ID
email
string
User’s email address
full_name
string
User’s full name (optional)
is_active
boolean
Whether the user account is active
is_superuser
boolean
Whether the user has superuser privileges
available_tenants
array
List of tenants (companies) the user has access to
id
integer
Tenant ID
name
string
Company name
rut
string
Chilean tax ID (RUT)
role_name
string
User’s role in this tenant (e.g., “ADMINISTRADOR”, “VENDEDOR”)
is_active
boolean
Whether the tenant is active
max_users
integer
Maximum number of users allowed for this tenant
permissions
object
Role-based permissions for this tenant
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "user": {
    "id": 1,
    "email": "[email protected]",
    "full_name": "John Doe",
    "is_active": true,
    "is_superuser": false
  },
  "available_tenants": [
    {
      "id": 1,
      "name": "Mi Empresa SPA",
      "rut": "12345678-9",
      "role_name": "ADMINISTRADOR",
      "is_active": true,
      "max_users": 5,
      "permissions": {
        "sales": ["create", "read", "update"],
        "products": ["create", "read", "update", "delete"]
      }
    }
  ]
}

POST /auth/token

OAuth2-compatible token endpoint using form data (alternative to /auth/login).
username
string
required
User’s email address (sent as “username” for OAuth2 compatibility)
password
string
required
User’s password
Request Example:
curl -X POST http://localhost:8000/auth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "[email protected]&password=securepassword123"
Response: Same response structure as /auth/login.
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "user": { ... },
  "available_tenants": [ ... ]
}

GET /auth/users/me

Retrieve the current authenticated user’s profile. Headers:
Authorization
string
required
Bearer token: Bearer YOUR_ACCESS_TOKEN
Request Example:
curl http://localhost:8000/auth/users/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response:
id
integer
User ID
email
string
User’s email address
full_name
string
User’s full name
is_active
boolean
Account status
is_superuser
boolean
Superuser status
{
  "id": 1,
  "email": "[email protected]",
  "full_name": "John Doe",
  "is_active": true,
  "is_superuser": false
}

GET /auth/validate

Validate the current session and refresh the list of available tenants. Headers:
Authorization
string
required
Bearer token: Bearer YOUR_ACCESS_TOKEN
Request Example:
curl http://localhost:8000/auth/validate \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response:
{
  "user": {
    "id": 1,
    "email": "[email protected]",
    "full_name": "John Doe",
    "is_active": true,
    "is_superuser": false
  },
  "available_tenants": [
    {
      "id": 1,
      "name": "Mi Empresa SPA",
      "rut": "12345678-9",
      "role_name": "ADMINISTRADOR",
      "is_active": true,
      "max_users": 5,
      "permissions": { ... }
    }
  ]
}

Using Authentication Tokens

After obtaining an access token, include it in the Authorization header for all authenticated requests:
curl http://localhost:8000/api/endpoint \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Tenant-ID: 1"

Token Format

The JWT token contains the following claims:
  • sub: User’s email address (subject)
  • exp: Token expiration timestamp (UTC)
Example decoded token payload:
{
  "sub": "[email protected]",
  "exp": 1709913600
}

Security Implementation

Password Hashing

Passwords are hashed using bcrypt algorithm via the passlib library:
  • Scheme: bcrypt
  • Deprecated schemes are automatically handled
  • Plain passwords are never stored
Reference: app/utils/security.py:15-21

Token Generation

Tokens are generated using the python-jose library:
from jose import jwt

def create_access_token(subject: str, expires_delta: timedelta = None) -> str:
    expire = datetime.now(timezone.utc) + expires_delta
    to_encode = {"exp": expire, "sub": str(subject)}
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt
Reference: app/utils/security.py:23-31

Token Validation

Token validation is handled by the get_current_global_user dependency:
  1. Extract token from Authorization header
  2. Decode JWT using SECRET_KEY
  3. Extract user email from “sub” claim
  4. Query database for user
  5. Return user object or raise 401 error
Reference: app/dependencies/tenant.py:34-56

Multi-Tenant Authentication Flow

Torn’s authentication works across two levels:

1. Global Authentication

First, authenticate at the SaaS level to get your access token:
curl -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "password"}'

2. Tenant Access

Use the token with X-Tenant-ID header to access tenant-specific resources:
curl http://localhost:8000/products/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: 1"
The API validates:
  • Token is valid and not expired
  • User has access to the specified tenant
  • User’s role has appropriate permissions
Reference: app/dependencies/tenant.py:59-82
Superusers can access any tenant, even without explicit membership. For regular users, access is validated through the TenantUser relationship.

Common Authentication Errors

{
  "detail": "Not authenticated"
}

Environment Variables

Authentication relies on the following environment variables:
SECRET_KEY
string
default:"super_secret_key_change_in_prod"
JWT signing secret key (MUST be changed in production)
Always set a strong, unique SECRET_KEY in production environments. Never use the default value.

Best Practices

  1. Store tokens securely: Keep access tokens in secure storage (httpOnly cookies, secure local storage)
  2. Handle token expiration: Implement token refresh logic or re-authentication after 12 hours
  3. Use HTTPS: Always use HTTPS in production to prevent token interception
  4. Validate permissions: Check user roles and permissions before performing operations
  5. Rotate secrets: Periodically rotate the SECRET_KEY in production environments

Next Steps

Build docs developers (and LLMs) love