authaction-ruby-sdk

JWT verification SDK for Ruby backends. Validates AuthAction access tokens via JWKS — handles key fetching, caching, and rotation automatically.

Works with Rails (API mode or full-stack) and any Rack application.

Installation

Add to your Gemfile:

gem "authaction"

Then run:

bundle install

Or install directly:

gem install authaction

Quick Start

Verify a token directly

require "authaction"

verifier = AuthAction::JwtVerifier.new(
  domain:   ENV["AUTHACTION_DOMAIN"],    # e.g. myapp.eu.authaction.com
  audience: ENV["AUTHACTION_AUDIENCE"]   # e.g. https://api.myapp.com
)

# Verify a raw token — raises TokenExpiredError / TokenInvalidError on failure
payload = verifier.verify_token(token)

# Verify from Authorization header — returns nil on missing/invalid, never raises
payload = verifier.verify_request(request.headers["Authorization"])

puts payload["sub"]   # user identifier
puts payload["email"] # any JWT claim

verify_token

Decodes and validates the JWT. Returns the claims hash on success.

Condition Raises
exp in the past AuthAction::TokenExpiredError
Bad signature, wrong issuer/audience, malformed JWT AuthAction::TokenInvalidError

verify_request

Convenience wrapper for HTTP handler code. Extracts the token from a Bearer <token> header value.

  • Returns nil (never raises) when the header is absent, not a Bearer scheme, or the token is invalid/expired.
  • Returns the claims hash on success.

Rails Integration

1. Require the concern

In config/application.rb or an initializer:

require "authaction/rails"

2. Include in your base controller

class ApplicationController < ActionController::API
  include AuthAction::Rails::JwtAuthenticatable
end

3. Protect actions with before_action

class ProtectedController < ApplicationController
  before_action :authenticate_request!

  def index
    render json: { sub: @current_payload["sub"] }
  end
end

On success, @current_payload (also available via the current_payload reader) contains the decoded JWT claims hash.

On failure the concern renders a 401 Unauthorized JSON response automatically:

{ "error": "Token has expired" }
{ "error": "Missing Bearer token" }

Public endpoints

Skip authentication for specific actions:

class UsersController < ApplicationController
  before_action :authenticate_request!, except: [:create]

  def create
    # public — no token required
  end

  def show
    render json: { sub: current_payload["sub"] }
  end
end

Configuration

The concern reads configuration from environment variables:

Variable Description Example
AUTHACTION_DOMAIN Your AuthAction tenant domain myapp.eu.authaction.com
AUTHACTION_AUDIENCE Expected aud claim in tokens https://api.myapp.com
AUTHACTION_DOMAIN=your-tenant.eu.authaction.com
AUTHACTION_AUDIENCE=https://api.your-app.com

When using JwtVerifier directly, pass these as keyword arguments instead of relying on ENV vars.


Exceptions

require "authaction"

begin
  payload = verifier.verify_token(token)
rescue AuthAction::TokenExpiredError
  # token exp claim is in the past
rescue AuthAction::TokenInvalidError => e
  # bad signature, wrong issuer/audience, malformed JWT
  puts e.message
end

Both error classes inherit from AuthAction::Error < StandardError.


How JWKS Caching Works

JwtVerifier fetches public keys from https://<domain>/.well-known/jwks.json on first use and caches the key set in memory:

  • TTL: 5 minutes (300 seconds). The cache is invalidated automatically after expiry.
  • Key rotation: When a JWT arrives with an unknown kid, the cache is busted immediately and the JWKS endpoint is re-fetched before retrying verification.
  • Thread safety: A Mutex protects the cache so the verifier is safe to share across threads (e.g. as a class-level instance variable in Rails controllers).

Running Tests

bundle install
bundle exec rspec

Tests use WebMock to stub the JWKS endpoint and real RSA key pairs to provide end-to-end coverage of the JWKS parsing and JWT validation path — no network calls required.


License

MIT