mix attesto_phoenix.gen.migration (AttestoPhoenix v3.2.1)

Copy Markdown View Source

Generates an Ecto migration that creates the persistence backing the Ecto-based stores shipped with attesto_phoenix.

The migration creates eleven tables, named to match the runtime schemas exactly so a by-the-docs deploy installs tables the Ecto-backed stores can use without modification:

  • attesto_authorization_codes - the authorization code grant store (AttestoPhoenix.Schema.Authorization). Holds one row per issued authorization code (RFC 6749, section 4.1) plus the PKCE binding (RFC 7636), the optional cnf key binding (RFC 7800), the OIDC nonce, mapped claims, the descendant family_id, consumed markers, and the access-token jti issued from a successful redemption so code reuse can revoke it. Keyed on code_hash as its PRIMARY KEY (no surrogate id); consulted exactly once at the token endpoint.

  • attesto_refresh_tokens - the refresh token store (AttestoPhoenix.Schema.RefreshToken, RFC 6749, section 6). Each row carries the rotation family_id and generation it belongs to, the consumed/consumed_at idempotency markers, successor retry payload, family_revoked sticky revocation flag, the cnf key binding, mapped claims, and the diagnostic parent_hash, so that reuse of a rotated token can be detected and the whole family revoked (RFC 6819, section 5.2.2.3 - refresh token rotation / replay detection).

  • attesto_refresh_family_revocations - durable refresh-family revocation tombstones. Refresh-token rows are eligible for expiry cleanup, so this separate table preserves a revocation after every row in the family has been swept. Existing installations upgrading to a release that uses this table must apply a forward migration before starting the new code; see the upgrade notes in the README and CHANGELOG.

  • attesto_device_codes - the device authorization grant store (AttestoPhoenix.Schema.DeviceCode, RFC 8628). One row per device code, keyed on device_code_hash (the poll key) and user_code (the verification key), carrying the bound scope/resource/dpop_jkt, the status state machine (pending → approved|denied → consumed), the approved subject/granted_scope/granted_claims, and last_polled_at for the section 3.5 poll-interval guard.

  • attesto_ciba_requests - the OpenID Connect CIBA authentication-request store (AttestoPhoenix.Schema.CIBARequest / EctoCIBAStore, CIBA Core 1.0). One row per auth_req_id, keyed on auth_req_id_hash, carrying the bound scope/resource/dpop_jkt, the delivery_mode, the ping client_notification_token, the hint-resolved hint_subject, the status state machine (pending → approved|denied → consumed), the approved subject/acr/auth_time/granted_scope/granted_claims, the frozen poll interval, and last_polled_at for the §7.3 poll-interval guard.

  • attesto_logout_sessions - the logout session store (AttestoPhoenix.Schema.LogoutSession, OpenID Connect Back-Channel Logout 1.0 + Front-Channel Logout 1.0). One row per (session, Relying Party) pair, recorded at ID-Token mint and read at the end-session endpoint to deliver a logout_token and/or render the RP's frontchannel_logout_uri. Upserted on (sid, client_id); carries the subject, the RP's backchannel_logout_uri/session_required and frontchannel_logout_uri/frontchannel_session_required, and the expires_at that bounds an abandoned session.

  • dpop_nonces - server-issued DPoP nonces (AttestoPhoenix.Schema.DPoPNonce, RFC 9449, section 8). Each row is a single-use nonce carrying issued_at, expires_at, and the used_at consumption marker.

  • dpop_replays - the DPoP proof replay cache keyed by the proof's jti as its PRIMARY KEY (AttestoPhoenix.Schema.DPoPReplay, RFC 9449, section 11.1). A row is the record that a given proof JWT has already been seen within its acceptance window.

  • attesto_pushed_authorization_requests - the Pushed Authorization Request store (AttestoPhoenix.Schema.PushedAuthorizationRequest, RFC 9126). Each row maps a one-time request_uri reference (the PRIMARY KEY) to the stored, validated authorization request params and the reference expires_at, so a request_uri pushed to one node is resolvable on every node (FAPI 2.0 requires PAR).

  • attesto_client_id_metadata - the Client ID Metadata Document cache (AttestoPhoenix.Schema.ClientIdMetadata, draft-ietf-oauth-client-id-metadata-document-01). Each row caches one validated CIMD document under its client_id URL (the PRIMARY KEY), as a jsonb metadata map plus the expires_at derived from the response's HTTP freshness directives (RFC 9111). Keeps every authorization request from re-fetching the URL and, being shared, makes the cache coherent across a cluster and bounds the outbound fetch fan-out.

  • attesto_consent_grants - the single-use, request-bound consent grant store (AttestoPhoenix.Schema.ConsentGrant / EctoConsentGrantStore, RFC 6749 §4.1.1). Each row records one consent decision keyed on an unguessable token (the PRIMARY KEY), with a binding_hash over the exact request the user saw and a short expires_at; consumed_at marks single use. The host consent screen mints a row; the host's :consent callback consumes it before a code is issued, so one consent click cannot approve a different client/redirect/scope/challenge.

