Skip to main content
Two options for apps that already own their public URLs: serve the library under a prefix of your choosing, and let OAuth/OIDC providers change at runtime.

Path prefix

Config.PathPrefix sets where Mount and Handler serve the auth routes. The default is /auth, so existing apps are unchanged.
The prefix must start with /, must not end with /, and must not contain a query, fragment, wildcard, whitespace or empty/dot segments. Anything else makes New return an error. Every URL the library generates follows the prefix: Register the redirect URI shown above at each identity provider. Routes outside the prefix (/oauth/*, /.well-known/*, /scim/v2/*, /admin/v1/*) keep their own paths. The device verification page stays at BaseURL + "/device" unless DeviceConfig.VerificationURI is set.

clientauth

clientauth.DeviceOptions.AuthPath and clientauth.Client.AuthPath take the same prefix. They default to clientauth.DefaultAuthPath (/auth).

Custom OAuth redirect URI

By default the redirect URI sent to a provider is BaseURL + prefix + /providers/{name}/callback. An app migrating from another auth stack often has different callback URLs already registered at Google, GitHub, Microsoft or an OIDC issuer. OAuthConfig.RedirectURI overrides the value without touching the registered URLs.
The hook takes the request so an app can derive the URI per request, and runs once at start. The result is stored with the flow state and reused verbatim at code exchange, so concurrent flows never share a value. With the hook unset, behavior is unchanged. The result must be an absolute URL with no fragment or userinfo. https is required, except http on a loopback host or when OAuthConfig.AllowInsecureRedirectURI is set (development only). Any hook error or invalid value fails the start with a 500: no redirect, no state.
Do not trust the Host header. Building the URI from r.Host or X-Forwarded-Host lets a spoofed header steer the authorization code to another host. Prefer a fixed URI, and set RedirectURIAllowedHosts (matched case-insensitively against the host or host:port) so any other host fails closed.
If your app serves its own callback route at a different path, call (*TheAuth).OAuthStart(r, provider, returnTo) and (*TheAuth).OAuthCallback(r, provider, code, state, binding). You set the binding cookie from OAuthStartResult.Binding and the session cookie from OAuthCallbackResult.SessionToken. The built-in routes keep working.

Dynamic providers

Config.ProviderResolver supplies providers that are not known at startup.
Return (nil, false, nil) for an unknown name. A resolver may also implement List(ctx) ([]Provider, error), which (*TheAuth).ListProviders includes.
Behavior:
  • Static Providers win. Set ProviderResolverFirst to consult the resolver first, falling back to static providers when it reports not found.
  • Names must match ^[a-z0-9][a-z0-9_-]{0,62}$ before they reach the resolver. Other names get a 404.
  • A resolver error fails the flow closed (503 on start, no fallback, not cached).
  • The returned provider’s Name() must equal the requested name.
  • ProviderResolverTTL caches answers, including “not found”. Zero disables caching. InvalidateProvider drops one entry immediately and also discards any lookup that was in flight.
  • State, PKCE, nonce and browser binding are unchanged. A provider removed between start and callback fails the callback.
Last modified on October 7, 2026