1. playbooks
temper
  • Temper documentation
  • cli
    • README
    • admin
    • auth
    • check
    • cogmap
    • config
    • context
    • edge
    • init
    • invitations
    • invocation
    • memory
    • pull
    • query
    • resource
    • search
    • skill
    • slack
    • status
    • steward
    • team
    • trail
    • update
    • version
    • warmup
  • config
    • README
  • auth
  • guides
  • doors
    • Building against Temper
    • Running Temper
    • Using Temper
  • concepts
    • Auth Identity
    • Authoring Authorization
    • Contexts and Refs
    • Machine Tokens
    • Operational Memory
    • Release Verification
    • The SAML Reconcile Channel
    • Slack Identity and Revocation
    • Teams and Roles
    • Telemetry
    • Token Verification
    • The Trust Boundary
  • playbooks
    • Adopt operational memory
    • Authenticate
    • Bootstrap an Org
    • Bootstrap a Team's Self-Cognition
    • Build a cognitive map
    • Connect Claude Code
    • Connect Temper to Claude Desktop
    • Deliver L0 Kernel Content
    • Deploy a Steward Agent
    • Deploy the Web UI
    • Enterprise Install
    • Provision a Read-Only GitHub Connection
    • Ingest a corpus
    • Install Temper
    • Run a Team
    • Self-Host Temper
    • Self-host Temper with Okta
    • Self-host Temper with a SAML IdP
    • Send traces to an OTLP backend
    • Slack Mentions
    • Standing Up a Machine Credential
  • sdks
    • Integrating Ruby with temper-rb
  • Temper Cloud API
    • Access
      • POST /api/access/requests — submit a join request for the gating team.
      • GET /api/access/requests/me — check own join request status.
      • DELETE /api/access/requests/me — withdraw a pending join request.
      • POST /api/access/reviews — a revoked principal asks an admin to reconsider (spec D15).
      • GET /api/access/settings — read public system settings.
    • Slack Link
      • Disconnect any principal. Operator path — offboarding and stuck users.
      • Disconnect EVERY Slack principal bound to the caller's own profile.
    • Auditor
      • auditor_dispatch
      • auditor_sweep
      • complete_auditor_job
    • Graph
      • POST /api/cogmaps/{id}/graph/slice — R4 cogmap-scoped neighborhood slice.
      • GET /api/graph/cogmaps/{id}/panorama — enter-a-cogmap Tier-0 interior.
      • GET /api/graph/contexts/composition — Beat E Tier-1: the force-graph composition of a container's (or a residual bucket's) members.
      • GET /api/graph/contexts/panorama — Beat E Tier-0: goal-container territories + residual tray.
      • GET /api/graph/home — the you→teams→cogmaps membership home.
      • GET /api/graph/regions/composition — Beat D region→resources composition drill.
    • Cognitive Maps
      • list_cognitive_maps
      • genesis
      • get_cognitive_map
      • reconcile
      • analytics
      • grant_cogmap_access
      • revoke_cogmap_access
      • materialize
      • materialize_delta
      • region_metrics
      • shape
      • bind_team
      • unbind_team
    • Contexts
      • list_contexts
      • create_context
      • get_context
      • context_materialize
      • reassign
      • context_region_metrics
      • rename
      • context_shape
      • share_team
      • unshare_team
    • Events
      • cursor
      • GET /api/graph/elements/{kind}/{id}/trail — R5 element event-trail. kind ∈ {node, edge}.
    • Facets
      • set_facet
      • Read the live facets of one edge.
      • Set a facet whose owner is an **edge** rather than a resource.
      • Read the live facets of one resource — the confirming read for a write that steers region formation and Atlas grouping.
    • Health
      • health_check
    • Ingest
      • create_ingest
      • update_ingest
      • list_blocks
      • append_block
      • finalize_resource
    • Invitations
      • accept
      • decline
      • list_mine
      • list_team_invitations
      • revoke_team_invitation
      • create_team_invitation
    • Invocations
      • list_invocations
      • open
      • show
      • close
    • Profile
      • get_profile
      • update_profile
      • list_auth_links
    • Query
      • `POST /api/query`.
    • Relationships
      • assert
      • fold
      • retype
      • reweight
    • Resources
      • `GET /api/resources` — **one response type, unconditionally.**
      • create_resource
      • get_resource
      • delete_resource
      • update_resource
      • List the finding at `{id}`'s citation-audit trail — one row per audit, each naming its auditor.
      • Record an auditor's signed defensibility verdict on one `(block, source)` citation of the finding at `{id}`. CONFORM to `handlers::edges::assert` (the sibling authored-write handler): thin — build the command, dispatch it, map the error. No persistence he
      • get_content
      • list_resource_edges
      • resource_evidence
      • grant_resource_access
      • revoke_resource_access
      • resource_lineage
      • provenance
      • annotate_resource
    • Meta
      • get_meta
      • update_meta
    • Reassign
      • reassign_resource
      • reassign_team
    • Search
      • `POST /api/search`.
    • Steward
      • candidates
      • dispatch
      • sweep
      • delta
      • advance
    • Teams
      • list_teams
      • create_team
      • detail
      • delete_team
      • update_team
      • add_member
      • remove_member
      • change_role
    • Schemas
      • AcceptInvitationResponse
      • ActInput
      • ActInvocation
      • ActName
      • ActQuantity
      • ActRefusal
      • ActorAuthority
      • AddMemberRequest
      • AdvanceWatermarkAck
      • AdvanceWatermarkRequest
      • AgentAuthorship
      • AppendBlockPayload
      • AssertRelationshipRequest
      • AtlasEdge
      • AtlasHome
      • AtlasNode
      • AtlasSubgraph
      • AuditCitation
      • AuditSweepRow
      • AuditorDispatchTickRequest
      • AuditorDispatchTickResponse
      • AuditorJobCompleteAck
      • BindTeamOutcome
      • BindTeamRequest
      • BlockId
      • BlockProvenanceRow
      • BlocksResponse
      • BodyStorage
      • Bridge
      • BulkReassignAck
      • BulkReassignRequest
      • ChangeRoleRequest
      • CharterBlock
      • CharterDisposition
      • CitationAuditRequest
      • CitationAuditRow
      • ClaimedAuditJob
      • ClaimedJob
      • CloseInvocationRequest
      • CogmapAnalyticsRow
      • CogmapDetail
      • CogmapFoundationRow
      • CogmapGrantBody
      • CogmapId
      • CogmapRegionMetricsRow
      • CogmapRegionRow
      • CogmapRegulationRow
      • CogmapRevokeBody
      • CogmapRow
      • CogmapStaleness
      • CombineNode
      • CombineOp
      • Composition
      • CompositionTrace
      • ConfidenceBand
      • ContentResponse
      • ContextCreateRequest
      • ContextId
      • ContextOwnerRef
      • ContextPanorama
      • ContextRow
      • ContextRowWithCounts
      • CorrelationId
      • CreateCogmapOutcome
      • CreateCogmapRequest
      • CreateInvitationRequest
      • CreateRequestBody
      • CreateReviewBody
      • DeleteResponse
      • DeviceOverrides
      • DispatchTickRequest
      • DispatchTickResponse
      • Disposition
      • DriftSweepRow
      • EdgeFacetRow
      • EdgeFacetSetRequest
      • EdgeFacetsResponse
      • EdgeFilter
      • EdgeId
      • EdgeKind
      • ElementEvent
      • ElementKind
      • EmbedDispatchSummary
      • Entitlements
      • ErrorBody
      • ErrorDetail
      • ErrorDetails
      • EventCursorResponse
      • EventTrail
      • ExactArm
      • ExactHit
      • Extent
      • FacetAck
      • FacetPredicate
      • FacetSetRequest
      • FinalizePayload
      • FoldRelationshipRequest
      • GrantOutcome
      • GraphEdgeRow
      • GroupKeyMeta
      • HealthResponse
      • HomeCogmap
      • HomeContext
      • IdKind
      • IdProvenance
      • IdSet
      • IdpRevocation
      • IngestCreateResponse
      • IngestDelta
      • IngestPayload
      • IngestState
      • InheritedReadGrant
      • InheritedShare
      • InputSource
      • Intention
      • InvitationStatus
      • InvitationTokenRequest
      • InviteeInvitation
      • InvocationAck
      • InvocationActRow
      • InvocationCloseAck
      • InvocationId
      • InvocationSummary
      • InvocationView
      • JoinRequest
      • JoinRequestStatus
      • LensId
      • LineageNode
      • ManagedMeta
      • MatchLocation
      • MaterializeAck
      • MaterializeDelta
      • MaterializeRequest
      • MergePolicy
      • MetaUpdatePayload
      • NarrowedBy
      • NodeHome
      • OpenInvocationRequest
      • OrdOp
      • OrphanNode
      • OutcomeDeclaration
      • PlanRefusal
      • PlanRefusalDetails
      • Polarity
      • Profile
      • ProfileAuthLink
      • ProfileId
      • ProfileUpdateRequest
      • ProfileWithEntitlements
      • PropertyOp
      • PropertyPredicate
      • ProvenanceSource
      • PublicSystemSettings
      • QuantityScale
      • QueryResponse
      • ReassignAck
      • ReassignContextOutcome
      • ReassignContextRequest
      • ReassignResourceRequest
      • ReconcileCogmapRequest
      • ReconcileEdge
      • ReconcileEdgeTombstone
      • ReconcileEntry
      • ReconcileOutcome
      • ReconcileTelos
      • ReconcileTelosBlock
      • ReconcileTombstone
      • Refusal
      • RefusalReason
      • RegionHit
      • RegionId
      • RelationshipAck
      • RemoveMemberOutcome
      • RenameContextOutcome
      • RenameContextRequest
      • ResidualBucket
      • ResidualContext
      • ResidualGroups
      • ResidualOwnedReach
      • ResourceAnnotateRequest
      • ResourceCreateRequest
      • ResourceFacetRow
      • ResourceFacets
      • ResourceFacetsResponse
      • ResourceFilter
      • ResourceGrantBody
      • ResourceHit
      • ResourceId
      • ResourceLineage
      • ResourceListResponse
      • ResourceRevokeBody
      • ResourceSection
      • ResourceSortField
      • ResourceUpdateRequest
      • ResourceView
      • ReturnSpec
      • RetypeRelationshipRequest
      • RevokeOutcome
      • ReweightRelationshipRequest
      • ScoreKind
      • Scoring
      • SearchParams
      • SearchReason
      • SearchResponse
      • SearchResultRow
      • SearchScope
      • SearchScopeInfo
      • SegmentInfo
      • SegmentedBegin
      • SegmentedBeginResponse
      • ShareContextOutcome
      • ShareContextRequest
      • SlackDisconnectRequest
      • SlackDisconnectResponse
      • SlackDisconnectedPrincipal
      • SliceRequest
      • SortOrder
      • StageDisposition
      • StageInput
      • StageInputTrace
      • StageName
      • StageNode
      • StageOutput
      • StageRelation
      • StageResult
      • StageTrace
      • Standing
      • StandingShape
      • Subscription
      • SubscriptionOverride
      • SystemAccessDetails
      • TeamCreateRequest
      • TeamDetail
      • TeamInvitation
      • TeamMemberDetail
      • TeamMemberRow
      • TeamMemberSource
      • TeamRole
      • TeamRow
      • TeamUpdateRequest
      • Territory
      • TerritoryKind
      • TerritoryOverview
      • UnbindTeamOutcome
      • UnshareContextOutcome
      • VaultConfig
      • ViaEntry
      • WideArm
      • WideHit
  • Temper /api/query — the compositional read surface
    • Run a declared composition of situated acts.
    • Schemas
      • Composition
      • OutcomeDeclaration
      • ReturnSpec
      • Intention
      • StageNode
      • ActInvocation
      • CombineNode
      • StageInput
      • StageRelation
      • Disclosure
      • QueryResponse
      • StageResult
      • StageOutput
      • ScoreKind
      • Scoring
      • ResourceHit
      • CompositionTrace
      • StageTrace
      • InputSource
      • NarrowedBy
      • StageName
      • ActName
      • IdKind
      • IdSet
      • BoundTerm
      • Extent
      • StageDisposition
      • ActRefusal
      • PlanRefusal
      • RefusalReason
      • ActQuantity
      • QuantityScale
      • ResourceFilter
      • EdgeFilter
      • PropertyPredicate
      • PropertyOp
      • ResourceSection
      • EdgeKind
      • OrdOp
      • CogmapId
      • ContextId
      • IdProvenance
      • FacetPredicate
      • CombineOp
      • BlockId
      • MatchLocation
      • BodyStorage
      • ResourceId
      • IngestState
      • ManagedMeta
      • ProfileId
      • ResourceView
      • Polarity
      • ViaEntry
      • LensId
      • RegionId
      • CogmapRegionRow
      • RegionHit
      • StageInputTrace
      • Standing
      • ActorAuthority
      • Refusal
      • SystemAccessDetails
      • PlanRefusalDetails
      • ErrorDetails
      • ErrorDetail
      • ErrorBody
  1. playbooks

