Skip to main content
Strait supports multiple authentication mechanisms and a layered authorization model: API key scopes for machine access and role-based access control (RBAC) for user access.

Authentication Schemes

1. Internal Secret

A global shared secret for administrative tasks and internal service-to-service communication.
Configure via the INTERNAL_SECRET environment variable.
Internal secret auth grants full access to all endpoints — no scope or role checks are applied. This is the path used by the app to communicate with Strait.

2. API Keys

Per-project API keys with scoped permissions. Recommended for external integrations.
API keys:
  • Start with strait_ prefix
  • Are SHA-256 hashed before storage
  • Track last_used_at timestamps
  • Support expires_at for time-limited access
  • Can be revoked at any time
  • Support rotation with grace periods (replaced_by_key_id, grace_expires_at)
  • Are scoped to a project and a set of permissions

3. JWT Run Tokens (SDK)

Short-lived JWTs scoped to a specific run, used by the SDK for /sdk/v1 endpoints.
  • Algorithm: HS256
  • Subject: The runID
  • Signing Key: JWT_SIGNING_KEY environment variable

4. OIDC Bearer Tokens (Optional)

For user-facing API calls, non-strait_ bearer tokens can be validated via OIDC when configured.
  • Configure issuer/audience/public-key verifier settings in server config
  • Valid tokens are mapped to actor identity and project context
  • OIDC and internal-secret header-based identity can coexist during rollout

API Key Scopes

Every API key has a list of scopes that control what it can do. Scopes are enforced on every request.

Available Scopes

Backwards Compatibility

API keys with empty scopes ([]) retain full access. This ensures pre-existing keys continue to work after the scope enforcement upgrade.

Scope Validation

Unknown scopes are rejected at API key creation time:

Actor Identity

Every request is attributed to an actor for audit purposes.

How Actor Identity Works

API key requests: The actor is always apikey:<key-id> with type api_key. Actor headers (X-Actor-Id) are ignored on API key requests to prevent impersonation. Internal secret requests: The app can pass user identity via headers:
When these headers are present, Strait:
  1. Sets actor_type = "user" and actor_id = "user_abc123" in the request context
  2. Applies X-Project-Id to request context when project-scoped authorization is required
  3. Asynchronously syncs the actor profile to the known_actors table
  4. Records the actor as created_by or updated_by on mutations

Audit Columns

The following tables have audit columns:
  • jobscreated_by, updated_by
  • workflowscreated_by, updated_by
  • job_runscreated_by
  • workflow_runscreated_by
These are populated automatically from the request actor context.
Security: API key auth never honors X-Actor-Id headers. This prevents API key holders from impersonating users to gain RBAC permissions. Only the internal secret auth path trusts actor headers.

Role-Based Access Control (RBAC)

RBAC provides fine-grained access control for users (as opposed to API keys, which use scopes).

How It Works

  1. Roles define a set of permissions (same scope strings as API keys)
  2. Members link a user (from your external auth provider) to a role within a project
  3. When a user makes a request (via internal secret + X-Actor-Id), the middleware loads their role and checks permissions

System Roles

Four built-in system roles are available: System roles cannot be modified or deleted.

Custom Roles

Create project-specific roles with any combination of permissions:

Managing Members

Assign a user to a role:
List project members:
Remove a member:

Permission Resolution

The requirePermission middleware resolves authorization in this order:
  1. Internal secret (no scopes in context) → allow immediately
  2. API key → check if key’s scopes include the required permission
  3. User → load role permissions from DB (cached for 30s), check if role includes the required permission

Permission Cache

User permissions are cached for 30 seconds to avoid hitting the database on every request. The cache is automatically invalidated when:
  • A member’s role is changed (POST /v1/members)
  • A member is removed (DELETE /v1/members/{userID})

Middleware Chain

Authentication flows through these middleware in apps/strait/internal/api/middleware.go:
  1. apiKeyOrSecretAuth — Routes to API key auth (strait_ bearer), OIDC bearer auth (optional), or internal secret auth
  2. apiKeyAuth — Validates key hash, checks revocation/expiration/rotation grace, sets scopes + actorType=api_key in context
  3. internalSecretAuth — Constant-time secret comparison, optionally extracts actor from X-Actor-* and project from X-Project-Id
  4. oidcAuth — Validates configured OIDC bearer tokens and maps claims into actor/project context
  5. requirePermission(scope) — Checks authorization based on actor type (see Permission Resolution above)
  6. runTokenAuth — JWT validation for SDK endpoints only

Managing API Keys

Create

The response includes the raw key (shown only once):

List

Revoke

Revoked keys immediately return 401 on subsequent requests.

Rotate (with Grace Period)

Rotation issues a new key and marks the previous key as replaced. The old key can remain valid until grace_expires_at to support zero-downtime credential rollouts.