Skip to content

Authentication API

The Authentication API provides endpoints for user registration, login, token management, and session control.


Overview

CalcBridge uses JWT (JSON Web Tokens) for authentication. After successful login, you receive:

  • Access Token: Short-lived token (15 minutes) for API requests
  • Refresh Token: Long-lived token (7 days) to obtain new access tokens

Endpoints

Register User

Create a new user account and tenant organization.

POST /api/v1/auth/register

Request Body

Field Type Required Description
email string Yes User email address
password string Yes Password (min 8 chars, must include uppercase, lowercase, digit)
full_name string Yes User's full name
organization_name string Yes Organization/tenant name

Example Request

curl -X POST https://api.calcbridge.io/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "SecurePass123!",
    "full_name": "John Smith",
    "organization_name": "Acme Capital"
  }'
import requests

response = requests.post(
    "https://api.calcbridge.io/api/v1/auth/register",
    json={
        "email": "user@example.com",
        "password": "SecurePass123!",
        "full_name": "John Smith",
        "organization_name": "Acme Capital"
    }
)
tokens = response.json()
print(f"Access Token: {tokens['access_token']}")
const response = await fetch('https://api.calcbridge.io/api/v1/auth/register', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'SecurePass123!',
    full_name: 'John Smith',
    organization_name: 'Acme Capital'
  })
});
const tokens = await response.json();
console.log('Access Token:', tokens.access_token);

Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer"
}

Error Responses

Status Error Description
400 Email already registered User with this email exists
422 Validation error Invalid email format or weak password

Login

Authenticate and obtain access tokens.

POST /api/v1/auth/login

Request Body

Uses OAuth2 password flow (application/x-www-form-urlencoded):

Field Type Required Description
username string Yes User email address
password string Yes User password

Example Request

curl -X POST https://api.calcbridge.io/api/v1/auth/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=user@example.com&password=SecurePass123!"
import requests

response = requests.post(
    "https://api.calcbridge.io/api/v1/auth/login",
    data={
        "username": "user@example.com",
        "password": "SecurePass123!"
    }
)
tokens = response.json()
access_token = tokens["access_token"]
const formData = new URLSearchParams();
formData.append('username', 'user@example.com');
formData.append('password', 'SecurePass123!');

const response = await fetch('https://api.calcbridge.io/api/v1/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: formData
});
const tokens = await response.json();

Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer"
}

Error Responses

Status Error Description
401 Incorrect email or password Invalid credentials
401 User account is deactivated Account has been disabled

Refresh Token

Obtain a new access token using a refresh token.

POST /api/v1/auth/refresh

Request Body

Field Type Required Description
refresh_token string Yes Valid refresh token

Example Request

curl -X POST https://api.calcbridge.io/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
import requests

response = requests.post(
    "https://api.calcbridge.io/api/v1/auth/refresh",
    json={"refresh_token": refresh_token}
)
new_tokens = response.json()
const response = await fetch('https://api.calcbridge.io/api/v1/auth/refresh', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ refresh_token: refreshToken })
});
const newTokens = await response.json();

Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer"
}

Error Responses

Status Error Description
401 Invalid refresh token Token is malformed or expired
401 Refresh token has been revoked Token was explicitly revoked
401 All tokens have been revoked User called revoke-all-tokens

Get Current User

Retrieve information about the authenticated user.

GET /api/v1/auth/me

Headers

Header Required Description
Authorization Yes Bearer <access_token>

Example Request

curl -X GET https://api.calcbridge.io/api/v1/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
import requests

response = requests.get(
    "https://api.calcbridge.io/api/v1/auth/me",
    headers={"Authorization": f"Bearer {access_token}"}
)
user = response.json()
print(f"Logged in as: {user['email']}")
const response = await fetch('https://api.calcbridge.io/api/v1/auth/me', {
  headers: { 'Authorization': `Bearer ${accessToken}` }
});
const user = await response.json();
console.log('Logged in as:', user.email);

