@faable/auth-sdk 2.7.94 → 2.7.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/spec/openapi.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "info": {
4
4
  "title": "@faablecloud/auth",
5
5
  "description": "Auth Platform made by Faable. Manage Users and Roles",
6
- "version": "2.73.3",
6
+ "version": "2.74.0",
7
7
  "license": {
8
8
  "name": "private",
9
9
  "url": "https://faable.com/docs/platform/privacy-policy"
@@ -1865,6 +1865,108 @@
1865
1865
  "description": "Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.",
1866
1866
  "nullable": true
1867
1867
  },
1868
+ "saml_sp": {
1869
+ "type": "object",
1870
+ "required": [
1871
+ "enabled",
1872
+ "entity_id",
1873
+ "acs_url"
1874
+ ],
1875
+ "properties": {
1876
+ "enabled": {
1877
+ "type": "boolean",
1878
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
1879
+ },
1880
+ "entity_id": {
1881
+ "type": "string",
1882
+ "minLength": 1,
1883
+ "maxLength": 1024,
1884
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
1885
+ },
1886
+ "acs_url": {
1887
+ "type": "string",
1888
+ "minLength": 1,
1889
+ "maxLength": 2048,
1890
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
1891
+ },
1892
+ "nameid_format": {
1893
+ "enum": [
1894
+ "emailAddress",
1895
+ "persistent",
1896
+ "unspecified"
1897
+ ],
1898
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
1899
+ },
1900
+ "attribute_mapping": {
1901
+ "type": "object",
1902
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
1903
+ "additionalProperties": {
1904
+ "anyOf": [
1905
+ {
1906
+ "type": "string",
1907
+ "enum": [
1908
+ "user_id"
1909
+ ]
1910
+ },
1911
+ {
1912
+ "type": "string",
1913
+ "enum": [
1914
+ "email"
1915
+ ]
1916
+ },
1917
+ {
1918
+ "type": "string",
1919
+ "enum": [
1920
+ "email_verified"
1921
+ ]
1922
+ },
1923
+ {
1924
+ "type": "string",
1925
+ "enum": [
1926
+ "name"
1927
+ ]
1928
+ },
1929
+ {
1930
+ "type": "string",
1931
+ "enum": [
1932
+ "given_name"
1933
+ ]
1934
+ },
1935
+ {
1936
+ "type": "string",
1937
+ "enum": [
1938
+ "family_name"
1939
+ ]
1940
+ },
1941
+ {
1942
+ "type": "string",
1943
+ "enum": [
1944
+ "nickname"
1945
+ ]
1946
+ },
1947
+ {
1948
+ "type": "string",
1949
+ "enum": [
1950
+ "picture"
1951
+ ]
1952
+ },
1953
+ {
1954
+ "type": "string",
1955
+ "enum": [
1956
+ "phone_number"
1957
+ ]
1958
+ }
1959
+ ]
1960
+ }
1961
+ },
1962
+ "sign_response": {
1963
+ "type": "boolean",
1964
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
1965
+ }
1966
+ },
1967
+ "additionalProperties": false,
1968
+ "nullable": true
1969
+ },
1868
1970
  "account": {
1869
1971
  "type": "string",
1870
1972
  "description": "Object is related with this account"
@@ -2555,6 +2657,114 @@
2555
2657
  "description": "Audience a client_credentials token request gets when it sends none. An explicit `audience` wins. `null` removes it.",
2556
2658
  "nullable": true
2557
2659
  },
2660
+ "saml_sp": {
2661
+ "anyOf": [
2662
+ {
2663
+ "type": "object",
2664
+ "required": [
2665
+ "enabled",
2666
+ "entity_id",
2667
+ "acs_url"
2668
+ ],
2669
+ "properties": {
2670
+ "enabled": {
2671
+ "type": "boolean",
2672
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
2673
+ },
2674
+ "entity_id": {
2675
+ "type": "string",
2676
+ "minLength": 1,
2677
+ "maxLength": 1024,
2678
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
2679
+ },
2680
+ "acs_url": {
2681
+ "type": "string",
2682
+ "minLength": 1,
2683
+ "maxLength": 2048,
2684
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
2685
+ },
2686
+ "nameid_format": {
2687
+ "enum": [
2688
+ "emailAddress",
2689
+ "persistent",
2690
+ "unspecified"
2691
+ ],
2692
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
2693
+ },
2694
+ "attribute_mapping": {
2695
+ "type": "object",
2696
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
2697
+ "additionalProperties": {
2698
+ "anyOf": [
2699
+ {
2700
+ "type": "string",
2701
+ "enum": [
2702
+ "user_id"
2703
+ ]
2704
+ },
2705
+ {
2706
+ "type": "string",
2707
+ "enum": [
2708
+ "email"
2709
+ ]
2710
+ },
2711
+ {
2712
+ "type": "string",
2713
+ "enum": [
2714
+ "email_verified"
2715
+ ]
2716
+ },
2717
+ {
2718
+ "type": "string",
2719
+ "enum": [
2720
+ "name"
2721
+ ]
2722
+ },
2723
+ {
2724
+ "type": "string",
2725
+ "enum": [
2726
+ "given_name"
2727
+ ]
2728
+ },
2729
+ {
2730
+ "type": "string",
2731
+ "enum": [
2732
+ "family_name"
2733
+ ]
2734
+ },
2735
+ {
2736
+ "type": "string",
2737
+ "enum": [
2738
+ "nickname"
2739
+ ]
2740
+ },
2741
+ {
2742
+ "type": "string",
2743
+ "enum": [
2744
+ "picture"
2745
+ ]
2746
+ },
2747
+ {
2748
+ "type": "string",
2749
+ "enum": [
2750
+ "phone_number"
2751
+ ]
2752
+ }
2753
+ ]
2754
+ }
2755
+ },
2756
+ "sign_response": {
2757
+ "type": "boolean",
2758
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
2759
+ }
2760
+ },
2761
+ "additionalProperties": false
2762
+ },
2763
+ {
2764
+ "type": "null"
2765
+ }
2766
+ ]
2767
+ },
2558
2768
  "metadata": {
2559
2769
  "type": "object",
2560
2770
  "properties": {},
@@ -8033,7 +8243,7 @@
8033
8243
  },
8034
8244
  "ErrorCode": {
8035
8245
  "type": "string",
8036
- "description": "Machine-readable error code. One HTTP status per code.\n\n- `bad_request` (400): The request is malformed. `message` says what is wrong.\n- `validation_error` (400): The body, query or path failed schema validation. `details.issues` lists each failing field.\n- `unauthorized` (401): The request carries no valid credentials.\n- `payment_required` (402): The feature is not included in the current plan.\n- `forbidden` (403): The credentials are valid but do not allow this operation.\n- `not_found` (404): The resource does not exist in this account.\n- `method_not_allowed` (405): The HTTP method is not supported on this path.\n- `conflict` (409): The request conflicts with the current state of the resource.\n- `payload_too_large` (413): The request body exceeds the size limit.\n- `unsupported_media_type` (415): The `Content-Type` is not accepted by this endpoint.\n- `unprocessable_entity` (422): The request is well-formed but cannot be processed.\n- `too_many_requests` (429): Rate limit exceeded. Honour the `Retry-After` header.\n- `internal_error` (500): Unexpected server error. Retry later; the request id is logged.\n- `already_exists` (400): A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n- `invalid_id` (400): The id in the path is not a valid id for this resource.\n- `account_not_found` (404): No Auth Account matches the request (domain, header or token).\n- `invalid_query` (400): The `?query=` FaableQL expression could not be parsed.\n- `search_not_supported` (400): This resource does not support `?search=`.\n- `invalid_expand` (400): An `?expand=` path is not allowed on this resource.\n- `access_denied` (401): The caller may not access this row (ownership or machine-to-machine restriction).\n- `insufficient_scope` (403): The token lacks a scope this operation requires. `message` names it.\n- `team_required` (400): An Auth Account must belong to a project (team).\n- `account_limit_reached` (409): The project already has the maximum number of Auth Accounts.\n- `invalid_project_id` (400): The project id is not of the form `project_<hex>` or `team_<hex>`.\n- `account_transfer_refused` (409): The Auth Account cannot be moved to that project. `details.blockers` lists why.\n- `email_sender_domain_not_allowed` (400): The sender address is not in a domain this platform signs email for (AUTH_EMAIL_SENDER_DOMAINS).\n- `plan_required` (402): The setting requires a higher plan. `message` says which.\n- `invalid_canonical_host` (400): `canonical_host` must be this account's own platform host or one of its verified custom domains.\n- `canonical_host_breaks_passkeys` (400): `canonical_host` is outside the account's current WebAuthn RP ID — every passkey already enrolled would stop working. Set `webauthn_rp_id` in the same request to a suffix that covers both.\n- `not_logged_in` (401): There is no signed-in session for this request.\n- `session_already_revoked` (409): The session was already revoked.\n- `invalid_token` (401): The bearer token is missing, malformed, expired or has no subject.\n- `not_owner` (403): The caller may only perform this operation on their own user.\n- `user_suspended` (403): The user is suspended and cannot sign in or be modified.\n- `invalid_credentials` (400): The email/username or password is incorrect.\n- `login_denied` (401): A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.\n- `signup_denied` (403): A pre-signup Action or an active block denied the sign-up. `message` carries the reason.\n- `client_credentials_denied` (401): An Action denied the client_credentials grant. `message` carries the reason.\n- `m2m_quota_exceeded` (402): The project used the machine-to-machine tokens its plan includes this month. Free is capped; Hobby and Pro are billed per token instead.\n- `signup_disabled` (403): Self-service sign-up is disabled on this connection.\n- `email_taken` (409): Another user in this account already uses that email.\n- `username_taken` (409): Another user of this database connection already uses that username.\n- `invalid_username` (400): The username is empty, or contains an `@` or whitespace. `message` says which.\n- `invalid_email` (400): The email address is not valid.\n- `invalid_name` (400): The name contains a link. `message` says which field.\n- `same_email` (400): The new email is the same as the current one.\n- `user_has_no_email` (400): The operation needs an email but the user has none.\n- `password_too_weak` (400): The password does not meet the connection policy. `message` lists each unmet rule.\n- `invalid_password_hash` (400): The imported password hash cannot be read, or its cost is above what a login can afford. `message` names the field.\n- `weak_password_hash` (400): The imported password hash uses a fast digest (MD5, SHA-1, SHA-2) or a cost below the minimum. `message` says which.\n- `invalid_code` (400): The verification code is wrong, expired or already used.\n- `invalid_phone` (400): The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n- `phone_changed` (400): The phone on the user changed after the code was sent. Start again.\n- `phone_already_verified` (409): The user already has a verified phone; the number cannot be swapped from the screen.\n- `sms_unavailable` (409): This account cannot send SMS right now. The suffix says why: `sms_unavailable:plan`, `:quota`, `:provider`.\n- `sms_failed` (409): The SMS provider rejected the message.\n- `invalid_state` (400): The `state` is missing, expired, or does not describe a resumable step.\n- `state_mismatch` (401): The `state` belongs to another account, session, client or ceremony.\n- `invalid_ticket` (400): The ticket does not exist or is not the kind this endpoint accepts.\n- `ticket_used` (400): The ticket was already consumed.\n- `ticket_expired` (400): The ticket is past its expiry.\n- `ticket_not_found` (404): No ticket with that id belongs to this user.\n- `credential_not_found` (404): The user has no password credential on this connection.\n- `invalid_link` (400): The magic link is malformed.\n- `expired_link` (400): The magic link token was not found or has expired.\n- `invalid_otp` (401): The one-time password is wrong or expired.\n- `invalid_client` (400): The `client_id` in the request does not name a client of this account.\n- `client_not_found` (404): No client with that `client_id`.\n- `client_mismatch` (401): The client belongs to a different account.\n- `invalid_client_metadata` (400): Dynamic client registration rejected a field (RFC 7591). `message` names it.\n- `invalid_connection` (400): The connection does not exist, is disabled, or is not of the type this flow needs.\n- `no_database_connection` (400): The tenant has no database connection, so there is nowhere to store a password. Create one and retry.\n- `connection_required` (400): No connection was given and the client has no `default_connection`.\n- `connection_misconfigured` (400): The connection is missing settings needed to talk to its provider.\n- `passwordless_unavailable` (400): No passwordless connection is configured or enabled for this client.\n- `ambiguous_connection` (400): Several connections match; pass `connection_id`.\n- `invalid_redirect_uri` (400): The redirect URI is not registered for the client.\n- `origin_not_allowed` (403): The request `Origin` is not in the client's Allowed Web Origins.\n- `audience_not_found` (403): The `audience` does not name an API registered in this account.\n- `system_resource` (403): The resource is managed by Faable and cannot be modified or deleted.\n- `invalid_request` (400): A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n- `invalid_device_code` (400): The device or user code is invalid or expired.\n- `provider_error` (400): The upstream identity provider returned an error. The suffix is the provider’s own code, when it sent one: `provider_error:bad_refresh_token` (the user must re-authorize), `provider_error:incorrect_client_credentials` (our configuration, the user can do nothing). `details.provider_error_description` carries the provider’s sentence.\n- `identity_already_linked` (409): That external identity is already linked to another user.\n- `identity_link_denied` (409): That external identity cannot be linked to this user.\n- `identity_conflict` (409): The user already has a linked identity for this connection.\n- `identity_orphaned` (400): The identity pointed at a user that no longer exists; it was removed. Sign in again.\n- `no_provider_token` (400): The identity holds no provider access token.\n- `no_refresh_token` (400): The identity holds no provider refresh token, so it cannot be refreshed.\n- `action_unavailable` (401): The Action that paused this login is disabled or belongs elsewhere.\n- `login_flow_misconfigured` (500): The login flow of this account cannot run. `message` carries the node error.\n- `mfa_required` (403): The login needs a second factor that this flow cannot collect, or one is still pending on the session.\n- `step_up_required` (403): Confirm an existing factor before changing your factors.\n- `interaction_required` (403): An Action asked for a redirect on a non-interactive flow.\n- `passkey_login_disabled` (400): Sign in with a passkey is not enabled for this client.\n- `mfa_pending` (401): A second-factor challenge is pending; answer it before continuing.\n- `mfa_invalid_code` (401): The authenticator code did not verify.\n- `mfa_invalid_recovery_code` (401): The recovery code did not verify.\n- `too_many_attempts` (429): Too many wrong codes. Wait a few minutes and try again.\n- `too_many_login_attempts` (429): Too many failed sign-in attempts for this email or username from this network. Wait a few minutes and try again.\n- `totp_not_allowed` (401): This account does not accept authenticator apps.\n- `no_usable_factor` (400): The user has no confirmed second factor to challenge.\n- `factor_not_found` (400): No factor with that id belongs to this user.\n- `factor_already_confirmed` (400): The factor was already confirmed.\n- `invalid_factor` (400): The factor cannot be verified (no secret material).\n- `session_missing` (401): The session that started this login no longer exists.\n- `passkey_verification_failed` (400): The WebAuthn response could not be verified.\n- `no_passkey_enrolled` (400): The user has no passkey to sign in with.\n- `malformed_credential` (400): The WebAuthn credential is malformed.\n- `unknown_passkey` (401): The passkey is not enrolled for this user.\n- `invalid_passkey` (401): The passkey assertion did not verify.\n- `invalid_invite_url` (400): An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.\n- `invite_email_mismatch` (403): The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.\n- `invite_email_unverified` (403): The accepting user's email matches the invite but is not verified yet.\n- `invite_not_found` (404): No invite with that id in this account.\n- `invite_used` (400): The invite was already accepted.\n- `already_member` (400): The user is already a member of the team.\n- `not_a_member` (404): The user is not a member of the team.\n- `consent_options_unavailable` (502): The consent screen could not load the choices from the API being authorized (its `consent_options.url`). Try again in a moment.\n- `consent_choice_invalid` (400): The choice sent with the consent decision is not one the API offered for this user.\n- `registration_disabled` (403): Dynamic client registration is turned off for this tenant. Create the client from the dashboard or the Management API, or turn on `dynamic_client_registration_enabled`.\n- `invalid_email_domain` (400): Not an email domain this organization can claim: it must be a plain domain name (`acme.com`), lowercase, without scheme, path or wildcard.\n- `public_email_domain` (400): A public email provider (gmail.com, outlook.com…) cannot belong to an organization: anyone can have an address there.\n- `organization_domain_not_found` (404): The organization has no such domain.\n- `organization_domain_taken` (409): Another organization of this tenant already verified this domain. A domain belongs to one organization.\n- `organization_domain_unverified` (400): The TXT record was not found or does not hold the expected value yet. `details.record` says what was expected and what resolves now; DNS can take a few minutes to propagate.\n- `plan_unavailable` (503): The plan could not be checked right now, and this setting is only for paid plans. Try again in a minute.\n- `organization_domain_mismatch` (403): This organization's identity provider can only sign in addresses of the organization's verified email domains.\n- `team_not_in_organization` (400): Auto-join can only put people in teams of the same organization. `details.teams` lists the others.\n- `organization_has_no_idp` (400): Requiring SSO needs an identity provider: bind an `oidc` connection to the organization first.\n- `organization_not_found` (404): No organization with that id in this account.\n- `invalid_user` (400): The user does not exist in this account.\n- `not_an_organization_admin` (404): The user is not an admin of the organization.\n- `last_organization_admin` (409): An organization keeps at least one admin. Add another admin before removing this one.\n- `team_id_unavailable` (409): The team id cannot be used in this tenant. Pick another id or let the API generate one.\n- `use_team_member_route` (400): This generic route cannot create memberships. Use `POST /team/{team_id}/member` for team members or `POST /role/{role_id}/users` for role grants.\n- `role_not_found` (404): No role with that id in this account.\n- `invalid_role` (400): The role belongs to another account.\n- `flow_not_found` (400): The login flow does not exist in this account.\n- `revision_not_found` (404): The revision is not in the flow history.\n- `invalid_webhook_event` (400): An event is not in the webhook event catalog. `message` and `details.valid_events` list the valid ones.\n- `invalid_webhook_url` (400): The webhook URL must be an absolute `https` URL.\n- `not_a_webhook` (400): The operation only applies to `webhook` subscriptions.\n- `delivery_not_found` (404): No webhook delivery with that id for this subscription.\n- `delivery_in_progress` (409): The delivery is being attempted right now; retry the redeliver in a minute.",
8246
+ "description": "Machine-readable error code. One HTTP status per code.\n\n- `bad_request` (400): The request is malformed. `message` says what is wrong.\n- `validation_error` (400): The body, query or path failed schema validation. `details.issues` lists each failing field.\n- `unauthorized` (401): The request carries no valid credentials.\n- `payment_required` (402): The feature is not included in the current plan.\n- `forbidden` (403): The credentials are valid but do not allow this operation.\n- `not_found` (404): The resource does not exist in this account.\n- `method_not_allowed` (405): The HTTP method is not supported on this path.\n- `conflict` (409): The request conflicts with the current state of the resource.\n- `payload_too_large` (413): The request body exceeds the size limit.\n- `unsupported_media_type` (415): The `Content-Type` is not accepted by this endpoint.\n- `unprocessable_entity` (422): The request is well-formed but cannot be processed.\n- `too_many_requests` (429): Rate limit exceeded. Honour the `Retry-After` header.\n- `internal_error` (500): Unexpected server error. Retry later; the request id is logged.\n- `already_exists` (400): A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n- `invalid_id` (400): The id in the path is not a valid id for this resource.\n- `account_not_found` (404): No Auth Account matches the request (domain, header or token).\n- `invalid_query` (400): The `?query=` FaableQL expression could not be parsed.\n- `search_not_supported` (400): This resource does not support `?search=`.\n- `invalid_expand` (400): An `?expand=` path is not allowed on this resource.\n- `access_denied` (401): The caller may not access this row (ownership or machine-to-machine restriction).\n- `insufficient_scope` (403): The token lacks a scope this operation requires. `message` names it.\n- `team_required` (400): An Auth Account must belong to a project (team).\n- `account_limit_reached` (409): The project already has the maximum number of Auth Accounts.\n- `invalid_project_id` (400): The project id is not of the form `project_<hex>` or `team_<hex>`.\n- `account_transfer_refused` (409): The Auth Account cannot be moved to that project. `details.blockers` lists why.\n- `email_sender_domain_not_allowed` (400): The sender address is not in a domain this platform signs email for (AUTH_EMAIL_SENDER_DOMAINS).\n- `plan_required` (402): The setting requires a higher plan. `message` says which.\n- `invalid_canonical_host` (400): `canonical_host` must be this account's own platform host or one of its verified custom domains.\n- `canonical_host_breaks_passkeys` (400): `canonical_host` is outside the account's current WebAuthn RP ID — every passkey already enrolled would stop working. Set `webauthn_rp_id` in the same request to a suffix that covers both.\n- `not_logged_in` (401): There is no signed-in session for this request.\n- `session_already_revoked` (409): The session was already revoked.\n- `invalid_token` (401): The bearer token is missing, malformed, expired or has no subject.\n- `not_owner` (403): The caller may only perform this operation on their own user.\n- `user_suspended` (403): The user is suspended and cannot sign in or be modified.\n- `invalid_credentials` (400): The email/username or password is incorrect.\n- `login_denied` (401): A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.\n- `signup_denied` (403): A pre-signup Action or an active block denied the sign-up. `message` carries the reason.\n- `client_credentials_denied` (401): An Action denied the client_credentials grant. `message` carries the reason.\n- `m2m_quota_exceeded` (402): The project used the machine-to-machine tokens its plan includes this month. Free is capped; Hobby and Pro are billed per token instead.\n- `signup_disabled` (403): Self-service sign-up is disabled on this connection.\n- `email_taken` (409): Another user in this account already uses that email.\n- `username_taken` (409): Another user of this database connection already uses that username.\n- `invalid_username` (400): The username is empty, or contains an `@` or whitespace. `message` says which.\n- `invalid_email` (400): The email address is not valid.\n- `invalid_name` (400): The name contains a link. `message` says which field.\n- `same_email` (400): The new email is the same as the current one.\n- `user_has_no_email` (400): The operation needs an email but the user has none.\n- `password_too_weak` (400): The password does not meet the connection policy. `message` lists each unmet rule.\n- `invalid_password_hash` (400): The imported password hash cannot be read, or its cost is above what a login can afford. `message` names the field.\n- `weak_password_hash` (400): The imported password hash uses a fast digest (MD5, SHA-1, SHA-2) or a cost below the minimum. `message` says which.\n- `invalid_code` (400): The verification code is wrong, expired or already used.\n- `invalid_phone` (400): The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n- `phone_changed` (400): The phone on the user changed after the code was sent. Start again.\n- `phone_already_verified` (409): The user already has a verified phone; the number cannot be swapped from the screen.\n- `sms_unavailable` (409): This account cannot send SMS right now. The suffix says why: `sms_unavailable:plan`, `:quota`, `:provider`.\n- `sms_failed` (409): The SMS provider rejected the message.\n- `invalid_state` (400): The `state` is missing, expired, or does not describe a resumable step.\n- `state_mismatch` (401): The `state` belongs to another account, session, client or ceremony.\n- `invalid_ticket` (400): The ticket does not exist or is not the kind this endpoint accepts.\n- `ticket_used` (400): The ticket was already consumed.\n- `ticket_expired` (400): The ticket is past its expiry.\n- `ticket_not_found` (404): No ticket with that id belongs to this user.\n- `credential_not_found` (404): The user has no password credential on this connection.\n- `invalid_link` (400): The magic link is malformed.\n- `expired_link` (400): The magic link token was not found or has expired.\n- `invalid_otp` (401): The one-time password is wrong or expired.\n- `invalid_client` (400): The `client_id` in the request does not name a client of this account.\n- `client_not_found` (404): No client with that `client_id`.\n- `client_mismatch` (401): The client belongs to a different account.\n- `invalid_client_metadata` (400): Dynamic client registration rejected a field (RFC 7591). `message` names it.\n- `invalid_connection` (400): The connection does not exist, is disabled, or is not of the type this flow needs.\n- `no_database_connection` (400): The tenant has no database connection, so there is nowhere to store a password. Create one and retry.\n- `connection_required` (400): No connection was given and the client has no `default_connection`.\n- `connection_misconfigured` (400): The connection is missing settings needed to talk to its provider.\n- `passwordless_unavailable` (400): No passwordless connection is configured or enabled for this client.\n- `ambiguous_connection` (400): Several connections match; pass `connection_id`.\n- `invalid_redirect_uri` (400): The redirect URI is not registered for the client.\n- `invalid_saml_request` (400): The SAMLRequest is missing, too large, not well-formed or not a SAML 2.0 AuthnRequest.\n- `saml_sp_mismatch` (400): The AuthnRequest does not match the client's SAML configuration: its Issuer is not the configured entity_id, or it asks for another Assertion Consumer Service URL or binding.\n- `saml_not_enabled` (404): This client does not have SAML enabled.\n- `invalid_saml_sp` (400): The `saml_sp` configuration is invalid: `acs_url` must be an absolute https URL without a fragment.\n- `origin_not_allowed` (403): The request `Origin` is not in the client's Allowed Web Origins.\n- `audience_not_found` (403): The `audience` does not name an API registered in this account.\n- `system_resource` (403): The resource is managed by Faable and cannot be modified or deleted.\n- `invalid_request` (400): A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n- `invalid_device_code` (400): The device or user code is invalid or expired.\n- `provider_error` (400): The upstream identity provider returned an error. The suffix is the provider’s own code, when it sent one: `provider_error:bad_refresh_token` (the user must re-authorize), `provider_error:incorrect_client_credentials` (our configuration, the user can do nothing). `details.provider_error_description` carries the provider’s sentence.\n- `identity_already_linked` (409): That external identity is already linked to another user.\n- `identity_link_denied` (409): That external identity cannot be linked to this user.\n- `identity_conflict` (409): The user already has a linked identity for this connection.\n- `identity_orphaned` (400): The identity pointed at a user that no longer exists; it was removed. Sign in again.\n- `no_provider_token` (400): The identity holds no provider access token.\n- `no_refresh_token` (400): The identity holds no provider refresh token, so it cannot be refreshed.\n- `action_unavailable` (401): The Action that paused this login is disabled or belongs elsewhere.\n- `login_flow_misconfigured` (500): The login flow of this account cannot run. `message` carries the node error.\n- `mfa_required` (403): The login needs a second factor that this flow cannot collect, or one is still pending on the session.\n- `step_up_required` (403): Confirm an existing factor before changing your factors.\n- `interaction_required` (403): An Action asked for a redirect on a non-interactive flow.\n- `passkey_login_disabled` (400): Sign in with a passkey is not enabled for this client.\n- `mfa_pending` (401): A second-factor challenge is pending; answer it before continuing.\n- `mfa_invalid_code` (401): The authenticator code did not verify.\n- `mfa_invalid_recovery_code` (401): The recovery code did not verify.\n- `too_many_attempts` (429): Too many wrong codes. Wait a few minutes and try again.\n- `too_many_login_attempts` (429): Too many failed sign-in attempts for this email or username from this network. Wait a few minutes and try again.\n- `totp_not_allowed` (401): This account does not accept authenticator apps.\n- `no_usable_factor` (400): The user has no confirmed second factor to challenge.\n- `factor_not_found` (400): No factor with that id belongs to this user.\n- `factor_already_confirmed` (400): The factor was already confirmed.\n- `invalid_factor` (400): The factor cannot be verified (no secret material).\n- `session_missing` (401): The session that started this login no longer exists.\n- `passkey_verification_failed` (400): The WebAuthn response could not be verified.\n- `no_passkey_enrolled` (400): The user has no passkey to sign in with.\n- `malformed_credential` (400): The WebAuthn credential is malformed.\n- `unknown_passkey` (401): The passkey is not enrolled for this user.\n- `invalid_passkey` (401): The passkey assertion did not verify.\n- `invalid_invite_url` (400): An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.\n- `invite_email_mismatch` (403): The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.\n- `invite_email_unverified` (403): The accepting user's email matches the invite but is not verified yet.\n- `invite_not_found` (404): No invite with that id in this account.\n- `invite_used` (400): The invite was already accepted.\n- `already_member` (400): The user is already a member of the team.\n- `not_a_member` (404): The user is not a member of the team.\n- `consent_options_unavailable` (502): The consent screen could not load the choices from the API being authorized (its `consent_options.url`). Try again in a moment.\n- `consent_choice_invalid` (400): The choice sent with the consent decision is not one the API offered for this user.\n- `registration_disabled` (403): Dynamic client registration is turned off for this tenant. Create the client from the dashboard or the Management API, or turn on `dynamic_client_registration_enabled`.\n- `invalid_email_domain` (400): Not an email domain this organization can claim: it must be a plain domain name (`acme.com`), lowercase, without scheme, path or wildcard.\n- `public_email_domain` (400): A public email provider (gmail.com, outlook.com…) cannot belong to an organization: anyone can have an address there.\n- `organization_domain_not_found` (404): The organization has no such domain.\n- `organization_domain_taken` (409): Another organization of this tenant already verified this domain. A domain belongs to one organization.\n- `organization_domain_unverified` (400): The TXT record was not found or does not hold the expected value yet. `details.record` says what was expected and what resolves now; DNS can take a few minutes to propagate.\n- `plan_unavailable` (503): The plan could not be checked right now, and this setting is only for paid plans. Try again in a minute.\n- `organization_domain_mismatch` (403): This organization's identity provider can only sign in addresses of the organization's verified email domains.\n- `team_not_in_organization` (400): Auto-join can only put people in teams of the same organization. `details.teams` lists the others.\n- `organization_has_no_idp` (400): Requiring SSO needs an identity provider: bind an `oidc` connection to the organization first.\n- `organization_not_found` (404): No organization with that id in this account.\n- `invalid_user` (400): The user does not exist in this account.\n- `not_an_organization_admin` (404): The user is not an admin of the organization.\n- `last_organization_admin` (409): An organization keeps at least one admin. Add another admin before removing this one.\n- `team_id_unavailable` (409): The team id cannot be used in this tenant. Pick another id or let the API generate one.\n- `use_team_member_route` (400): This generic route cannot create memberships. Use `POST /team/{team_id}/member` for team members or `POST /role/{role_id}/users` for role grants.\n- `role_not_found` (404): No role with that id in this account.\n- `invalid_role` (400): The role belongs to another account.\n- `flow_not_found` (400): The login flow does not exist in this account.\n- `revision_not_found` (404): The revision is not in the flow history.\n- `invalid_webhook_event` (400): An event is not in the webhook event catalog. `message` and `details.valid_events` list the valid ones.\n- `invalid_webhook_url` (400): The webhook URL must be an absolute `https` URL.\n- `not_a_webhook` (400): The operation only applies to `webhook` subscriptions.\n- `delivery_not_found` (404): No webhook delivery with that id for this subscription.\n- `delivery_in_progress` (409): The delivery is being attempted right now; retry the redeliver in a minute.",
8037
8247
  "enum": [
8038
8248
  "bad_request",
8039
8249
  "validation_error",
@@ -8112,6 +8322,10 @@
8112
8322
  "passwordless_unavailable",
8113
8323
  "ambiguous_connection",
8114
8324
  "invalid_redirect_uri",
8325
+ "invalid_saml_request",
8326
+ "saml_sp_mismatch",
8327
+ "saml_not_enabled",
8328
+ "invalid_saml_sp",
8115
8329
  "origin_not_allowed",
8116
8330
  "audience_not_found",
8117
8331
  "system_resource",
@@ -18897,6 +19111,108 @@
18897
19111
  "description": "Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.",
18898
19112
  "nullable": true
18899
19113
  },
19114
+ "saml_sp": {
19115
+ "type": "object",
19116
+ "required": [
19117
+ "enabled",
19118
+ "entity_id",
19119
+ "acs_url"
19120
+ ],
19121
+ "properties": {
19122
+ "enabled": {
19123
+ "type": "boolean",
19124
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
19125
+ },
19126
+ "entity_id": {
19127
+ "type": "string",
19128
+ "minLength": 1,
19129
+ "maxLength": 1024,
19130
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
19131
+ },
19132
+ "acs_url": {
19133
+ "type": "string",
19134
+ "minLength": 1,
19135
+ "maxLength": 2048,
19136
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
19137
+ },
19138
+ "nameid_format": {
19139
+ "enum": [
19140
+ "emailAddress",
19141
+ "persistent",
19142
+ "unspecified"
19143
+ ],
19144
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
19145
+ },
19146
+ "attribute_mapping": {
19147
+ "type": "object",
19148
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
19149
+ "additionalProperties": {
19150
+ "anyOf": [
19151
+ {
19152
+ "type": "string",
19153
+ "enum": [
19154
+ "user_id"
19155
+ ]
19156
+ },
19157
+ {
19158
+ "type": "string",
19159
+ "enum": [
19160
+ "email"
19161
+ ]
19162
+ },
19163
+ {
19164
+ "type": "string",
19165
+ "enum": [
19166
+ "email_verified"
19167
+ ]
19168
+ },
19169
+ {
19170
+ "type": "string",
19171
+ "enum": [
19172
+ "name"
19173
+ ]
19174
+ },
19175
+ {
19176
+ "type": "string",
19177
+ "enum": [
19178
+ "given_name"
19179
+ ]
19180
+ },
19181
+ {
19182
+ "type": "string",
19183
+ "enum": [
19184
+ "family_name"
19185
+ ]
19186
+ },
19187
+ {
19188
+ "type": "string",
19189
+ "enum": [
19190
+ "nickname"
19191
+ ]
19192
+ },
19193
+ {
19194
+ "type": "string",
19195
+ "enum": [
19196
+ "picture"
19197
+ ]
19198
+ },
19199
+ {
19200
+ "type": "string",
19201
+ "enum": [
19202
+ "phone_number"
19203
+ ]
19204
+ }
19205
+ ]
19206
+ }
19207
+ },
19208
+ "sign_response": {
19209
+ "type": "boolean",
19210
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
19211
+ }
19212
+ },
19213
+ "additionalProperties": false,
19214
+ "nullable": true
19215
+ },
18900
19216
  "account": {
18901
19217
  "type": "string",
18902
19218
  "description": "Object is related with this account"
@@ -19550,6 +19866,108 @@
19550
19866
  "description": "Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.",
19551
19867
  "nullable": true
19552
19868
  },
19869
+ "saml_sp": {
19870
+ "type": "object",
19871
+ "required": [
19872
+ "enabled",
19873
+ "entity_id",
19874
+ "acs_url"
19875
+ ],
19876
+ "properties": {
19877
+ "enabled": {
19878
+ "type": "boolean",
19879
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
19880
+ },
19881
+ "entity_id": {
19882
+ "type": "string",
19883
+ "minLength": 1,
19884
+ "maxLength": 1024,
19885
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
19886
+ },
19887
+ "acs_url": {
19888
+ "type": "string",
19889
+ "minLength": 1,
19890
+ "maxLength": 2048,
19891
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
19892
+ },
19893
+ "nameid_format": {
19894
+ "enum": [
19895
+ "emailAddress",
19896
+ "persistent",
19897
+ "unspecified"
19898
+ ],
19899
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
19900
+ },
19901
+ "attribute_mapping": {
19902
+ "type": "object",
19903
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
19904
+ "additionalProperties": {
19905
+ "anyOf": [
19906
+ {
19907
+ "type": "string",
19908
+ "enum": [
19909
+ "user_id"
19910
+ ]
19911
+ },
19912
+ {
19913
+ "type": "string",
19914
+ "enum": [
19915
+ "email"
19916
+ ]
19917
+ },
19918
+ {
19919
+ "type": "string",
19920
+ "enum": [
19921
+ "email_verified"
19922
+ ]
19923
+ },
19924
+ {
19925
+ "type": "string",
19926
+ "enum": [
19927
+ "name"
19928
+ ]
19929
+ },
19930
+ {
19931
+ "type": "string",
19932
+ "enum": [
19933
+ "given_name"
19934
+ ]
19935
+ },
19936
+ {
19937
+ "type": "string",
19938
+ "enum": [
19939
+ "family_name"
19940
+ ]
19941
+ },
19942
+ {
19943
+ "type": "string",
19944
+ "enum": [
19945
+ "nickname"
19946
+ ]
19947
+ },
19948
+ {
19949
+ "type": "string",
19950
+ "enum": [
19951
+ "picture"
19952
+ ]
19953
+ },
19954
+ {
19955
+ "type": "string",
19956
+ "enum": [
19957
+ "phone_number"
19958
+ ]
19959
+ }
19960
+ ]
19961
+ }
19962
+ },
19963
+ "sign_response": {
19964
+ "type": "boolean",
19965
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
19966
+ }
19967
+ },
19968
+ "additionalProperties": false,
19969
+ "nullable": true
19970
+ },
19553
19971
  "account": {
19554
19972
  "type": "string",
19555
19973
  "description": "Object is related with this account"
@@ -20236,6 +20654,114 @@
20236
20654
  "description": "Audience a client_credentials token request gets when it sends none. An explicit `audience` wins. `null` removes it.",
20237
20655
  "nullable": true
20238
20656
  },
20657
+ "saml_sp": {
20658
+ "anyOf": [
20659
+ {
20660
+ "type": "object",
20661
+ "required": [
20662
+ "enabled",
20663
+ "entity_id",
20664
+ "acs_url"
20665
+ ],
20666
+ "properties": {
20667
+ "enabled": {
20668
+ "type": "boolean",
20669
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
20670
+ },
20671
+ "entity_id": {
20672
+ "type": "string",
20673
+ "minLength": 1,
20674
+ "maxLength": 1024,
20675
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
20676
+ },
20677
+ "acs_url": {
20678
+ "type": "string",
20679
+ "minLength": 1,
20680
+ "maxLength": 2048,
20681
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
20682
+ },
20683
+ "nameid_format": {
20684
+ "enum": [
20685
+ "emailAddress",
20686
+ "persistent",
20687
+ "unspecified"
20688
+ ],
20689
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
20690
+ },
20691
+ "attribute_mapping": {
20692
+ "type": "object",
20693
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
20694
+ "additionalProperties": {
20695
+ "anyOf": [
20696
+ {
20697
+ "type": "string",
20698
+ "enum": [
20699
+ "user_id"
20700
+ ]
20701
+ },
20702
+ {
20703
+ "type": "string",
20704
+ "enum": [
20705
+ "email"
20706
+ ]
20707
+ },
20708
+ {
20709
+ "type": "string",
20710
+ "enum": [
20711
+ "email_verified"
20712
+ ]
20713
+ },
20714
+ {
20715
+ "type": "string",
20716
+ "enum": [
20717
+ "name"
20718
+ ]
20719
+ },
20720
+ {
20721
+ "type": "string",
20722
+ "enum": [
20723
+ "given_name"
20724
+ ]
20725
+ },
20726
+ {
20727
+ "type": "string",
20728
+ "enum": [
20729
+ "family_name"
20730
+ ]
20731
+ },
20732
+ {
20733
+ "type": "string",
20734
+ "enum": [
20735
+ "nickname"
20736
+ ]
20737
+ },
20738
+ {
20739
+ "type": "string",
20740
+ "enum": [
20741
+ "picture"
20742
+ ]
20743
+ },
20744
+ {
20745
+ "type": "string",
20746
+ "enum": [
20747
+ "phone_number"
20748
+ ]
20749
+ }
20750
+ ]
20751
+ }
20752
+ },
20753
+ "sign_response": {
20754
+ "type": "boolean",
20755
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
20756
+ }
20757
+ },
20758
+ "additionalProperties": false
20759
+ },
20760
+ {
20761
+ "type": "null"
20762
+ }
20763
+ ]
20764
+ },
20239
20765
  "metadata": {
20240
20766
  "type": "object",
20241
20767
  "properties": {},
@@ -20714,6 +21240,108 @@
20714
21240
  "description": "Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.",
20715
21241
  "nullable": true
20716
21242
  },
21243
+ "saml_sp": {
21244
+ "type": "object",
21245
+ "required": [
21246
+ "enabled",
21247
+ "entity_id",
21248
+ "acs_url"
21249
+ ],
21250
+ "properties": {
21251
+ "enabled": {
21252
+ "type": "boolean",
21253
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
21254
+ },
21255
+ "entity_id": {
21256
+ "type": "string",
21257
+ "minLength": 1,
21258
+ "maxLength": 1024,
21259
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
21260
+ },
21261
+ "acs_url": {
21262
+ "type": "string",
21263
+ "minLength": 1,
21264
+ "maxLength": 2048,
21265
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
21266
+ },
21267
+ "nameid_format": {
21268
+ "enum": [
21269
+ "emailAddress",
21270
+ "persistent",
21271
+ "unspecified"
21272
+ ],
21273
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
21274
+ },
21275
+ "attribute_mapping": {
21276
+ "type": "object",
21277
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
21278
+ "additionalProperties": {
21279
+ "anyOf": [
21280
+ {
21281
+ "type": "string",
21282
+ "enum": [
21283
+ "user_id"
21284
+ ]
21285
+ },
21286
+ {
21287
+ "type": "string",
21288
+ "enum": [
21289
+ "email"
21290
+ ]
21291
+ },
21292
+ {
21293
+ "type": "string",
21294
+ "enum": [
21295
+ "email_verified"
21296
+ ]
21297
+ },
21298
+ {
21299
+ "type": "string",
21300
+ "enum": [
21301
+ "name"
21302
+ ]
21303
+ },
21304
+ {
21305
+ "type": "string",
21306
+ "enum": [
21307
+ "given_name"
21308
+ ]
21309
+ },
21310
+ {
21311
+ "type": "string",
21312
+ "enum": [
21313
+ "family_name"
21314
+ ]
21315
+ },
21316
+ {
21317
+ "type": "string",
21318
+ "enum": [
21319
+ "nickname"
21320
+ ]
21321
+ },
21322
+ {
21323
+ "type": "string",
21324
+ "enum": [
21325
+ "picture"
21326
+ ]
21327
+ },
21328
+ {
21329
+ "type": "string",
21330
+ "enum": [
21331
+ "phone_number"
21332
+ ]
21333
+ }
21334
+ ]
21335
+ }
21336
+ },
21337
+ "sign_response": {
21338
+ "type": "boolean",
21339
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
21340
+ }
21341
+ },
21342
+ "additionalProperties": false,
21343
+ "nullable": true
21344
+ },
20717
21345
  "account": {
20718
21346
  "type": "string",
20719
21347
  "description": "Object is related with this account"
@@ -21367,6 +21995,108 @@
21367
21995
  "description": "Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.",
21368
21996
  "nullable": true
21369
21997
  },
21998
+ "saml_sp": {
21999
+ "type": "object",
22000
+ "required": [
22001
+ "enabled",
22002
+ "entity_id",
22003
+ "acs_url"
22004
+ ],
22005
+ "properties": {
22006
+ "enabled": {
22007
+ "type": "boolean",
22008
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
22009
+ },
22010
+ "entity_id": {
22011
+ "type": "string",
22012
+ "minLength": 1,
22013
+ "maxLength": 1024,
22014
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
22015
+ },
22016
+ "acs_url": {
22017
+ "type": "string",
22018
+ "minLength": 1,
22019
+ "maxLength": 2048,
22020
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
22021
+ },
22022
+ "nameid_format": {
22023
+ "enum": [
22024
+ "emailAddress",
22025
+ "persistent",
22026
+ "unspecified"
22027
+ ],
22028
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
22029
+ },
22030
+ "attribute_mapping": {
22031
+ "type": "object",
22032
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
22033
+ "additionalProperties": {
22034
+ "anyOf": [
22035
+ {
22036
+ "type": "string",
22037
+ "enum": [
22038
+ "user_id"
22039
+ ]
22040
+ },
22041
+ {
22042
+ "type": "string",
22043
+ "enum": [
22044
+ "email"
22045
+ ]
22046
+ },
22047
+ {
22048
+ "type": "string",
22049
+ "enum": [
22050
+ "email_verified"
22051
+ ]
22052
+ },
22053
+ {
22054
+ "type": "string",
22055
+ "enum": [
22056
+ "name"
22057
+ ]
22058
+ },
22059
+ {
22060
+ "type": "string",
22061
+ "enum": [
22062
+ "given_name"
22063
+ ]
22064
+ },
22065
+ {
22066
+ "type": "string",
22067
+ "enum": [
22068
+ "family_name"
22069
+ ]
22070
+ },
22071
+ {
22072
+ "type": "string",
22073
+ "enum": [
22074
+ "nickname"
22075
+ ]
22076
+ },
22077
+ {
22078
+ "type": "string",
22079
+ "enum": [
22080
+ "picture"
22081
+ ]
22082
+ },
22083
+ {
22084
+ "type": "string",
22085
+ "enum": [
22086
+ "phone_number"
22087
+ ]
22088
+ }
22089
+ ]
22090
+ }
22091
+ },
22092
+ "sign_response": {
22093
+ "type": "boolean",
22094
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
22095
+ }
22096
+ },
22097
+ "additionalProperties": false,
22098
+ "nullable": true
22099
+ },
21370
22100
  "account": {
21371
22101
  "type": "string",
21372
22102
  "description": "Object is related with this account"
@@ -22021,6 +22751,108 @@
22021
22751
  "description": "Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.",
22022
22752
  "nullable": true
22023
22753
  },
22754
+ "saml_sp": {
22755
+ "type": "object",
22756
+ "required": [
22757
+ "enabled",
22758
+ "entity_id",
22759
+ "acs_url"
22760
+ ],
22761
+ "properties": {
22762
+ "enabled": {
22763
+ "type": "boolean",
22764
+ "description": "Whether this client answers SAML requests. Off keeps the configuration but every /saml route of the client returns 404."
22765
+ },
22766
+ "entity_id": {
22767
+ "type": "string",
22768
+ "minLength": 1,
22769
+ "maxLength": 1024,
22770
+ "description": "The Service Provider's Entity ID. Its AuthnRequests must carry it as Issuer, and every assertion is restricted to it as Audience."
22771
+ },
22772
+ "acs_url": {
22773
+ "type": "string",
22774
+ "minLength": 1,
22775
+ "maxLength": 2048,
22776
+ "description": "The Service Provider's Assertion Consumer Service URL (HTTP-POST binding), https. The only URL a response is ever posted to: an AuthnRequest naming another one is refused."
22777
+ },
22778
+ "nameid_format": {
22779
+ "enum": [
22780
+ "emailAddress",
22781
+ "persistent",
22782
+ "unspecified"
22783
+ ],
22784
+ "description": "`emailAddress` (default) sends the email as NameID; `persistent` sends the stable user id; `unspecified` sends the email when the user has one, the user id otherwise."
22785
+ },
22786
+ "attribute_mapping": {
22787
+ "type": "object",
22788
+ "description": "SAML attribute name → user field. Omit for the default (email, name, given_name, family_name). Custom ID token claims set by post-login actions are added as attributes too.",
22789
+ "additionalProperties": {
22790
+ "anyOf": [
22791
+ {
22792
+ "type": "string",
22793
+ "enum": [
22794
+ "user_id"
22795
+ ]
22796
+ },
22797
+ {
22798
+ "type": "string",
22799
+ "enum": [
22800
+ "email"
22801
+ ]
22802
+ },
22803
+ {
22804
+ "type": "string",
22805
+ "enum": [
22806
+ "email_verified"
22807
+ ]
22808
+ },
22809
+ {
22810
+ "type": "string",
22811
+ "enum": [
22812
+ "name"
22813
+ ]
22814
+ },
22815
+ {
22816
+ "type": "string",
22817
+ "enum": [
22818
+ "given_name"
22819
+ ]
22820
+ },
22821
+ {
22822
+ "type": "string",
22823
+ "enum": [
22824
+ "family_name"
22825
+ ]
22826
+ },
22827
+ {
22828
+ "type": "string",
22829
+ "enum": [
22830
+ "nickname"
22831
+ ]
22832
+ },
22833
+ {
22834
+ "type": "string",
22835
+ "enum": [
22836
+ "picture"
22837
+ ]
22838
+ },
22839
+ {
22840
+ "type": "string",
22841
+ "enum": [
22842
+ "phone_number"
22843
+ ]
22844
+ }
22845
+ ]
22846
+ }
22847
+ },
22848
+ "sign_response": {
22849
+ "type": "boolean",
22850
+ "description": "Also sign the Response envelope, not only the Assertion. Default true; a few SPs reject a doubly-signed message."
22851
+ }
22852
+ },
22853
+ "additionalProperties": false,
22854
+ "nullable": true
22855
+ },
22024
22856
  "account": {
22025
22857
  "type": "string",
22026
22858
  "description": "Object is related with this account"
@@ -46472,14 +47304,14 @@
46472
47304
  }
46473
47305
  }
46474
47306
  },
