fusionauth logo
search-interface-symbol
Downloads
Quickstarts
API Docs
SDK
search-interface-symbol
talk to an expert
Log In
talk to an expert
Navigate to...
  • Welcome
  • Getting Started
    • Getting Started
    • 5-minute Setup Guide
      • Overview
      • Docker
      • Fast Path
      • Sandbox
    • Setup Wizard & First Login
    • Register a User and Login
    • Self-service Registration
    • Start and Stop FusionAuth
    • Core Concepts
      • Overview
      • Users
      • Roles
      • Groups
      • Registrations
      • Applications
      • Tenants
      • Identity Providers
      • Authentication/Authorization
      • Integration Points
    • Example Apps
      • Overview
      • Dart
      • Go
      • Java
      • JavaScript
      • .NET Core
      • PHP
      • Python
      • Ruby
    • Tutorials
      • Overview
      • Express.js
      • Java Spring
      • Python Django
      • Python Flask
      • React
      • Ruby on Rails
      • Ruby on Rails API
  • Installation Guide
    • Overview
    • System Requirements
    • Server Layout
    • Cloud
    • Cluster
    • Docker
    • Fast Path
    • Kubernetes
      • Overview
      • Deployment Guide
      • Minikube Setup
      • Amazon EKS Setup
      • Google GKE Setup
      • Microsoft AKS Setup
    • Kickstart™
    • Homebrew
    • Marketplaces
    • Packages
    • Database
    • FusionAuth App
    • FusionAuth Search
    • Common Configuration
  • Migration Guide
    • Overview
    • General
    • Auth0
    • Keycloak
    • Amazon Cognito
    • Firebase
    • Microsoft Azure AD B2C
    • Tutorial
  • Admin Guide
    • Overview
    • Account Portal
    • Config Management
    • Editions and Features
    • Key Rotation
    • Licensing
    • Monitoring
    • Prometheus Setup
    • Proxy Setup
    • Reference
      • Overview
      • Configuration
      • CORS
      • Data Types
      • Hosted Login Pages Cookies
      • Known Limitations
      • Password Hashes
    • Releases
    • Roadmap
    • Search And FusionAuth
    • Securing
    • Switch Search Engines
    • Technical Support
    • Troubleshooting
    • Upgrading
    • WebAuthn
  • Login Methods
    • Identity Providers
      • Overview
      • Apple
      • Epic Games
      • External JWT
        • Overview
        • Example
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
        • Overview
        • Amazon Cognito
        • Azure AD
        • Discord
        • Github
        • Okta
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • SAML v2
        • Overview
        • ADFS
        • Azure AD
        • Okta
      • SAML v2 IdP Initiated
        • Overview
        • Okta
      • Xbox
    • OIDC & OAuth 2.0
      • Overview
      • Endpoints
      • Tokens
      • OAuth Modes
      • URL Validation
      • Integrations
        • CockroachDB
        • Salesforce
    • Passwordless
      • Overview
      • Magic Links
      • WebAuthn & Passkeys
    • SAML v2 IdP
      • Overview
      • Google
      • PagerDuty
      • SendGrid
      • Tableau Cloud
      • Zendesk
  • Developer Guide
    • Overview
    • API Gateways
      • Overview
      • Amazon API Gateway
      • Kong Gateway
      • ngrok Cloud Edge
    • Client Libraries & SDKs
      • Overview
      • Dart
      • Go
      • Java
      • JavaScript
      • .NET Core
      • Node
      • OpenAPI
      • PHP
      • Python
      • React
      • Ruby
      • Typescript
    • Events & Webhooks
      • Overview
      • Writing a Webhook
      • Securing Webhooks
      • Events
        • Overview
        • Audit Log Create
        • Event Log Create
        • JWT Public Key Update
        • JWT Refresh
        • JWT Refresh Token Revoke
        • Kickstart Success
        • Group Create
        • Group Create Complete
        • Group Delete
        • Group Delete Complete
        • Group Update
        • Group Update Complete
        • Group Member Add
        • Group Member Add Complete
        • Group Member Remove
        • Group Member Remove Complete
        • Group Member Update
        • Group Member Update Complete
        • User Action
        • User Bulk Create
        • User Create
        • User Create Complete
        • User Deactivate
        • User Delete
        • User Delete Complete
        • User Email Update
        • User Email Verified
        • User IdP Link
        • User IdP Unlink
        • User Login Failed
        • User Login Id Dup. Create
        • User Login Id Dup. Update
        • User Login New Device
        • User Login Success
        • User Login Suspicious
        • User Password Breach
        • User Password Reset Send
        • User Password Reset Start
        • User Password Reset Success
        • User Password Update
        • User Reactivate
        • User Reg. Create
        • User Reg. Create Complete
        • User Reg. Delete
        • User Reg. Delete Complete
        • User Registration Update
        • User Reg. Update Complete
        • User Reg. Verified
        • User 2FA Method Add
        • User 2FA Method Remove
        • User Update
        • User Update Complete
    • Guides
      • Overview
      • Application Specific Email Templates
      • Authentication Tokens
      • Exposing A Local Instance
      • JSON Web Tokens
      • Key Master
      • Localization and Internationalization
      • Multi-Factor Authentication
      • Multi-Tenant
      • Passwordless
      • Registration-based Email Verification
      • Searching With Elasticsearch
      • Securing Your APIs
      • Silent Mode
      • Single Sign-on
      • Two Factor (pre 1.26)
    • Integrations
      • Overview
      • CleanSpeak
      • Kafka
      • Twilio
    • Plugins
      • Overview
      • Writing a Plugin
      • Custom Password Hashing
    • User Control & Gating
      • Overview
      • Gate Unverified Users
      • Gate Unverified Registrations
      • User Account Lockout
  • Customization
    • Email & Templates
      • Overview
      • Configure Email
      • Email Templates
      • Email Variables
      • Message Templates
    • Lambdas
      • Overview
      • Apple Reconcile
      • Client Cred. JWT Populate
      • Epic Games Reconcile
      • External JWT Reconcile
      • Facebook Reconcile
      • Google Reconcile
      • HYPR Reconcile
      • JWT Populate
      • LDAP Connector Reconcile
      • LinkedIn Reconcile
      • Nintendo Reconcile
      • OpenID Connect Reconcile
      • SAML v2 Populate
      • SAML v2 Reconcile
      • SCIM Group Req. Converter
      • SCIM Group Resp. Convtr.
      • SCIM User Req. Converter
      • SCIM User Resp. Converter
      • Self-Service Registration
      • Sony PSN Reconcile
      • Steam Reconcile
      • Twitch Reconcile
      • Twitter Reconcile
      • Xbox Reconcile
    • Messengers
      • Overview
      • Generic Messenger
      • Twilio Messenger
    • Themes
      • Overview
      • Examples
      • Helpers
      • Localization
      • Template Variables
      • Kickstart Custom Theme
  • Premium Features
    • Overview
    • Advanced Registration Forms
    • Advanced Threat Detection
    • Application Specific Themes
    • Breached Password Detection
    • Connectors
      • Overview
      • Generic Connector
      • LDAP Connector
      • FusionAuth Connector
    • Entity Management
    • SCIM
      • Overview
      • Azure AD Client
      • Okta Client
      • SCIM-SDK
    • Self Service Account Mgmt
      • Overview
      • Updating User Data & Password
      • Add Two-Factor Authenticator
      • Add Two-Factor Email
      • Add Two-Factor SMS
      • Add WebAuthn Passkey
      • Customizing
      • Bootstrapping Login
      • Troubleshooting
    • WebAuthn
  • APIs
    • Overview
    • Authentication
    • Errors
    • API Explorer
    • Actioning Users
    • API Keys
    • Applications
    • Audit Logs
    • Connectors
      • Overview
      • Generic
      • LDAP
    • Consents
    • Emails
    • Entity Management
      • Overview
      • Entities
      • Entity Types
      • Grants
    • Event Logs
    • Families
    • Forms
    • Form Fields
    • Groups
    • Hosted Backend
    • Identity Providers
      • Overview
      • Links
      • Apple
      • External JWT
      • Epic Games
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
      • SAML v2
      • SAML v2 IdP Initiated
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • Xbox
    • Integrations
    • IP Access Control Lists
    • JWT
    • Keys
    • Lambdas
    • Login
    • Message Templates
    • Messengers
      • Overview
      • Generic
      • Twilio
    • Multi-Factor/Two Factor
    • Passwordless
    • Reactor
    • Registrations
    • Reports
    • SCIM
      • Overview
      • SCIM User
      • SCIM Group
      • SCIM EnterpriseUser
      • SCIM Service Provider Config.
    • System
    • Tenants
    • Themes
    • Users
    • User Actions
    • User Action Reasons
    • User Comments
    • WebAuthn
    • Webhooks
  • Release Notes

    Integrate Your Ruby on Rails API With FusionAuth

    Integrate Your Ruby on Rails API With FusionAuth

    In this article, you are going to learn how to integrate a Ruby on Rails API with FusionAuth. This presupposes you’ve built an application that is going to retrieve an access token from FusionAuth via one of the OAuth grants. The grant will typically be the Authorization Code grant for users or the Client Credentials grant for programmatic access.

    The token provided by FusionAuth can be stored by the client in a number of locations. For server side applications, it can be stored in a database or on the file system. In mobile applications, store them securely as files accessible only to your app. For a browser application like a SPA, use a cookie if possible and server-side sessions if not.

    Here’s a typical API request flow before integrating FusionAuth with your Ruby on Rails API.

    API access before FusionAuth.
    API access before FusionAuth.

    Here’s the same API request flow when FusionAuth is introduced.

    API access with FusionAuth.
    API access with FusionAuth.

    This document will walk through the use case where a Ruby on Rails API validates the token. You can also use an API gateway to verify claims and signatures. For more information on doing that with FusionAuth, visit the API gateway documentation.

    Prerequisites

    For this tutorial, you’ll need to have Ruby, bundler and Rails installed.

    You’ll also need Docker, since that is how you’ll install FusionAuth.

    The commands below are for macOS, but are limited to mkdir and cd, which have equivalents in Windows and Linux.

    Download and Install FusionAuth

    First, make a project directory:

    mkdir integrate-fusionauth && cd integrate-fusionauth

    Then, install FusionAuth:

    curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.yml curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/.env docker-compose up -d

    Create a User and an API Key

    Next, log into your FusionAuth instance. You’ll need to set up a user and a password, as well as accept the terms and conditions.

    Then, you’re at the FusionAuth admin UI. This lets you configure FusionAuth manually. But for this tutorial, you’re going to create an API key and then you’ll configure FusionAuth using our client library.

    Navigate to Settings -> API Keys. Click the + button to add a new API Key. Copy the value of the Key field and then save the key. It might be a value like CY1EUq2oAQrCgE7azl3A2xwG-OEwGPqLryDRBCoz-13IqyFYMn1_Udjt.

    Doing so creates an API key that can be used for any FusionAuth API call. Save that key value off as you’ll be using it later.

    Configure FusionAuth

    Next, you need to set up FusionAuth. This can be done in different ways, but we’re going to use the Ruby client library. You can use the client library with an IDE of your preference as well.

    First, make a directory:

    mkdir setup-fusionauth && cd setup-fusionauth

    Then, create the required files:

    touch Gemfile

    Now, copy and paste the following file into Gemfile.

    source 'https://rubygems.org' gem "fusionauth_client"

    Install the gems.

    bundle install

    Create a file called setup.rb. Then copy and paste the following code into it.

    require 'fusionauth/fusionauth_client' APPLICATION_ID = "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e"; RSA_KEY_ID = "356a6624-b33c-471a-b707-48bbfcfbc593" # You must supply your API key as an envt var api_key_name = 'fusionauth_api_key' api_key = ENV[api_key_name] unless api_key puts "please set api key in the '" + api_key_name.to_s + "' environment variable" exit 1 end client = FusionAuth::FusionAuthClient.new(api_key, 'http://localhost:9011') # set the issuer up correctly client_response = client.retrieve_tenants() if client_response.was_successful tenant = client_response.success_response["tenants"][0] else puts "couldn't find tenants " + client_response.error_response.to_s exit 1 end client_response = client.patch_tenant(tenant["id"], {"tenant": {"issuer":"http://localhost:9011"}}) unless client_response.was_successful puts "couldn't update tenant "+ client_response.error_response.to_s exit 1 end # generate RSA keypair for signing client_response = client.generate_key(RSA_KEY_ID, {"key": {"algorithm":"RS256", "name":"For RailsExampleApp", "length": 2048}}) unless client_response.was_successful puts "couldn't create RSA key "+ client_response.error_response.to_s exit 1 end # create application # too much to inline it application = {} application["name"] = "RubyExampleAPI" # configure oauth application["oauthConfiguration"] = {} application["oauthConfiguration"]["authorizedRedirectURLs"] = ["http://localhost:3000/auth/my_provider/callback"] application["oauthConfiguration"]["clientSecret"] = "change-this-in-production-to-be-a-real-secret" application["roles"] = ["ceo","dev","intern"] # assign key from above to sign our tokens. This needs to be asymmetric application["jwtConfiguration"] = {} application["jwtConfiguration"]["enabled"] = true application["jwtConfiguration"]["accessTokenKeyId"] = RSA_KEY_ID application["jwtConfiguration"]["idTokenKeyId"] = RSA_KEY_ID client_response = client.create_application(APPLICATION_ID, {"application": application}) unless client_response.was_successful puts "couldn't create application "+ client_response.error_response.to_s exit 1 end # register user, there should be only one, so grab the first client_response = client.search_users_by_query({"search": {"queryString":"*"}}) unless client_response.was_successful puts "couldn't find users "+ client_response.error_response.to_s exit 1 end user = client_response.success_response["users"][0] # now register the user client_response = client.register(user["id"], {"registration":{"applicationId":APPLICATION_ID}}) unless client_response.was_successful puts "couldn't register user "+ client_response.error_response.to_s exit 1 end

    Then, you can run the setup script.

    The setup script is designed to run on a newly installed FusionAuth instance with only one user and no tenants other than Default. To follow this guide on a FusionAuth instance that does not meet these criteria, you may need to modify the above script.

    Refer to the Ruby client library documentation for more information.

    This will create the FusionAuth configuration for your Ruby on Rails API.

    fusionauth_api_key=YOUR_API_KEY_FROM_ABOVE ruby setup.rb

    If you are using PowerShell, you will need to set the environment variable in a separate command before executing the script.

    $env:fusionauth_api_key='YOUR_API_KEY_FROM_ABOVE' ruby setup.rb

    If you want, you can log into your instance and examine the new API configuration the script created for you. You’d navigate to the Applications tab to do so.

    Create Your Ruby on Rails API

    Now you are going to create a Ruby on Rails API. While this section builds a simple Ruby on Rails API, you can use the same configuration to build a more complex Ruby on Rails API.

    First, create the skeleton of the Ruby on Rails API. Rails has a nice generator to build this out.

    rails new myapi --api && cd myapi

    Now, update your Gemfile to look like this:

    source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby "3.2.2" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "rails", "~> 7.0.4", ">= 7.0.4.3" # Use sqlite3 as the database for Active Record gem "sqlite3", "~> 1.4" # Use the Puma web server [https://github.com/puma/puma] gem "puma", "~> 5.0" # Build JSON APIs with ease [https://github.com/rails/jbuilder] # gem "jbuilder" # Use Redis adapter to run Action Cable in production # gem "redis", "~> 4.0" # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] # gem "kredis" # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] # gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", require: false # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem "rack-cors" gem 'rack-jwt', git: 'https://github.com/FusionAuth/rack-jwt' gem 'dotenv-rails' group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri mingw x64_mingw ] end group :development do # Speed up commands on slow machines / big apps [https://github.com/rails/spring] # gem "spring" end

    You may need to modify the version of ruby specified in the Gemfile. As long as Rails 7 is supported, you will be fine.

    Then, install these new gems.

    bundle install

    Next, create a file called .env.development and insert the following into it.

    # for rails FUSIONAUTH_LOCATION=http://localhost:9011 CLIENT_ID=e9fdb985-9173-4e01-9d73-ac2d60d1dc8e

    You can now start writing the code for your Rails API. First, let’s create a controller which gives back a JSON message. Create a new file called app/controllers/messages_controller.rb, then add the following code:

    class MessagesController < ApplicationController def index messages = [] messages << "Hello" # further claims/authorization checks roles = [] if request.env['jwt.payload'] && request.env['jwt.payload']['roles'] roles = request.env['jwt.payload']['roles'] end if roles.include?('ceo') messages << "Hiya, boss" end render json: { messages: messages }.to_json, status: :ok end end

    This controller returns a JSON array with messages.

    Next, update the config/routes.rb file to look like this:

    Rails.application.routes.draw do resources :messages, only: [:index] end

    This tells Ruby on Rails to return the content generated by the messages_controller.rb file when the /messages path is set up.

    You can now start up your server. You should do it in a new terminal window so that you can continue to edit the Ruby on Rails code.

    bundle e rails s -p 4001

    And visit http://localhost:4001/messages and view the JSON.

    Next, let’s configure the token protection for this API. Go back to your previous terminal window and create a config/initializers/jwt_rack.rb file, and update it to look like this:

    require 'net/http' require 'jwt' source = ENV['FUSIONAUTH_LOCATION'] + '/.well-known/jwks.json' resp = Net::HTTP.get_response(URI.parse(source)) data = resp.body jwks_hash = JSON.parse(data) jwks = JWT::JWK::Set.new(jwks_hash) jwks.select! { |key| key[:use] == 'sig' } # Signing Keys only jwt_auth_args = { secret: nil, options: { cookie_name: 'app.at', iss: ENV['FUSIONAUTH_LOCATION'], verify_iss: true, aud: ENV['CLIENT_ID'], verify_aud: true, verify_iat: true, verify_expiration: true, required_claims: ['applicationId'], jwks: jwks, algorithm: 'RS256' } } Rails.application.config.middleware.use Rack::JWT::Auth, jwt_auth_args

    This tells Ruby on Rails to check for various attributes of the token, including the iss and the aud. Read the rack_jwt gem documentation for more.

    You can also access the JWT in the controller. Below, the messages controller adds a special message if the user has a certain role.

    class MessagesController < ApplicationController def index messages = [] messages << "Hello" # further claims/authorization checks roles = [] if request.env['jwt.payload'] && request.env['jwt.payload']['roles'] roles = request.env['jwt.payload']['roles'] end if roles.include?('ceo') messages << "Hiya, boss" end render json: { messages: messages }.to_json, status: :ok end end

    Now, back to the terminal where your server is running. Stop it (using control-C) and restart it.

    bundle e rails s -p 4001

    Visit http://localhost:4001/messages, you’ll get an error:

    {"error":"Missing token cookie and Authorization header"}

    Your API is protected. Now, let’s get an access token so authorized clients can get the API results.

    Testing the API Flow

    There are a number of ways to get an access token, as mentioned, but for clarity, let’s use the login API to mimic a client.

    Run this command in a terminal:

    curl -H 'Authorization: YOUR_API_KEY_FROM_ABOVE' \ -H 'Content-type: application/json' \ -d '{"loginId": "YOUR_EMAIL", "password":"YOUR_PASSWORD","applicationId": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e"} \ http://localhost:9011/api/login

    Replace YOUR_EMAIL and YOUR_PASSWORD with the username and password you set up previously.

    This request will return something like this:

    { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo", "user": { "active": true, "birthDate": "1976-05-30", "connectorId": "e3306678-a53a-4964-9040-1c96f36dda72", "data": { "displayName": "Johnny Boy", "favoriteColors": [ "Red", "Blue" ] }, "email": "example@fusionauth.io", "expiry": 1571786483322, "firstName": "John", "fullName": "John Doe", "id": "00000000-0000-0001-0000-000000000000", "imageUrl": "http://65.media.tumblr.com/tumblr_l7dbl0MHbU1qz50x3o1_500.png", "lastLoginInstant": 1471786483322, "lastName": "Doe", "middleName": "William", "mobilePhone": "303-555-1234", "passwordChangeRequired": false, "passwordLastUpdateInstant": 1471786483322, "preferredLanguages": [ "en", "fr" ], "registrations": [ { "applicationId": "10000000-0000-0002-0000-000000000001", "data": { "displayName": "Johnny", "favoriteSports": [ "Football", "Basketball" ] }, "id": "00000000-0000-0002-0000-000000000000", "insertInstant": 1446064706250, "lastLoginInstant": 1456064601291, "preferredLanguages": [ "en", "fr" ], "roles": [ "user", "community_helper" ], "username": "johnny123", "usernameStatus": "ACTIVE" } ], "timezone": "America/Denver", "tenantId": "f24aca2b-ce4a-4dad-951a-c9d690e71415", "twoFactorEnabled": false, "usernameStatus": "ACTIVE", "username": "johnny123", "verified": true } }

    Grab the token field (which begins with ey). Replace YOUR_TOKEN below with that value, and run this command:

    curl --cookie 'app.at=YOUR_TOKEN' http://localhost:4001/messages

    Here you are placing the token in a cookie named app.at. This is for compatibility with the FusionAuth best practices and the hosted backend.

    If you want to store it in a different cookie or send it in the header, make sure you modify the rack_jwt initializer and restart the Ruby on Rails API.

    This will result in the JSON below.

    {"messages":["Hello"]}

    Feedback

    How helpful was this page?

    See a problem?

    File an issue in our docs repo

    Have a question or comment to share?

    Visit the FusionAuth community forum.

    © 2023 FusionAuth
    How-to
    Blog
    Expert Advice
    Download
    Release Notes
    Subscribe for developer updates