Ruby on Rails with SSO, API Versioning & Audit Trails

Avatar

By squashlabs, Last Updated: Sept. 28, 2023

Ruby on Rails with SSO, API Versioning & Audit Trails

What is Ruby on Rails?

Ruby on Rails, commonly referred to as Rails, is a popular web application framework written in the Ruby programming language. It follows the Model-View-Controller (MVC) architectural pattern and provides a set of conventions that allow developers to build robust and scalable web applications quickly. Rails is known for its simplicity, productivity, and emphasis on convention over configuration.

Related Article: Techniques for Handling Large Data with Ruby on Rails

How does OAuth work?

OAuth is an open standard protocol that allows users to grant third-party applications access to their resources in a secure and controlled manner. It is commonly used for authentication and authorization purposes in web and mobile applications.

OAuth works by enabling users to authorize an application to access their data on a server without sharing their credentials (username and password) with the application. Instead, the application obtains an access token from the server, which it can use to make authorized requests on behalf of the user.

The OAuth protocol involves several actors: the user, the client application, the authorization server, and the resource server. Here is a simplified overview of the OAuth flow:

1. The client application redirects the user to the authorization server, along with its client ID, client secret, redirect URI, and requested scopes.

2. The user authenticates with the authorization server and grants permission for the client application to access their resources.

3. The authorization server generates an authorization code and redirects the user back to the client application's redirect URI.

4. The client application sends the authorization code, along with its client ID and client secret, to the authorization server to obtain an access token.

5. The authorization server validates the authorization code and client credentials and issues an access token to the client application.

6. The client application can then use the access token to make authorized requests to the resource server on behalf of the user.

What is SAML and how does it relate to Single Sign-On?

Security Assertion Markup Language (SAML) is an XML-based open standard for exchanging authentication and authorization data between parties, particularly between an identity provider (IdP) and a service provider (SP). SAML is commonly used for implementing Single Sign-On (SSO) solutions.

In the context of SSO, SAML enables users to authenticate once with an identity provider and then access multiple applications or services without the need to provide their credentials again. SAML achieves this by allowing the identity provider to assert the user's identity to the service provider using digitally signed XML documents.

The SAML flow typically involves the following steps:

1. The user accesses a service provider and requests access to a protected resource.

2. The service provider redirects the user to the identity provider for authentication.

3. The identity provider authenticates the user and generates a SAML response containing the user's identity information.

4. The identity provider digitally signs the SAML response and sends it back to the service provider.

5. The service provider verifies the signature of the SAML response and extracts the user's identity information.

6. The service provider grants access to the requested resource based on the user's identity.

SAML provides a secure and decentralized approach to SSO, allowing organizations to leverage their existing identity management systems and provide a seamless user experience across multiple applications.

SSO Setup in Rails

Setting up Single Sign-On (SSO) in Rails with OAuth and SAML can be achieved using various libraries and gems. Here, we will explore two popular options: Devise for OAuth-based SSO and OneLogin for SAML-based SSO.

OAuth-based SSO with Devise

Devise is a flexible and widely used authentication solution for Rails applications. It provides out-of-the-box support for OAuth-based authentication through its omniauthable module.

To set up OAuth-based SSO with Devise, follow these steps:

1. Add the devise and omniauth gems to your Gemfile and run bundle install:

gem 'devise'
gem 'omniauth'

2. Configure Devise to use :omniauthable and specify the provider(s) you want to support. For example, to add support for GitHub OAuth:

# config/initializers/devise.rb
Devise.setup do |config|
  config.omniauth :github, 'CLIENT_ID', 'CLIENT_SECRET'
end

3. Add the necessary routes for authentication and callbacks:

# config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end

4. Create an OmniauthCallbacksController to handle the authentication callbacks:

# app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def github
    @user = User.from_omniauth(request.env['omniauth.auth'])
    sign_in_and_redirect @user
  end
end

