Docs menu: OIDC discovery
Provider strategy

vestibule_oidc

OpenID Connect discovery that builds a strategy from a standards-compliant issuer URL, including a self-hosted provider.

When to use it

Use OIDC discovery to authenticate with an OpenID Connect provider, including a self-hosted provider. Supply its issuer URL instead of configuring each endpoint.

Default scopes: openid profile email

Install

Vestibule packages are not available on Hex. Add them from GitHub with the moving v0 tag. Use Gleam 1.18 or later because companion packages use Git path dependencies.

[dependencies]
vestibule_oidc = { git = "https://github.com/tylerbutler/vestibule.git", ref = "v0", path = "packages/vestibule_oidc" }

Setup

  1. Register a client with your OIDC provider and copy the client ID and secret.
  2. Configure the redirect URI to match the one passed to config.new.
  3. Call discover with the provider's issuer URL to fetch its well-known configuration.
  4. Pair the discovered strategy with config.new holding your client credentials.

Usage

import vestibule
import vestibule/config
import vestibule_oidc
// Discover the provider's endpoints from its issuer URL.
let assert Ok(strategy) =
vestibule_oidc.discover("https://accounts.google.com")
let client_config =
config.new(
client_id: "your-client-id",
redirect_uri: "https://myapp.example.com/auth/oidc/callback",
auth: config.ClientSecret("your-client-secret"),
)
let options = config.authorize_options()
let assert Ok(auth_request) =
vestibule.create_authorization_request(
strategy,
config: client_config,
options: options,
)

What Vestibule handles

  • discover reads /.well-known/openid-configuration and builds a Strategy.
  • Issuer validation rejects discovery documents whose issuer does not match the requested URL.
  • HTTPS and public-host checks on the issuer and endpoints reduce SSRF risk.
  • Standard OIDC claims map to UserInfo. These claims include sub, name, email, preferred_username, and picture.
  • Email is only populated when the provider reports email_verified.

What you handle

  • Discovery performs HTTP requests, so the strategy targets the Erlang (BEAM) runtime only.
  • Store authorization_request.nonce(auth_request) and pass it as `expected_nonce` during callback handling.
  • For a multi-tenant app, allow only issuers that your application trusts. Built-in URL checks do not define your tenant policy.