Skip to main content

Token Service

Token Service is the Wormhole authority for creating, validating, rotating, and exchanging Wormhole access tokens.

Table of Contents

Description

Token Service is a Python FastAPI service that manages Wormhole access tokens. It creates and stores user/admin tokens, validates X-Token credentials, exchanges valid tokens or MFA/OIDC sessions for short-lived JWTs, publishes JWKS for downstream validation, and exposes internal admin APIs for identity sync and impersonated token creation. Major dependencies include FastAPI, fastapi-offline, SQLAlchemy, Alembic, Dynaconf, Authlib, bcrypt, Pendulum, psycopg2, attrs, tenacity, requests, and multimethod. The bundled UI is built with Node 18 or newer, Mithril, Tailwind, and Webpack.

The service uses a layered architecture. token_service/command.py provides the CLI entry point for run, openapi, and generate-jwks; token_service/server.py assembles FastAPI routes and versioned API aliases; token_service/routers contains public token, MFA, and well-known JWKS endpoints; token_service/internal contains admin routes for users, groups, and tokens; token_service/models.py contains attrs domain models; token_service/pydantic_models.py defines API schemas; token_service/store and token_service/service/uow.py implement SQLAlchemy repositories and unit of work; and token_service/services.py implements token lifecycle logic.

Within Wormhole, Token Service is the identity and token authority. Holepunch uses it to exchange Wormhole access tokens and OAuth sessions for short-lived JWTs; Route Registry uses it to authenticate callers and validate JWTs; Airlock uses its JWKS endpoint to validate tokens before forwarding requests to local apps; and users or automation acquire tokens to access Wormhole-protected apps without managing a separate credential per service.

How Token Service Fits Into Wormhole

flowchart LR
    User["User or automation"]
    UI["Token Service UI/API"]
    DB["Postgres"]
    JWKS["JWKS endpoint"]
    Holepunch["Holepunch"]
    Registry["Route Registry"]
    Airlock["Airlock"]
    App["Wormhole app"]

    User --> UI
    UI --> DB
    UI --> JWKS
    Holepunch --> UI
    Registry --> UI
    Airlock -. validates JWTs .-> JWKS
    Holepunch --> App
    Registry --> App

The service connects enterprise authentication to Wormhole tokens and JWTs so interactive browser access and automated API access use the same access-control foundation.

Configuration

Token Service uses Dynaconf. Defaults live in token_service/config/settings.toml; local overrides can be supplied with settings.toml, settings.local.toml, .secrets.toml, or DYNACONF_* environment variables.

Important configuration groups include:

Group Purpose
SERVER FastAPI host and port.
DB SQLAlchemy database URL and credentials.
AUTH.authlib_oidc OIDC provider, session cookie, redirect, and discovery settings.
AUTH.jwt JWT algorithm, signing keys, and key ID.
AUTH.admin Admin API secret material.
TOKEN Session name, session lifetime, and maximum token lifetime.

Generate JWKS material per environment:

uv run token_service generate-jwks --write-settings --overwrite

This creates jwks/private.pem, jwks/public.pem, jwks/kid.txt, and a local settings override when requested. Treat generated private keys as secrets.

Deployment

OpenShift and Helm

The Helm chart in helm/token-service deploys the app, services, ingress, service account, and config map. The app deployment loads non-secret Dynaconf settings from token-service-config, loads secret settings from token-service-secrets, and runs /app/scripts/migrate-db in an init container before the API starts.

Create the required secrets before deploying.

1. Initialize PostgreSQL

Connect to PostgreSQL with psql and create the database, user, and schema:

CREATE DATABASE "tokenServiceDB";
CREATE USER "tokenServiceUser" WITH PASSWORD 'yourStrongPassword';
\c tokenServiceDB
CREATE SCHEMA "tokenServiceUser" AUTHORIZATION "tokenServiceUser";

Validate the connection:

psql -U tokenServiceUser -d tokenServiceDB -h localhost -p 5432

2. Create token-service-secrets

Required keys:

  • DYNACONF_AUTH__admin__secret_key: randomly generated admin secret.
  • DYNACONF_AUTH__authlib_oidc__client_id: client ID registered with the OAuth provider.
  • DYNACONF_AUTH__authlib_oidc__client_secret: client secret from the OAuth provider.
  • DYNACONF_AUTH__authlib_oidc__session_config__secret_key: randomly generated session secret.
  • DYNACONF_AUTH__jwt__private_pem: JWT private PEM generated by token_service generate-jwks.
  • DYNACONF_AUTH__jwt__public_pem: JWT public PEM generated by token_service generate-jwks.
  • DYNACONF_DB__username: postgres username.
  • DYNACONF_DB__password: postgres password.
  • DYNACONF_DB__url: postgres connection string.

3. Deploy Token Service

Deploy with the Helm values for the target environment:

helm upgrade --install token-service ./helm/token-service -f ./helm/token-service/values.yaml

Use the appropriate overlay and namespace for the target deployment.

CI builds the UI, publishes OpenAPI, publishes the Python package and container image, copies images into OpenShift, and deploys with Helm.