5. Implement the from_omniauth method in your User model to handle the user creation or retrieval based on the OAuth response:

# app/models/user.rb
class User < ApplicationRecord
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0, 20]
    end
  end
end

With these configurations in place, users will be able to authenticate with your Rails application using OAuth providers such as GitHub. You can customize the SSO behavior by adding additional logic to the OmniauthCallbacksController or modifying the from_omniauth method.

SAML-based SSO with OneLogin

OneLogin is a popular identity and access management platform that provides a comprehensive solution for SAML-based SSO. It offers a Ruby gem called ruby-saml that simplifies the integration of SAML authentication in Rails applications.

To set up SAML-based SSO with OneLogin in Rails, follow these steps:

1. Add the ruby-saml gem to your Gemfile and run bundle install:

gem 'ruby-saml'

2. Generate a SAML configuration file using the OneLogin command-line tool:

onelogin saml --init

This will generate a onelogin.saml.yml file with the necessary SAML configuration.

3. Configure your Rails application to load the SAML configuration:

# config/initializers/onelogin_saml.rb
OneLogin::RubySaml::Settings.load('onelogin.saml.yml')

4. Implement the necessary routes and controllers to handle SAML authentication:

# config/routes.rb
Rails.application.routes.draw do
  post '/saml/auth' => 'saml#auth'
  post '/saml/logout' => 'saml#logout'
end

# app/controllers/saml_controller.rb
class SamlController < ApplicationController
  def auth
    request = OneLogin::RubySaml::Authrequest.new
    redirect_to(request.create)
  end

  def logout
    # Implement the logout logic here
  end
end

5. Create a view for the SAML authentication form:

<!-- app/views/saml/auth.html.erb -->
<%= form_tag('/saml/auth', method: :post) do %>
  <%= hidden_field_tag :SAMLRequest, generate_saml_request %>
  <%= submit_tag 'Sign in with SAML' %>
<% end %>

6. Implement the generate_saml_request method to generate the SAML request:

# app/controllers/saml_controller.rb
class SamlController < ApplicationController
  def generate_saml_request
    auth = OneLogin::RubySaml::Authrequest.new
    auth.create_params
  end
end

With these configurations in place, users will be able to authenticate with your Rails application using SAML-based SSO through OneLogin. You can customize the SSO behavior by adding additional logic to the SamlController or modifying the SAML configuration.

Related Article: Ruby on Rails with Internationalization and Character Encoding

API Versioning in Rails

API versioning is an essential aspect of developing enterprise applications in Rails. It allows you to introduce changes to your API without breaking existing clients and provides a way to manage backward compatibility.

There are several approaches to API versioning in Rails, including URL-based versioning, request header-based versioning, and namespace-based versioning. In this section, we will focus on URL-based versioning and namespace-based versioning.

URL-based Versioning

URL-based versioning involves including the version number in the URL of the API endpoint. This approach creates a clear separation between different versions of the API and allows clients to specify the version they want to use explicitly.

To implement URL-based versioning in Rails, follow these steps:

1. Define a route for each version of the API in the config/routes.rb file:

# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users
    end

    namespace :v2 do
      resources :users
    end
  end
end

2. Create corresponding controllers and namespaces for each version of the API. For example, for the v1 version:

# app/controllers/api/v1/users_controller.rb
module Api
  module V1
    class UsersController < ApplicationController
      def index
        # Implementation for v1
      end
    end
  end
end

3. Repeat the steps above for each version of the API, making sure to implement any necessary changes or updates in the respective controllers.

With URL-based versioning, clients can access different versions of the API by specifying the version number in the URL, such as /api/v1/users or /api/v2/users.

Namespace-based Versioning

Namespace-based versioning involves using different namespaces for each version of the API. This approach provides a cleaner and more organized structure for managing multiple versions of the API.

To implement namespace-based versioning in Rails, follow these steps:

1. Define a namespace for each version of the API in the config/routes.rb file:

# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users
    end

    namespace :v2 do
      resources :users
    end
  end
