Chevron left

Authentication

Overview

Theme

Themes/hound/logo.svg

Hound

Framework

Tailwind CSS logo

Tailwind CSS

Rails UI authentication design patterns.

Rails UI authentication templates map to the Devise gem's view templates at this time. There may be future support for different authentication libraries or a solution built from scratch in the future.

Before diving into your Rails UI configuration there are some patterns in use for more efficient front-end development. Most of those patterns involve abstractions where it makes sense and others are a bit more opinionated.

Use a global authentication layout partial

Rails UI uses a reusable partial with a block pattern designed to repeat the existing authentication design. Because Devise ships with many different authentication strategies and forms it makes sense to extract repeatable code away.

<!-- render auth_layout partial with block (app/views/devise/_auth_layout.html.erb) -->
<%= render "auth_layout" do %>
  <!-- Devise view code -->
<% end %>

Each corresponding view makes use of the layout partial as a means to keep code DRY. From there you can simply swap out forms and featured content relative to the Rails UI theme you choose.

<!-- app/views/devise/_auth_layout.html.erb -->
<div class="sm:h-[calc(100vh_-_52px)] pt-10 sm:pt-0 flex flex-col items-center justify-center bg-cover bg-center px-4" style="background-image: url('<%= asset_url('fusion.png')%>')" >
  <div class="sm:flex-1 flex flex-col justify-center sm:w-[428px] w-full">
    <div>
      <div class="flex justify-center">
        <%= link_to root_path do %>
          <%= image_tag "logo.svg", alt: "Hound logo", class: "w-10 h-auto" %>
        <% end %>
      </div>

      <div class="mt-6">
        <%= yield :masthead %>
      </div>

      <div class="bg-white shadow-sm rounded-lg p-8 border border-slate-300/60">
        <%= yield %>

        <!-- Add additional provider svg icons in app/assets/images/omniauth as necessary. These should ideally be full color versions of each provider's logo for max consistency. File name conventions should be lowercase without hyphens or spaces (i.e., google, linkedin, twitter, facebook)
        Default: Google, LinkedIn, Twitter, Facebook. -->

        <% if devise_mapping.omniauthable? && %w{ registrations sessions }.include?(controller_name) %>
          <hr class="my-6"/>
          <% resource_class.omniauth_providers.each do |provider| %>
            <div class="my-2">
            <%= button_to omniauth_authorize_path(resource_name, provider), class: "btn btn-white w-full", data: { turbo: false } do %>
              <%= inline_svg "omniauth/#{provider.gsub(/\s+/, '').downcase}.svg", class: "mr-2 w-5 h-5" %>
              <span>"Sign in with <%= OmniAuth::Utils.camelize(provider) %></span>
            <% end %>
            </div>
          <% end %>
        <% end %>
      </div>

      <div class="mt-4">
        <%= render "devise/shared/links" %>
      </div>
    </div>
  </div>
</div>
/ app/views/devise/_auth_layout.html.erb
.sm:h-[calc(100vh_-_52px)].pt-10.sm:pt-0.flex.flex-col.items-center.justify-center.bg-cover.bg-center.px-4{style: "background-image: url('#{asset_url('fusion.png')}')"}
  .sm:flex-1.flex.flex-col.justify-center.w-full{class: "sm:w-[428px]"}
    %div
      .flex.justify-center
        = link_to root_path do
          = image_tag "logo.svg", alt: "Hound logo", class: "w-10 h-auto"
      .mt-6
        = yield :masthead
      .bg-white.shadow-sm.rounded-lg.p-8.border{class: "border-slate-300/60"}
        = yield
        - if devise_mapping.omniauthable? && %w{ registrations sessions }.include?(controller_name)
          %hr.my-6/
          - resource_class.omniauth_providers.each do |provider|
            .my-2
              = button_to omniauth_authorize_path(resource_name, provider), class: "btn btn-white w-full", data: { turbo: false } do
                = inline_svg "omniauth/#{provider.gsub(/\s+/, '').downcase}.svg", class: "mr-2 w-5 h-5"
                %span
                  "Sign in with #{OmniAuth::Utils.camelize(provider)}
      .mt-4
        = render "devise/shared/links"

Rails UI modifies the default app/views/devise/shared/_links.html.erb partial supplied by the Devise gem for a more improved user experience. During the configuration phase of installing Rails UI the modified partial is copied over to your app so you needn't worry about updating this manually.

If you were to ever reinstall devise or use their supplied view template generator this would be overwritten.

<!-- app/views/devise/shared/_links -->
<% if devise_mapping.confirmable? && controller_name != 'confirmations' %>
  <div class="text-center mb-1">
    <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800" %>
  </div>
<% end %>

<% if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
  <div class="text-center mb-2">
    <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800" %>
  </div>
<% end %>
/ app/views/devise/shared/_links
- if devise_mapping.confirmable? && controller_name != 'confirmations'
  .text-center.mb-1
    = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800"
- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
  .text-center.mb-2
    = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: "text-sm text-slate-600 hover:text-slate-800"

OmniAuth

The Hound theme supports themed and branded Omniauth provider buttons for Facebook, Twitter, LinkedIn, and Google. Icons for those providers are dynamically service to the view from app/assets/images/omniauth.

Note: The logic in the layout assumes you've installed OmniAuth which doesn't come pre-installed with Rails UI.

Devise error_messages partial

By default Rails UI copies over a pre-styled error partial made to work out of the box and save you time during development.

This file is called _error_messages.html.erb. Unlike what ships with Devise, this partial lives in the root view directory within app/views/shared and is used throughout the app.

The Hound theme leverages this partial for all form error rendering to keep the error/validation handling experience consistent.

Forget your password?

Reset it in an instant

<% if resource.errors.any? %>
<div class="bg-rose-50 text-rose-700 sm:px-9 sm:py-6 px-6 py-6 rounded-lg mb-6 dark:bg-rose-400/10 dark:border dark:border-rose-400/20 dark:text-rose-50 text-sm" role="alert">
  <div class="flex items-start space-x-4">
    <%= icon "shield-exclamation", classes: "w-6 h-6 text-rose-700 flex-shrink-0", variant: :solid %>
    <div class="flex-1">
      <p class="font-bold"><%= pluralize(resource.errors.count, "error") %> prohibited this post from being saved:</p>
      <ul class="list-disc mt-3 ml-4">
        <% resource.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  </div>
</div>
<% end %>
- if resource.errors.any?
  .bg-rose-50.text-rose-700.sm:px-9.sm:py-6.px-6.py-6.rounded-lg.mb-6.dark:border.dark:text-rose-50.text-sm{class: "dark:bg-rose-400/10 dark:border-rose-400/20", role: "alert"}
    .flex.items-start.space-x-4
      = icon "shield-exclamation", classes: "w-6 h-6 text-rose-700 flex-shrink-0", variant: :solid
      .flex-1
        %p.font-bold
          = pluralize(resource.errors.count, "error")   prohibited this post from being saved:
        %ul.list-disc.mt-3.ml-4
          - resource.errors.each do |error|
            %li
            = error.full_message