Controllers
Built-in controllers under Supabase::Rails::* and the supabase_authentication_routes routing DSL.
supabase-rails ships six controllers under Supabase::Rails::* that the bin/rails generate supabase:install generator subclasses into the host app's top-level namespace (SessionsController, RegistrationsController, …). Action bodies live in the gem so improvements arrive via bundle update; hosts override behaviour by redefining individual actions in their 3-line subclass.
The supabase_authentication_routes DSL helper mounts the routes the six controllers expect — sign-in / sign-up / sign-out, password reset, OTP / magic link, and the two-leg OAuth + PKCE flow.
Controller hierarchy
The host's controllers are top-level constants (e.g. ::SessionsController) that inherit from the gem's namespaced base classes. The routes DSL resolves the unprefixed action names (resource :session, resources :passwords, …), so route lookup hits the host's top-level subclass first — that subclass either inherits the gem's action body unchanged or overrides it.
| Layer | Class | Where it lives |
|---|---|---|
| Host (generated, 3-line subclass) | ::SessionsController | app/controllers/sessions_controller.rb |
| Gem base controller | Supabase::Rails::SessionsController | app/controllers/supabase/rails/sessions_controller.rb |
| Gem common base | Supabase::Rails::BaseController | app/controllers/supabase/rails/base_controller.rb |
| Host application | ::ApplicationController | app/controllers/application_controller.rb |
Supabase::Rails::BaseController inherits from ::ApplicationController, not from ActionController::Base directly. That hop is what makes the host's layouts, helpers, protect_from_forgery, and before_actions flow through to the gem's actions.
What lives where
| Controller | Actions | Reference |
|---|---|---|
Supabase::Rails::BaseController | — | base |
Supabase::Rails::SessionsController | new, create, destroy | sessions |
Supabase::Rails::RegistrationsController | new, create | registrations |
Supabase::Rails::PasswordsController | new, create, edit, update | passwords |
Supabase::Rails::OtpController | new, create, verify | otp |
Supabase::Rails::OauthController | authorize, callback | oauth |
Routes
The supabase_authentication_routes helper is installed onto ActionDispatch::Routing::Mapper by Supabase::Rails::Engine so it is callable directly inside Rails.application.routes.draw do ... end — no mount is required, and there is no engine to namespace under.
# config/routes.rb (after running `bin/rails generate supabase:install`)
Rails.application.routes.draw do
supabase_authentication_routes
endOne line expands to the full route table:
| Helper | Verb | URL | Controller#Action |
|---|---|---|---|
new_session_path | GET | /session/new | SessionsController#new |
session_path | POST | /session | SessionsController#create |
session_path | DELETE | /session | SessionsController#destroy |
new_registration_path | GET | /registration/new | RegistrationsController#new |
registration_path | POST | /registration | RegistrationsController#create |
passwords_path | GET | /passwords | PasswordsController#index |
passwords_path | POST | /passwords | PasswordsController#create |
new_password_path | GET | /passwords/new | PasswordsController#new |
edit_password_path(token) | GET | /passwords/:token/edit | PasswordsController#edit |
password_path(token) | PATCH/PUT | /passwords/:token | PasswordsController#update |
new_otp_path | GET | /otp/new | OtpController#new |
otp_index_path | POST | /otp | OtpController#create |
verify_otp_index_path | GET, POST | /otp/verify | OtpController#verify |
oauth_authorize_path(provider) | GET | /oauth/:provider/authorize | OauthController#authorize |
oauth_callback_path | GET | /oauth/callback | OauthController#callback |
Filtering with only: / except:
The helper accepts an only: or except: array of group symbols — one of :session, :registration, :passwords, :otp, :oauth. A host that only wants email + password sign-in writes:
Rails.application.routes.draw do
supabase_authentication_routes only: %i[session registration]
endUnknown group symbols raise ArgumentError at boot with a message listing the valid set (Supabase::Rails::Routes::GROUPS). The helper does not name controllers — resource :session resolves to ::SessionsController (the top-level constant), which is exactly the wrapper the install generator writes.
Nesting under a scope
The DSL is a regular routing helper, so it can be wrapped in any scope/namespace/constraint Rails supports. Hosts that want auth under /auth write:
Rails.application.routes.draw do
scope "/auth" do
supabase_authentication_routes
end
endThe shipped views call URL helpers (session_path, new_password_path, …), not raw paths — so URL helpers stay valid under any prefix without view edits.
The override pattern
The install generator writes a 3-line subclass for each controller into the host's top-level namespace. The subclass inherits every action body from the gem; the host overrides any action by redefining it.
# app/controllers/sessions_controller.rb (written by `bin/rails generate supabase:install`)
class SessionsController < Supabase::Rails::SessionsController
endTo customise an action, redefine it in the subclass — optionally calling super to keep the gem behaviour and add to it:
class SessionsController < Supabase::Rails::SessionsController
def create
super
AnalyticsJob.perform_later(event: "sign_in", user_id: Current.user.id) if authenticated?
end
endTo replace the action wholesale, redefine it without super:
class SessionsController < Supabase::Rails::SessionsController
def create
session = authenticate_with_supabase(email: params[:email], password: params[:password])
if session
start_new_session_for(session)
redirect_to dashboard_path, notice: "Welcome back!"
else
flash.now[:alert] = "Bad credentials."
render :new, status: :unauthorized
end
end
endThe methods the action bodies call — authenticate_with_supabase, start_new_session_for, terminate_session, and the supabase_* low-level helpers — are all instance methods on the Authentication concern included via BaseController. They are available unchanged in any host subclass.
Flash messages and I18n
Every redirect / re-render in the shipped action bodies passes its notice: / alert: through I18n.t("supabase.rails.<scope>.<key>"). The English defaults ship in the gem under config/locales/en.yml:
| Key | Default copy |
|---|---|
supabase.rails.sessions.created | Signed in successfully. |
supabase.rails.sessions.invalid | Try another email address or password. |
supabase.rails.sessions.destroyed | Signed out successfully. |
supabase.rails.registrations.created | Welcome — your account is ready. |
supabase.rails.registrations.pending_confirmation | Check your inbox to confirm your email before signing in. |
supabase.rails.passwords.reset_sent | Check your inbox for a password-reset link. |
supabase.rails.passwords.updated | Password updated. Sign in with your new password. |
supabase.rails.otp.sent | We sent you a code — enter it below. |
supabase.rails.otp.verified | Signed in successfully. |
supabase.rails.oauth.failed | We couldn't start that sign-in. Please try again. |
supabase.rails.oauth.connected | Signed in successfully. |
Hosts override copy by adding the same keys to their own config/locales/en.yml (or any locale file). The gem-shipped defaults lose to host-provided keys via Rails' standard locale-file load order.
The :alert flash on validation failures (supabase_sign_up, supabase_reset_password, supabase_update_user, supabase_sign_in_with_otp, supabase_verify_otp, supabase_exchange_code_for_session) is the mapped AuthError.message — see AuthErrorMapper for the full code → message table.
See also
base— the common parent class.sessions— sign-in and sign-out.registrations— sign-up.passwords— password reset.otp— OTP / magic-link sign-in.oauth— OAuth + PKCE.Authenticationconcern —authenticated?,current_user,require_authentication,start_new_session_for,terminate_session, and thesupabase_*helpers.supabase:installgenerator — writes the 3-line top-level subclasses.supabase:viewsgenerator — copies the default ERB into the host app.