end

2. Create corresponding controllers and namespaces for each version of the API. For example, for the v1 version:

# app/controllers/api/v1/users_controller.rb
module Api
  module V1
    class UsersController < ApplicationController
      def index
        # Implementation for v1
      end
    end
  end
end

3. Repeat the steps above for each version of the API, making sure to implement any necessary changes or updates in the respective controllers.

With namespace-based versioning, clients can access different versions of the API by navigating to the respective namespace, such as /api/v1/users or /api/v2/users.

Audit Trails in Rails

Audit trails, also known as activity logs or change logs, are essential for tracking and auditing changes made to data in enterprise applications. They provide a historical record of all modifications, helping to ensure data integrity, compliance with regulations, and accountability.

In Rails, there are several approaches to implementing audit trails, including using gems like paper_trail or building a custom solution. In this section, we will explore the use of the paper_trail gem to add audit trail functionality to a Rails application.

Using the paper_trail gem

The paper_trail gem is a popular choice for implementing audit trails in Rails applications. It provides a simple and flexible solution for tracking changes to ActiveRecord models.

To add audit trail functionality to a Rails application using the paper_trail gem, follow these steps:

1. Add the paper_trail gem to your Gemfile and run bundle install:

gem 'paper_trail'

2. Generate a migration to add the necessary columns to the tables you want to track changes for:

rails generate paper_trail:install
rails db:migrate

3. Configure the models you want to track changes for by adding the has_paper_trail method:

class User < ApplicationRecord
  has_paper_trail
end

4. (Optional) Customize the paper_trail configuration by creating an initializer file:

# config/initializers/paper_trail.rb
PaperTrail.config.track_associations = true

5. Start tracking changes to the models by saving instances of the models:

user = User.find(1)
user.update(name: 'John Doe')

6. Retrieve the audit trail history for a model instance:

user = User.find(1)
versions = user.versions

7. Access the details of a specific version:

version = versions.last
version.changeset

The paper_trail gem provides a range of additional features, such as restoring previous versions, tracking associated models, and more. Refer to the gem's documentation for more information on advanced usage and customization options.

Documentation in Rails for APIs

Documentation is crucial for maintaining and sharing knowledge about APIs in enterprise applications. It helps developers understand how to use the API, provides examples, and ensures consistency in the API design and usage.

In Rails, there are several tools and approaches for documenting APIs, including using API Blueprint, RSpec, or Swagger. In this section, we will focus on documenting APIs using RSpec and the rspec_api_documentation gem.

Using RSpec and rspec_api_documentation

RSpec is a popular testing framework for Ruby applications, and it can be leveraged to generate API documentation using the rspec_api_documentation gem. This approach allows you to write tests that serve as documentation, ensuring that the documentation stays up-to-date with the API implementation.

To document your API using RSpec and the rspec_api_documentation gem, follow these steps:

1. Add the rspec_api_documentation gem to your Gemfile and run bundle install:

gem 'rspec_api_documentation'

2. Generate a configuration file for the documentation:

rails generate rspec_api_documentation:install

3. Create a new RSpec example group for your API documentation:

# spec/acceptance/api/v1/users_spec.rb
require 'rails_helper'
require 'rspec_api_documentation/dsl'

resource 'Users' do
  explanation 'API endpoints for managing users'

  header 'Content-Type', 'application/json'

  get '/api/v1/users' do
    example 'Listing users' do
      do_request
      expect(status).to eq(200)
    end
  end
end

4. Generate the API documentation based on the RSpec examples:

bundle exec rake docs:generate

5. View the generated documentation by starting a local server:

bundle exec rake docs:serve

With these configurations in place, you can write RSpec examples that serve as API documentation. The rspec_api_documentation gem generates HTML documentation based on these examples, allowing you to keep your API documentation in sync with your tests.

Authentication Best Practices in Rails

