supabase-rb-rb
Controllers

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.

LayerClassWhere it lives
Host (generated, 3-line subclass)::SessionsControllerapp/controllers/sessions_controller.rb
Gem base controllerSupabase::Rails::SessionsControllerapp/controllers/supabase/rails/sessions_controller.rb
Gem common baseSupabase::Rails::BaseControllerapp/controllers/supabase/rails/base_controller.rb
Host application::ApplicationControllerapp/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

ControllerActionsReference
Supabase::Rails::BaseControllerbase
Supabase::Rails::SessionsControllernew, create, destroysessions
Supabase::Rails::RegistrationsControllernew, createregistrations
Supabase::Rails::PasswordsControllernew, create, edit, updatepasswords
Supabase::Rails::OtpControllernew, create, verifyotp
Supabase::Rails::OauthControllerauthorize, callbackoauth

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
end

One line expands to the full route table:

HelperVerbURLController#Action
new_session_pathGET/session/newSessionsController#new
session_pathPOST/sessionSessionsController#create
session_pathDELETE/sessionSessionsController#destroy
new_registration_pathGET/registration/newRegistrationsController#new
registration_pathPOST/registrationRegistrationsController#create
passwords_pathGET/passwordsPasswordsController#index
passwords_pathPOST/passwordsPasswordsController#create
new_password_pathGET/passwords/newPasswordsController#new
edit_password_path(token)GET/passwords/:token/editPasswordsController#edit
password_path(token)PATCH/PUT/passwords/:tokenPasswordsController#update
new_otp_pathGET/otp/newOtpController#new
otp_index_pathPOST/otpOtpController#create
verify_otp_index_pathGET, POST/otp/verifyOtpController#verify
oauth_authorize_path(provider)GET/oauth/:provider/authorizeOauthController#authorize
oauth_callback_pathGET/oauth/callbackOauthController#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]
end

Unknown 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
end

The 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
end

To 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
end

To 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
end

The 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:

KeyDefault copy
supabase.rails.sessions.createdSigned in successfully.
supabase.rails.sessions.invalidTry another email address or password.
supabase.rails.sessions.destroyedSigned out successfully.
supabase.rails.registrations.createdWelcome — your account is ready.
supabase.rails.registrations.pending_confirmationCheck your inbox to confirm your email before signing in.
supabase.rails.passwords.reset_sentCheck your inbox for a password-reset link.
supabase.rails.passwords.updatedPassword updated. Sign in with your new password.
supabase.rails.otp.sentWe sent you a code — enter it below.
supabase.rails.otp.verifiedSigned in successfully.
supabase.rails.oauth.failedWe couldn't start that sign-in. Please try again.
supabase.rails.oauth.connectedSigned 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

On this page