CRTX
DocsSign in →

Authentication

Every CRTX API endpoint requires a valid Supabase-issued JWT in the Authorization header. There are no anonymous endpoints.

Authorization: Bearer <access_token>

#How it works today

CRTX delegates identity to Supabase Auth. The flow:

  1. A user signs in (email/password) through Supabase — in the frontend this is the browser Supabase client; programmatically it's the Supabase Auth API.
  2. Supabase returns an access token (a JWT signed with ES256) and a refresh token.
  3. Your client sends the access token as a Bearer token on every API call.
  4. The backend validates it against Supabase's JWKS endpoint:
    • fetches the signing key for the token (cached in-process),
    • verifies the ES256 signature,
    • requires audience = "authenticated",
    • enforces expiry.
  5. On success, the decoded claims are available to the endpoint; sub is the user id used for all ownership and access checks.

Invalid or missing tokens return 401 (Not authenticated, Token expired, or Invalid token). No access to a resource returns 403.

Relevant config: SUPABASE_JWKS_URL (the well-known JWKS URL for your project). The Next.js frontend additionally enforces auth at the edge via middleware — unauthenticated browser requests are redirected to /login.


#Getting a token programmatically

For scripts and server-to-server use, sign in via the Supabase Auth REST API to obtain an access token, then call CRTX with it.

# 1. Sign in to Supabase to get an access_token
TOKEN=$(curl -s -X POST \
  "$SUPABASE_URL/auth/v1/token?grant_type=password" \
  -H "apikey: $SUPABASE_ANON_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com", "password": "…" }' \
  | jq -r .access_token)

# 2. Call CRTX
curl "$BASE_URL/collections/" -H "Authorization: Bearer $TOKEN"
# Python, using the Supabase client
from supabase import create_client

sb = create_client(SUPABASE_URL, SUPABASE_ANON_KEY)
session = sb.auth.sign_in_with_password({"email": EMAIL, "password": PASSWORD})
token = session.session.access_token

import httpx
r = httpx.get(f"{BASE_URL}/collections/",
              headers={"Authorization": f"Bearer {token}"})

Access tokens are short-lived; refresh them with the Supabase refresh token (the client SDKs do this automatically). Build retry-on-401 + refresh into any long-running integration.


#Admin claims

Some operations are gated to admins via a custom claim. The retrieval-config endpoints (GET/PUT /collections/{id}/config) require app_metadata.is_admin to be truthy on the user. Set this claim in Supabase (via the Admin API / a service-role update to the user's app_metadata) for operator accounts.


#Limitations of the current model (and what's next)

The JWT model is designed for interactive users. For headless automation, CI pipelines, embedding CRTX into another product, or giving customers scoped access, a per-user JWT is awkward — it expires, it represents a human, and it carries that human's full access.

The recommended answer is a dedicated API-key layer that sits alongside JWT auth. That design — hashed keys, scopes, per-collection restriction, rate limiting, webhooks, and an embeddable widget — is written up in Programmatic access & API keys.

See the full endpoint contract in the API reference.