API: Auth and sessions
Capability state: every operation in this file is
staging-durable(fixture-only). Login ceremonies, session issuance/refresh, recovery policy/execution, rate-limit evaluation, and WebAuthn passkey registration/assertion are recorded in durable M7 state but refuse production admission. Sessions keep anauth_session:fixture_*prefix so a fixture session cannot be confused with a production session at any downstream gate. See 022 gap report for the broader end-user surface.
The auth and sessions cluster covers Gestalt’s login ceremony,
session lifecycle, recovery, and rate-limit operations — the
membrane surface that an end user (or a hosted operator acting on
their behalf) interacts with after humanAuth.* presence.
Three coupled lanes:
- Login ceremony lane —
auth.loginStartissues a WebAuthn assertion challenge plus a hash-only rate-limit decision;auth.loginFinishconsumes a HumanAuth presence receipt and a durable rate-limit allow record before issuing a signed staging session. - Session lifecycle lane —
auth.sessionIssue,auth.sessionRefresh, andauthority.sessionRevokerecord session issue/refresh/revoke lifecycle atoms with token commitments only. No raw bearer token is stored durably. - Recovery lane —
auth.recoveryPolicyrecords hash-only recovery contact commitments after HumanAuth presence;auth.recoveryExecuterecords replacement-passkey recovery with the consumed policy. Neither lane creates standing.
A login or session lifecycle never creates standing or delegation on its own — see authority.md for those.
WebAuthn passkey registration and assertion options, and
the register and import finishers, live alongside
humanAuth.challenge / humanAuth.verifyPasskey
(human-auth.md) but are documented here because
they share the login ceremony’s WebAuthn boundary.
See concepts: glossary — HumanAuth for the human-presence half of this surface.
auth.loginStart
Section titled “auth.loginStart”Start staging login ceremony by recording hash-only rate-limit state and issuing a WebAuthn assertion challenge.
POST /v1/auth/login/startstate: staging-durablesdk_role: start staging login ceremony by recording hash-only rate-limit state and issuing a WebAuthn assertion challenge without raw identifiers or biometric materialrequest_record: CloudAuthLoginStartRequestresponses: rate_limit_decision | human_auth_challenge | public_key_credential_request_options | refusal | receiptRequest
Section titled “Request”interface AuthLoginStartRequest { tenant: GestaltRef; subject: GestaltRef; passkey_binding?: GestaltRef; relying_party_id: string; origin: string; scopes: string[]; policy_ref: GestaltRef; source_ip_hash: string; // sha256: prefix required device_binding: GestaltRef; window_seconds: number; attempt_count: number; soft_limit: number; lockout_threshold: number; lockout_seconds: number; fixture: boolean; // must be true; production_login must be false production_login: boolean; contains_customer_data: boolean; contains_biometric_material: boolean; raw_ip_address?: string; // refused if present raw_user_agent?: string; // refused if present raw_credential_id?: string; // refused if present}Response
Section titled “Response”{ "operation": "auth.loginStart", "outcome": "admitted", "body": { "login_attempt": "auth_login_attempt:...", "auth_rate_limit_evaluation": "auth_rate_limit_evaluation:...", "rate_limit_decision": "allow", "challenge": { "id": "human_auth_challenge:...", "...": "..." }, "public_key_credential_request_options": { "challenge": "...", "rp_id": "..." }, "passkey_binding": { "id": "...", "credential_id_hash": "...", "...": "..." }, "durable_records": { "rate_limit": {...}, "challenge": {...}, "login_attempt": {...} }, "privacy_boundary": { "raw_assertion_bytes_stored": false, "raw_credential_id_stored": false, "biometric_material_seen_by_gestalt": false, "production_admission": false }, "webauthn_origin_policy": {...}, "authority_boundaries": {...}, "source_ip_raw_stored": false, "raw_user_agent_stored": false, "raw_credential_id_stored": false, "raw_biometric_material_stored": false, "raw_session_token_stored": false, "database_locator_exposed": false, "production_login": false, "production_admission": false }, "receipt": {...}}When the rate-limit decision is throttle or lockout, the
operation responds outcome: "refused" with failed_gate set to
auth_rate_limit_throttled or auth_rate_limit_locked and HTTP
429.
SDK example
Section titled “SDK example”const start = await client.authLoginStart({ tenant: "tenant_node:rheinwerk_calibration", subject: "human_person:anna", passkey_binding: "passkey_binding:anna_platform_passkey_fixture", relying_party_id: "gestalt.local", origin: "https://gestalt.local", scopes: ["auth.login.finish"], policy_ref: "auth_rate_limit_policy:login_fixture", source_ip_hash: "sha256:fixture_login_source_ip_hash", device_binding: "device_binding:staging_login_fixture", window_seconds: 300, attempt_count: 1, soft_limit: 5, lockout_threshold: 10, lockout_seconds: 900, fixture: true, production_login: false, contains_customer_data: false, contains_biometric_material: false,});auth.loginFinish
Section titled “auth.loginFinish”Finish staging login ceremony by consuming a durable HumanAuth presence receipt plus a rate-limit allow record before issuing a signed session commitment.
POST /v1/auth/login/finishstate: staging-durablesdk_role: finish staging login ceremony by consuming durable HumanAuth presence and rate-limit allow records before issuing a signed session commitmentrequest_record: CloudAuthLoginFinishRequestresponses: session_lifecycle | human_presence_receipt | rate_limit_decision | refusal | receiptRequest
Section titled “Request”interface AuthLoginFinishRequest { tenant: GestaltRef; session: GestaltRef; // must start with "auth_session:fixture_" actor: GestaltRef; vessel: GestaltRef; standing: GestaltRef; scopes: string[]; human_presence_receipt: GestaltRef; login_challenge: GestaltRef; auth_rate_limit_evaluation: GestaltRef; passkey_binding?: GestaltRef; holder_key: GestaltRef; device_binding: GestaltRef; expires_in_seconds: number; // 1..=86400 fixture: boolean; production_login: boolean; production_session: boolean;}Response
Section titled “Response”{ "operation": "auth.loginFinish", "outcome": "admitted", "body": { "session": "auth_session:fixture_login_staging", "session_lifecycle": "session_lifecycle:...", "bearer_token": "...", "token_type": "Bearer", "token_commitment": "sha256:...", "human_presence_receipt": "human_presence_receipt:...", "login_challenge": "human_auth_challenge:...", "auth_rate_limit_evaluation": "auth_rate_limit_evaluation:...", "satisfies_sensitive_approval_requirement": true, "identity_binding_created": false, "standing_created": false, "company_authority_created": false, "source_ip_raw_stored": false, "raw_user_agent_stored": false, "raw_credential_id_stored": false, "raw_session_token_stored": false, "raw_session_token_exposed_in_proof": false, "raw_biometric_material_stored": false, "fixture_session": true, "production_login": false, "production_session": false, "signer": {...}, "durable_commit": {...}, "durable_state": {...}, "production_admission": false }, "receipt": {...}}identity_binding_created, standing_created, and
company_authority_created are asserted explicitly as false:
login finishing proves presence and issues a session, but never
elevates the session into standing or company authority.
SDK example
Section titled “SDK example”const finished = await client.authLoginFinish({ tenant: "tenant_node:rheinwerk_calibration", session: "auth_session:fixture_login_staging", actor: "human_person:anna", vessel: "vessel:anna_browser_fixture", standing: "standing:anna_geschaeftsfuehrer_fixture", scopes: ["invoice.issue", "auth.session.inspect", "auth.session.refresh"], human_presence_receipt: "human_presence_receipt:fixture_private_presence", login_challenge: start.body.challenge.id, auth_rate_limit_evaluation: start.body.auth_rate_limit_evaluation, passkey_binding: "passkey_binding:anna_platform_passkey_fixture", holder_key: "key_registry:fixture_session_signing", device_binding: "device_binding:staging_login_finish_fixture", expires_in_seconds: 3600, fixture: true, production_login: false, production_session: false,});auth.sessionExchange
Section titled “auth.sessionExchange”Trade the fixture bearer for a signed session context. This is a fixture-rehearsed convenience for tests and worked examples — it does not run a real authentication ceremony, does not require a HumanAuth presence receipt, and binds no production session.
POST /v1/auth/session/exchangestate: fixture-rehearsedsdk_role: not yet wrapped — call directly with the fixture bearerrequest_record: (empty body)responses: signed_session_context | receiptRequest
Section titled “Request”The endpoint takes no body. Authenticate with the fixture bearer
token (Authorization: Bearer fixture-session-token).
Response
Section titled “Response”{ "operation": "auth.session.exchange", "outcome": "admitted", "fixture": true, "body": { "token": "fixture-session-token", "token_type": "Bearer", "session": "auth_session:fixture_valid", "tenant": "tenant_node:rheinwerk_calibration", "production_admission": false }, "receipt": { "ref": "receipt:fixture_session_exchange", "outcome": "admitted", "reasons": ["fixture bearer token exchanged for signed session context"], "fixture": true }}The TS SDK does not yet have a thin wrapper for this route. Use
fetch directly when you need it:
const exchanged = await fetch("http://127.0.0.1:3011/v1/auth/session/exchange", { method: "POST", headers: { authorization: "Bearer fixture-session-token", },}).then(r => r.json());For real session issuance (with HumanAuth presence, scope, and signer
provenance) use auth.sessionIssue below.
auth.sessionIssue
Section titled “auth.sessionIssue”Issue a staging signed session token after HumanAuth presence while storing only the token commitment.
POST /v1/auth/sessions/issuestate: staging-durablesdk_role: issue staging signed session token after HumanAuth presence while storing only token commitmentrequest_record: CloudAuthSessionIssueRequestresponses: session_lifecycle | refusal | receiptRequest
Section titled “Request”interface AuthSessionIssueRequest { tenant: GestaltRef; session: GestaltRef; // must start with "auth_session:fixture_" actor: GestaltRef; vessel: GestaltRef; standing: GestaltRef; scopes: string[]; human_presence_receipt: GestaltRef; holder_key: GestaltRef; device_binding: GestaltRef; expires_in_seconds: number; // 1..=86400 fixture: boolean; // must be true production_session: boolean; // must be false}Response
Section titled “Response”{ "operation": "auth.sessionIssue", "outcome": "admitted", "body": { "session": "auth_session:fixture_signed_staging", "session_lifecycle": "session_lifecycle:...", "bearer_token": "...", "token_type": "Bearer", "token_commitment": "sha256:...", "raw_session_token_stored": false, "raw_session_token_exposed_in_proof": false, "fixture_session": true, "production_session": false, "signer": {...}, "durable_commit": {...}, "durable_state": {...}, "production_admission": false }, "receipt": {...}}The bearer token is returned in the response body but never stored
durably; only token_commitment (a sha256 of the token) is
persisted.
SDK example
Section titled “SDK example”const issued = await client.authSessionIssue({ tenant: "tenant_node:rheinwerk_calibration", session: "auth_session:fixture_signed_staging", actor: "human_person:anna", vessel: "vessel:anna_browser_fixture", standing: "standing:anna_geschaeftsfuehrer_fixture", scopes: ["invoice.issue"], human_presence_receipt: "human_presence_receipt:fixture_private_presence", holder_key: "key_registry:fixture_session_signing", device_binding: "device_binding:staging_signed_fixture", expires_in_seconds: 3600, fixture: true, production_session: false,});auth.sessionInspect
Section titled “auth.sessionInspect”Inspect membrane-safe authenticated session metadata without exposing bearer token material or raw database access.
POST /v1/auth/sessions/inspectstate: staging-durablesdk_role: inspect membrane-safe authenticated session metadata without exposing bearer token material or raw database accessrequest_record: CloudAuthSessionInspectRequestresponses: session_metadata | refusal | receiptRequest
Section titled “Request”interface AuthSessionInspectRequest { tenant?: GestaltRef; session?: GestaltRef; // if set, must equal the authenticated session}The session ref is optional. If omitted, inspection targets the
authenticated session. If supplied, it must match the
authenticated session — otherwise the membrane refuses with
auth_session_inspect_session_mismatch.
Response
Section titled “Response”{ "operation": "auth.sessionInspect", "outcome": "admitted", "body": { "session": "auth_session:...", "session_lifecycle": "session_lifecycle:...", "tenant": "tenant_node:...", "actor": "human_person:...", "vessel": "vessel:...", "standing": "standing:...", "scopes": ["..."], "holder_key": "key_registry:...", "issued_at": "...", "expires_at": "...", "token_commitment": "sha256:...", "fixture_session": true, "production_session": false, "revoked": false, "stale_key": false, "raw_session_token_exposed": false, "raw_session_token_stored_durably": false, "database_locator_exposed": false, "production_admission": false }, "receipt": {...}}The bearer token itself is never returned — only the
token_commitment (sha256 of the issued token) is exposed.
database_locator_exposed: false and
raw_session_token_stored_durably: false are asserted explicitly
as membrane invariants.
Refusal codes:
auth_session_inspect_session_mismatch,
auth_session_unknown.
SDK example
Section titled “SDK example”const inspected = await client.authSessionInspect({ tenant: "tenant_node:rheinwerk_calibration",});The SDK method accepts an empty object — client.authSessionInspect()
inspects the authenticated session.
auth.sessionRefresh
Section titled “auth.sessionRefresh”Refresh a staging signed session after HumanAuth presence while storing only token commitments and lifecycle provenance.
POST /v1/auth/sessions/refreshstate: staging-durablesdk_role: refresh staging signed session after HumanAuth presence while storing only token commitments and lifecycle provenancerequest_record: CloudAuthSessionRefreshRequestresponses: session_lifecycle | refusal | receiptRequest
Section titled “Request”interface AuthSessionRefreshRequest { tenant: GestaltRef; session: GestaltRef; // must start with "auth_session:fixture_" scopes: string[]; // cannot expand beyond authenticated scopes human_presence_receipt: GestaltRef; holder_key: GestaltRef; device_binding: GestaltRef; expires_in_seconds: number; // 1..=86400 fixture: boolean; production_session: boolean;}Response
Section titled “Response”{ "operation": "auth.sessionRefresh", "outcome": "admitted", "body": { "session": "auth_session:fixture_refreshed_staging", "previous_session": "auth_session:fixture_signed_staging", "session_lifecycle": "session_lifecycle:...", "bearer_token": "...", "token_type": "Bearer", "token_commitment": "sha256:...", "previous_token_commitment": "sha256:...", "scope_expansion_allowed": false, "previous_session_remains_valid": true, "raw_session_token_stored": false, "raw_session_token_exposed_in_proof": false, "fixture_session": true, "production_session": false, "signer": {...}, "durable_commit": {...}, "durable_state": {...}, "production_admission": false }, "receipt": {...}}Refresh refuses with auth_session_refresh_scope_expansion_refused
if requested scopes exceed the authenticated session’s scopes.
SDK example
Section titled “SDK example”const refreshed = await client.authSessionRefresh({ tenant: "tenant_node:rheinwerk_calibration", session: "auth_session:fixture_refreshed_staging", scopes: [], human_presence_receipt: "human_presence_receipt:fixture_private_presence", holder_key: "key_registry:fixture_session_signing", device_binding: "device_binding:staging_refresh_fixture", expires_in_seconds: 3600, fixture: true, production_session: false,});authority.sessionRevoke
Section titled “authority.sessionRevoke”Record session revocation lifecycle without exposing raw session
token. (The contract operation name is authority.sessionRevoke;
the underlying request record is M13SessionRevokeRequest and
the SDK method is m13SessionRevoke.)
POST /v1/authority/sessions/revokestate: staging-durablesdk_role: record session revocation lifecycle without exposing raw session tokenrequest_record: CloudM13SessionRevokeRequestresponses: session_lifecycle | refusal | receiptRequest
Section titled “Request”interface M13SessionRevokeRequest { tenant: GestaltRef; session: GestaltRef; enforce_runtime_overlay: boolean; // when true, future bearer use of `session` is rejected}Response
Section titled “Response”{ "operation": "authority.sessionRevoke", "outcome": "admitted", "body": { "session_lifecycle": "session_lifecycle:...", "session": "auth_session:fixture_valid", "revoked": true, "enforced_in_runtime_overlay": true, "raw_session_token_exposed": false, "signer": {...}, "durable_commit": {...}, "durable_state": {...}, "production_admission": false }, "receipt": {...}}A repeat call for an already-revoked session returns
outcome: "verified" with body.stable_code: "auth_session_already_revoked",
which is the idempotent shape callers should rely on.
SDK example
Section titled “SDK example”const revoked = await client.m13SessionRevoke({ tenant: "tenant_node:rheinwerk_calibration", session: "auth_session:fixture_valid", enforce_runtime_overlay: true,});auth.rateLimitEvaluate
Section titled “auth.rateLimitEvaluate”Evaluate staging auth rate-limit and lockout policy with hash-only
identifiers and durable policy provenance. (The user-facing
operation is auth.rateLimit; the contract names it
auth.rateLimitEvaluate.)
POST /v1/auth/rate-limit/evaluatestate: staging-durablesdk_role: evaluate staging auth rate-limit and lockout policy with hash-only identifiers and durable policy provenancerequest_record: CloudAuthRateLimitEvaluateRequestresponses: rate_limit_decision | refusal | receiptRequest
Section titled “Request”interface AuthRateLimitEvaluateRequest { tenant: GestaltRef; policy_ref: GestaltRef; subject: GestaltRef; route: string; action: string; source_ip_hash: string; // sha256: prefix required device_binding: GestaltRef; window_seconds: number; attempt_count: number; soft_limit: number; lockout_threshold: number; lockout_seconds: number; fixture: boolean; production_login: boolean; enforce_runtime_overlay: boolean; contains_customer_data: boolean; raw_ip_address?: string; // refused if present raw_user_agent?: string; // refused if present raw_credential_id?: string; // refused if present}Response
Section titled “Response”{ "operation": "auth.rateLimitEvaluate", "outcome": "admitted", "body": { "auth_rate_limit_evaluation": "auth_rate_limit_evaluation:...", "policy": "auth_rate_limit_policy:m13_fixture", "subject": "human_person:anna", "route": "/v1/auth/sessions/issue", "action": "auth.session.issue", "decision": "allow", "allowed": true, "failed_gate": null, "retry_after_seconds": 0, "source_ip_hash": "sha256:...", "device_binding": "device_binding:...", "source_ip_raw_stored": false, "raw_user_agent_stored": false, "raw_credential_id_stored": false, "raw_session_token_stored": false, "database_locator_exposed": false, "enforced_in_runtime_overlay": false, "production_login": false, "production_admission": false, "durable_state": {...} }, "receipt": {...}}decision is allow, throttle, or lockout. Non-allow
decisions return outcome: "refused" with HTTP 429 and
failed_gate set to auth_rate_limit_throttled or
auth_rate_limit_locked.
SDK example
Section titled “SDK example”const decision = await client.authRateLimitEvaluate({ tenant: "tenant_node:rheinwerk_calibration", policy_ref: "auth_rate_limit_policy:m13_fixture", subject: "human_person:anna", route: "/v1/auth/sessions/issue", action: "auth.session.issue", source_ip_hash: "sha256:fixture_auth_source_ip_hash", device_binding: "device_binding:staging_rate_limit_fixture", window_seconds: 300, attempt_count: 1, soft_limit: 5, lockout_threshold: 10, lockout_seconds: 900, fixture: true, production_login: false, enforce_runtime_overlay: false, contains_customer_data: false,});auth.recoveryPolicy
Section titled “auth.recoveryPolicy”Record staging auth recovery policy with hash-only recovery contact commitments after HumanAuth presence without creating standing.
POST /v1/auth/recovery/policystate: staging-durablesdk_role: record staging auth recovery policy with hash-only recovery contact commitments after HumanAuth presence without creating standingrequest_record: CloudAuthRecoveryPolicyRequestresponses: recovery_policy | human_presence_receipt | refusal | receiptRequest
Section titled “Request”interface AuthRecoveryPolicyRequest { tenant: GestaltRef; actor: GestaltRef; vessel: GestaltRef; standing: GestaltRef; passkey_binding: GestaltRef; human_presence_receipt: GestaltRef; recovery_policy_ref: GestaltRef; recovery_policy_hash: string; // sha256: prefix required recovery_contact_hashes: string[]; // each sha256: prefix required recovery_methods: string[]; fixture: boolean; production_login: boolean; contains_customer_data: boolean; contains_biometric_material: boolean; raw_recovery_secret?: string; // refused if present raw_recovery_contact?: string; // refused if present create_standing_from_recovery: boolean; // refused if true}Response
Section titled “Response”{ "operation": "auth.recoveryPolicy", "outcome": "admitted", "body": { "auth_recovery_policy": "auth_recovery_policy:fixture", "passkey_binding": "passkey_binding:...", "human_presence_receipt": "human_presence_receipt:...", "recovery_policy_hash": "sha256:...", "recovery_contact_hashes": ["sha256:..."], "recovery_methods": ["passkey_reenrollment_with_human_presence"], "raw_recovery_secret_stored": false, "raw_recovery_contact_stored": false, "raw_biometric_material_stored": false, "identity_binding_created": false, "standing_created": false, "company_authority_created": false, "signer": {...}, "durable_commit": {...}, "durable_state": {...}, "production_login": false, "production_admission": false }, "receipt": {...}}SDK example
Section titled “SDK example”const policy = await client.authRecoveryPolicy({ tenant: "tenant_node:rheinwerk_calibration", actor: "human_person:anna", vessel: "vessel:anna_browser_fixture", standing: "standing:anna_geschaeftsfuehrer_fixture", passkey_binding: "passkey_binding:anna_platform_passkey_fixture", human_presence_receipt: "human_presence_receipt:fixture_private_presence", recovery_policy_ref: "auth_recovery_policy:fixture", recovery_policy_hash: "sha256:fixture_recovery_policy_hash", recovery_contact_hashes: ["sha256:fixture_recovery_contact_hash"], recovery_methods: ["passkey_reenrollment_with_human_presence"], fixture: true, production_login: false, contains_customer_data: false, contains_biometric_material: false, create_standing_from_recovery: false,});auth.recoveryExecute
Section titled “auth.recoveryExecute”Execute staging auth recovery with a durable recovery policy plus replacement-passkey HumanAuth presence without exposing raw recovery material or creating standing.
POST /v1/auth/recovery/executestate: staging-durablesdk_role: execute staging auth recovery with durable recovery policy plus replacement-passkey HumanAuth presence without exposing raw recovery material or creating standingrequest_record: CloudAuthRecoveryExecuteRequestresponses: recovery_execution | recovery_policy | replacement_passkey_binding | human_presence_receipt | refusal | receiptRequest
Section titled “Request”interface AuthRecoveryExecuteRequest { tenant: GestaltRef; actor: GestaltRef; vessel: GestaltRef; standing: GestaltRef; recovery_policy: GestaltRef; recovery_execution_ref: GestaltRef; replacement_passkey_binding: GestaltRef; human_presence_receipt: GestaltRef; recovery_contact_hash: string; device_binding: GestaltRef; fixture: boolean; production_login: boolean; contains_customer_data: boolean; contains_biometric_material: boolean; raw_recovery_secret?: string; // refused if present raw_recovery_contact?: string; // refused if present create_identity_binding_from_recovery: boolean; create_standing_from_recovery: boolean; create_company_authority_from_recovery: boolean;}Response
Section titled “Response”{ "operation": "auth.recoveryExecute", "outcome": "admitted", "body": { "auth_recovery_execution": "auth_recovery_execution:fixture", "auth_recovery_policy": "auth_recovery_policy:fixture", "replacement_passkey_binding": "passkey_binding:fixture_recovery_replacement", "human_presence_receipt": "human_presence_receipt:fixture_recovery_presence", "recovery_contact_hash": "sha256:...", "recovery_policy_consumed": true, "raw_recovery_secret_stored": false, "raw_recovery_contact_stored": false, "raw_biometric_material_stored": false, "identity_binding_created": false, "standing_created": false, "company_authority_created": false, "signer": {...}, "durable_commit": {...}, "durable_state": {...}, "production_login": false, "production_admission": false }, "receipt": {...}}The handler enforces that recovery cannot create
identity_binding, standing, or company authority — those flags are
explicit boundary assertions in both the request (refused if true)
and the response (always false).
SDK example
Section titled “SDK example”const recovery = await client.authRecoveryExecute({ tenant: "tenant_node:rheinwerk_calibration", actor: "human_person:anna", vessel: "vessel:anna_browser_fixture", standing: "standing:anna_geschaeftsfuehrer_fixture", recovery_policy: "auth_recovery_policy:fixture", recovery_execution_ref: "auth_recovery_execution:fixture", replacement_passkey_binding: "passkey_binding:fixture_recovery_replacement", human_presence_receipt: "human_presence_receipt:fixture_recovery_presence", recovery_contact_hash: "sha256:fixture_recovery_contact_hash", device_binding: "device_binding:fixture_recovery_device", fixture: true, production_login: false, contains_customer_data: false, contains_biometric_material: false, create_identity_binding_from_recovery: false, create_standing_from_recovery: false, create_company_authority_from_recovery: false,});humanAuth.passkeyRegistrationOptions
Section titled “humanAuth.passkeyRegistrationOptions”Issue browser WebAuthn registration options under secure RP/origin policy while storing only a durable challenge and hashed user/credential boundary material.
POST /v1/human-auth/passkey/registration/optionsstate: staging-durablesdk_role: issue browser WebAuthn registration options under secure RP/origin policy while storing only a durable challenge and hashed user/credential boundary materialrequest_record: CloudHumanAuthPasskeyRegistrationOptionsRequestresponses: public_key_credential_creation_options | human_auth_challenge | refusal | receiptRequest
Section titled “Request”interface HumanAuthPasskeyRegistrationOptionsRequest { tenant: GestaltRef; subject: GestaltRef; relying_party_id: string; origin: string; user_handle_hash: string; // sha256: hash of the WebAuthn user handle user_name_hash: string; user_display_name_hash: string; attestation: string; // expected: "none" authenticator_attachment: string; // e.g. "platform" resident_key: string; // e.g. "preferred" user_verification: string; // e.g. "required" exclude_credential_id_hashes: string[]; contains_biometric_material: boolean; // refused if true contains_customer_data: boolean; // refused if true}Response
Section titled “Response”{ "operation": "humanAuth.passkeyRegistrationOptions", "outcome": "verified", "body": { "challenge": { "id": "human_auth_challenge:...", "...": "..." }, "public_key_credential_creation_options": { "challenge": "...b64url...", "rp": { "id": "gestalt.local", "name": "Gestalt" }, "user": { "id_b64url": "...", "handle_hash": "sha256:...", "name_hash": "sha256:...", "display_name_hash": "sha256:...", "raw_name_exposed": false, "raw_display_name_exposed": false }, "pub_key_cred_params": [{ "type": "public-key", "alg": -7 }], "authenticator_selection": {...}, "attestation": "none", "exclude_credential_id_hashes": [], "raw_credential_ids_exposed": false }, "durable_record": {...}, "privacy_boundary": { "raw_credential_id_stored": false, "raw_attestation_object_stored": false, "biometric_material_seen_by_gestalt": false, "production_admission": false }, "webauthn_origin_policy": {...}, "authority_boundaries": {...}, "production_admission": false }, "receipt": {...}}SDK example
Section titled “SDK example”const options = await client.humanAuthPasskeyRegistrationOptions({ tenant: "tenant_node:rheinwerk_calibration", subject: "human_person:anna", relying_party_id: "gestalt.local", origin: "https://gestalt.local", user_handle_hash: "sha256:fixture_user_handle_hash", user_name_hash: "sha256:fixture_user_name_hash", user_display_name_hash: "sha256:fixture_user_display_name_hash", attestation: "none", authenticator_attachment: "platform", resident_key: "preferred", user_verification: "required", exclude_credential_id_hashes: [], contains_biometric_material: false, contains_customer_data: false,});humanAuth.passkeyAssertionOptions
Section titled “humanAuth.passkeyAssertionOptions”Issue browser WebAuthn assertion options for discoverable passkeys under secure RP/origin policy without exposing raw credential ids.
POST /v1/human-auth/passkey/assertion/optionsstate: staging-durablesdk_role: issue browser WebAuthn assertion options for discoverable passkeys under secure RP/origin policy without exposing raw credential idsrequest_record: CloudHumanAuthPasskeyAssertionOptionsRequestresponses: public_key_credential_request_options | human_auth_challenge | refusal | receiptRequest
Section titled “Request”interface HumanAuthPasskeyAssertionOptionsRequest { tenant: GestaltRef; subject: GestaltRef; relying_party_id: string; origin: string; passkey_binding?: GestaltRef; scopes: string[]; user_verification: string; // e.g. "required" contains_biometric_material: boolean; contains_customer_data: boolean;}Response
Section titled “Response”{ "operation": "humanAuth.passkeyAssertionOptions", "outcome": "verified", "body": { "challenge": { "id": "human_auth_challenge:...", "...": "..." }, "public_key_credential_request_options": { "challenge": "...b64url...", "rp_id": "gestalt.local", "user_verification": "required", "timeout_ms": 300000, "allow_credentials": "omitted_discoverable_credentials_only", "allow_credential_id_hashes": ["sha256:..."], "raw_credential_ids_exposed": false }, "passkey_binding": { "id": "...", "credential_id_hash": "sha256:...", "...": "..." }, "durable_record": {...}, "privacy_boundary": {...}, "webauthn_origin_policy": {...}, "authority_boundaries": {...}, "production_admission": false }, "receipt": {...}}SDK example
Section titled “SDK example”const options = await client.humanAuthPasskeyAssertionOptions({ tenant: "tenant_node:rheinwerk_calibration", subject: "human_person:anna", relying_party_id: "gestalt.local", origin: "https://gestalt.local", passkey_binding: "passkey_binding:anna_platform_passkey_fixture", scopes: ["sensitive_approval"], user_verification: "required", contains_biometric_material: false, contains_customer_data: false,});humanAuth.registerPasskey
Section titled “humanAuth.registerPasskey”Finish browser WebAuthn registration under secure RP/origin and
attestation:none policy while storing only credential hash, public
verification material, clientDataJSON hash, and durable challenge
state. (SDK method: humanAuthRegisterPasskey. Earlier drafts
referred to this as humanAuth.passkeyRegister.)
POST /v1/human-auth/passkey/registerstate: staging-durablesdk_role: finish browser WebAuthn registration under secure RP/origin and attestation:none policy while storing only credential hash, public verification material, clientDataJSON hash, and durable challenge staterequest_record: CloudHumanAuthPasskeyRegisterRequestresponses: passkey_binding | attestation_policy | refusal | receiptRequest
Section titled “Request”interface HumanAuthPasskeyRegisterRequest { tenant: GestaltRef; subject: GestaltRef; challenge: GestaltRef; passkey_binding: GestaltRef; relying_party_id: string; origin: string; credential_id_hash: string; // sha256: prefix required public_key_algorithm: string; // expected: "ES256" public_key_uncompressed_hex: string; authenticator_attachment: string; synced_passkey_allowed: boolean; sign_count: number; attestation_conveyance: string; // must be "" or "none" attestation_object_hash: string; // sha256: prefix required client_data_json_hash: string; client_data_json_b64url?: string; // required at boundary; verified, not stored contains_biometric_material: boolean; // refused if true contains_customer_data: boolean; // refused if true raw_credential_id_supplied: boolean; // refused if true private_key_material_supplied: boolean; // refused if true}Response
Section titled “Response”{ "operation": "humanAuth.registerPasskey", "outcome": "admitted", "body": { "passkey_binding": { "id": "passkey_binding:m24_registered_passkey", "subject": "human_person:anna", "relying_party_id": "gestalt.local", "origin": "https://gestalt.local", "credential_id_hash": "sha256:...", "public_key_algorithm": "ES256", "public_key_hash": "sha256:...", "authenticator_attachment": "platform", "synced_passkey_allowed": true, "sign_count": 0, "biometric_data_seen_by_gestalt": false, "commitment": "sha256:..." }, "durable_records": { "passkey_binding": {...}, "challenge": {...} }, "registration_finish": {...}, "attestation_policy": { "mode": "none_only_staging", "verified_attestation_statement": false, "direct_attestation_allowed": false, "enterprise_attestation_allowed": false }, "raw_credential_id_stored": false, "raw_attestation_object_stored": false, "raw_client_data_json_stored": false, "raw_private_key_material_stored": false, "raw_biometric_material_stored": false, "webauthn_origin_policy": {...}, "authority_boundaries": {...}, "production_admission": false }, "receipt": {...}}The challenge consumed must have been issued by
humanAuth.passkeyRegistrationOptions (it must carry the
passkey_registration scope and not be expired or already
consumed). Only attestation:none is admitted — direct or
enterprise attestation will be refused with
human_auth_attestation_policy_refused.
SDK example
Section titled “SDK example”const registered = await client.humanAuthRegisterPasskey({ tenant: "tenant_node:rheinwerk_calibration", subject: "human_person:anna", challenge: options.body.challenge.id, passkey_binding: "passkey_binding:m24_registered_passkey", relying_party_id: "gestalt.local", origin: "https://gestalt.local", credential_id_hash: "sha256:m24_registered_credential_id_hash", public_key_algorithm: "ES256", public_key_uncompressed_hex: "04...", authenticator_attachment: "platform", synced_passkey_allowed: true, sign_count: 0, attestation_conveyance: "none", attestation_object_hash: "sha256:fixture_registration_attestation_object", client_data_json_hash: "", client_data_json_b64url: "...", contains_biometric_material: false, contains_customer_data: false, raw_credential_id_supplied: false, private_key_material_supplied: false,});humanAuth.passkeyImport
Section titled “humanAuth.passkeyImport”Import passkey public verification material without raw credential id, private key, or biometric material.
POST /v1/human-auth/passkey/importstate: staging-durablesdk_role: import passkey public verification material without raw credential id, private key, or biometric materialrequest_record: CloudHumanAuthPasskeyImportRequestresponses: passkey_binding | refusal | receiptRequest
Section titled “Request”interface HumanAuthPasskeyImportRequest { tenant: GestaltRef; subject: GestaltRef; passkey_binding: GestaltRef; relying_party_id: string; origin: string; credential_id_hash: string; public_key_algorithm: string; // expected: "ES256" public_key_uncompressed_hex: string; authenticator_attachment: string; synced_passkey_allowed: boolean; sign_count: number; attestation_object_hash?: string; fixture: boolean; // must be true contains_biometric_material: boolean; contains_customer_data: boolean;}Response
Section titled “Response”{ "operation": "humanAuth.passkeyImport", "outcome": "admitted", "body": { "passkey_binding": { "id": "passkey_binding:m24_imported_passkey", "subject": "human_person:anna", "relying_party_id": "gestalt.local", "origin": "https://gestalt.local", "credential_id_hash": "sha256:...", "public_key_algorithm": "ES256", "public_key_hash": "sha256:...", "sign_count": 0, "biometric_data_seen_by_gestalt": false, "commitment": "sha256:..." }, "durable_record": {...}, "raw_credential_id_stored": false, "raw_private_key_material_stored": false, "raw_biometric_material_stored": false, "webauthn_origin_policy": {...}, "authority_boundaries": {...}, "production_admission": false }, "receipt": {...}}SDK example
Section titled “SDK example”const imported = await client.humanAuthPasskeyImport({ tenant: "tenant_node:rheinwerk_calibration", subject: "human_person:anna", passkey_binding: "passkey_binding:m24_imported_passkey", relying_party_id: "gestalt.local", origin: "https://gestalt.local", credential_id_hash: "sha256:m24_imported_credential_id_hash", public_key_algorithm: "ES256", public_key_uncompressed_hex: "04...", authenticator_attachment: "platform", synced_passkey_allowed: true, sign_count: 0, fixture: true, contains_biometric_material: false, contains_customer_data: false,});Refusal codes
Section titled “Refusal codes”Selected refusal codes returned by this cluster (see
refusal.codes for the full list):
auth_login_fixture_onlyauth_login_raw_identifier_refusedauth_login_source_hash_requiredauth_login_rate_limit_policy_invalidauth_login_passkey_binding_unknownauth_login_passkey_subject_mismatchauth_login_passkey_origin_mismatchauth_login_rate_limit_missingauth_login_rate_limit_not_allowedauth_login_standing_mismatchauth_login_holder_key_mismatchauth_login_expiry_invalidauth_login_challenge_mismatchauth_login_human_presence_invalidauth_login_session_already_issuedauth_session_issue_fixture_onlyauth_session_issue_standing_mismatchauth_session_issue_holder_key_mismatchauth_session_issue_expiry_invalidauth_session_refresh_fixture_onlyauth_session_refresh_holder_key_mismatchauth_session_refresh_expiry_invalidauth_session_refresh_scope_expansion_refusedauth_rate_limit_fixture_onlyauth_rate_limit_raw_identifier_refusedauth_rate_limit_hash_requiredauth_rate_limit_policy_invalidauth_rate_limit_throttledauth_rate_limit_lockedauth_recovery_policy_fixture_onlyauth_recovery_policy_raw_material_refusedauth_recovery_policy_cannot_create_standingauth_recovery_policy_standing_mismatchauth_recovery_policy_hash_requiredauth_recovery_policy_passkey_unknownauth_recovery_policy_passkey_subject_mismatchauth_recovery_policy_presence_invalidhuman_auth_biometric_material_refusedhuman_auth_raw_credential_material_refusedhuman_auth_attestation_policy_refusedhuman_auth_passkey_public_key_missinghuman_auth_passkey_binding_unknownhuman_auth_passkey_credential_mismatchhuman_auth_passkey_subject_mismatchhuman_auth_passkey_import_fixture_onlyhuman_auth_registration_challenge_requiredhuman_auth_challenge_unknownhuman_auth_challenge_expiredhuman_auth_challenge_replayedhuman_auth_user_verification_missinghuman_auth_webauthn_assertion_missinghuman_auth_webauthn_challenge_mismatchhuman_auth_webauthn_client_data_invalidhuman_auth_wrong_rp_idhuman_auth_wrong_originm13_human_presence_missingm13_human_presence_subject_mismatchm13_session_unknownm13_wrong_actorm13_wrong_vesselWhere to read next
Section titled “Where to read next”- API: human auth —
humanAuth.challenge,humanAuth.verifyPasskey,humanAuth.faceMatchFallback - API: authority —
authority.presenceApproval,authority.keyRotate - API: admission and key custody — the production-gate counterparts to session and rate-limit records
- Reference: refusal codes