For AI agents: the complete documentation index is at llms.txt. Markdown versions are available by appending .md or sending Accept: text/markdown.
Reflex Logo
Docs Logo
Enterprise

/

Auth

/

Custom Pages

New in reflex-enterprise v0.9.1.

Customizing the Auth Pages

rxe.AuthPlugin registers four auth routes and owns their protocol wiring. The component rendered on each route is customizable; the OIDC redirect, callback token exchange, and logout dispatch remain plugin-owned.

EndpointDefault routePlugin-owned wiringBuilder
login_endpoint/loginRenders the login palette and starts the OIDC redirect.login_page
auth_callback_endpoint/callbackCSRF (OAuth state) check + authorization-code token exchange, then redirect back.callback_page
logout_endpoint/logoutDispatches the active provider's logout; a CSRF guard blocks cross-site logout (see secure by default).logout_page
forbidden_endpoint/forbiddenShown when an authenticated user lacks permission to view a page.forbidden_page

The routes themselves are configurable through login_endpoint, logout_endpoint, auth_callback_endpoint, and forbidden_endpoint. See the providers page for configuring identity providers, and the overview for how the plugin fits together.

The page builder contract

A page builder is a callable that receives the build context as keyword arguments:

KeywordTypeMeaning
providersSequence[type[OIDCAuthState]]The resolved provider state classes.
pluginAuthPluginThe plugin instance.

Name the required entries and add **context to ignore the rest:

A builder may also accept only **context. The same contract applies to the login, callback, logout, and forbidden builders.

A custom login page

In most apps, send users to /login. Customize login_page when the default login buttons need a different layout.

Call each provider's get_login_button(*children) and pass the clickable element as children. This preserves the OIDC redirect wiring and the iframe popup listener. provider.display_name() returns the provider label; by default it is the title-cased __provider__ value:

With two or more providers, render one get_login_button() per provider. See running inside an iframe for the popup flow requirement.

Custom callback and logout pages

The callback and logout routes only show an interstitial while their plugin-owned on_load runs. Reuse providers[0].get_authentication_loading_page(), which already shows the validating and redirecting states as the exchange (or logout) proceeds, plus an error view if it fails (see auth-failure UX and troubleshooting):

Wrap that view in an app-specific layout when the interstitial needs branding.

Auth-failure UX and troubleshooting

When a token exchange or validation fails, get_authentication_loading_page() swaps its spinner for an error view: a user-facing message plus an error ID (a per-flow UUID) the user can hand to support. The same failure is logged on the backend at ERROR level, prefixed <client_token> [txid=<id>]. The log entry is emitted even when the app configures no logging. Use the displayed ID to find the matching server log.

The page builders do not take an error override. default_callback_page calls get_authentication_loading_page(). There are two supported ways to customize the failure UI:

1. Override the state classmethods. Subclass your provider state and override get_error_component, get_authentication_error_component, or get_logout_error_component. The loading page picks up the override automatically:

2. Hand-write a page reading the public vars. has_error, user_error_message, and last_error_txid are public Vars on the provider state, A custom callback or logout builder can branch on them directly:

A custom forbidden page

/forbidden is shown when an authenticated user tries to load a page they are not authorized to view. This happens when the global default AuthPlugin(auth=...) is a callable check that fails on a page load. The forbidden page has no plugin-owned on_load.

Wiring them up

Pass the builders to the plugin in rxconfig.py as import-path strings ("module.function"). The builder modules import reflex_enterprise, which loads rxconfig at import time. Importing them directly in rxconfig.py would re-enter the config. Import-path strings are resolved lazily at compile time:

Defaults

Omit a builder and the plugin falls back to its defaults from reflex_enterprise.auth.pages:

Builder argumentDefaultRenders
login_pagedefault_login_pageOne provider.get_login_button() per provider.
callback_pagedefault_callback_pageproviders[0].get_authentication_loading_page().
logout_pagedefault_logout_pageproviders[0].get_authentication_loading_page().
forbidden_pagedefault_forbidden_pageA 403 access denied view.

The defaults take the same keyword context. A custom builder may call a default builder and wrap the returned content:

Built with Reflex