Provider preset registry for known SAML IdPs.
Presets carry safe defaults, label translations from our internal field names to the IdP's admin UI vocabulary, and known-footgun checks. They exist so that:
- adopters get fail-closed defaults that match each IdP's quirks
(e.g. Microsoft Entra's NameID
:persistentrequirement); - admin-channel error hints can speak the IdP's language (e.g. "Audience URI" for Okta, "Identifier (Entity ID)" for Entra);
- footgun checks surface known cross-IdP gotchas before they become production incidents.
Shape
Each preset is a module implementing Relyra.Provider callbacks. They
are pure data — no GenServers, no compile-time magic. The module-per-
provider shape (Assent's default_config/1 idiom) plays naturally with
runtime config:
config :my_app, :saml_connection,
provider_preset: :okta,
sp_entity_id: "https://my.app/sso/metadata",
...Public API
Relyra.Provider.apply_defaults(:okta, user_keyword)
Relyra.Provider.translate_label(:okta, :sp_entity_id)
Relyra.Provider.check_footguns(:okta, %Relyra.Connection{...})
Relyra.Provider.guide_url(:okta)Adding a preset
Create a module that @behaviour Relyra.Provider and register its id
in @presets below. Keep default_config/0 strictly safer-than-spec —
presets exist to narrow the trust surface, never to widen it.
Summary
Types
Footgun definition. check.(conn) returns :ok or {:warn, reason}.
Severity guides whether the warning is logged or raised in
strict-config validation.
Per-field translation entry.
Internal field name we expose to adopters.
Functions
Merge preset defaults under a user-supplied keyword list. User-supplied keys win — presets only fill in what the adopter omitted.
Run every footgun check defined by the preset against a resolved connection. Returns a list of results in declaration order; the caller decides whether to log warnings, raise errors, or aggregate.
Display name suitable for log lines and admin UIs.
Resolve a preset id to its implementing module. Raises if unknown so typos fail loudly at config time, not at runtime during a login flow.
Bootstrap a preset from an IdP metadata URL.
Public guide URL for the preset.
Build an admin-facing hint string suitable for Relyra.Error details.
Returns nil when no preset is bound to the connection (adopters
without a preset get clean errors with no hints injected).
Return the full label entry (label + section + hint) for a field. Use this when building admin error hints so the operator gets the IdP's section name and a one-liner hint, not just the label.
List supported preset ids.
Translate one of our internal field names to the IdP's admin UI label. Falls back to the underscored key as a string when the preset has no entry, so missing translations degrade gracefully.
Types
@type footgun() :: %{ id: atom(), severity: :error | :warning, message: String.t(), check: (Relyra.Connection.t() -> :ok | {:warn, String.t()}) }
Footgun definition. check.(conn) returns :ok or {:warn, reason}.
Severity guides whether the warning is logged or raised in
strict-config validation.
@type label_entry() :: %{ :idp_label => String.t(), optional(:idp_section) => String.t() | nil, optional(:hint) => String.t() | nil }
Per-field translation entry.
@type label_key() ::
:sp_entity_id
| :acs_url
| :idp_entity_id
| :idp_sso_url
| :idp_certificate
| :name_id_format
| :signing_algorithm
| :digest_algorithm
Internal field name we expose to adopters.
@type label_map() :: %{required(label_key()) => label_entry()}
Callbacks
Functions
Merge preset defaults under a user-supplied keyword list. User-supplied keys win — presets only fill in what the adopter omitted.
Returns a keyword list suitable for building a Relyra.Connection.t().
@spec check_footguns(atom(), Relyra.Connection.t()) :: [ :ok | {:warn, atom(), String.t()} | {:check_failed, atom(), term()} ]
Run every footgun check defined by the preset against a resolved connection. Returns a list of results in declaration order; the caller decides whether to log warnings, raise errors, or aggregate.
Footguns whose check raises are caught and reported as
{:check_failed, id, reason} so a buggy preset never breaks the
consume pipeline.
Display name suitable for log lines and admin UIs.
Resolve a preset id to its implementing module. Raises if unknown so typos fail loudly at config time, not at runtime during a login flow.
Bootstrap a preset from an IdP metadata URL.
The URL is stored as :idp_metadata_url and the preset defaults are
still applied underneath user overrides.
Public guide URL for the preset.
@spec hint_for(Relyra.Connection.t() | nil, label_key()) :: String.t() | nil
Build an admin-facing hint string suitable for Relyra.Error details.
Returns nil when no preset is bound to the connection (adopters
without a preset get clean errors with no hints injected).
@spec label_entry(atom(), label_key()) :: label_entry() | nil
Return the full label entry (label + section + hint) for a field. Use this when building admin error hints so the operator gets the IdP's section name and a one-liner hint, not just the label.
@spec list() :: [atom()]
List supported preset ids.
Translate one of our internal field names to the IdP's admin UI label. Falls back to the underscored key as a string when the preset has no entry, so missing translations degrade gracefully.