Self-host Temper with Okta

For operators — anyone standing up a self-hosted Temper deployment who needs to point it
at Okta as the identity provider. This is the Okta variant of the base deployment; it
replaces only the auth provisioning and CLI configuration steps.
By the end you will have a Temper instance that validates Okta-issued access tokens on both
its HTTP and MCP surfaces, a CLI configured to authenticate through your Okta authorization
server, and (optionally) a web UI signing in via Okta. The scope is the API + MCP surfaces plus
the optional web UI, all configured against an Okta custom authorization server.

Prerequisites#

A base Temper deployment. This page replaces only the "Provision Auth0" and "Configure
the CLI" sections of Self-hosting Temper. Everything outside auth —
topology, provisioning Neon, deploying to Vercel, the vercel.json routing contract, and the
verification steps — is identical. Complete the base deployment first, then return here.
The auth-identity contract. Temper validates every request's JWT against an issuer, an
audience, and a JWKS URL — and the AUTH_PROVIDER_NAME value is a label, not a validation
switch. Read Auth identity for the contract this page
configures and the rules the server enforces at boot.

Why Okta works as a drop-in issuer#

Temper validates tokens against the issuer (AUTH_ISSUER), the audience
(AUTH_AUDIENCE), and the signing keys at JWKS_URL — nothing else. Get those three right and
Okta-issued tokens validate. See Auth identity for the full
contract.
Two consequences shape the rest of this page:
1.
Temper requires a custom audience on its access tokens. Okta can only mint custom
audiences from a custom authorization server, which requires the API Access
Management
add-on.
2.
Okta's authorization-server URLs differ from Auth0's (an issuer with no trailing slash;
/oauth2/<authServerId>/v1/* endpoints). temper init emits these for you, and Temper
resolves the /userinfo endpoint via OIDC discovery, so the differences surface only in the
values you configure — not as manual workarounds.

Prerequisite: API Access Management#

Okta gives every org a built-in org authorization server, but it cannot customize the
audience
(aud) claim, and its access tokens are intended for Okta's own APIs — not for
validation by your services. Temper needs a custom audience, so you must use a custom
authorization server
.
Custom authorization servers are part of Okta's API Access Management product — an
optional, paid add-on in production orgs. Confirm your tenant has API Access Management
enabled before continuing.
Without it, there is no supported way to host Temper on Okta.

Provision the custom authorization server#

In the Okta Admin Console: Security → API → Authorization Servers → Add Authorization Server.
1.
Name — e.g. temper.
2.
Audience — set this to the value you will use for AUTH_AUDIENCE and MCP_AUDIENCE
(e.g. https://<instance>/api). The access-token aud claim will carry this value, and
Temper checks it on every request.
Once created, note the authorization server's issuer URI, shown on its Settings tab. It
has the form:
https://<okta-domain>/oauth2/<authServerId>
<okta-domain> is your Okta org domain (<org>.okta.com, <org>.oktapreview.com, or a custom
domain). <authServerId> is the server's ID (the built-in default server uses the literal
default; a server you create gets a generated ID like aus1a2b3c...).
No trailing slash. Okta's issuer is https://<okta-domain>/oauth2/<authServerId> with no
trailing slash. Auth0's issuer requires a trailing slash; Okta's must not have one.
AUTH_ISSUER must match the token's iss claim exactly, so copy the issuer URI verbatim.

Add an access policy and rule#

Custom authorization servers deny by default — if a client matches no access policy, the
token request fails. On the authorization server's Access Policies tab:
1.
Add Policy — assign it to the apps you will create below (or to All clients).
2.
Add Rule — in the rule's grant-type conditions, allow Authorization Code and
Refresh Token. (These are the only grants Temper's CLI uses.)
Without at least one policy + rule, login will fail even when every URL and ID is correct.

Add an email claim to the access token (recommended)#

Temper resolves the user's email from the access token's email claim. When that claim is
absent it falls back to the OIDC /userinfo endpoint, which Temper resolves via discovery
({issuer}/.well-known/openid-configuration), so the fallback works against Okta. Putting
email directly on the access token is still recommended — it's the fast path and avoids a
per-process discovery + userinfo round-trip — but it is not mandatory.
On the authorization server's Claims tab: Add Claim —
Name: email
Include in token type: Access Token
Value type: Expression
Value: user.email
Include in: the scopes/policies your apps use (or "Any scope")
With neither the claim nor a reachable /userinfo (e.g. the token lacks the email scope),
login fails with Token missing email claim and userinfo lookup failed.

Provision the applications#

The contract mirrors the base deployment: two native applications (CLI + MCP), plus an
optional confidential web application if you deploy the web UI. The two native apps are
created under Applications → Create App Integration → OIDC - OpenID Connect → Native
Application
.

1. CLI native application#

The temper CLI uses the Authorization Code + PKCE flow with a loopback relay — not
the device authorization grant. Configure the app accordingly:
Grant types: Authorization Code and Refresh Token. (Do not enable Device
Authorization — Temper does not use it.)
Sign-in redirect URI: https://<instance>/api/auth/cli-callback
PKCE is required for native apps by default, which is exactly what the CLI sends.
Assign the app to the custom authorization server's access policy (above).
The app's Client ID is the CLI client ID used in config.toml (below).

2. MCP native application#

Create a second Native application for MCP clients (e.g. Claude Desktop):
Sign-in redirect URIs: the callbacks for the MCP clients you support, e.g.
https://claude.ai/api/mcp/auth_callback, https://claude.com/api/mcp/auth_callback,
http://localhost.
Assign it to the custom authorization server's access policy.
This app's Client ID becomes MCP_CLIENT_ID.
Dynamic client registration is handled for you. MCP's OAuth flow normally expects dynamic
client registration (DCR), but Okta's DCR endpoint returns 403 for custom authorization
servers unless called with an admin API token — which arbitrary MCP clients cannot do. Temper
sidesteps this entirely: its /oauth/register endpoint is a proxy that returns the
pre-registered MCP_CLIENT_ID. You do not need to enable or configure Okta DCR.

3. UI web application (optional)#

Only if you deploy the web UI. Create a Web Application (confidential client) under
Applications → Create App Integration → OIDC - OpenID Connect → Web Application:
Grant types: Authorization Code and Refresh Token.
Sign-in redirect URI: https://<ui-host>/auth/callback
Sign-out redirect URI: https://<ui-host> (enables RP-initiated logout via the
authorization server's end_session_endpoint).
Assign the app to the custom authorization server's access policy (above), and ensure the
email claim is reachable (per the claim/scope note earlier) so the UI can populate the user
identity.
The app's Client ID and Client secret become the UI's OIDC_CLIENT_ID /
OIDC_CLIENT_SECRET.

Reading values from a live tenant#

If your tenant is already configured, the okta CLI and the Okta management API can enumerate
these values (authorization server issuer and audience, application client IDs). The Admin
Console shows the same information on each authorization server's Settings tab and each
application's General tab.

Environment variable contract (Okta values)#

Set these in your Vercel project. They follow the same contract as the
base deployment's table; only the auth
values differ.
VariableSurfaceOkta value
AUTH_ISSUERapi, mcphttps://<okta-domain>/oauth2/<authServerId> — no trailing slash
JWKS_URLapi, mcphttps://<okta-domain>/oauth2/<authServerId>/v1/keys
AUTH_AUDIENCEapiThe custom authorization server's Audience value (e.g. https://<instance>/api)
MCP_AUDIENCE—Optional. An instance has one audience; both surfaces read AUTH_AUDIENCE. If you set this, it must equal AUTH_AUDIENCE or the instance refuses to boot.
AUTH_PROVIDER_NAMEapi, mcpKeep auth0. It is a profile label and email-cache key, not a validation switch; leave it at the default rather than inventing an okta value
MCP_CLIENT_IDmcpThe MCP native application's Client ID
MCP_BASE_URLmcphttps://<instance> — no trailing slash
Everything else in the base deployment's environment contract (DATABASE_URL,
DATABASE_URL_UNPOOLED, BLOB_READ_WRITE_TOKEN, SQLX_OFFLINE, CORS_ORIGINS, etc.) is
provider-independent — set those exactly as the base deployment describes.

UI project (Okta values)#

If you deploy the web UI, set these in its separate Vercel project. These are the
Okta-specific values for the base deployment's
UI contract:
VariableOkta value
API_BASE_URLThe API backend's own origin (not the UI's public origin — see the loop warning in the base deployment), e.g. https://<api-host>
OIDC_ISSUERhttps://<okta-domain>/oauth2/<authServerId> — no trailing slash. Discovery is served at <issuer>/.well-known/openid-configuration
OIDC_CLIENT_IDThe UI web application's Client ID
OIDC_CLIENT_SECRETThe UI web application's Client secret
OIDC_AUDIENCEOmit. An Okta custom authorization server stamps its configured Audience on tokens implicitly, so no audience request param is needed; omitting it makes the access-token aud match AUTH_AUDIENCE automatically
APP_URLhttps://<ui-host> — the UI's own public origin
SESSION_SECRET≥32 bytes of entropy (64-char hex or 44-char base64)
Set OIDC_* directly — do not rely on the AUTH0_* fallback for an Okta install. Because
the UI proxies browser-facing API/MCP traffic same-origin to API_BASE_URL, the UI does not
require CORS_ORIGINS on the API for its own traffic.

Configure the CLI (Okta)#

temper init supports Okta. Interactively, choose self-hosted → Okta and enter your
authorization server ID. Headless, pass --idp okta --auth-server-id <authServerId> alongside
the existing self-host flags:
temper init --no-interactive \
  --instance-url https://<instance> \
  --auth-domain <okta-domain> \
  --idp okta --auth-server-id <authServerId> \
  --auth-client-id <cli-app-client-id> \
  --auth-audience <custom-auth-server-audience>
The hand-written block below is a valid reference (note provider/name stay auth0):
[cloud]
api_url = "https://<instance>"

[auth]
provider = "auth0"

[[auth.providers]]
name = "auth0"
authorize_url = "https://<okta-domain>/oauth2/<authServerId>/v1/authorize"
token_url = "https://<okta-domain>/oauth2/<authServerId>/v1/token"
client_id = "<cli-app-client-id>"
audience = "<custom-auth-server-audience>"
callback_url = "https://<instance>/api/auth/cli-callback"
scopes = ["openid", "profile", "email", "offline_access"]
Notes:
provider and name must both be auth0 and must match each other. The value is a
label the CLI uses to select the provider block; it does not need to read "okta". The OAuth
flow is identical regardless of the name.
audience is the custom authorization server's Audience value — the same string as
AUTH_AUDIENCE.
scopes includes offline_access so Okta issues a refresh token; keep it.

Environment variable overrides#

The same overrides documented in the base deployment apply unchanged: TEMPER_API_URL,
TEMPER_PROVIDER, and TEMPER_TOKEN. For a fully headless agent session, export
TEMPER_TOKEN (a JWT minted by your Okta authorization server) alongside TEMPER_API_URL; no
config.toml is needed.

Connect MCP clients#

Identical to the base deployment — point MCP clients at https://<instance>/mcp. OAuth
discovery (/.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource)
and the /oauth/register DCR proxy are served by Temper itself, not by Okta, so MCP clients
discover and register against your instance regardless of the upstream IdP. Ensure
MCP_CLIENT_ID matches the Okta MCP native application and that the MCP clients' callback URLs
are listed as sign-in redirect URIs on that application.

Deploy the UI (Okta)#

The web UI is provider-agnostic: its login is generic OIDC Authorization Code + PKCE resolved
from OIDC_ISSUER's discovery document, so it works against an Okta custom authorization server
with no UI source changes. Follow the base deployment's UI section
for the deployment mechanics (separate Vercel project, same-origin reverse proxy), using the
Okta UI env values above and the
confidential web application you registered.
The only Okta-specific points: set OIDC_ISSUER to
https://<okta-domain>/oauth2/<authServerId> and omit OIDC_AUDIENCE (the custom
authorization server stamps its Audience implicitly). Discovery resolves
authorization_endpoint, token_endpoint, and end_session_endpoint from Okta automatically,
so login, refresh, and RP-initiated logout all work without any provider-specific configuration
in the UI.

Verify#

Use the same verification steps as the base deployment:
/api/health, temper auth login, and a resource round-trip. temper auth login opens a
browser to your Okta authorization server's /v1/authorize endpoint and completes the
Authorization Code + PKCE flow.
If login fails with Token missing email claim and userinfo lookup failed, either add the
access-token email claim (above) or ensure the CLI's granted scopes include email so the
/userinfo fallback can return it.

UI login (if deployed)#

Visit https://<ui-host> and sign in. The UI redirects to Okta's /v1/authorize, returns to
/auth/callback, and lands you in the vault — exercising discovery, the token exchange, and the
same-origin API proxy end to end against Okta. Sign out and confirm you're returned to
https://<ui-host> via the authorization server's end_session_endpoint. If the callback
errors, check that https://<ui-host>/auth/callback is a registered sign-in redirect URI on
the UI web application and that APP_URL exactly matches the UI origin.

Not covered#

The exclusions from the base deployment apply
here too (multi-region Neon, alternative messaging backends).

Further reading#

The auth-identity contract this page configures:
Auth identity.
The base deployment this page variants:
Self-hosting Temper.
Native SAML SSO as an alternative to an OIDC bridge:
Self-host with SAML.
What the architecture fixes vs. what a deployment chooses:
temperkb.io/operating/deployment.
Modified at 2026-08-20 02:05:41
Previous
Self-Host Temper
Next
Self-host Temper with a SAML IdP
Built with