Development

Requirements:

  • Python 3.11 or newer
  • uv
  • Node 18 or newer for the UI
  • Postgres for migration work

Install locally:

uv venv
uv pip install -e .

Pre-Commit Hook:

uvx pre-commit install

This runs basic lint checks on the repository. We highly encourage using this hook to avoid unnecessary lint CI failures.

Generate local JWKS/settings:

uv run wormhole_token_service generate-jwks --write-settings --overwrite

Run the API:

uv run wormhole_token_service run --host localhost --port 5000

Run migrations:

uv run alembic upgrade head

Generate OpenAPI:

uv run wormhole_token_service openapi

Build the UI:

cd token_service/ui
npm ci
npm run build

The UI build is bundled into token_service/ui/dist.

API and Token Notes

Important public endpoints include:

Endpoint Purpose
/.well-known/jwks.json Publishes public keys used to validate JWTs.
/api/v1/token/jwt Exchanges a valid Wormhole access token for a short-lived JWT.
/api/v1/mfa/jwt Exchanges a valid OIDC/MFA session for a short-lived JWT.
/api/v1/token/rotate Rotates eligible tokens after attestation.

Important internal/admin endpoints include user, group, and admin token management routes under /api/v1/admin. Admin token creation is implemented at:

/api/v1/admin/token

Token strings are represented as id.secret. Normal token secrets are stored as bcrypt hashes. Subtokens are delegated child credentials that inherit parent token lifetime constraints. JWT claims include user, group, scope, DUID, parent-token, external ID, and token ID context used by downstream Wormhole components.

User Syncing Bootstrap

If you have an identity syncing service, it needs an admin token so it can populate Token Service identity tables.

Use this bootstrap flow for a new environment.

1. Add a temporary admin user

INSERT INTO "tokenServiceUser"."user" (uid, is_admin, duid)
VALUES ('<your user>', TRUE, 1);

2. Authenticate to Token Service

Open this URL in a browser and complete authentication:

https://<token-service>/api/v1/mfa/jwt

Use browser developer tools to find the token-service-session cookie.

3. Create the Sync admin token

curl -H "Cookie: token-service-session={cookie}" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"name": "sync", "role": "IDENTITY"}' \
  https://<token-service>/api/v1/admin/token

4. Store Secret

Store the secret where your user syncing service expects it.

5. Remove the temporary admin user

DELETE FROM "tokenServiceUser"."user" WHERE uid = '<your user>';

6. Verify cron behavior

When a new sync job starts, it should populate the user table in the Token Service database.

Testing

Run lint checks:

uvx tox -e lint

Run the Python test suite used by CI:

uvx tox -e py311

Run non-integration tests directly:

uv run pytest -m "not integration"

Tox runs Ruff checks and format checks for token_service and tests.

Developer Notes

  • token_service/server.py mounts versioned aliases under /api/v1, /api/latest, and /api/stable.
  • token_service/dependencies.py owns auth dependency wiring for OIDC sessions, admin tokens, user tokens, and impersonation flows.
  • token_service/services.py owns token creation, validation, JWT issuance, attestation, and rotation logic.
  • token_service/store/orm.py contains SQLAlchemy table mappings; keep Alembic migrations in sync with model changes.
  • E2E tests start a real Uvicorn server with fake OIDC dependencies.

Governance

Contributions are welcome. Contributors should look in CONTRIBUTING.md for project guidelines on how to create and structure pull requests.

This project is licensed under the Apache 2.0 license with LLVM exception. The full license text is available in LICENSE.

LLNL-CODE-2020712

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wormhole_token_service-0.2.2.tar.gz (235.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wormhole_token_service-0.2.2-py3-none-any.whl (70.8 kB view details)

Uploaded Python 3

File details

Details for the file wormhole_token_service-0.2.2.tar.gz.

File metadata

  • Download URL: wormhole_token_service-0.2.2.tar.gz
  • Upload date:
  • Size: 235.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for wormhole_token_service-0.2.2.tar.gz
Algorithm Hash digest
SHA256 0441c3cc7ec1ffb19c6b083daea9a97f6cb645d928b057d8fe01baf63381d512
MD5 1e964f697dfdefdb0628e937a093279b
BLAKE2b-256 02bacbe9829da509a50f145a60bf9ccf0aa5fa8bdea90107c28d79ac55451b78

See more details on using hashes here.

Provenance

The following attestation bundles were made for wormhole_token_service-0.2.2.tar.gz:

Publisher: release.yml on llnl/wormhole-token-service

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wormhole_token_service-0.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for wormhole_token_service-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 035d6937c80ad60fb4ebdf628c33ad8b0e2eed377d7b2a2a974dd102f440ac03
MD5 a3fc58be476771f84c58940ffa7d3ab7
BLAKE2b-256 66f5c154f93a1ee9f161aa1732417e40cb3f65924b7d022154a9c7cd433ed573

See more details on using hashes here.

Provenance

The following attestation bundles were made for wormhole_token_service-0.2.2-py3-none-any.whl:

Publisher: release.yml on llnl/wormhole-token-service

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page