46475
- "/account/keys": {
47307
+ "/account/keys/saml": {
46476
47308
  "get": {
46477
- "operationId": "account/getKeys",
46478
- "summary": "Get Account signing keys info",
47309
+ "operationId": "account/getSamlKeys",
47310
+ "summary": "Get the SAML signing certificates",
46479
47311
  "tags": [
46480
47312
  "account"
46481
47313
  ],
46482
- "description": "Returns metadata for the Account signing keys: the current production kid, the kid queued for next rotation, and all kids currently published in the JWK Set. Does not expose private key material.",
47314
+ "description": "The X.509 certificate this Account signs SAML assertions with, and the next one when a rotation is staged. Separate from the JWKS keys: rotating tokens never touches SAML. Created on first use.",
46483
47315
  "security": [
46484
47316
  {
46485
47317
  "bearerAuth": []
@@ -46493,25 +47325,52 @@
46493
47325
  "schema": {
46494
47326
  "type": "object",
46495
47327
  "required": [
46496
- "production_kid",
46497
- "next_in_queue_kid",
46498
- "kids_active"
47328
+ "current"
46499
47329
  ],
46500
47330
  "properties": {
46501
- "production_kid": {
46502
- "type": "string",
46503
- "description": "kid currently used to sign new tokens"
46504
- },
46505
- "next_in_queue_kid": {
46506
- "type": "string",
46507
- "description": "kid that will be promoted on the next rotation"
47331
+ "current": {
47332
+ "type": "object",
47333
+ "required": [
47334
+ "id",
47335
+ "certificate",
47336
+ "not_after"
47337
+ ],
47338
+ "properties": {
47339
+ "id": {
47340
+ "type": "string"
47341
+ },
47342
+ "certificate": {
47343
+ "type": "string",
47344
+ "description": "PEM X.509 certificate"
47345
+ },
47346
+ "not_after": {
47347
+ "type": "string",
47348
+ "format": "date-time"
47349
+ }
47350
+ },
47351
+ "additionalProperties": false
46508
47352
  },
46509
- "kids_active": {
46510
- "type": "array",
46511
- "items": {
46512
- "type": "string"
47353
+ "next": {
47354
+ "type": "object",
47355
+ "required": [
47356
+ "id",
47357
+ "certificate",
47358
+ "not_after"
47359
+ ],
47360
+ "properties": {
47361
+ "id": {
47362
+ "type": "string"
47363
+ },
47364
+ "certificate": {
47365
+ "type": "string",
47366
+ "description": "PEM X.509 certificate"
47367
+ },
47368
+ "not_after": {
47369
+ "type": "string",
47370
+ "format": "date-time"
47371
+ }
46513
47372
  },
46514
- "description": "All kids currently published in the JWK Set"
47373
+ "additionalProperties": false
46515
47374
  }
46516
47375
  },
46517
47376
  "additionalProperties": false
@@ -46649,26 +47508,14 @@
46649
47508
  }
46650
47509
  }
46651
47510
  },
46652
- "/account/keys/rotate": {
47511
+ "/account/keys/saml/rotate": {
46653
47512
  "post": {
46654
- "operationId": "account/rotateKeys",
46655
- "summary": "Rotate Account signing keys",
47513
+ "operationId": "account/rotateSamlKeys",
47514
+ "summary": "Rotate the SAML signing certificate",
46656
47515
  "tags": [
46657
47516
  "account"
46658
47517
  ],
46659
- "description": "Promotes the queued key to production and generates a fresh queued key. The previously-active key is retained inside the JWK Set so tokens already issued remain verifiable via `/.well-known/jwks.json`.",
46660
- "requestBody": {
46661
- "required": true,
46662
- "content": {
46663
- "application/json": {
46664
- "schema": {
46665
- "type": "object",
46666
- "properties": {},
46667
- "additionalProperties": false
46668
- }
46669
- }
46670
- }
46671
- },
47518
+ "description": "Two steps. The first call stages a new certificate: the IdP metadata publishes both and signing continues with the current one, so each Service Provider can be updated. The second call promotes it: assertions are signed with the new certificate and the old one is dropped.",
46672
47519
  "security": [
46673
47520
  {
46674
47521
  "bearerAuth": []
@@ -46682,63 +47529,34 @@
46682
47529
  "schema": {
46683
47530
  "type": "object",
46684
47531
  "required": [
46685
- "rotated_at",
46686
- "previous_production_kid",
46687
- "production_kid",
46688
- "next_in_queue_kid",
46689
- "kids_active"
47532
+ "step",
47533
+ "current_id"
46690
47534
  ],
46691
47535
  "properties": {
46692
- "rotated_at": {
46693
- "type": "string",
46694
- "description": "ISO 8601 timestamp of the rotation"
46695
- },
46696
- "previous_production_kid": {
46697
- "type": "string",
46698
- "description": "kid that was active before rotation. Tokens signed with it are still verifiable via /.well-known/jwks.json"
46699
- },
46700
- "production_kid": {
46701
- "type": "string",
46702
- "description": "kid now used to sign new tokens"
46703
- },
46704
- "next_in_queue_kid": {
46705
- "type": "string",
46706
- "description": "kid that will be promoted on the next rotation"
46707
- },
46708
- "kids_active": {
46709
- "type": "array",
46710
- "items": {
46711
- "type": "string"
46712
- },
46713
- "description": "All kids currently published in the JWK Set"
46714
- }
46715
- },
46716
- "additionalProperties": false
46717
- }
46718
- }
46719
- }
46720
- },
46721
- "400": {
46722
- "description": "`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
46723
- "content": {
46724
- "application/json": {
46725
- "schema": {
46726
- "allOf": [
46727
- {
46728
- "$ref": "#/components/schemas/ErrorResponse"
46729
- },
46730
- {
46731
- "type": "object",
46732
- "properties": {
46733
- "error_code": {
47536
+ "step": {
47537
+ "anyOf": [
47538
+ {
46734
47539
  "type": "string",
46735
47540
  "enum": [
46736
- "validation_error"
47541
+ "staged"
47542
+ ]
47543
+ },
47544
+ {
47545
+ "type": "string",
47546
+ "enum": [
47547
+ "promoted"
46737
47548
  ]
46738
47549
  }
46739
- }
47550
+ ]
47551
+ },
47552
+ "current_id": {
47553
+ "type": "string"
47554
+ },
47555
+ "next_id": {
47556
+ "type": "string"
46740
47557
  }
46741
- ]
47558
+ },
47559
+ "additionalProperties": false
46742
47560
  }
46743
47561
  }
46744
47562
  }
@@ -46873,63 +47691,14 @@
46873
47691
  }
46874
47692
  }
46875
47693
  },
46876
- "/action": {
47694
+ "/account/keys": {
46877
47695
  "get": {
46878
- "operationId": "action/list",
46879
- "summary": "List Actions",
47696
+ "operationId": "account/getKeys",
47697
+ "summary": "Get Account signing keys info",
46880
47698
  "tags": [
46881
- "action"
46882
- ],
46883
- "description": "List Actions",
46884
- "parameters": [
46885
- {
46886
- "schema": {
46887
- "type": "number",
46888
- "minimum": 1,
46889
- "maximum": 200
46890
- },
46891
- "in": "query",
46892
- "name": "pageSize",
46893
- "required": false,
46894
- "description": "Number of items per page (max 200)"
46895
- },
46896
- {
46897
- "schema": {
46898
- "type": "string"
46899
- },
46900
- "in": "query",
46901
- "name": "cursor",
46902
- "required": false,
46903
- "description": "Cursor for next page"
46904
- },
46905
- {
46906
- "schema": {
46907
- "type": "string"
46908
- },
46909
- "in": "query",
46910
- "name": "next",
46911
- "required": false,
46912
- "description": "Cursor returned by the previous page"
46913
- },
46914
- {
46915
- "schema": {
46916
- "type": "boolean"
46917
- },
46918
- "in": "query",
46919
- "name": "count",
46920
- "required": false,
46921
- "description": "Also return `total`: the rows matching the whole filter, across every page."
46922
- },
46923
- {
46924
- "schema": {
46925
- "type": "string"
46926
- },
46927
- "in": "query",
46928
- "name": "query",
46929
- "required": false,
46930
- "description": "Filter using a FaableQL query"
46931
- }
47699
+ "account"
46932
47700
  ],
47701
+ "description": "Returns metadata for the Account signing keys: the current production kid, the kid queued for next rotation, and all kids currently published in the JWK Set. Does not expose private key material.",
46933
47702
  "security": [
46934
47703
  {
46935
47704
  "bearerAuth": []
@@ -46943,29 +47712,25 @@
46943
47712
  "schema": {
46944
47713
  "type": "object",
46945
47714
  "required": [
46946
- "next",
46947
- "results"
47715
+ "production_kid",
47716
+ "next_in_queue_kid",
47717
+ "kids_active"
46948
47718
  ],
46949
47719
  "properties": {
46950
- "next": {
46951
- "anyOf": [
46952
- {
46953
- "type": "string"
46954
- },
46955
- {
46956
- "type": "null"
46957
- }
46958
- ]
47720
+ "production_kid": {
47721
+ "type": "string",
47722
+ "description": "kid currently used to sign new tokens"
46959
47723
  },
46960
- "results": {
47724
+ "next_in_queue_kid": {
47725
+ "type": "string",
47726
+ "description": "kid that will be promoted on the next rotation"
47727
+ },
47728
+ "kids_active": {
46961
47729
  "type": "array",
46962
47730
  "items": {
46963
- "$ref": "#/components/schemas/Action"
46964
- }
46965
- },
46966
- "total": {
46967
- "type": "integer",
46968
- "description": "Rows matching the whole filter, across every page. Only with `?count=true`."
47731
+ "type": "string"
47732
+ },
47733
+ "description": "All kids currently published in the JWK Set"
46969
47734
  }
46970
47735
  },
46971
47736
  "additionalProperties": false
@@ -46973,34 +47738,6 @@
46973
47738
  }
46974
47739
  }
46975
47740
  },
46976
- "400": {
46977
- "description": "`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
46978
- "content": {
46979
- "application/json": {
46980
- "schema": {
46981
- "allOf": [
46982
- {
46983
- "$ref": "#/components/schemas/ErrorResponse"
46984
- },
46985
- {
46986
- "type": "object",
46987
- "properties": {
46988
- "error_code": {
46989
- "type": "string",
46990
- "enum": [
46991
- "invalid_query",
46992
- "invalid_expand",
46993
- "search_not_supported",
46994
- "validation_error"
46995
- ]
46996
- }
46997
- }
46998
- }
46999
- ]
47000
- }
47001
- }
47002
- }
47003
- },
47004
47741
  "401": {
47005
47742
  "description": "`unauthorized` — The request carries no valid credentials.",
47006
47743
  "content": {
@@ -47129,45 +47866,24 @@
47129
47866
  }
47130
47867
  }
47131
47868
  }
47132
- },
47869
+ }
47870
+ },
47871
+ "/account/keys/rotate": {
47133
47872
  "post": {
47134
- "operationId": "action/create",
47135
- "summary": "Create Action",
47873
+ "operationId": "account/rotateKeys",
47874
+ "summary": "Rotate Account signing keys",
47136
47875
  "tags": [
47137
- "action"
47876
+ "account"
47138
47877
  ],
47139
- "description": "Create Action",
47878
+ "description": "Promotes the queued key to production and generates a fresh queued key. The previously-active key is retained inside the JWK Set so tokens already issued remain verifiable via `/.well-known/jwks.json`.",
47140
47879
  "requestBody": {
47141
47880
  "required": true,
47142
47881
  "content": {
47143
47882
  "application/json": {
47144
47883
  "schema": {
47145
47884
  "type": "object",
47146
- "required": [
47147
- "name",
47148
- "code"
47149
- ],
47150
- "properties": {
47151
- "name": {
47152
- "type": "string",
47153
- "description": "Action Name",
47154
- "maxLength": 200
47155
- },
47156
- "code": {
47157
- "type": "string",
47158
- "description": "JavaScript code. Must export at least one hook: exports.onExecutePostLogin, exports.onExecuteContinue and/or exports.onExecuteClientCredentials. The triggers are derived from the exported hooks."
47159
- },
47160
- "enabled": {
47161
- "type": "boolean",
47162
- "description": "Is this action active?",
47163
- "default": true
47164
- },
47165
- "order": {
47166
- "type": "number",
47167
- "description": "Execution order: lower runs first; on ties the newest action runs first",
47168
- "default": 0
47169
- }
47170
- }
47885
+ "properties": {},
47886
+ "additionalProperties": false
47171
47887
  }
47172
47888
  }
47173
47889
  }
@@ -47179,87 +47895,50 @@
47179
47895
  ],
47180
47896
  "responses": {
47181
47897
  "200": {
47182
- "description": "Action",
47898
+ "description": "Default Response",
47183
47899
  "content": {
47184
47900
  "application/json": {
47185
47901
  "schema": {
47186
47902
  "type": "object",
47187
47903
  "required": [
47188
- "id",
47189
- "name",
47190
- "triggers",
47191
- "code",
47192
- "enabled",
47193
- "account",
47194
- "createdAt"
47904
+ "rotated_at",
47905
+ "previous_production_kid",
47906
+ "production_kid",
47907
+ "next_in_queue_kid",
47908
+ "kids_active"
47195
47909
  ],
47196
47910
  "properties": {
47197
- "id": {
47198
- "type": "string",
47199
- "description": "Action ID"
47200
- },
47201
- "name": {
47911
+ "rotated_at": {
47202
47912
  "type": "string",
47203
- "description": "Action Name",
47204
- "maxLength": 200
47205
- },
47206
- "triggers": {
47207
- "type": "array",
47208
- "items": {
47209
- "enum": [
47210
- "continue",
47211
- "post-login",
47212
- "client-credentials"
47213
- ]
47214
- },
47215
- "description": "Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."
47913
+ "description": "ISO 8601 timestamp of the rotation"
47216
47914
  },
47217
- "code": {
47915
+ "previous_production_kid": {
47218
47916
  "type": "string",
47219
- "description": "JavaScript Code to execute"
47220
- },
47221
- "enabled": {
47222
- "type": "boolean",
47223
- "description": "Is this action active?",
47224
- "default": true
47225
- },
47226
- "order": {
47227
- "type": "number",
47228
- "description": "Execution order: lower runs first; on ties the newest action runs first",
47229
- "default": 0
47230
- },
47231
- "revision": {
47232
- "type": "number",
47233
- "description": "Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.",
47234
- "default": 1
47917
+ "description": "kid that was active before rotation. Tokens signed with it are still verifiable via /.well-known/jwks.json"
47235
47918
  },
47236
- "account": {
47919
+ "production_kid": {
47237
47920
  "type": "string",
47238
- "description": "Object is related with this account"
47239
- },
47240
- "metadata": {
47241
- "type": "object",
47242
- "properties": {},
47243
- "additionalProperties": true,
47244
- "description": "ActionMetadata",
47245
- "default": {}
47921
+ "description": "kid now used to sign new tokens"
47246
47922
  },
47247
- "createdAt": {
47923
+ "next_in_queue_kid": {
47248
47924
  "type": "string",
47249
- "description": "Action creation date"
47925
+ "description": "kid that will be promoted on the next rotation"
47250
47926
  },
47251
- "updatedAt": {
47252
- "type": "string",
47253
- "description": "Action updated date"
47927
+ "kids_active": {
47928
+ "type": "array",
47929
+ "items": {
47930
+ "type": "string"
47931
+ },
47932
+ "description": "All kids currently published in the JWK Set"
47254
47933
  }
47255
47934
  },
47256
- "description": "Action"
47935
+ "additionalProperties": false
47257
47936
  }
47258
47937
  }
47259
47938
  }
47260
47939
  },
47261
47940
  "400": {
47262
- "description": "`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
47941
+ "description": "`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
47263
47942
  "content": {
47264
47943
  "application/json": {
47265
47944
  "schema": {
@@ -47273,7 +47952,6 @@
47273
47952
  "error_code": {
47274
47953
  "type": "string",
47275
47954
  "enum": [
47276
- "already_exists",
47277
47955
  "validation_error"
47278
47956
  ]
47279
47957
  }
@@ -47414,24 +48092,305 @@
47414
48092
  }
47415
48093
  }
47416
48094
  },
47417
- "/action/{action_id}": {
48095
+ "/action": {
47418
48096
  "get": {
47419
- "operationId": "action/get",
47420
- "summary": "Get Action",
48097
+ "operationId": "action/list",
48098
+ "summary": "List Actions",
47421
48099
  "tags": [
47422
48100
  "action"
47423
48101
  ],
47424
- "description": "Get Action",
48102
+ "description": "List Actions",
47425
48103
  "parameters": [
48104
+ {
48105
+ "schema": {
48106
+ "type": "number",
48107
+ "minimum": 1,
48108
+ "maximum": 200
48109
+ },
48110
+ "in": "query",
48111
+ "name": "pageSize",
48112
+ "required": false,
48113
+ "description": "Number of items per page (max 200)"
48114
+ },
47426
48115
  {
47427
48116
  "schema": {
47428
48117
  "type": "string"
47429
48118
  },
47430
- "in": "path",
47431
- "name": "action_id",
47432
- "required": true
48119
+ "in": "query",
48120
+ "name": "cursor",
48121
+ "required": false,
48122
+ "description": "Cursor for next page"
48123
+ },
48124
+ {
48125
+ "schema": {
48126
+ "type": "string"
48127
+ },
48128
+ "in": "query",
48129
+ "name": "next",
48130
+ "required": false,
48131
+ "description": "Cursor returned by the previous page"
48132
+ },
48133
+ {
48134
+ "schema": {
48135
+ "type": "boolean"
48136
+ },
48137
+ "in": "query",
48138
+ "name": "count",
48139
+ "required": false,
48140
+ "description": "Also return `total`: the rows matching the whole filter, across every page."
48141
+ },
48142
+ {
48143
+ "schema": {
48144
+ "type": "string"
48145
+ },
48146
+ "in": "query",
48147
+ "name": "query",
48148
+ "required": false,
48149
+ "description": "Filter using a FaableQL query"
48150
+ }
48151
+ ],
48152
+ "security": [
48153
+ {
48154
+ "bearerAuth": []
48155
+ }
48156
+ ],
48157
+ "responses": {
48158
+ "200": {
48159
+ "description": "Default Response",
48160
+ "content": {
48161
+ "application/json": {
48162
+ "schema": {
48163
+ "type": "object",
48164
+ "required": [
48165
+ "next",
48166
+ "results"
48167
+ ],
48168
+ "properties": {
48169
+ "next": {
48170
+ "anyOf": [
48171
+ {
48172
+ "type": "string"
48173
+ },
48174
+ {
48175
+ "type": "null"
48176
+ }
48177
+ ]
48178
+ },
48179
+ "results": {
48180
+ "type": "array",
48181
+ "items": {
48182
+ "$ref": "#/components/schemas/Action"
48183
+ }
48184
+ },
48185
+ "total": {
48186
+ "type": "integer",
48187
+ "description": "Rows matching the whole filter, across every page. Only with `?count=true`."
48188
+ }
48189
+ },
48190
+ "additionalProperties": false
48191
+ }
48192
+ }
48193
+ }
48194
+ },
48195
+ "400": {
48196
+ "description": "`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
48197
+ "content": {
48198
+ "application/json": {
48199
+ "schema": {
48200
+ "allOf": [
48201
+ {
48202
+ "$ref": "#/components/schemas/ErrorResponse"
48203
+ },
48204
+ {
48205
+ "type": "object",
48206
+ "properties": {
48207
+ "error_code": {
48208
+ "type": "string",
48209
+ "enum": [
48210
+ "invalid_query",
48211
+ "invalid_expand",
48212
+ "search_not_supported",
48213
+ "validation_error"
48214
+ ]
48215
+ }
48216
+ }
48217
+ }
48218
+ ]
48219
+ }
48220
+ }
48221
+ }
48222
+ },
48223
+ "401": {
48224
+ "description": "`unauthorized` — The request carries no valid credentials.",
48225
+ "content": {
48226
+ "application/json": {
48227
+ "schema": {
48228
+ "allOf": [
48229
+ {
48230
+ "$ref": "#/components/schemas/ErrorResponse"
48231
+ },
48232
+ {
48233
+ "type": "object",
48234
+ "properties": {
48235
+ "error_code": {
48236
+ "type": "string",
48237
+ "enum": [
48238
+ "unauthorized"
48239
+ ]
48240
+ }
48241
+ }
48242
+ }
48243
+ ]
48244
+ }
48245
+ }
48246
+ }
48247
+ },
48248
+ "403": {
48249
+ "description": "`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.",
48250
+ "content": {
48251
+ "application/json": {
48252
+ "schema": {
48253
+ "allOf": [
48254
+ {
48255
+ "$ref": "#/components/schemas/ErrorResponse"
48256
+ },
48257
+ {
48258
+ "type": "object",
48259
+ "properties": {
48260
+ "error_code": {
48261
+ "type": "string",
48262
+ "enum": [
48263
+ "forbidden",
48264
+ "insufficient_scope",
48265
+ "user_suspended"
48266
+ ]
48267
+ }
48268
+ }
48269
+ }
48270
+ ]
48271
+ }
48272
+ }
48273
+ }
48274
+ },
48275
+ "404": {
48276
+ "description": "`account_not_found` — No Auth Account matches the request (domain, header or token).",
48277
+ "content": {
48278
+ "application/json": {
48279
+ "schema": {
48280
+ "allOf": [
48281
+ {
48282
+ "$ref": "#/components/schemas/ErrorResponse"
48283
+ },
48284
+ {
48285
+ "type": "object",
48286
+ "properties": {
48287
+ "error_code": {
48288
+ "type": "string",
48289
+ "enum": [
48290
+ "account_not_found"
48291
+ ]
48292
+ }
48293
+ }
48294
+ }
48295
+ ]
48296
+ }
48297
+ }
48298
+ }
48299
+ },
48300
+ "429": {
48301
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
48302
+ "content": {
48303
+ "application/json": {
48304
+ "schema": {
48305
+ "allOf": [
48306
+ {
48307
+ "$ref": "#/components/schemas/ErrorResponse"
48308
+ },
48309
+ {
48310
+ "type": "object",
48311
+ "properties": {
48312
+ "error_code": {
48313
+ "type": "string",
48314
+ "enum": [
48315
+ "too_many_requests"
48316
+ ]
48317
+ }
48318
+ }
48319
+ }
48320
+ ]
48321
+ }
48322
+ }
48323
+ }
48324
+ },
48325
+ "500": {
48326
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
48327
+ "content": {
48328
+ "application/json": {
48329
+ "schema": {
48330
+ "allOf": [
48331
+ {
48332
+ "$ref": "#/components/schemas/ErrorResponse"
48333
+ },
48334
+ {
48335
+ "type": "object",
48336
+ "properties": {
48337
+ "error_code": {
48338
+ "type": "string",
48339
+ "enum": [
48340
+ "internal_error"
48341
+ ]
48342
+ }
48343
+ }
48344
+ }
48345
+ ]
48346
+ }
48347
+ }
48348
+ }
47433
48349
  }
48350
+ }
48351
+ },
48352
+ "post": {
48353
+ "operationId": "action/create",
48354
+ "summary": "Create Action",
48355
+ "tags": [
48356
+ "action"
47434
48357
  ],
48358
+ "description": "Create Action",
48359
+ "requestBody": {
48360
+ "required": true,
48361
+ "content": {
48362
+ "application/json": {
48363
+ "schema": {
48364
+ "type": "object",
48365
+ "required": [
48366
+ "name",
48367
+ "code"
48368
+ ],
48369
+ "properties": {
48370
+ "name": {
48371
+ "type": "string",
48372
+ "description": "Action Name",
48373
+ "maxLength": 200
48374
+ },
48375
+ "code": {
48376
+ "type": "string",
48377
+ "description": "JavaScript code. Must export at least one hook: exports.onExecutePostLogin, exports.onExecuteContinue and/or exports.onExecuteClientCredentials. The triggers are derived from the exported hooks."
48378
+ },
48379
+ "enabled": {
48380
+ "type": "boolean",
48381
+ "description": "Is this action active?",
48382
+ "default": true
48383
+ },
48384
+ "order": {
48385
+ "type": "number",
48386
+ "description": "Execution order: lower runs first; on ties the newest action runs first",
48387
+ "default": 0
48388
+ }
48389
+ }
48390
+ }
48391
+ }
48392
+ }
48393
+ },
47435
48394
  "security": [
47436
48395
  {
47437
48396
  "bearerAuth": []
@@ -47519,7 +48478,7 @@
47519
48478
  }
47520
48479
  },
47521
48480
  "400": {
47522
- "description": "`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
48481
+ "description": "`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
47523
48482
  "content": {
47524
48483
  "application/json": {
47525
48484
  "schema": {
@@ -47533,8 +48492,7 @@
47533
48492
  "error_code": {
47534
48493
  "type": "string",
47535
48494
  "enum": [
47536
- "invalid_id",
47537
- "invalid_expand",
48495
+ "already_exists",
47538
48496
  "validation_error"
47539
48497
  ]
47540
48498
  }
@@ -47598,7 +48556,7 @@
47598
48556
  }
47599
48557
  },
47600
48558
  "404": {
47601
- "description": "`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).",
48559
+ "description": "`account_not_found` — No Auth Account matches the request (domain, header or token).",
47602
48560
  "content": {
47603
48561
  "application/json": {
47604
48562
  "schema": {
@@ -47612,7 +48570,6 @@
47612
48570
  "error_code": {
47613
48571
  "type": "string",
47614
48572
  "enum": [
47615
- "not_found",
47616
48573
  "account_not_found"
47617
48574
  ]
47618
48575
  }
@@ -47674,46 +48631,16 @@
47674
48631
  }
47675
48632
  }
47676
48633
  }
47677
- },
47678
- "post": {
47679
- "operationId": "action/update",
47680
- "summary": "Update Action",
48634
+ }
48635
+ },
48636
+ "/action/{action_id}": {
48637
+ "get": {
48638
+ "operationId": "action/get",
48639
+ "summary": "Get Action",
47681
48640
  "tags": [
47682
48641
  "action"
47683
48642
  ],
47684
- "description": "Update Action",
47685
- "requestBody": {
47686
- "required": true,
47687
- "content": {
47688
- "application/json": {
47689
- "schema": {
47690
- "type": "object",
47691
- "properties": {
47692
- "name": {
47693
- "type": "string",
47694
- "description": "Action Name",
47695
- "maxLength": 200
47696
- },
47697
- "code": {
47698
- "type": "string",
47699
- "description": "JavaScript code. The triggers are re-derived from the hooks it exports."
47700
- },
47701
- "enabled": {
47702
- "type": "boolean",
47703
- "description": "Is this action active?"
47704
- },
47705
- "order": {
47706
- "type": "number",
47707
- "description": "Execution order"
47708
- }
47709
- },
47710
- "description": "Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`.",
47711
- "additionalProperties": false
47712
- }
47713
- }
47714
- },
47715
- "description": "Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`."
47716
- },
48643
+ "description": "Get Action",
47717
48644
  "parameters": [
47718
48645
  {
47719
48646
  "schema": {
@@ -47811,7 +48738,7 @@
47811
48738
  }
47812
48739
  },
47813
48740
  "400": {
47814
- "description": "`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
48741
+ "description": "`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
47815
48742
  "content": {
47816
48743
  "application/json": {
47817
48744
  "schema": {
@@ -47826,7 +48753,7 @@
47826
48753
  "type": "string",
47827
48754
  "enum": [
47828
48755
  "invalid_id",
47829
- "already_exists",
48756
+ "invalid_expand",
47830
48757
  "validation_error"
47831
48758
  ]
47832
48759
  }
@@ -47967,13 +48894,45 @@
47967
48894
  }
47968
48895
  }
47969
48896
  },
47970
- "delete": {
47971
- "operationId": "action/delete",
47972
- "summary": "Delete Action",
48897
+ "post": {
48898
+ "operationId": "action/update",
48899
+ "summary": "Update Action",
47973
48900
  "tags": [
47974
48901
  "action"
47975
48902
  ],
47976
- "description": "Delete Action",
48903
+ "description": "Update Action",
48904
+ "requestBody": {
48905
+ "required": true,
48906
+ "content": {
48907
+ "application/json": {
48908
+ "schema": {
48909
+ "type": "object",
48910
+ "properties": {
48911
+ "name": {
48912
+ "type": "string",
48913
+ "description": "Action Name",
48914
+ "maxLength": 200
48915
+ },
48916
+ "code": {
48917
+ "type": "string",
48918
+ "description": "JavaScript code. The triggers are re-derived from the hooks it exports."
48919
+ },
48920
+ "enabled": {
48921
+ "type": "boolean",
48922
+ "description": "Is this action active?"
48923
+ },
48924
+ "order": {
48925
+ "type": "number",
48926
+ "description": "Execution order"
48927
+ }
48928
+ },
48929
+ "description": "Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`.",
48930
+ "additionalProperties": false
48931
+ }
48932
+ }
48933
+ },
48934
+ "description": "Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`."
48935
+ },
47977
48936
  "parameters": [
47978
48937
  {
47979
48938
  "schema": {
@@ -48071,7 +49030,267 @@
48071
49030
  }
48072
49031
  },
48073
49032
  "400": {
48074
- "description": "`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
49033
+ "description": "`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
49034
+ "content": {
49035
+ "application/json": {
49036
+ "schema": {
49037
+ "allOf": [
49038
+ {
49039
+ "$ref": "#/components/schemas/ErrorResponse"
49040
+ },
49041
+ {
49042
+ "type": "object",
49043
+ "properties": {
49044
+ "error_code": {
49045
+ "type": "string",
49046
+ "enum": [
49047
+ "invalid_id",
49048
+ "already_exists",
49049
+ "validation_error"
49050
+ ]
49051
+ }
49052
+ }
49053
+ }
49054
+ ]
49055
+ }
49056
+ }
49057
+ }
49058
+ },
49059
+ "401": {
49060
+ "description": "`unauthorized` — The request carries no valid credentials.",
49061
+ "content": {
49062
+ "application/json": {
49063
+ "schema": {
49064
+ "allOf": [
49065
+ {
49066
+ "$ref": "#/components/schemas/ErrorResponse"
49067
+ },
49068
+ {
49069
+ "type": "object",
49070
+ "properties": {
49071
+ "error_code": {
49072
+ "type": "string",
49073
+ "enum": [
49074
+ "unauthorized"
49075
+ ]
49076
+ }
49077
+ }
49078
+ }
49079
+ ]
49080
+ }
49081
+ }
49082
+ }
49083
+ },
49084
+ "403": {
49085
+ "description": "`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.",
49086
+ "content": {
49087
+ "application/json": {
49088
+ "schema": {
49089
+ "allOf": [
49090
+ {
49091
+ "$ref": "#/components/schemas/ErrorResponse"
49092
+ },
49093
+ {
49094
+ "type": "object",
49095
+ "properties": {
49096
+ "error_code": {
49097
+ "type": "string",
49098
+ "enum": [
49099
+ "forbidden",
49100
+ "insufficient_scope",
49101
+ "user_suspended"
49102
+ ]
49103
+ }
49104
+ }
49105
+ }
49106
+ ]
49107
+ }
49108
+ }
49109
+ }
49110
+ },
49111
+ "404": {
49112
+ "description": "`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).",
49113
+ "content": {
49114
+ "application/json": {
49115
+ "schema": {
49116
+ "allOf": [
49117
+ {
49118
+ "$ref": "#/components/schemas/ErrorResponse"
49119
+ },
49120
+ {
49121
+ "type": "object",
49122
+ "properties": {
49123
+ "error_code": {
49124
+ "type": "string",
49125
+ "enum": [
49126
+ "not_found",
49127
+ "account_not_found"
49128
+ ]
49129
+ }
49130
+ }
49131
+ }
49132
+ ]
49133
+ }
49134
+ }
49135
+ }
49136
+ },
49137
+ "429": {
49138
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
49139
+ "content": {
49140
+ "application/json": {
49141
+ "schema": {
49142
+ "allOf": [
49143
+ {
49144
+ "$ref": "#/components/schemas/ErrorResponse"
49145
+ },
49146
+ {
49147
+ "type": "object",
49148
+ "properties": {
49149
+ "error_code": {
49150
+ "type": "string",
49151
+ "enum": [
49152
+ "too_many_requests"
49153
+ ]
49154
+ }
49155
+ }
49156
+ }
49157
+ ]
49158
+ }
49159
+ }
49160
+ }
49161
+ },
49162
+ "500": {
49163
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
49164
+ "content": {
49165
+ "application/json": {
49166
+ "schema": {
49167
+ "allOf": [
49168
+ {
49169
+ "$ref": "#/components/schemas/ErrorResponse"
49170
+ },
49171
+ {
49172
+ "type": "object",
49173
+ "properties": {
49174
+ "error_code": {
49175
+ "type": "string",
49176
+ "enum": [
49177
+ "internal_error"
49178
+ ]
49179
+ }
49180
+ }
49181
+ }
49182
+ ]
49183
+ }
49184
+ }
49185
+ }
49186
+ }
49187
+ }
49188
+ },
49189
+ "delete": {
49190
+ "operationId": "action/delete",
49191
+ "summary": "Delete Action",
49192
+ "tags": [
49193
+ "action"
49194
+ ],
49195
+ "description": "Delete Action",
49196
+ "parameters": [
49197
+ {
49198
+ "schema": {
49199
+ "type": "string"
49200
+ },
49201
+ "in": "path",
49202
+ "name": "action_id",
49203
+ "required": true
49204
+ }
49205
+ ],
49206
+ "security": [
49207
+ {
49208
+ "bearerAuth": []
49209
+ }
49210
+ ],
49211
+ "responses": {
49212
+ "200": {
49213
+ "description": "Action",
49214
+ "content": {
49215
+ "application/json": {
49216
+ "schema": {
49217
+ "type": "object",
49218
+ "required": [
49219
+ "id",
49220
+ "name",
49221
+ "triggers",
49222
+ "code",
49223
+ "enabled",
49224
+ "account",
49225
+ "createdAt"
49226
+ ],
49227
+ "properties": {
49228
+ "id": {
49229
+ "type": "string",
49230
+ "description": "Action ID"
49231
+ },
49232
+ "name": {
49233
+ "type": "string",
49234
+ "description": "Action Name",
49235
+ "maxLength": 200
49236
+ },
49237
+ "triggers": {
49238
+ "type": "array",
49239
+ "items": {
49240
+ "enum": [
49241
+ "continue",
49242
+ "post-login",
49243
+ "client-credentials"
49244
+ ]
49245
+ },
49246
+ "description": "Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."
49247
+ },
49248
+ "code": {
49249
+ "type": "string",
49250
+ "description": "JavaScript Code to execute"
49251
+ },
49252
+ "enabled": {
49253
+ "type": "boolean",
49254
+ "description": "Is this action active?",
49255
+ "default": true
49256
+ },
49257
+ "order": {
49258
+ "type": "number",
49259
+ "description": "Execution order: lower runs first; on ties the newest action runs first",
49260
+ "default": 0
49261
+ },
49262
+ "revision": {
49263
+ "type": "number",
49264
+ "description": "Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.",
49265
+ "default": 1
49266
+ },
49267
+ "account": {
49268
+ "type": "string",
49269
+ "description": "Object is related with this account"
49270
+ },
49271
+ "metadata": {
49272
+ "type": "object",
49273
+ "properties": {},
49274
+ "additionalProperties": true,
49275
+ "description": "ActionMetadata",
49276
+ "default": {}
49277
+ },
49278
+ "createdAt": {
49279
+ "type": "string",
49280
+ "description": "Action creation date"
49281
+ },
49282
+ "updatedAt": {
49283
+ "type": "string",
49284
+ "description": "Action updated date"
49285
+ }
49286
+ },
49287
+ "description": "Action"
49288
+ }
49289
+ }
49290
+ }
49291
+ },
49292
+ "400": {
49293
+ "description": "`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
48075
49294
  "content": {
48076
49295
  "application/json": {
48077
49296
  "schema": {
@@ -71948,6 +73167,608 @@
71948
73167
  }
71949
73168
  }
71950
73169
  },
73170
+ "/saml/metadata/{client_id}": {
73171
+ "get": {
73172
+ "operationId": "saml/metadata",
73173
+ "summary": "SAML IdP metadata",
73174
+ "tags": [
73175
+ "saml"
73176
+ ],
73177
+ "description": "This tenant's SAML 2.0 IdP metadata for one client: entity ID, single sign-on URL and signing certificates. Paste it, or its URL, into the Service Provider.",
73178
+ "parameters": [
73179
+ {
73180
+ "schema": {
73181
+ "type": "string",
73182
+ "minLength": 1
73183
+ },
73184
+ "in": "path",
73185
+ "name": "client_id",
73186
+ "required": true
73187
+ }
73188
+ ],
73189
+ "responses": {
73190
+ "200": {
73191
+ "description": "Default Response"
73192
+ },
73193
+ "400": {
73194
+ "description": "`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
73195
+ "content": {
73196
+ "application/json": {
73197
+ "schema": {
73198
+ "allOf": [
73199
+ {
73200
+ "$ref": "#/components/schemas/ErrorResponse"
73201
+ },
73202
+ {
73203
+ "type": "object",
73204
+ "properties": {
73205
+ "error_code": {
73206
+ "type": "string",
73207
+ "enum": [
73208
+ "validation_error"
73209
+ ]
73210
+ }
73211
+ }
73212
+ }
73213
+ ]
73214
+ }
73215
+ }
73216
+ }
73217
+ },
73218
+ "404": {
73219
+ "description": "`saml_not_enabled` — This client does not have SAML enabled.\n\n`client_not_found` — No client with that `client_id`.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).",
73220
+ "content": {
73221
+ "application/json": {
73222
+ "schema": {
73223
+ "allOf": [
73224
+ {
73225
+ "$ref": "#/components/schemas/ErrorResponse"
73226
+ },
73227
+ {
73228
+ "type": "object",
73229
+ "properties": {
73230
+ "error_code": {
73231
+ "type": "string",
73232
+ "enum": [
73233
+ "saml_not_enabled",
73234
+ "client_not_found",
73235
+ "account_not_found"
73236
+ ]
73237
+ }
73238
+ }
73239
+ }
73240
+ ]
73241
+ }
73242
+ }
73243
+ }
73244
+ },
73245
+ "429": {
73246
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
73247
+ "content": {
73248
+ "application/json": {
73249
+ "schema": {
73250
+ "allOf": [
73251
+ {
73252
+ "$ref": "#/components/schemas/ErrorResponse"
73253
+ },
73254
+ {
73255
+ "type": "object",
73256
+ "properties": {
73257
+ "error_code": {
73258
+ "type": "string",
73259
+ "enum": [
73260
+ "too_many_requests"
73261
+ ]
73262
+ }
73263
+ }
73264
+ }
73265
+ ]
73266
+ }
73267
+ }
73268
+ }
73269
+ },
73270
+ "500": {
73271
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
73272
+ "content": {
73273
+ "application/json": {
73274
+ "schema": {
73275
+ "allOf": [
73276
+ {
73277
+ "$ref": "#/components/schemas/ErrorResponse"
73278
+ },
73279
+ {
73280
+ "type": "object",
73281
+ "properties": {
73282
+ "error_code": {
73283
+ "type": "string",
73284
+ "enum": [
73285
+ "internal_error"
73286
+ ]
73287
+ }
73288
+ }
73289
+ }
73290
+ ]
73291
+ }
73292
+ }
73293
+ }
73294
+ }
73295
+ }
73296
+ }
73297
+ },
73298
+ "/saml/sso/{client_id}": {
73299
+ "get": {
73300
+ "operationId": "saml/sso",
73301
+ "summary": "SAML single sign-on",
73302
+ "tags": [
73303
+ "saml"
73304
+ ],
73305
+ "description": "Single sign-on service (HTTP-Redirect and HTTP-POST bindings). With a SAMLRequest it answers the Service Provider; without one it starts an IdP-initiated login. The signed response is posted to the client's configured ACS URL.",
73306
+ "parameters": [
73307
+ {
73308
+ "schema": {
73309
+ "type": "string"
73310
+ },
73311
+ "in": "query",
73312
+ "name": "SAMLRequest",
73313
+ "required": false
73314
+ },
73315
+ {
73316
+ "schema": {
73317
+ "type": "string"
73318
+ },
73319
+ "in": "query",
73320
+ "name": "RelayState",
73321
+ "required": false
73322
+ },
73323
+ {
73324
+ "schema": {
73325
+ "type": "string"
73326
+ },
73327
+ "in": "query",
73328
+ "name": "SigAlg",
73329
+ "required": false
73330
+ },
73331
+ {
73332
+ "schema": {
73333
+ "type": "string"
73334
+ },
73335
+ "in": "query",
73336
+ "name": "Signature",
73337
+ "required": false
73338
+ },
73339
+ {
73340
+ "schema": {
73341
+ "type": "string",
73342
+ "minLength": 1
73343
+ },
73344
+ "in": "path",
73345
+ "name": "client_id",
73346
+ "required": true
73347
+ }
73348
+ ],
73349
+ "responses": {
73350
+ "200": {
73351
+ "description": "Default Response"
73352
+ },
73353
+ "400": {
73354
+ "description": "`invalid_saml_request` — The SAMLRequest is missing, too large, not well-formed or not a SAML 2.0 AuthnRequest.\n\n`saml_sp_mismatch` — The AuthnRequest does not match the client's SAML configuration: its Issuer is not the configured entity_id, or it asks for another Assertion Consumer Service URL or binding.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
73355
+ "content": {
73356
+ "application/json": {
73357
+ "schema": {
73358
+ "allOf": [
73359
+ {
73360
+ "$ref": "#/components/schemas/ErrorResponse"
73361
+ },
73362
+ {
73363
+ "type": "object",
73364
+ "properties": {
73365
+ "error_code": {
73366
+ "type": "string",
73367
+ "enum": [
73368
+ "invalid_saml_request",
73369
+ "saml_sp_mismatch",
73370
+ "validation_error"
73371
+ ]
73372
+ }
73373
+ }
73374
+ }
73375
+ ]
73376
+ }
73377
+ }
73378
+ }
73379
+ },
73380
+ "404": {
73381
+ "description": "`saml_not_enabled` — This client does not have SAML enabled.\n\n`client_not_found` — No client with that `client_id`.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).",
73382
+ "content": {
73383
+ "application/json": {
73384
+ "schema": {
73385
+ "allOf": [
73386
+ {
73387
+ "$ref": "#/components/schemas/ErrorResponse"
73388
+ },
73389
+ {
73390
+ "type": "object",
73391
+ "properties": {
73392
+ "error_code": {
73393
+ "type": "string",
73394
+ "enum": [
73395
+ "saml_not_enabled",
73396
+ "client_not_found",
73397
+ "account_not_found"
73398
+ ]
73399
+ }
73400
+ }
73401
+ }
73402
+ ]
73403
+ }
73404
+ }
73405
+ }
73406
+ },
73407
+ "429": {
73408
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
73409
+ "content": {
73410
+ "application/json": {
73411
+ "schema": {
73412
+ "allOf": [
73413
+ {
73414
+ "$ref": "#/components/schemas/ErrorResponse"
73415
+ },
73416
+ {
73417
+ "type": "object",
73418
+ "properties": {
73419
+ "error_code": {
73420
+ "type": "string",
73421
+ "enum": [
73422
+ "too_many_requests"
73423
+ ]
73424
+ }
73425
+ }
73426
+ }
73427
+ ]
73428
+ }
73429
+ }
73430
+ }
73431
+ },
73432
+ "500": {
73433
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
73434
+ "content": {
73435
+ "application/json": {
73436
+ "schema": {
73437
+ "allOf": [
73438
+ {
73439
+ "$ref": "#/components/schemas/ErrorResponse"
73440
+ },
73441
+ {
73442
+ "type": "object",
73443
+ "properties": {
73444
+ "error_code": {
73445
+ "type": "string",
73446
+ "enum": [
73447
+ "internal_error"
73448
+ ]
73449
+ }
73450
+ }
73451
+ }
73452
+ ]
73453
+ }
73454
+ }
73455
+ }
73456
+ }
73457
+ }
73458
+ },
73459
+ "post": {
73460
+ "operationId": "saml/sso_post",
73461
+ "summary": "SAML single sign-on",
73462
+ "tags": [
73463
+ "saml"
73464
+ ],
73465
+ "description": "Single sign-on service (HTTP-Redirect and HTTP-POST bindings). With a SAMLRequest it answers the Service Provider; without one it starts an IdP-initiated login. The signed response is posted to the client's configured ACS URL.",
73466
+ "requestBody": {
73467
+ "required": true,
73468
+ "content": {
73469
+ "application/json": {
73470
+ "schema": {
73471
+ "type": "object",
73472
+ "properties": {
73473
+ "SAMLRequest": {
73474
+ "type": "string"
73475
+ },
73476
+ "RelayState": {
73477
+ "type": "string"
73478
+ },
73479
+ "SigAlg": {
73480
+ "type": "string"
73481
+ },
73482
+ "Signature": {
73483
+ "type": "string"
73484
+ }
73485
+ }
73486
+ }
73487
+ }
73488
+ }
73489
+ },
73490
+ "parameters": [
73491
+ {
73492
+ "schema": {
73493
+ "type": "string",
73494
+ "minLength": 1
73495
+ },
73496
+ "in": "path",
73497
+ "name": "client_id",
73498
+ "required": true
73499
+ }
73500
+ ],
73501
+ "responses": {
73502
+ "200": {
73503
+ "description": "Default Response"
73504
+ },
73505
+ "400": {
73506
+ "description": "`invalid_saml_request` — The SAMLRequest is missing, too large, not well-formed or not a SAML 2.0 AuthnRequest.\n\n`saml_sp_mismatch` — The AuthnRequest does not match the client's SAML configuration: its Issuer is not the configured entity_id, or it asks for another Assertion Consumer Service URL or binding.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
73507
+ "content": {
73508
+ "application/json": {
73509
+ "schema": {
73510
+ "allOf": [
73511
+ {
73512
+ "$ref": "#/components/schemas/ErrorResponse"
73513
+ },
73514
+ {
73515
+ "type": "object",
73516
+ "properties": {
73517
+ "error_code": {
73518
+ "type": "string",
73519
+ "enum": [
73520
+ "invalid_saml_request",
73521
+ "saml_sp_mismatch",
73522
+ "validation_error"
73523
+ ]
73524
+ }
73525
+ }
73526
+ }
73527
+ ]
73528
+ }
73529
+ }
73530
+ }
73531
+ },
73532
+ "404": {
73533
+ "description": "`saml_not_enabled` — This client does not have SAML enabled.\n\n`client_not_found` — No client with that `client_id`.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).",
73534
+ "content": {
73535
+ "application/json": {
73536
+ "schema": {
73537
+ "allOf": [
73538
+ {
73539
+ "$ref": "#/components/schemas/ErrorResponse"
73540
+ },
73541
+ {
73542
+ "type": "object",
73543
+ "properties": {
73544
+ "error_code": {
73545
+ "type": "string",
73546
+ "enum": [
73547
+ "saml_not_enabled",
73548
+ "client_not_found",
73549
+ "account_not_found"
73550
+ ]
73551
+ }
73552
+ }
73553
+ }
73554
+ ]
73555
+ }
73556
+ }
73557
+ }
73558
+ },
73559
+ "429": {
73560
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
73561
+ "content": {
73562
+ "application/json": {
73563
+ "schema": {
73564
+ "allOf": [
73565
+ {
73566
+ "$ref": "#/components/schemas/ErrorResponse"
73567
+ },
73568
+ {
73569
+ "type": "object",
73570
+ "properties": {
73571
+ "error_code": {
73572
+ "type": "string",
73573
+ "enum": [
73574
+ "too_many_requests"
73575
+ ]
73576
+ }
73577
+ }
73578
+ }
73579
+ ]
73580
+ }
73581
+ }
73582
+ }
73583
+ },
73584
+ "500": {
73585
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
73586
+ "content": {
73587
+ "application/json": {
73588
+ "schema": {
73589
+ "allOf": [
73590
+ {
73591
+ "$ref": "#/components/schemas/ErrorResponse"
73592
+ },
73593
+ {
73594
+ "type": "object",
73595
+ "properties": {
73596
+ "error_code": {
73597
+ "type": "string",
73598
+ "enum": [
73599
+ "internal_error"
73600
+ ]
73601
+ }
73602
+ }
73603
+ }
73604
+ ]
73605
+ }
73606
+ }
73607
+ }
73608
+ }
73609
+ }
73610
+ }
73611
+ },
73612
+ "/saml/callback": {
73613
+ "get": {
73614
+ "operationId": "saml/callback",
73615
+ "summary": "SAML bridge callback",
73616
+ "tags": [
73617
+ "saml"
73618
+ ],
73619
+ "description": "Internal: where the login flow returns to finish a SAML sign-on. Not for direct use.",
73620
+ "parameters": [
73621
+ {
73622
+ "schema": {
73623
+ "type": "string",
73624
+ "minLength": 1
73625
+ },
73626
+ "in": "query",
73627
+ "name": "state",
73628
+ "required": true
73629
+ },
73630
+ {
73631
+ "schema": {
73632
+ "type": "string"
73633
+ },
73634
+ "in": "query",
73635
+ "name": "code",
73636
+ "required": false
73637
+ },
73638
+ {
73639
+ "schema": {
73640
+ "type": "string"
73641
+ },
73642
+ "in": "query",
73643
+ "name": "error",
73644
+ "required": false
73645
+ },
73646
+ {
73647
+ "schema": {
73648
+ "type": "string"
73649
+ },
73650
+ "in": "query",
73651
+ "name": "error_description",
73652
+ "required": false
73653
+ },
73654
+ {
73655
+ "schema": {
73656
+ "type": "string"
73657
+ },
73658
+ "in": "query",
73659
+ "name": "iss",
73660
+ "required": false
73661
+ }
73662
+ ],
73663
+ "responses": {
73664
+ "200": {
73665
+ "description": "Default Response"
73666
+ },
73667
+ "400": {
73668
+ "description": "`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
73669
+ "content": {
73670
+ "application/json": {
73671
+ "schema": {
73672
+ "allOf": [
73673
+ {
73674
+ "$ref": "#/components/schemas/ErrorResponse"
73675
+ },
73676
+ {
73677
+ "type": "object",
73678
+ "properties": {
73679
+ "error_code": {
73680
+ "type": "string",
73681
+ "enum": [
73682
+ "invalid_state",
73683
+ "validation_error"
73684
+ ]
73685
+ }
73686
+ }
73687
+ }
73688
+ ]
73689
+ }
73690
+ }
73691
+ }
73692
+ },
73693
+ "404": {
73694
+ "description": "`saml_not_enabled` — This client does not have SAML enabled.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).",
73695
+ "content": {
73696
+ "application/json": {
73697
+ "schema": {
73698
+ "allOf": [
73699
+ {
73700
+ "$ref": "#/components/schemas/ErrorResponse"
73701
+ },
73702
+ {
73703
+ "type": "object",
73704
+ "properties": {
73705
+ "error_code": {
73706
+ "type": "string",
73707
+ "enum": [
73708
+ "saml_not_enabled",
73709
+ "account_not_found"
73710
+ ]
73711
+ }
73712
+ }
73713
+ }
73714
+ ]
73715
+ }
73716
+ }
73717
+ }
73718
+ },
73719
+ "429": {
73720
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
73721
+ "content": {
73722
+ "application/json": {
73723
+ "schema": {
73724
+ "allOf": [
73725
+ {
73726
+ "$ref": "#/components/schemas/ErrorResponse"
73727
+ },
73728
+ {
73729
+ "type": "object",
73730
+ "properties": {
73731
+ "error_code": {
73732
+ "type": "string",
73733
+ "enum": [
73734
+ "too_many_requests"
73735
+ ]
73736
+ }
73737
+ }
73738
+ }
73739
+ ]
73740
+ }
73741
+ }
73742
+ }
73743
+ },
73744
+ "500": {
73745
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
73746
+ "content": {
73747
+ "application/json": {
73748
+ "schema": {
73749
+ "allOf": [
73750
+ {
73751
+ "$ref": "#/components/schemas/ErrorResponse"
73752
+ },
73753
+ {
73754
+ "type": "object",
73755
+ "properties": {
73756
+ "error_code": {
73757
+ "type": "string",
73758
+ "enum": [
73759
+ "internal_error"
73760
+ ]
73761
+ }
73762
+ }
73763
+ }
73764
+ ]
73765
+ }
73766
+ }
73767
+ }
73768
+ }
73769
+ }
73770
+ }
73771
+ },
71951
73772
  "/usernamepassword/login": {
71952
73773
  "post": {
71953
73774
  "operationId": "usernamepassword/login",