Web Application
Ruby on Rails

MojoAuth Hosted Login Page with Ruby on Rails

Introduction

Ruby on Rails (or Rails) is a server-side web application framework written in Ruby. Rails is a model–view–controller (MVC) framework that provides default structures for a database, a web service, and web pages. It emphasizes the use of well-known software engineering patterns and paradigms, including convention over configuration (CoC), don't repeat yourself (DRY), and the active record pattern.

This guide will show you how to integrate MojoAuth's Hosted Login Page with your Rails application using OpenID Connect (OIDC) through the omniauth-openid-connect gem, allowing your users to authenticate securely with minimal setup.

Links:

Prerequisites

Before you begin, make sure you have:

  1. A MojoAuth account and an OIDC application set up in the MojoAuth dashboard
  2. Your Client ID and Client Secret from the MojoAuth dashboard
  3. Ruby 2.7.0+ and Rails 6.0+ installed on your development machine
  4. Basic knowledge of Ruby on Rails

Create a New Rails Application

If you don't have an existing Rails application, create one:

# Create a new Rails application rails new mojoauth-rails-demo cd mojoauth-rails-demo

Install Required Gems

Add the necessary gems to your Gemfile:

# Gemfile gem 'omniauth-rails_csrf_protection' gem 'omniauth-openid-connect'

Then install the gems:

bundle install

Configure Environment Variables

Create a .env file in your project root to store your MojoAuth credentials. We'll use the dotenv-rails gem to manage environment variables:

Add to your Gemfile:

# Gemfile gem 'dotenv-rails', groups: [:development, :test]

Install the gem:

bundle install

Create the .env file:

MOJOAUTH_CLIENT_ID=your-client-id MOJOAUTH_CLIENT_SECRET=your-client-secret MOJOAUTH_ISSUER=https://your-project.auth.mojoauth.com MOJOAUTH_REDIRECT_URI=http://localhost:3000/auth/openid_connect/callback

Make sure to add .env to your .gitignore file to keep your credentials secure.

Configure OmniAuth Middleware

Create an initializer file to set up OmniAuth with OpenID Connect:

# config/initializers/omniauth.rb require 'omniauth-openid-connect'   Rails.application.config.middleware.use OmniAuth::Builder do  provider :openid_connect, {  name: :mojoauth,  scope: [:openid, :profile, :email],  response_type: :code,  discovery: true,  issuer: ENV['MOJOAUTH_ISSUER'],  client_options: {  identifier: ENV['MOJOAUTH_CLIENT_ID'],  secret: ENV['MOJOAUTH_CLIENT_SECRET'],  redirect_uri: ENV['MOJOAUTH_REDIRECT_URI']  }  } end   # Configure CSRF protection for OmniAuth OmniAuth.config.allowed_request_methods = [:post, :get]

Create User Model

Let's create a User model to store authenticated users:

rails generate model User provider:string uid:string name:string email:string rails db:migrate

Create Authentication Controllers

Create a sessions controller to handle authentication:

rails generate controller Sessions

Update the sessions controller:

# app/controllers/sessions_controller.rb class SessionsController < ApplicationController  def create  # Get user information from OmniAuth auth hash  auth = request.env['omniauth.auth']    # Find existing user or create a new one  user = User.find_or_initialize_by(provider: auth.provider, uid: auth.uid)  user.update(  name: auth.info.name,  email: auth.info.email  )    # Store user ID in session  session[:user_id] = user.id    # Redirect to homepage or a protected resource  redirect_to root_path, notice: 'Logged in successfully'  end    def destroy  # Clear the session  session[:user_id] = nil  redirect_to root_path, notice: 'Logged out'  end    def failure  redirect_to root_path, alert: "Authentication failed: #{params[:message]}"  end end

Create Application Controller Methods

Add helper methods to your application controller to check if a user is authenticated:

# app/controllers/application_controller.rb class ApplicationController < ActionController::Base  helper_method :current_user, :user_signed_in?    protected    def current_user  @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]  end    def user_signed_in?  !!current_user  end    def authenticate_user!  redirect_to '/auth/mojoauth' unless user_signed_in?  end end

Create Routes

Configure your routes to support authentication:

# config/routes.rb Rails.application.routes.draw do  # OmniAuth callback routes  get 'auth/:provider/callback', to: 'sessions#create'  get 'auth/failure', to: 'sessions#failure'    # Session routes  get 'login', to: redirect('/auth/mojoauth'), as: :login  delete 'logout', to: 'sessions#destroy', as: :logout    # Home page  root 'home#index' end

Create Home Controller and View

Let's create a simple home page to test the authentication:

rails generate controller Home index

Update the home controller:

# app/controllers/home_controller.rb class HomeController < ApplicationController  def index  end    def protected  authenticate_user!  # Protected content here  end end

Create the home view:

