Skip to content

Instantly share code, notes, and snippets.

@mcat56
Last active June 18, 2020 14:21
Show Gist options
  • Save mcat56/2cc4903afed3d9054b9f5ef51150cdc5 to your computer and use it in GitHub Desktop.
Save mcat56/2cc4903afed3d9054b9f5ef51150cdc5 to your computer and use it in GitHub Desktop.

How to integrate FusionAuth using Sinatra

FusionAuth is a platform and framework agnositc modern CIAM tool to integrate authentication, authorization and user management into your applications. FusionAuth supports many applications in one easy to use browser interface. Features are exposed in API's that allow flexibility and customizable user experiences. In this tutorial we will demonstrate integrating FusionAuth with the light-weight Ruby framework Sinatra.

The best way to integrate with Sinatra is using the FusionAuth Ruby client library. The API will provide all of the necessary methods to create user access management.

This tutorial will illustrate creating a Sinatra application, JSCRUM - a Pied Piper Agile Management App by Jared, with FusionAuth that allows users to register, login, view and edit profile information, logout, and delete their accounts. Check out the example FusionAuth Sinatra Application here

Overview

  1. Installing and configuring FusionAuth
  2. Requiring FusionAuth in Sinatra
  3. Registering a user
  4. Log in a user
  5. Accessing and viewing profile information
  6. User updates their profile information
  7. Log out a user
  8. Delete a user
  9. Summary

What You Need

  • FusionAuth
  • Ruby
  • Bundler
  • Ruby development environment
  • Web browser

Installing and configuring FusionAuth

To begin you can follow the Setup Guide. For this tutorial there is no need to complete the OAuth settings. It does require creating an API key and an Application. The API key and Application ID will be necessary in the Sinatra application.

After you have created an application and API key you may continue.

Requiring FusionAuth in Sinatra

Assuming you have bundler installed all you need to do to include FusionAuth is add the gem to your Gemfile as such:

in Gemfile:

source 'https://rubygems.org'

#all other gems needed 
gem fusionauth_client

To install the gem simply run bundle in your command line.

To access FusionAuth in your Sinatra application file simply require it at the top:

in app.rb:

require 'fusionauth/fusionauth_client'
require 'sinatra'
require 'sinatra/flash'
require 'sinatra/json'
require 'sinatra/cookies'
require 'json'
require 'pry'
Tilt.register Tilt::ERBTemplate, 'html.erb'

class FusionAuthApp < Sinatra::Base
  helpers Sinatra::Cookies
  use Rack::Session::Cookie, :key => 'rack.session',
                             :path => '/',
                             :secret => ENV['SECRET']


  def fusionauth_client
    @client = FusionAuth::FusionAuthClient.new(
      'XyF5-fU3a-eeYMCx_PHaGAs18NMIGxRF4UPE1A8dA-U',
      'http://localhost:9011'
    )
  end

  def current_user
    @current_user ||= fusionauth_client.retrieve_user(session[:user_id]).success_response.user if session[:user_id]
  end

  def application_id
    '9eef04cd-b188-42fa-b535-1962ff788b46'
  end

The above code shows the necessary requirements in Sinatra. It also sets the application to allow html.erb extensions on views, and enable sessions and cookies. The FusionAuth client is defined in a method using the API key and the default address FusionAuth runs on locally (localhost:9011). The application ID will be needed for various API calls so is made available in a method within the class. Current user is helpful for displaying user information in views and in many methods throughout this tutorial.

Registering a User

When registering a user there are two options:

  1. Full Registration - Provide the User and UserRegistration object to create the user and register them for the application.
  2. Existing users may be registered by simply providing the UserRegistration object.

You may also provide a user id which allows FusionAuth to look up an existing user for registration or use that ID for the newly created User.

This tutorial will follow Full Registration

The following code shows the app.rb file in the Sinatra application:

  get '/register' do
    if current_user != nil
      erb :'/users/show'
    else
      erb :'/users/new'
    end
  end

  post '/register' do
    user_data = params[:user_data]
    id = SecureRandom.uuid

    response = fusionauth_client.register(id, {
      :user => {
        :firstName => user_data[:first_name],
        :lastName => user_data[:last_name],
        :email => user_data[:email],
        :password => user_data[:password]
      },
      :registration => {
        :applicationId => application_id,
        :preferredLanguages => %w(en fr),
        :roles => %w(user)
      }
    })
    if response.success_response
      session[:user_id] = id
      erb :'/users/show'
    else
      flash[:error] = 'Registration unsuccessful. Please try again.'
      erb :'/welcome/index'
    end
  end

The user can click the link on the home page to register. As long as no one is logged in already it will take the user to the registration form.

welcome page

Clicking the registration link takes the user to the registration form:

gilfoyle registration form

As seen in the code above, the register method on FusionAuth client takes in user data as passed in through the form. The Application ID is a necessary parameter for registration. Most others are optional. A random UUID is generated for safer indexing of the user. Remember to use the Registration API as a reference.

Upon successful registration:

gilfoyle success registration

