Integrating with Ruby on Rails API
This guide explains how to implement JWT authentication in your Ruby on Rails API using AuthAction’s JWKS (JSON Web Key Set) endpoint.
Example Repository: For a complete working example, check out our example repository.
Prerequisites
Section titled “Prerequisites”Before you begin, ensure you have:
- Ruby 3.2+ and Bundler: Install via rbenv or RVM
- AuthAction Account: You’ll need your AuthAction tenant domain and API identifier
Configuration
Section titled “Configuration”1. Add Required Gems
Section titled “1. Add Required Gems”Add to your Gemfile:
gem "rails", "~> 7.2"gem "jwt", "~> 2.9"gem "dotenv-rails", groups: [:development, :test]Then run:
bundle install2. Configure AuthAction Settings
Section titled “2. Configure AuthAction Settings”Create a .env file in your project root:
AUTHACTION_DOMAIN=your-authaction-tenant-domainAUTHACTION_AUDIENCE=your-authaction-api-identifierSECRET_KEY_BASE=$(rails secret)3. Implement JWT Validation
Section titled “3. Implement JWT Validation”Create lib/jwt_validator.rb:
require "jwt"require "net/http"require "json"
class JwtValidator DOMAIN = ENV["AUTHACTION_DOMAIN"] AUDIENCE = ENV["AUTHACTION_AUDIENCE"] ISSUER = "https://#{DOMAIN}" JWKS_URI = "https://#{DOMAIN}/.well-known/jwks.json"
CACHE_KEY = "authaction_jwks" CACHE_TTL = 1.hour
def self.verify(token) payload, _header = JWT.decode( token, nil, true, algorithms: ["RS256"], iss: ISSUER, verify_iss: true, aud: AUDIENCE, verify_aud: true, jwks: jwks_loader ) payload end
def self.jwks_loader # The jwt gem calls this lambda with kid_not_found: true when the signing # key is absent from the cached set, enabling a single cache-bust on key rotation. lambda do |options| Rails.cache.delete(CACHE_KEY) if options[:kid_not_found] Rails.cache.fetch(CACHE_KEY, expires_in: CACHE_TTL) do response = Net::HTTP.get(URI(JWKS_URI)) JSON.parse(response, symbolize_names: true) end end endend4. Add an Auth Concern
Section titled “4. Add an Auth Concern”Create app/controllers/concerns/jwt_authenticatable.rb:
require "jwt_validator"
module JwtAuthenticatable extend ActiveSupport::Concern
private
def authenticate_request! token = extract_bearer_token @current_payload = JwtValidator.verify(token) rescue JWT::ExpiredSignature render json: { error: "Token has expired" }, status: :unauthorized rescue JWT::DecodeError => e render json: { error: e.message }, status: :unauthorized end
def extract_bearer_token header = request.headers["Authorization"] raise JWT::DecodeError, "Missing token" unless header&.start_with?("Bearer ") header.split(" ", 2).last.strip endendInclude the concern in ApplicationController:
class ApplicationController < ActionController::API include JwtAuthenticatableend1. Create Your Controller
Section titled “1. Create Your Controller”class MessagesController < ApplicationController before_action :authenticate_request!, only: [:protected_message]
def public_message render json: { message: "This is a public message!" } end
def protected_message render json: { message: "This is a protected message!", sub: @current_payload["sub"] } endend2. Define Routes
Section titled “2. Define Routes”In config/routes.rb:
Rails.application.routes.draw do get "/public", to: "messages#public_message" get "/protected", to: "messages#protected_message"end3. Start the Server
Section titled “3. Start the Server”rails serverThe API will be available at http://localhost:3000.
4. Testing the API
Section titled “4. Testing the API”- Obtain an Access Token:
curl --request POST \ --url https://your-authaction-tenant-domain/oauth2/m2m/token \ --header 'content-type: application/json' \ --data '{ "client_id": "your-authaction-m2m-app-clientid", "client_secret": "your-authaction-m2m-app-client-secret", "audience": "your-authaction-api-identifier", "grant_type": "client_credentials" }'- Call the Public Endpoint:
curl http://localhost:3000/public- Call the Protected Endpoint:
curl --request GET \ --url http://localhost:3000/protected \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'Common Issues
Section titled “Common Issues”Invalid Token Errors
Section titled “Invalid Token Errors”- Verify
AUTHACTION_DOMAINandAUTHACTION_AUDIENCEmatch your AuthAction dashboard exactly - Ensure the token is signed with the RS256 algorithm
Public Key Fetching Errors
Section titled “Public Key Fetching Errors”- Verify your application can reach
https://your-authaction-tenant-domain/.well-known/jwks.json
Unauthorized Access
Section titled “Unauthorized Access”- Ensure the
Authorization: Bearer <token>header is present and the token is not expired - Confirm the token’s audience matches your API identifier