Response

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "full_name": "John Smith",
  "tenant_id": "660e8400-e29b-41d4-a716-446655440001",
  "role": "owner"
}

Logout

Invalidate the current access token.

POST /api/v1/auth/logout

Headers

Header Required Description
Authorization Yes Bearer <access_token>

Example Request

curl -X POST https://api.calcbridge.io/api/v1/auth/logout \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
import requests

response = requests.post(
    "https://api.calcbridge.io/api/v1/auth/logout",
    headers={"Authorization": f"Bearer {access_token}"}
)
# Returns 204 No Content on success
await fetch('https://api.calcbridge.io/api/v1/auth/logout', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${accessToken}` }
});
// Returns 204 No Content on success

Response

Returns 204 No Content on success.


Revoke All Tokens

Invalidate all tokens for the current user. Use this for security incidents.

POST /api/v1/auth/revoke-all-tokens

Headers

Header Required Description
Authorization Yes Bearer <access_token>

Example Request

curl -X POST https://api.calcbridge.io/api/v1/auth/revoke-all-tokens \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
import requests

response = requests.post(
    "https://api.calcbridge.io/api/v1/auth/revoke-all-tokens",
    headers={"Authorization": f"Bearer {access_token}"}
)
result = response.json()
print(f"Revoked {result['tokens_revoked']} tokens")
const response = await fetch('https://api.calcbridge.io/api/v1/auth/revoke-all-tokens', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${accessToken}` }
});
const result = await response.json();
console.log(`Revoked ${result.tokens_revoked} tokens`);

Response

{
  "message": "All tokens have been revoked. Please log in again.",
  "tokens_revoked": 1
}

API Key Authentication

For service-to-service integrations, use API keys instead of JWT tokens.

Using API Keys

Include the API key in the X-API-Key header:

curl -X GET https://api.calcbridge.io/api/v1/workbooks \
  -H "X-API-Key: cb_live_a1b2c3d4e5f6g7h8i9j0..."

API Key Format

  • Live keys: cb_live_ prefix
  • Test keys: cb_test_ prefix

API Key Permissions

API keys can be scoped to specific permissions:

Permission Description
workbooks:read List and view workbooks
workbooks:write Create, update, delete workbooks
calculations:execute Run formula evaluations
compliance:read View compliance results
compliance:write Run compliance tests
scenarios:read View scenarios
scenarios:write Create and run scenarios

Creating API Keys

API keys are created via the Admin API or dashboard. See Admin API for details.


Token Structure

Access Token Claims

{
  "sub": "550e8400-e29b-41d4-a716-446655440000",
  "tenant_id": "660e8400-e29b-41d4-a716-446655440001",
  "role": "owner",
  "type": "access",
  "jti": "unique-token-id",
  "iat": 1706200000,
  "exp": 1706200900
}
Claim Description
sub User ID
tenant_id Tenant/organization ID
role User role (owner, admin, analyst)
type Token type (access or refresh)
jti Unique token identifier
iat Issued at timestamp
exp Expiration timestamp

Password Requirements

Passwords must meet the following criteria:

  • Minimum 8 characters
  • At least one uppercase letter (A-Z)
  • At least one lowercase letter (a-z)
  • At least one digit (0-9)

Security Best Practices

  1. Store tokens securely: Never store tokens in localStorage for sensitive applications. Use httpOnly cookies or secure storage.

  2. Implement token refresh: Refresh access tokens before they expire to maintain seamless user sessions.

  3. Handle token expiration: Implement retry logic that refreshes tokens on 401 responses.

  4. Revoke on logout: Always call the logout endpoint when users sign out.

  5. Use HTTPS: Always use HTTPS in production to protect tokens in transit.

  6. Rotate API keys: Regularly rotate API keys and use short-lived keys when possible.


Rate Limits

Authentication endpoints have specific rate limits to prevent brute-force attacks:

Endpoint Limit Window
/auth/register 5 per hour per IP
/auth/login 10 per minute per IP
/auth/refresh 30 per minute per user
/auth/logout 10 per minute per user