Auth Container Overview

The Auth container is the runtime boundary for Authlance identity flows. It fronts Ory Kratos/Hydra, exposes API endpoints, runs the mail callbacks, and relays lifecycle events across Stripe and NATS. The service is distributed as a binary inside the Docker image and is deployed alongside Ory Kratos, Ory Hydra, MySQL, and NATS. Identity CRUD, consent, and OAuth token issuance are delegated to Kratos/Hydra while the Auth Container focuses on dashboard integration, session exchange, Stripe alignment, and emitting events that unblock worker fleets.

Architecture at a glance

                +-------------+      +----------------+
                |  Dashboard  |      |  Mobile / IoT  |
                +-------------+      +----------------+
                      |                      |
                      | /authlance/**        | /authlance/identity/api/**
                      v                      v
  +---------+   +---------------------+     +-----------+
  | Kratos  |<->|                     |<--->|   NATS    |
  +---------+   |   Auth Container    |     +-----------+
  | Hydra   |<->|  - loopToken JWT    |            |
  +---------+   |  - mail relay       |            v
                |  - Stripe webhooks  |     +------------------+
                |  - device mgmt      |     | Downstream       |
                |  - group secrets    |     | Postmark/workers |
                |  - App Store verify |     +------------------+
                +---------^-----------+
                          |
                  +-------+-------+
                  |               |
             +---------+   +-----------+
             | Stripe  |   | App Store |
             +---------+   +-----------+

Requests coming from the dashboard land on /authlance/** routes, get authenticated, and fan out to Kratos/Hydra for identity guarantees. Stripe webhooks flow in the opposite direction, hitting the Auth Container first so it can persist data, emit NATS events, and notify downstream workers. Mail requests are published to Postmark via the relay so template rendering is centralized.

Core responsibilities

  • Session exchange – Validates Kratos sessions and issues the short-lived JWT loopToken consumed by Authlance dashboards.
  • Tenant bootstrap – Seeds users, groups, and subscriptions on first login (when autoCreateGroup is enabled) and enforces profile completeness toggles (gender/birth-date extras).
  • Mail relay – Kratos courier posts to the internal /authlance/identity/email routes exposed on the mailAuth.port, and Authlance publishes those payloads to NATS for downstream delivery. The router is only reachable from within the cluster; it is not a public endpoint.
  • Payment webhooks – Accepts POST /authlance/payments/api/v1/stripe/webhook, validates signature headers, fan-outs events to the async workers, and publishes payment.success / payment.declined.
  • Device management – Registers, activates, and revokes devices with ECDH P-256 key exchange. Each device progresses through a defined lifecycle (NOT_REGISTERED → PENDING_ACTIVATION → ACTIVE → REVOKED) and the Auth Container emits NATS events at every transition so downstream services (mobile apps, IoT agents) can react in real time.
  • Group secrets & E2E encryption – Provides server-side storage for client-encrypted secrets. Encryption is performed entirely on the client using AES-256-GCM with ECDH P-256 key agreement and HKDF key derivation. The Auth Container never sees plaintext; it stores and distributes ciphertext, wrapped keys, and public key material.
  • App Store subscriptions – Validates Apple App Store receipts (StoreKit 2) alongside Stripe. When appStore.enabled is true, the Auth Container verifies signed transactions, maps iOS product IDs to payment tiers, and emits the same subscription.* events as Stripe-backed purchases.
  • OIDC social login – Integrates with Kratos OIDC providers (Google, Apple, etc.) via Docker Compose overlay files. The Auth Container seamlessly handles session exchange for OIDC-authenticated users through the same /authlance/identity/me flow.
  • Commands & automation – Ships CLI sub-commands (serve, backup, load-backup, stripe-webhook) for day-two tasks such as MySQL dumps and webhook provisioning.
  • Event emission – Publishes user logins, subscription transitions, payment states, device lifecycle changes, and license notifications to NATS so downstream services can react without polling.

Key routes

RouteMethodDescription
/authlance/identity/mePOSTExchanges a Kratos session ID for the Authlance JWT and sets the loopToken cookie.
/authlance/identity/me/logoutGETClears the loopToken cookie and invalidates the browser session.
/authlance/payments/api/v1/stripe/webhookPOSTStripe webhook endpoint used to forward events into the async payment pipeline.
/authlance/identity/api/v1/devicesGET/POSTLists devices for the current user or registers a new device with its ECDH public key.
/authlance/identity/api/v1/devices/:id/activatePOSTActivates a pending device by exchanging wrapped keys. Emits device.activated.
/authlance/identity/api/v1/devices/:id/revokePOSTRevokes an active device and emits device.revoked.
/authlance/identity/api/v1/secretsGET/PUTRetrieves or updates client-encrypted group secrets (ciphertext only).
/authlance/identity/api/v1/patsGET/POSTLists or creates personal access tokens scoped to configured route scopes.

All routes are mounted under the NGINX configuration shipped in the docker container, so the public paths match what the dashboard expects (/authlance/...).

Operational checklist

  1. Configuration – Populate /app/config/config.yaml with database credentials, Kratos/Hydra URLs, JWT signing key, NATS URL, Stripe webhook secret, and S3 settings. See the configuration guide for details.
  2. Commands – Familiarize yourself with the CLI workflows (serve, backup, load-backup, stripe-webhook) documented in the Auth Container Commands reference.
  3. Events – Subscribe to the subjects described in Auth Container NATS Events to automate provisioning, billing, and observability.
  4. Secrets – Store sensitive values (JWT key, database password, Stripe credentials, S3 keys) in a secret manager or .env file consumed by the compose templates.
  5. Monitoring – Tail the authlance container logs, monitor /healthz, and track user.authenticated or subscription.* events for real-time health signals.

Mail relay examples

Kratos courier (or any other internal system) issues basic-auth requests against http://authlance:<mailAuth.port>/authlance/identity/email. Those JSON payloads are validated, logged, and published to the mail.send subject on NATS. Downstream workers (e.g., Postmark relays) expect the SendEmailMessage contract:

{
  "to": "sam@example.com",
  "from": "Authlance <team@example.com>",
  "templateId": "password-reset",
  "templateModel": {
    "email": "sam@example.com",
    "name": {
      "first": "Sam",
      "last": "Wright"
    },
    "recovery_url": "https://dashboard.example.com/reset?token=abc",
    "verification_url": "",
    "recovery_code": "",
    "verification_code": ""
  }
}

Templates are React components in the authlance/mails repository. Each key inside templateModel becomes an injected prop so designers can preview emails locally and deploy them alongside the binary. Postmark delivers the final message, while Auth Container logs each relay and the subsequent NATS events for audit trails.

NATS event examples

Events are emitted to subjects like subscription.updated and payment.success. Consumers should subscribe with durable queue groups so retries are handled by NATS instead of reprocessing Stripe webhooks. Representative payloads:

// subscription.updated
{
  "tierName": "pro",
  "group": {
    "id": 42,
    "name": "redfruit"
  },
  "status": "active"
}

To know more details about all the events emitted check the NATS Events documentation.

Downstream workers typically hydrate these events with tenant metadata and trigger provisioning, feature unlocks, or license file regeneration. Keep handlers idempotent so replays are safe.

loopToken details

  • Claims – Issued as a JWT that embeds the normalized user payload (identity ID, email, first/last name, roles, group memberships, verification state). It intentionally omits tenant metadata and feature flags so downstream services always re-fetch those from the API.
  • Purpose – Used by UI widgets and API requests to prove a Kratos session was validated by Authlance. It is not a replacement for Hydra-issued OAuth tokens.
  • Renewal – The dashboard silently calls /authlance/identity/me when the cookie is close to expiring; Kratos sessions stay long-lived while loopToken rotates.
  • Third parties – Only first-party dashboards and services behind Authlance should rely on loopToken. External apps should use OAuth2/OIDC via Hydra or Personal Access Tokens.

Kratos and Hydra integration

Registration, login, password recovery, and OAuth consent all live inside Kratos/Hydra. The Auth Container fronts these services by validating browser sessions, owning the /authlance/** routes, and performing session exchange to mint loopToken plus dashboard-ready claims. This keeps the Kratos/Hydra APIs private while still letting the dashboard and workers rely on a consistent Authlance surface.

Personal access tokens

Automation can call the REST API using personal access tokens (PATs) instead of loopToken cookies. Admins create them under /authlance/identity/api/v1/pats, and every request is scoped by the personalAccessTokens.routeScopes configuration. The middleware enforces scopes before hitting controllers, so only the paths you list in the config can ever be reached with a PAT. Tokens are verified via /authlance/identity/api/v1/pats/verify which returns metadata (owner, group, scope, expiry) for auditing.

Scope hierarchy

PATs support a two-level scope system:

  • public_scope – Read-only access to public endpoints (profile, group info). Suitable for CI integrations and monitoring.
  • full_scope – Full access to all configured routes, including write operations. Use for admin automation and backend-to-backend calls.

Scopes are enforced per route using path matching with support for path variables (:id) and wildcards (/*). A PAT with public_scope cannot reach routes that require full_scope, even if the path pattern matches.

Device management

The Auth Container tracks devices per user, enabling multi-device workflows such as mobile app registration, IoT onboarding, and secure key distribution. Each device follows a lifecycle:

NOT_REGISTERED → PENDING_ACTIVATION → ACTIVE → REVOKED
  • Register – The client sends its ECDH P-256 public key to POST /authlance/identity/api/v1/devices. The device enters PENDING_ACTIVATION and a device.registered event is emitted to NATS.
  • Activate – An admin (or another trusted device) calls POST /devices/:id/activate with the wrapped group key. The device transitions to ACTIVE and a device.activated event is emitted, allowing the device to decrypt group secrets.
  • RevokePOST /devices/:id/revoke transitions to REVOKED. A device.revoked event is emitted so downstream services can invalidate cached keys.

Devices are scoped to the user's active group. The dashboard UI renders a device management table where group admins can approve pending devices, view active devices, and revoke compromised ones.

Group secrets & E2E encryption

Authlance supports client-side end-to-end encryption for group secrets. The cryptographic operations happen entirely in the browser (or native app) using the Web Crypto API:

  • Key agreement – ECDH P-256 key pairs. Each device generates a key pair during registration. Group keys are derived using HKDF with SHA-256 and the info string "group-key-encryption".
  • Encryption – AES-256-GCM with random 12-byte IVs. The server stores only ciphertext, IVs, and wrapped keys.
  • Key distribution – When a new device is activated, an existing trusted device wraps the group encryption key with the new device's ECDH public key and uploads the wrapped key through the activation endpoint.

This zero-knowledge design means the Auth Container never sees plaintext secrets. Extensions can use the useGroupSecrets and useKeyStatus hooks from @authlance/identity to read and write secrets, while initializeSecrets bootstraps the key hierarchy for a new group.

OIDC social login

The Docker Compose deployment supports social login through Ory Kratos OIDC providers. To enable Google login (or other providers), pass the --enable-oidc flag to start.sh:

./start.sh --enable-oidc

This merges docker-compose.oidc.yml into the stack, which renders kratos.yml.oidc.template instead of the base template and copies the provider-specific Jsonnet mapper (e.g., oidc.google.jsonnet). Set OIDC_GOOGLE_CLIENT_ID and OIDC_GOOGLE_CLIENT_SECRET in .env before starting. The session exchange flow through /authlance/identity/me works identically for OIDC-authenticated users.

App Store subscriptions

When appStore.enabled is true in config.yaml, the Auth Container accepts Apple App Store signed transactions alongside Stripe. The configuration requires:

  • bundleId – Your iOS app's bundle identifier.
  • rootCerts – Paths to Apple root certificates for transaction verification.
  • allowSandbox – Set to true during development to accept sandbox receipts.

Each payment tier can declare an iosProductId and platforms array. When the backend receives a StoreKit 2 signed transaction, it maps the product ID to a tier, creates or updates the subscription, and emits the standard subscription.* NATS events. This enables a unified subscription model where iOS in-app purchases and Stripe web purchases produce identical downstream behavior.

Common flows

  • Login → session → loopToken – User completes the Kratos login UI, the dashboard calls /authlance/identity/me with the Kratos session cookie, and Authlance returns loopToken + the normalized user object for rendering.
  • User signup → tenant bootstrap – On the first login (with autoCreateGroup=true), Authlance seeds the tenant, adds the user to the default group, enforces extra profile fields, and subsequently emits user.authenticated to NATS so workers can react.
  • Subscription → Stripe webhook → NATS event – Checkout flows hit Stripe, the webhook posts to /authlance/payments/api/v1/stripe/webhook, Authlance verifies the signature, updates MySQL, and publishes payment.success / subscription.updated so downstream workers can provision seats or licenses.
  • Device registration → key exchange → secrets access – A new device registers with its public key, an admin activates it by wrapping the group key, and the device can then decrypt group secrets client-side. NATS events fire at each step.
  • OIDC login → session exchange – User authenticates via a social provider (Google, Apple), Kratos handles the OIDC callback and creates a session, and the dashboard exchanges it for loopToken through the standard /authlance/identity/me endpoint.
  • iOS purchase → subscription sync – The iOS app completes a StoreKit 2 purchase, sends the signed transaction to the Auth Container, which verifies it, maps the product to a tier, and emits subscription.created/subscription.updated.

Deployment notes

  • The container runs NGINX and the Go binary side by side. start-authlance.sh launches both and terminates cleanly on signals.
  • /app/config is expected to be a read-only mount. Docker Compose templates render it via ory-templates and share the volume with the runtime container.
  • When deploying on Kubernetes, using a nginx Controller you can configure to have multiple replicas.

Use this overview as a primer, then jump into the dedicated configuration, commands, and events guides to wire up the container for your environment.