NulogySSO

Gem

NOTE: The default branch for this repository was changed to main. Please read this Github blog post for more information.

Auth0

For more information on Auth0 see the Auth0 documentation.

Authentication Flow

The Auth0 docs can be confusing, so we provide a more detailed picture of the Authentication Flow implemented by this gem.

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

Tests must be run manually before submitting a PR. This can be done using rspec spec.

Please consult this documentation for advice on how to implement tests on an application using nulogy_sso.

Deployment

  1. Ensure that all tests are passing on the project
  2. Increment the library version in version.rb, adhering to semver principles
  3. Add a entry to the Changelog. Move all entries in the Unreleased section into the new version's section. Now is also a good chance to add in any addition bullet points that may have been forgotten in the Unreleased section. Append today's date to the new version.
  4. Run these commands to complete the deployment. Rubygem push rights on this gem is required. Obviously, replace X.Y.Z with the version being deployed.

    # Commit changes to the files listed above
    git commit -m "Cuts version X.Y.Z"
    git push
    
    # Push the gem to Rubygems
    gem build nulogy_sso.gemspec
    gem push nulogy_sso-X.Y.Z.gem
    
    # Cut a release on GitHub
    git tag vX.Y.Z
    git push --tags
    

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.