Authentication is a critical aspect of enterprise applications to ensure that only authorized users can access sensitive data and perform privileged actions. Rails provides several features and best practices for implementing authentication securely.

In this section, we will cover some best practices for implementing authentication in Rails:

1. Use strong and unique passwords: Encourage users to choose strong and unique passwords by enforcing password complexity requirements and providing guidance on creating strong passwords.

2. Hash passwords securely: Store user passwords securely by using a strong password hashing algorithm like bcrypt. Rails provides the has_secure_password method to handle password hashing and salting automatically.

3. Implement account lockout mechanisms: Protect against brute-force attacks by implementing account lockout mechanisms. For example, lock an account after a certain number of failed login attempts and require a password reset to unlock the account.

4. Use secure session management: Rails provides secure session management out of the box. Ensure that session cookies are secure by setting appropriate options, such as secure: true and httponly: true, to prevent session hijacking and cross-site scripting (XSS) attacks.

5. Implement multi-factor authentication (MFA): Consider implementing MFA to add an extra layer of security to the authentication process. Rails provides libraries and gems like devise-two-factor that simplify the integration of MFA.

6. Protect against session fixation attacks: Prevent session fixation attacks by generating a new session ID upon successful authentication. Rails does this automatically, but ensure that you are using the latest version of Rails to take advantage of any security enhancements.

7. Regularly update and patch dependencies: Keep your Rails application and its dependencies up to date by regularly applying security patches and updates. Vulnerabilities in third-party libraries can compromise the security of your authentication system.

8. Implement secure password reset functionality: Protect against unauthorized password resets by implementing secure password reset functionality. Rails provides ActionMailer for sending password reset emails securely.

Related Article: Ruby on Rails with Bootstrap, Elasticsearch and Databases

Authorization in Rails for Enterprise Applications

Authorization is the process of determining whether a user has the necessary permissions to perform specific actions within an application. Rails provides various mechanisms and best practices for implementing authorization in enterprise applications.

In this section, we will explore some best practices for implementing authorization in Rails:

1. Use role-based access control (RBAC): RBAC is a widely-used authorization model that assigns roles to users and defines the permissions associated with each role. Rails provides gems like cancancan and pundit that simplify the implementation of RBAC.

2. Implement fine-grained access control: In addition to RBAC, consider implementing fine-grained access control to enforce specific permissions at the individual action or attribute level. This can be achieved using conditional logic in controllers or models.

3. Leverage policies or rules: Use policies or rules to encapsulate authorization logic and separate it from the controllers. This promotes code reusability and simplifies the management of complex authorization rules. Gems like pundit provide a convenient way to define policies.

4. Implement attribute-level access control: In some cases, it may be necessary to restrict access to specific attributes or fields within a resource. Implement attribute-level access control to ensure that users can only access or modify the attributes they are authorized to.

5. Implement ownership-based authorization: For resources that are associated with specific users or groups, implement ownership-based authorization to ensure that users can only access or modify their own resources. This can be achieved by comparing the resource's owner with the current user's ID.

6. Handle authorization errors gracefully: When a user attempts to perform an unauthorized action, handle the authorization error gracefully by displaying an appropriate error message or redirecting them to a relevant page. This helps to improve the user experience and maintain security.

7. Regularly review and update authorization rules: As the application evolves and new features are added, periodically review and update the authorization rules to ensure that they align with the current requirements and business logic.

Additional Resources



- Ruby on Rails Guides

You May Also Like

Ruby on Rails Performance Tuning and Optimization

This article provides a detailed examination of Ruby on Rails performance tuning and optimization. It covers a range of topics including ActiveRecord… read more

Ruby on Rails with Modular Rails, ActiveRecord & Testing

This article covers advanced practices in Ruby on Rails. It explores topics such as building modular apps, managing databases with ActiveRecord, and … read more

Ruby on Rails with WebSockets & Real-time Features

This tutorial delves into the use of ActionCable in building real-time features in Rails, such as chat and notifications. The article explores the ro… read more