With successful registration the user is directed to their profile page where a flash message shows registration was successful and we can see user information displayed on the page. An unsuccessful registration results in an error message and the user is taken back to the registration page.

After completing registration and going to the FusionAuth UI, we can see the new user was added under users.

fusionauth ui erlich added

Attempting to create a user with the same e-mail will result in an error. Using the provided Errors API will help debug issues such as duplicate email error:

error duplicate email

Log in a user

Logging in a user is a simple API call. The users loginID (their email or username) as well as the users password are required. All other parameters are optional though it is common use case to provide the Application ID.


  post '/login' do
    user_data = params[:user_data]
    response = fusionauth_client.login({
      :loginId => user_data[:email],
      :password => user_data[:password],
      :applicationId => application_id,
      })
    if response.success_response
      id = response.success_response.user.id
      session[:user_id] = id
      erb :'/users/show'
    else
      flash[:error] = "Unsuccessful login. Please try again."
      erb :'sessions/new'
    end
  end

Accessing and viewing profile information

If you wanted to see user information as pure JSON the Ruby client provides retrieve_user methods. You can retrieve user with email, username, verification ID, user ID, change password ID, or JWT. Reference: Retrieve User API

In this tutorial we will retrieve the user by user ID using our current_user method.

  get '/user', :provides => :json do
    response = fusionauth_client.retrieve_user(current_user.id)
    if response.success_response
      new_response = response.success_response.user.marshal_dump
      registration = new_response[:registrations][0].marshal_dump
      new_response[:registrations][0] = registration
      json new_response
    else
      flash[:error] = "Cannot find user information. Please try again."
    end
  end

By going to /user when the user is logged in we get a JSON response of the user data:

get user data json

Above you can see the user data has been retrieved and displayed in JSON format in the browser.

User updates their profile information

Let's say a user wants to update their profile, we should give them a way to do so! The FusionAuth Ruby client provides a patch_user method that will do exactly what we need. See the Update a User API here.

In the application within the user profile there is a link to update their profile.

update button

Clicking the link allows the user to update specified information in the user table. For this example we are simply allowing the user to update their name or e-mail.

update gilfoyle form

By submitting the form the updated information is passed into the patch method :

  get '/edit' do
    erb :'users/edit'
  end

  patch '/users/:id' do
    request = params[:user_data].select {|k,v| v != ''}
    patch_request = { user: request }
    response = fusionauth_client.patch_user(current_user.id, patch_request)
    if response.success_response
      flash[:success] = "Update successful!"
      erb :'/users/show'
    else
      flash[:error] = "Update unsuccessful. Please try again."
      erb :'/upate'
    end
  end

Here we pull in the new user information in parameters, and without having any requirements to make sure no fields are left empty, we instead filter which data we want to update by any data passed in that is not an empty string.

The patch request is formatted as a hash, and must have the user: key. The value of the user key is the hash of updated key value pairs obtained from the params.

Next we call the patch_user method on the FusionAuth client, with two arguments. The user ID must be provided, as well as the patch request. If we get a successful response, we are able to see the users updated profile and a success message. You may need to refresh the page to see the updated user information.

updated gilfoyle profile

Success! Gilfoyle's profile just got a little better.

Log out a user

Gilfoyle is a busy guy, so he can't stay logged in forever. We need to give him a way to get back to work and stay ahead of Dinesh. On the profile we have added a logout button.

logout button

Simply clicking the logout button will route to the method in the app.rb file that takes care of logging out the user. We use the FusionAuth logout.


  get '/logout' do
    fusionauth_client.logout(true, nil)
    session.clear
    cookies.clear
    flash[:notice] = "Logout Successful"
    erb  :'/welcome/index'
  end

The method takes two arguments, the first being the param global, which takes a boolean. Setting global to true revokes all refresh tokens attached to the current user. We have not enabled refresh tokens, but for the sake of example we are setting this value to true. The second argument is optional and can be used to provide a specific refresh token. If provided the argument token would take precedence over the refresh token coming in as a cookie.

Without using refresh_tokens we use the manual session.clear and cookies.clear to remove any tokens associated in the browser with the user.

Simply clicking the logout button logs the user out and returns them to the welcome page with a success message letting the user know they were logged out.

logout sucessful

Delete a user

Dinesh is really getting ahead, so to keep up Gilfoyle has decided to delete his account. We allow him to do so by providing a button on his profile.

delete button

In our app.rb file we user the FusionAuth delete to remove the user from our application.

  delete '/delete_account' do
    fusionauth_client.delete_user(current_user.id)
    flash[:notice] = "Account Successfully deleted"
    erb :'/welcome/index'
  end

Once deleted we see the success message and are returned to the welcome page.

account deleted

Summary

We have integrated FusionAuth with Sinatra and easily introduced user management to our application.

FusionAuth is not intended to replace domain management systems such as Active Directory and does not offer native desktop integration.

FusionAuth is useful when you are managing users among many applications and scales well. There is a multitude of untouched functionality in this tutorial such as e-mail, multi-factor authentication, and reporting. Best of all, the web console provides an easy to use interface to visualize your applications, users and more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment