Skip to main content

documentation/dsls/DSL-AshAuthentication.Strategy.DynamicOidc.md

<!--
This file was generated by Spark. Do not edit it by hand.
-->
# AshAuthentication.Strategy.DynamicOidc

Strategy for authenticating against arbitrary OpenID Connect providers
whose configuration lives in a database table rather than in your
application's compile-time DSL.

This is the building block for B2B/multi-tenant SSO patterns: each row in
your `OidcConnection` resource is one customer's IdP configuration
(`base_url`, `client_id`, `client_secret`, plus optional UI metadata).
At sign-in time the strategy queries the resource — typically scoped by
the current Ash tenant — and runs the standard OIDC flow against the
matched row.

## Setup

First, define a connection resource using
`AshAuthentication.OidcConnection`:

```elixir
defmodule MyApp.Accounts.OidcConnection do
  use Ash.Resource,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshAuthentication.OidcConnection],
    domain: MyApp.Accounts

  oidc_connection do
    domain MyApp.Accounts
  end

  postgres do
    table "oidc_connections"
    repo MyApp.Repo
  end
end
```

Then add the strategy to your user resource:

```elixir
authentication do
  strategies do
    dynamic_oidc :sso do
      connection_resource MyApp.Accounts.OidcConnection
      identity_resource MyApp.Accounts.UserIdentity
      redirect_uri MyApp.Secrets
    end
  end
end
```

## URL shape

The strategy generates two routes:

  - `GET /<subject>/<strategy_name>/:connection_id/request` — initiate
    sign-in for a specific connection (the user/UI is responsible for
    knowing which connection_id to send to).
  - `GET /<subject>/<strategy_name>/callback` — single, shared callback
    URL. Each customer's IdP admin only ever needs to register *this*
    URL as their redirect URI. The connection_id is remembered between
    request and callback via the user's session.

## Tenant context

If your connection resource is multitenant, the strategy will scope the
lookup using the tenant set on the conn (`Ash.PlugHelpers.set_tenant/2`).
Set the tenant **upstream** of the auth router — typically in a Phoenix
plug that maps subdomain or header to your tenant. If no tenant is set
and the resource is multitenant, the lookup will fail.

Non-multitenant connection resources are also supported — the strategy
simply queries globally.

## More documentation

- `AshAuthentication.OidcConnection` — the resource extension this
  strategy depends on.
- `AshAuthentication.Strategy.Oidc` — the underlying compile-time OIDC
  strategy. The runtime behaviour is identical aside from where the
  config comes from.



### authentication.strategies.dynamic_oidc
```elixir
dynamic_oidc name \\ :dynamic_oidc
```


An OpenID Connect strategy whose connection details (`base_url`,
`client_id`, `client_secret`) are loaded at request time from a
database resource extended with `AshAuthentication.OidcConnection`.

This is the building block for data-driven multi-tenant SSO: each row
in the connection resource is one customer's IdP configuration, and
the strategy looks up the right row based on the request path
(`:connection_id` segment) plus the current Ash tenant.

###### More documentation:
- `AshAuthentication.OidcConnection` — the resource extension this strategy depends on
- `AshAuthentication.Strategy.Oidc` — the underlying compile-time OIDC strategy






### Arguments

