The project is in a healthy, maintained state
Verifies JWT signature, validates claims (expiration, issuer), and handles public key retrieval to ensure requests are securely authenticated by an external SSO provider.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 2.0
~> 13.0
~> 3.0
~> 0.9
~> 3.0

Runtime

~> 2.8
>= 2.0
 Project Readme

RackJwtVerifier

A robust and production-ready Rack middleware for authenticating requests using JSON Web Tokens (JWT) signed by an external Single Sign-On (SSO) provider.

This gem handles the cryptographic verification, caching of public keys, and integration into any Rack application (including Ruby on Rails).

Features

  • Cryptographic Verification: Verifies the JWT signature using the RS256 algorithm and public keys fetched from a remote URL.

  • Flexible Caching: Uses a configurable cache store for public keys, defaulting to in-memory caching but easily upgradable to Redis or Memcached for multi-process environments.

  • Claim Validation: Enforces standard JWT claims, including expiration (exp) and not-before (nbf).

  • Rack Middleware: Protects application routes by halting unauthorized requests with a 401 Unauthorized response.

Installation

Add this line to your application's Gemfile:

gem 'rack_jwt_verifier'

And then execute:

$ bundle install

Usage and Integration

1. Basic Setup (In-Memory Caching)

The simplest way to integrate is by providing the URL where your SSO provider exposes its public key (usually a PEM-encoded RSA key). The public key will be cached in memory for 5 minutes per worker process.

In config/application.rb (for Rails) or config.ru (for generic Rack):

# Requires `rack_jwt_verifier` implicitly in Rails, or explicitly in config.ru
# Replace the URL with your actual SSO Public Key endpoint
PUBLIC_KEY_URL = "https://sso.example.com/api/v1/public_key"
Rails.application.config.middleware.use RackJwtVerifier::Middleware,
                                        public_key_url: PUBLIC_KEY_URL

2. Production Setup with Redis Caching

For horizontally scaled applications (e.g., using Puma or multiple Docker containers), the default in-memory cache is inefficient. To prevent a "cache stampede" where all workers simultaneously fetch the key, you must use a distributed cache like Redis.

Prerequisites: You need a Redis client library installed (e.g., redis-rails or connection\_pool).

Example: Using Redis as the Cache Store

The RackJwtVerifier expects a cache object that responds to the standard Ruby cache interface: #read(key) and #write(key, value, expires\_in: ttl).

Example using ActiveSupport::Cache::RedisCacheStore (Common in Rails apps):

# In config/initializers/rack_jwt_verifier.rb

# 1. Configure your Redis cache client
# This example assumes you have Redis configured via Rails:
REDIS_CACHE_CLIENT = ActiveSupport::Cache.lookup_store(:redis_cache_store, {
    url: ENV.fetch("REDIS_URL", "redis://localhost:6379/1"),
    reconnect_attempts: 1  })
# 2. Configure the Verifier with the Redis client
Rails.application.config.middleware.use RackJwtVerifier::Middleware,
  public_key_url: ENV.fetch("SSO_PUBLIC_KEY_URL"),
  cache_store: REDIS_CACHE_CLIENT

By passing the Redis cache client via the cache\_store option, all your application workers will share a single cache, ensuring the public key is fetched from the network only once every 5 minutes (or whatever TTL is configured internally).

3. Customizing JWT Decoding

You can pass a :decode\_options hash to the middleware to override the default settings for the JWT gem.

Option Default Value Purpose
:leeway 60 seconds Sets clock skew tolerance for exp and nbf checks. Set to 0 for strict timing.
:algorithm "RS256" The expected signing algorithm.
:iss (None) RECOMMENDED: Set this to enforce a specific issuer claim.
:aud (None) RECOMMENDED: Set this to enforce an audience claim.

Example: Strict Expiration and Issuer Check:

Rails.application.config.middleware.use RackJwtVerifier::Middleware,
  public_key_url: ENV.fetch("SSO_PUBLIC_KEY_URL"),
  decode_options: {
    # No clock skew tolerance
    leeway: 0,
    # Ensure the token was issued by our expected SSO provider
    iss: "[https://my-sso.com/token-service](https://my-sso.com/token-service)"
  }

How it Works

  1. Request Flow: On every incoming HTTP request, the middleware intercepts the call.

  2. Token Extraction: It looks for a token in the Authorization: Bearer <token> header.

  3. Key Retrieval: The Verifier attempts to read the public key PEM string from the configured cache\_store (Redis or In-Memory).

    • If the key is present, it's used immediately.
    • If the key is missing or expired, a network request is made to the public\_key\_url, and the key is written back to the cache for 5 minutes.
  4. Verification: The public key is used to cryptographically verify the JWT's signature and validate its claims (exp, nbf, iss, etc.).

  5. Authorization:

    • If verification succeeds, the request passes to your application.
    • If verification fails (e.g., token expired, bad signature, or missing token), the request is halted, and a 401 Unauthorized response is immediately returned.

Development

The testing setup uses RSpec, WebMock for network request mocking, and Timecop for robust time-dependent testing (like expiration checks).

To run the full suite:

$ bundle install
$ bundle exec rspec

License

The gem is available as open source under the terms of the MIT License.