<!-- app/views/home/index.html.erb --> <h1>MojoAuth Rails Demo</h1>   <% if user_signed_in? %>  <p>Welcome, <%= current_user.name %>!</p>  <p>Email: <%= current_user.email %></p>  <%= button_to 'Logout', logout_path, method: :delete %> <% else %>  <p>You are not logged in.</p>  <%= link_to 'Login with MojoAuth', login_path %> <% end %>

Creating a Protected Resource

Add a protected route:

# config/routes.rb Rails.application.routes.draw do  # ... existing routes    get 'protected', to: 'home#protected' end

Create a protected page view:

<!-- app/views/home/protected.html.erb --> <h1>Protected Resource</h1> <p>This page is only visible if you're authenticated!</p> <p>Hello, <%= current_user.name %>!</p> <%= link_to 'Back to Home', root_path %>

Implementing PKCE Flow (Enhanced Security)

For better security, you can implement PKCE (Proof Key for Code Exchange) flow. The omniauth-openid-connect gem supports PKCE:

# config/initializers/omniauth.rb Rails.application.config.middleware.use OmniAuth::Builder do  provider :openid_connect, {  name: :mojoauth,  scope: [:openid, :profile, :email],  response_type: :code,  discovery: true,  issuer: ENV['MOJOAUTH_ISSUER'],  client_options: {  identifier: ENV['MOJOAUTH_CLIENT_ID'],  secret: ENV['MOJOAUTH_CLIENT_SECRET'],  redirect_uri: ENV['MOJOAUTH_REDIRECT_URI']  },  pkce: true # Enable PKCE  } end

Handling Refresh Tokens

If your MojoAuth configuration provides refresh tokens, you can use them to maintain the user's session:

# app/controllers/sessions_controller.rb class SessionsController < ApplicationController  def create  auth = request.env['omniauth.auth']    user = User.find_or_initialize_by(provider: auth.provider, uid: auth.uid)  user.update(  name: auth.info.name,  email: auth.info.email  )    # Store tokens in the session  session[:user_id] = user.id  session[:access_token] = auth.credentials.token  session[:refresh_token] = auth.credentials.refresh_token  session[:expires_at] = auth.credentials.expires_at    redirect_to root_path, notice: 'Logged in successfully'  end    # Other actions... end

Create a helper to check if the access token is expired and refresh it:

# app/controllers/application_controller.rb class ApplicationController < ActionController::Base  # Existing methods...    def ensure_fresh_token  if session[:expires_at].present? && session[:expires_at] < Time.now.to_i  refresh_access_token  end  end    private    def refresh_access_token  return unless session[:refresh_token]    # Initialize OpenID client  issuer = OpenIDConnect::Discovery::Provider::Config.discover!(ENV['MOJOAUTH_ISSUER'])  client = OpenIDConnect::Client.new(  identifier: ENV['MOJOAUTH_CLIENT_ID'],  secret: ENV['MOJOAUTH_CLIENT_SECRET'],  redirect_uri: ENV['MOJOAUTH_REDIRECT_URI'],  authorization_endpoint: issuer.authorization_endpoint,  token_endpoint: issuer.token_endpoint,  userinfo_endpoint: issuer.userinfo_endpoint  )    # Request new tokens using the refresh token  begin  response = client.refresh_token!(session[:refresh_token])    # Update session with new tokens  session[:access_token] = response.access_token  session[:refresh_token] = response.refresh_token if response.refresh_token  session[:expires_at] = Time.now.to_i + response.expires_in  rescue => e  # If refresh fails, force re-authentication  session.clear  redirect_to login_path, alert: 'Your session expired. Please log in again.'  end  end end

Complete Example

Here's a complete Rails project structure for the MojoAuth integration:

mojoauth-rails-demo/ ├── app/ │ ├── controllers/ │ │ ├── application_controller.rb │ │ ├── home_controller.rb │ │ └── sessions_controller.rb │ ├── models/ │ │ └── user.rb │ └── views/ │ ├── home/ │ │ ├── index.html.erb │ │ └── protected.html.erb │ └── layouts/ │ └── application.html.erb ├── config/ │ ├── initializers/ │ │ └── omniauth.rb │ └── routes.rb ├── db/ │ ├── migrate/ │ │ └── YYYYMMDDHHMMSS_create_users.rb │ └── schema.rb ├── Gemfile └── .env

Testing the Integration

  1. Start your Rails server:

    rails server
  2. Visit http://localhost:3000 in your browser

  3. Click the "Login with MojoAuth" link

  4. You'll be redirected to the MojoAuth Hosted Login Page

  5. After successful authentication, you'll be redirected back to your Rails application

  6. You should now see your user information displayed on the home page

Next Steps

  1. Error Handling: Add more comprehensive error handling for authentication failures.
  2. Profile Management: Allow users to view and update their profiles.
  3. Authorization: Implement role-based access control using user claims from MojoAuth.
  4. Token Storage: In production, consider using encrypted cookies or a database for token storage.
  5. Session Management: Implement automatic token refresh and proper session management.

Reference Links