# Production Readiness

From [Going to Production](https://learn.descope.io/c/production) — The work between an integration that functions and one you are willing to launch.

Take this course interactively at https://learn.descope.io/c/production/production-readiness

---

A custom domain lets your users see `auth.yourapp.com` instead of a generic Descope address. Part of that is branding, but it also unlocks a meaningfully safer place to keep the refresh token.

By default Descope stores both tokens in localStorage — convenient, but readable by any JavaScript on the page, and therefore exposed to XSS. The refresh token is the critical one: if it leaks, an attacker can mint new sessions indefinitely. Moving it to an HttpOnly cookie fixes that, but `sameSite=strict` requires the cookie domain to be a subdomain of your own site. A custom domain gives Descope a hostname that belongs to you, so the refresh-token cookie can be scoped to it safely.

## What turning on a custom domain changes

- Approved Domains — the allowlist of domains authorized to interact with your project; your custom domain must be reflected here.
- Cookie Domain — the scope of the refresh-token cookie once tokens move into cookies.
- OAuth Callback URL / Callback Domain — the redirect target your OAuth providers return users to; must match what is registered on the provider's side.
- Passkey Top-Level Domain — passkeys are bound to a domain, so this must reflect production before real users enroll.
- Base URL — the value your SDKs use to reach the Descope API.
- SAML ACS URL — per tenant, in SSO configuration: where SAML assertions are posted back to.

> [!NOTE]
> For OIDC SSO connections specifically, the Callback Domain and Callback URL fields update automatically when you change the project's custom domain — one less place to remember during a cutover.

## Key terms

- Custom domain — a hostname you own, letting the refresh-token cookie be scoped safely under `sameSite=strict`.
- Cookie Domain — the scope of the refresh-token cookie once tokens move out of localStorage.
- Passkey Top-Level Domain — the domain passkeys are bound to.

## Summary

- Why the refresh token specifically is the one worth protecting
- What a custom domain unlocks that branding alone does not
- The six settings that shift when you enable one
- Which fields update themselves, and which you must remember

---

Descope treats development, staging, and production as genuinely separate projects rather than modes of one project. That isolation is a feature — a mistake in staging cannot touch production users — but it means you need a deliberate way to move configuration between them.

A project snapshot is a portable export of a project's configuration: a folder of JSON files covering `auth/` (one file per authentication method), `flows/` (an index plus a subfolder per flow), `connectors/`, `roles.json`, `applications.json`, `attributes.json`, `styles/`, `widgets/`, and, where FGA is configured, `fgaschema.txt`. Exporting from the Console produces the same contents as exporting with the CLI; the difference is only packaging — a zip versus a folder on disk.

> [!WARNING]
> Secrets are handled deliberately. Connector and OAuth provider credentials are stripped on export and replaced with `PLACEHOLDER_VALUE`. On import, leaving the placeholder keeps the destination's existing secret untouched; replacing it with a real value overrides the destination's secret; and blanking it, or removing the line, clears the secret entirely.

Descope publishes a GitHub Actions template, and a GitLab equivalent, that turns snapshot promotion into a reviewable pull request rather than a manual export/import. The setup stores a management key scoped to both projects as a repo secret, plus `PRODUCTION_PROJECT_ID` and `STAGING_PROJECT_ID` as repo variables.

## How the CI/CD promotion runs

- Create Pull Request from Staging Project exports staging's current configuration into a ProjectSnapshot folder and opens a PR containing the diff.
- Reviewing and merging that PR automatically triggers Deploy to Production Project, applying the snapshot.
- If a connector or OAuth provider in staging has no matching configuration in production yet, the deployment fails with the exact JSON needed — you re-run the workflow supplying that JSON with real secrets filled in.

Snapshots and CI/CD move static configuration safely. Dynamic resources — users, live SSO connections — stay per-environment and are never promoted this way.

## Key terms

- Project snapshot — a portable export of a project's configuration as a folder of JSON files.
- `PLACEHOLDER_VALUE` — what connector and OAuth secrets are replaced with on export; leaving it keeps the destination's secret.

## Summary

- That environments are separate projects, and what that buys you
- What a snapshot contains and how secrets survive the round trip
- How promotion becomes a reviewable pull request
- What a failed deploy tells you, and how to complete it

---

The rest of this module covers getting a project into production. This section covers making sure it is safe once it is there — a checklist to run before go-live, and again on a recurring schedule afterwards.

## Session and token settings

- Session Token Timeout — shorter timeouts shrink the blast radius of a stolen token. Reasonable production defaults are 15–60 minutes for admin-style sessions, 60–120 minutes for standard users.
- Refresh Token Timeout — 1–7 days for admin sessions, 7–30 days for standard users, balancing re-authentication friction against exposure window.
- Refresh Token Rotation (Pro+) — each refresh issues a new refresh token and invalidates the old one, so reuse of an old token is itself a theft signal.
- Session Inactivity — expires idle sessions automatically; 15–30 minutes is reasonable for administrative accounts.
- Trusted Device Tokens — reduce MFA prompts on returning devices. Convenient for standard users, generally worth disabling for admins, where you want MFA every time.

> [!WARNING]
> Always populate Approved Domains — web domains, no protocol prefix. Leaving the list empty disables redirect and verification validation entirely, which is a genuine open-redirect risk rather than a stylistic warning.

## Domains, redirects, and access control

- Cross-Company Domain Protection — automatic and requiring no configuration; Descope blocks a flow from one company running on a custom domain belonging to a different company's trusted domain list.
- RBAC least privilege — define roles around the specific job functions that need them, mark internal or system roles Hidden so tenant admins cannot self-assign them, and keep default-role assignment for new users minimal.
- Federated App default access — decide deliberately between granting new users access to all applications versus requiring explicit assignment. The latter is tighter and costs more provisioning work.

Notice that several of these tradeoffs cut both ways depending on the role. Trusted Device Tokens help standard users and weaken admin accounts; a permissive Federated App default speeds onboarding and widens exposure. The checklist is not a set of universally correct values — it is a set of decisions to make deliberately rather than inherit.

## Key terms

- Refresh Token Rotation — each refresh issues a new token and invalidates the old, making reuse a theft signal.
- Cross-Company Domain Protection — an automatic safeguard blocking a flow from running on another company's trusted custom domain.

## Summary

- Reasonable production timeout defaults for admin versus standard sessions
- Why an empty Approved Domains list is an open-redirect risk
- Which protections are automatic and which you must configure
- That several settings are role-dependent tradeoffs, not universal defaults

---

Snapshots and CI/CD templates cover promotion between two projects. Terraform takes a different angle: you declare the configuration you want as code, and Terraform reconciles the live project to match — the same infrastructure-as-code discipline teams already apply to servers and cloud resources.

Descope publishes an official provider (`descope/descope`) built around a `descope_project` resource, alongside standalone resources for access keys, management keys, the Descope Engine, Descopers, and inbound apps.

> [!WARNING]
> Terraform only manages what you explicitly declare, and the asymmetry is the single most common footgun with this provider. Omitting a block entirely — say, connectors — means Terraform leaves it alone. But adding that block, even empty, means Terraform now owns everything inside it, and on the next apply it will remove anything present in the live project that is not declared in your configuration.

## Key terms

- `descope_project` — the core resource in Descope's official Terraform provider, alongside standalone resources for access keys, management keys, and inbound apps.

## Summary

- What Terraform adds beyond snapshot promotion
- That omitting a block is safe and declaring an empty one is not
- Which resources the provider exposes

---
