---
title: "Add JWT Authentication to Your Ruby on Rails API"
description: "Secure your Ruby on Rails API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/ruby/rubyonrails/jwt-backend"
framework: "Ruby on Rails"
language: "Ruby"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Ruby on Rails API

Secure your Ruby on Rails API using JWT authentication with JWKS.

## How Zuplo Handles It

Let Zuplo issue short-lived JWTs signed with a JWKS your Ruby on Rails backend can verify — no long-lived API keys touch your origin.

## Ruby on Rails Backend Code

```ruby
# app/controllers/concerns/jwt_authenticatable.rb
require 'jwt'
require 'net/http'
require 'json'

module JwtAuthenticatable
  extend ActiveSupport::Concern

  ISSUER = 'https://my-api-a32f34.zuplo.api/__zuplo/issuer'
  JWKS_URI = "#{ISSUER}/.well-known/jwks.json"

  included do
    before_action :authenticate_jwt!
  end

  private

  def authenticate_jwt!
    token = extract_token
    return render_unauthorized('No token provided') unless token

    begin
      decoded = decode_token(token)
      @current_user = decoded
    rescue JWT::DecodeError, JWT::VerificationError => e
      render_unauthorized("Invalid token: #{e.message}")
    end
  end

  def extract_token
    auth_header = request.headers['Authorization']
    return nil unless auth_header&.start_with?('Bearer ')
    auth_header.split(' ').last
  end

  def decode_token(token)
    jwks = fetch_jwks
    JWT.decode(token, nil, true, {
      iss: ISSUER,
      verify_iss: true,
      algorithms: ['RS256'],
      jwks: jwks
    }).first
  end

  def fetch_jwks
    Rails.cache.fetch('jwks', expires_in: 10.minutes) do
      uri = URI(JWKS_URI)
      response = Net::HTTP.get(uri)
      JSON.parse(response)
    end
  end

  def render_unauthorized(message)
    render json: { error: message }, status: :unauthorized
  end
end

# app/controllers/protected_controller.rb
class ProtectedController < ApplicationController
  include JwtAuthenticatable

  def show
    render json: { message: 'Access granted', user: @current_user }
  end
end

# config/routes.rb
# Rails.application.routes.draw do
#   get '/protected', to: 'protected#show'
# end
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
