Authentication & OAuth
User registration, authentication, forgotten passwords, social logins with OAuth, and more.
Authentication
Jumpstart Pro uses Rails 8's built-in authentication generator for user registration and authentication. Sessions are database-backed (the Session model) and identified with a signed, http-only cookie, so there are no third-party authentication dependencies to keep up to date.
Passwords use Rails' built-in has_secure_password. Devise's digests were plain bcrypt, so digests created by previous Devise-based versions continue to verify without a rehash. The password column is still encrypted_password, aliased to password_digest.
The controllers, views, and routes live under app/controllers/users, app/views/users, and config/routes/users.rb. If you would like to collect other information on registration, add the fields to the registration views and permit the additional params in Users::RegistrationsController.
Changing Email Addresses
The change email form on the profile page (Users::EmailChangesController) saves the new address to unconfirmed_email on the user, so it is confirmed before it's used. It must not belong to another account. It receives a single-use link that expires in an hour, and clicking it replaces email and notifies the previous address, so the owner learns of a change they didn't make. No password is asked for, so this works for OAuth sign ups, and requests are rate limited. Addresses are not confirmed on sign up; the confirmed_at column is still there if your app needs that.
Social Login / OAuth with Omniauth Providers
To enable an oauth provider you can do so in the OAuth section of the Jumpstart Pro configuration wizard UI located at /jumpstart.
Jumpstart Pro is preconfigured to connect with OmniAuth providers so users can register or sign in with their social accounts.
We automatically handle the various situations that can arise with social auth:
- Registering a new user with an OAuth account
- Connecting an OAuth account to an existing user who is logged in
- Signing into an existing user via their previously connected social account
- Rejecting login to an existing user who hasn't connected the social account before
You're free to override any of this functionality by adding your own methods to the OmniauthCallbacksController like you normally would.
To create a button for OAuth, you will use the following:
<%= button_to(
t("oauth.twitter"),
omniauth_authorize_path(:twitter),
class: "btn btn-primary m-1",
data: {
turbo: false,
disable_with: t(".redirecting")
}
) %>
This makes a POST request to our Rails app. OmniAuth handles the request and redirects to the OAuth provider for login. This POST request is required for security.
data-turbo="false" is important because we are redirecting to an external site after making a POST request to Rails. Disabling Turbo on the request tells it to allow the external redirect.
By default, OmniAuth callbacks controller will create a ConnectedAccount record and associate it with current_user.
Custom OAuth Providers
To add custom OmniAuth OAuth providers to Jumpstart Pro, you can follow these steps:
- Add the
omniauth-[provider]to your Gemfile and runbundle. - Edit the Rails credentials and add your credentials under
omniauth:for your provider. - Edit
config/locales/en.ymland add the OAuth provider to the list. - Enable the OAuth provider in
config/jumpstart.rb(or the configuration wizard) and reference the Rails credentials for your client ID, secret, and scope.
Jumpstart Pro automatically generates buttons for enabled OmniAuth providers. The Users::OmniauthCallbacksController saves the OAuth credentials to a ConnectedAccount and generates a scope for each provider.
OAuth Connected Accounts
Users can sign up or login with connected accounts. We automatically add links based upon which OmniAuth providers are enabled. By default, Jumpstart Pro has options for a few OmniAuth providers, but you're free to add other providers and they will be automatically added to parts of the UI.
Associate ConnectedAccount with other models
If you would like to associate the ConnectedAccount with a different model, you can pass in the record param set to a Signed GlobalID. The callback controller will detect this and use this as the owner of the ConnectedAccount record.
Here we will associate the Twitter ConnectedAccount with the current_account. Optionally, you can set the redirect_to to send the user to a different location afterwards.
<%= button_to(
t("oauth.twitter"),
omniauth_authorize_path(
:twitter,
record: current_account.to_sgid(for: :oauth),
redirect_to: account_path(current_account)
),
class: "btn btn-primary m-1",
data: {
turbo: false,
disable_with: t(".redirecting")
}
) %>
Working with Connected Account Data
The User model has_many :connected_accounts which you can use to access the data collected from the accounts such as credentials and user information.
We automatically generate a scope for each provider, so you can filter out connected accounts easily: @user.connected_accounts.twitter.first
If you would like to do something after connecting an OAuth account, you can add a method to the OmniauthCallbacksController for the provider and it will be called after the account is connected.
ConnectedAccount has a method named token that will always return an active API token. It will seamlessly renew expired tokens for you so your code never has to deal with services who provide you expiring tokens.
ConnectedAccount records also store the entire auth hash from Omniauth so you can easily debug integrations and access any of that data later on as necessary.
For example, to use the Twitter gem, you would initialize with the token and access_token_secret:
connected_account = current_user.connected_accounts.twitter.first
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.credentials.dig(:omniauth, :twitter, :public_key)
config.consumer_secret = Rails.application.credentials.dig(:omniauth, :twitter, :private_key)
config.access_token = connected_account.token
config.access_token_secret = connected_account.access_token_secret
end
Single log in across all sub domains
By default, users will be required to enter their credentials for each of their subdomains. If instead you want them to log a single time and have access to all their subdomains, add these two lines:
# config/application.rb
config.session_store :cookie_store, :key => '_some_key', :domain => '.lvh.me'
# config/environments/test.rb
config.session_store :cookie_store, :key => '_some_key', :domain => '.example.com'