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.
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
Mutexprotects 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