NulogySSO

Gem

Auth0

For more information on Auth0 see the Auth0 documentation.

Installation

This gem is a Rails Engine. It follows best practices documented here.

To begin with, add the gem to your Gemfile:

gem "nulogy_sso"

Install the gem:

bundle

Routes can now be mounted into your application in routes.rb and served up at a specific URI prefix:

mount NulogySSO::Engine, at: "/sso"

# Optional redirects
get "login", to: redirect("sso/login")
get "logout", to: redirect("sso/logout")

The engine now needs to be configured. First create a YAML config file, perhaps named config/auth_sso.yml, to configure your app's Auth0 settings. This assumes that the necessary Auth0 applications have been created in the correct Auth0 tenants. An example configuration for local development would look like:

default: &default
  audience: <%= ENV.fetch("SSO_AUDIENCE", "auth.nulogy.net") %>
  client_id: <%= ENV.fetch("SSO_CLIENT_ID", "SSO CLIENT ID FROM AUTH0") %>
  client_secret: <%= ENV.fetch("SSO_CLIENT_SECRET", "SSO CLIENT SECRET FROM AUTH0") %>
  base_uri: <%= ENV.fetch("SSO_BASE_URI", "") %>
  cookie_prefix: <%= ENV.fetch("SSO_COOKIE_PREFIX", "") %>
  login_uri: <%= ENV.fetch("SSO_LOGIN_URI", "") %>
  redirect_uri: <%= ENV.fetch("SSO_REDIRECT_URI", "") %>

development:
  <<: *default
  base_uri: <%= ENV.fetch("SSO_BASE_URI", "https://auth-dev.nulogy.net") %>
  cookie_prefix: <%= ENV.fetch("SSO_COOKIE_PREFIX", "dev") %>
  login_uri: <%= ENV.fetch("SSO_LOGIN_URI", "http://localhost:3000/sso/verify_authentication_code") %>
  redirect_uri: <%= ENV.fetch("SSO_REDIRECT_URI", "http://localhost:3000") %>

test:
  <<: *default

production:
  <<: *default

With that available, you can configure the engine with an initializer file. This is where NulogySSO can be customized according to your application's needs. Put the below code into config/initializers/nulogy_sso.rb, with the appropriate modifications implemented. For sso_config, refer to nulogy_sso.rb for a list of required keys and sso_config.yml for an example config file.

# Compiles config/auth_sso.yml into a Ruby object. An error is thrown if required keys are missing.
# See lib/nulogy_sso.rb for required keys.
NulogySSO.sso_config = Rails::Application.config_for(:sso)

# Return the user matching the provided email, or nil if not found.
NulogySSO.find_user_by_email = ->(email) { nil } 

# Handle errors from the SSO authentication flow, according to the app's design.
# This includes internal errors from the Auth0 gem (ie, token signature mismatch) and no user from the app DB matching the email from the JWT.
# The controller is passed as an argument to give the handler access to the "controller context", which is useful for tasks such as rendering a view.
NulogySSO.handle_sso_error = ->(controller: , error:) { }

# Handle how unauthenticated requests should be responded to. The default is to redirect to the defined login page.
# For API based applications this should be overriden to provide a meaningful error as per your API's contract (i.e. an HTTP 401 error code)
# Additionally, this could be overriden if an application should bypass SSO when running tests. 
NulogySSO.handle_unauthenticated_request = ->(controller) { controller.redirect_to sso_engine. } 

The app is now ready to authenticate a user with Auth0! With NulogySSO and Auth0, the user's identity is maintained across requests (and apps!) via a JWT stored as a browser cookie. Add this code to the ApplicationController:

class ApplicationController < ActionController::Base
  include NulogySSO::ControllerHelper
  before_action :authenticate_sso_user
  # ...
end

As an added bonus, NulogySSO also emulates the common Devise pattern of making the current User's user model object available via current_user. This is made available through including ControllerHelper.

Development

Setup

# Setup Ruby
rvm install $(cat .ruby-version)
bundle

# Setup project files
cp .env.sample .env
rake app:db:test:prepare

# Launch Docker containers, required for testing
docker-compose up -d

Testing

There are multiple helpers made available via the NulogySSO::TestUtilities module. These are helpful for doing things such as grabbing test JWT values and interacting with a Mockserver mock of the Auth0 API.

It is a common use case for a Rails app to switch from Devise-powered authentication to Auth0. Here's a pattern that could be applied around a feature flag (e.g. environment variable) to switch between Devise user authentication test helpers and NulogySSO test helpers: (TODO: insert link to CPI ControllerIntegrationSpecMacros)

Contributing

Feel free to create a PR if you wish to add new functionality to this Engine or detect a bug. A developer on CN1 will review and merge.

This project follows Semver Versioning. Please add an entry into CHANGELOG.md in your PR. Deployments are done manually on an as-needed basis.

Roadmap

  • Parameterize app name for error page
  • Buildkite pipeline
  • Rubocop
  • Rake install task (ie, generate required files automatically, instead of requiring a heavy amount of manual work to integrate nulogy_sso into a Rails app)
  • Add additional hooks, as necessitated by unique use cases for new apps using this engine