| Name | Type | Default | Docs |
|------|------|---------|------|
| [`name`](#authentication-strategies-dynamic_oidc-name){: #authentication-strategies-dynamic_oidc-name .spark-required} | `atom` |  | Uniquely identifies the strategy. |
### Options

| Name | Type | Default | Docs |
|------|------|---------|------|
| [`redirect_uri`](#authentication-strategies-dynamic_oidc-redirect_uri){: #authentication-strategies-dynamic_oidc-redirect_uri .spark-required} | `(any, any -> any) \| module \| String.t` |  | The callback URI *base*. Not the whole URI back to the callback endpoint, but the URI to your `AuthPlug`. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`connection_resource`](#authentication-strategies-dynamic_oidc-connection_resource){: #authentication-strategies-dynamic_oidc-connection_resource .spark-required} | `module` |  | The Ash resource (extended with `AshAuthentication.OidcConnection`) that stores per-tenant OIDC client configuration. |
| [`site`](#authentication-strategies-dynamic_oidc-site){: #authentication-strategies-dynamic_oidc-site } | `(any, any -> any) \| module \| String.t` |  | Deprecated: Use `base_url` instead. |
| [`prevent_hijacking?`](#authentication-strategies-dynamic_oidc-prevent_hijacking?){: #authentication-strategies-dynamic_oidc-prevent_hijacking? } | `boolean` | `true` | Requires a confirmation add_on to be present if the password strategy is used with the same identity_field. |
| [`auth_method`](#authentication-strategies-dynamic_oidc-auth_method){: #authentication-strategies-dynamic_oidc-auth_method } | `nil \| :client_secret_basic \| :client_secret_post \| :client_secret_jwt \| :private_key_jwt` | `:client_secret_post` | The authentication strategy used, optional. If not set, no authentication will be used during the access token request. |
| [`trusted_audiences`](#authentication-strategies-dynamic_oidc-trusted_audiences){: #authentication-strategies-dynamic_oidc-trusted_audiences } | `(any, any -> any) \| module \| list(any) \| nil` |  | A list of audiences which are trusted. Takes either a module which implements the `AshAuthentication.Secret` behaviour, a 2 arity anonymous function or a string. |
| [`code_verifier`](#authentication-strategies-dynamic_oidc-code_verifier){: #authentication-strategies-dynamic_oidc-code_verifier } | `boolean` | `false` | Boolean to generate and use a random 128 byte long url safe code verifier for PKCE flow, optional, defaults to false. When set to true the session params will contain :code_verifier, :code_challenge, and :code_challenge_method params |
| [`registration_enabled?`](#authentication-strategies-dynamic_oidc-registration_enabled?){: #authentication-strategies-dynamic_oidc-registration_enabled? } | `boolean` | `true` | If enabled, new users will be able to register for your site when authenticating and not already present. If not, only existing users will be able to authenticate. |
| [`register_action_name`](#authentication-strategies-dynamic_oidc-register_action_name){: #authentication-strategies-dynamic_oidc-register_action_name } | `atom` |  | The name of the action to use to register a user, if `registration_enabled?` is `true`. Defaults to `register_with_<name>` See the "Registration and Sign-in" section of the strategy docs for more. |
| [`sign_in_action_name`](#authentication-strategies-dynamic_oidc-sign_in_action_name){: #authentication-strategies-dynamic_oidc-sign_in_action_name } | `atom` |  | The name of the action to use to sign in an existing user, if `sign_in_enabled?` is `true`. Defaults to `sign_in_with_<strategy>`, which is generated for you by default. See the "Registration and Sign-in" section of the strategy docs for more information. |
| [`identity_resource`](#authentication-strategies-dynamic_oidc-identity_resource){: #authentication-strategies-dynamic_oidc-identity_resource } | `module \| false` | `false` | The resource used to store user identities. Required: matching users by email or other provider claims is unsafe, so the provider's `iss`/`sub` claims must be persisted. See the User Identities section of the strategy docs for more. |
| [`warn_on_missing_identity_resource?`](#authentication-strategies-dynamic_oidc-warn_on_missing_identity_resource?){: #authentication-strategies-dynamic_oidc-warn_on_missing_identity_resource? } | `boolean` | `true` | Whether to emit a compile-time warning when no `identity_resource` is configured. Set to `false` only when you have deliberately chosen not to use an identity resource and accept that users are matched by provider claims such as email, which is unsafe and will become unsupported in a future release. |
| [`trust_email_verified?`](#authentication-strategies-dynamic_oidc-trust_email_verified?){: #authentication-strategies-dynamic_oidc-trust_email_verified? } | `boolean` | `false` | Whether the provider's `email_verified` claim can be trusted to attach an OAuth2 sign-in to a pre-existing local account with the same email. Only enable this for providers that reliably assert email ownership. When `false`, a sign-in whose `iss`/`sub` is not yet known will never be matched to an existing account by email. |
| [`on_untrusted_email_match`](#authentication-strategies-dynamic_oidc-on_untrusted_email_match){: #authentication-strategies-dynamic_oidc-on_untrusted_email_match } | `:reject \| :confirm` | `:reject` | What to do when a new `iss`/`sub` presents an email matching an existing account but the email can't be trusted (see `trust_email_verified?`). `:reject` (the default) refuses the sign-in. `:confirm` issues a confirmation to the existing account's email and links the provider only once the recipient proves ownership; requires a `confirmation` add-on. Note: confirming binds whatever provider identity initiated the flow, so the confirmation email must make clear which provider is being linked - otherwise a user can be tricked into linking an attacker's provider account. |
| [`identity_relationship_name`](#authentication-strategies-dynamic_oidc-identity_relationship_name){: #authentication-strategies-dynamic_oidc-identity_relationship_name } | `atom` | `:identities` | Name of the relationship to the provider identities resource |
| [`identity_relationship_user_id_attribute`](#authentication-strategies-dynamic_oidc-identity_relationship_user_id_attribute){: #authentication-strategies-dynamic_oidc-identity_relationship_user_id_attribute } | `atom` | `:user_id` | The name of the destination (user_id) attribute on your provider identity resource. Only necessary if you've changed the `user_id_attribute_name` option of the provider identity. |
| [`openid_configuration_uri`](#authentication-strategies-dynamic_oidc-openid_configuration_uri){: #authentication-strategies-dynamic_oidc-openid_configuration_uri } | `(any, any -> any) \| module \| String.t` | `"/.well-known/openid-configuration"` | The URI for the OpenID provider |
| [`client_authentication_method`](#authentication-strategies-dynamic_oidc-client_authentication_method){: #authentication-strategies-dynamic_oidc-client_authentication_method } | `"client_secret_basic" \| "client_secret_post" \| "client_secret_jwt" \| "private_key_jwt" \| "none"` | `"client_secret_basic"` | The client authentication method to use. |
| [`openid_configuration`](#authentication-strategies-dynamic_oidc-openid_configuration){: #authentication-strategies-dynamic_oidc-openid_configuration } | `nil \| %{optional(String.t) => any}` |  | The OpenID configuration.  If not set, the configuration will be retrieved from `openid_configuration_uri`. |
| [`id_token_signed_response_alg`](#authentication-strategies-dynamic_oidc-id_token_signed_response_alg){: #authentication-strategies-dynamic_oidc-id_token_signed_response_alg } | `"HS256" \| "HS384" \| "HS512" \| "RS256" \| "RS384" \| "RS512" \| "ES256" \| "ES384" \| "ES512" \| "PS256" \| "PS384" \| "PS512" \| "Ed25519" \| "Ed25519ph" \| "Ed448" \| "Ed448ph" \| "EdDSA"` | `"RS256"` | The `id_token_signed_response_alg` parameter sent by the Client during Registration. |
| [`id_token_ttl_seconds`](#authentication-strategies-dynamic_oidc-id_token_ttl_seconds){: #authentication-strategies-dynamic_oidc-id_token_ttl_seconds } | `nil \| pos_integer` |  | The number of seconds from `iat` that an ID Token will be considered valid. |
| [`nonce`](#authentication-strategies-dynamic_oidc-nonce){: #authentication-strategies-dynamic_oidc-nonce } | `boolean \| (any, any -> any) \| module \| String.t` | `true` | A function for generating the session nonce, `true` to automatically generate it with `AshAuthentication.Strategy.Oidc.NonceGenerator`, or `false` to disable. |
| [`authorization_params`](#authentication-strategies-dynamic_oidc-authorization_params){: #authentication-strategies-dynamic_oidc-authorization_params } | `(any, any -> any) \| module \| keyword \| nil` | `[scope: "profile email"]` | Any additional parameters to encode in the request phase. eg: `authorization_params scope: "openid profile email"` |





### Introspection

Target: `AshAuthentication.Strategy.DynamicOidc`



<style type="text/css">.spark-required::after { content: "*"; color: red !important; }</style>