Usage

# Fresh installation:
mix attesto_phoenix.gen.migration --repo MyApp.Repo

# Upgrading an existing database to 3.0:
mix attesto_phoenix.gen.migration --upgrade 3.0 --repo MyApp.Repo

# Promoting the historical authorization-code unique index to its
# primary key (run after the 3.0 migration when upgrading 2.x):
mix attesto_phoenix.gen.migration --upgrade 3.1 --repo MyApp.Repo

Options

  • --upgrade - generate a migration to upgrade an existing database rather than creating fresh tables. Supported values: 3.0/3.0.0 and 3.1/3.1.0. The 3.0 migration adopts or creates the exact unique index on attesto_refresh_tokens(family_id, generation), creates or adopts the attesto_refresh_family_revocations table, and safely backfills every currently-revoked refresh-token family. The 3.1 migration promotes the historical unique index on attesto_authorization_codes(code_hash) to the primary key required by current schemas. Run both, in order, for a 2.x database. Each upgrade validates any pre-existing object before backfilling or changing it; malformed collisions fail the migration transaction rather than being silently accepted.

  • --repo, -r - the Ecto repo module the migration is generated for. May be given more than once to target several repos. When omitted the repos configured for the host application are used (the same resolution mix ecto.gen.migration performs).

  • --schema-prefix - an optional PostgreSQL schema selected by Ecto's prefix: option for every generated table and index (for example --schema-prefix oauth creates attesto_authorization_codes in schema oauth). Runtime Ecto queries use the same prefix: option; the table names themselves remain canonical. When omitted, the prefix configured for the host (:schema_prefix on the AttestoPhoenix.Config the host puts in its application environment) is used so the generated schema matches the prefix the Ecto stores read at runtime. If the host has no configured prefix, the generated migration defers to the migrator or repo default at execution time. The task never invents a prefix. The 2.x --table-prefix option is rejected because it controlled literal names in generated migrations, not one coherent runtime layout: most 2.x stores queried canonical tables in public, while only the CIBA store and sweeper treated the value as an Ecto schema prefix. Inventory an existing database before choosing a 3.0 schema; without --upgrade, this task is for fresh migrations only.

  • --migrations-path - directory the migration file is written to. Defaults to the repo's priv/<repo>/migrations directory, the same location mix ecto.gen.migration uses.

  • --otp-app - the host application whose environment holds the AttestoPhoenix.Config keyword or struct to read :schema_prefix from when --schema-prefix is omitted. Optional; when omitted, the task first uses config :attesto_phoenix, otp_app: ... and then the current Mix project's :app. If neither application has a configured prefix, the task embeds no explicit prefix and the migration uses the migrator or repo default at execution time (normally public). Keep a custom migration default aligned with the runtime database connection's search path, or pass --schema-prefix explicitly.

  • --config-key - the application environment key the host stores its AttestoPhoenix.Config keyword under. Defaults to AttestoPhoenix.Config, matching AttestoPhoenix.Config.from_otp_app/2.

Fresh-install migrations are reversible. The 3.0 upgrade migration has a guarded rollback that refuses to discard revocations which 2.x cannot represent. An application downgrade also requires stopped writers and a drain of active 3.x refresh-retry deadlines because 2.x cannot recover 3.x successor envelopes.