You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby JWT gem如何从远程OpenID Connect授权服务器自动获取公钥以验证JWT签名

Solution for Automatic Public Key Retrieval with Ruby JWT Gem

Great question! I’ve worked through this exact scenario before—replicating that .NET-style automatic public key fetching from an OIDC identity server in Ruby/Rails using the JWT gem. Here are two solid approaches to achieve this:

The jwks-rsa gem is purpose-built for this use case: it handles fetching public keys from an OIDC server's JWKS endpoint, caching them, and automatically handling key rotation. It integrates seamlessly with the Ruby JWT gem.

Step 1: Add the Gem

Add this to your Gemfile and run bundle install:

gem 'jwks-rsa'
gem 'jwt'

Step 2: Implement Token Verification

Here’s how to set it up (in a Rails initializer like config/initializers/jwt.rb, or directly in your authentication logic):

require 'jwt'
require 'jwks-rsa'

# Initialize the JWKS client with your identity server's details
jwks_client = Jwks::RSA.new(
  jwks_uri: 'https://uri.to.my.identity.server/.well-known/jwks.json',
  cache: true,
  cache_ttl: 3600 # Cache keys for 1 hour (adjust as needed)
)

# Configure JWT verification options
jwt_verification_options = {
  algorithm: 'RS256',
  verify_iss: true,
  iss: 'https://uri.to.my.identity.server', # Match your identity server's issuer
  verify_iat: true,
  verify_expiration: true,
  # Tell JWT gem to use the JWKS client to fetch the appropriate key
  keyfinder: ->(header) { jwks_client.get_key(header) }
}

# Example verification method
def verify_access_token(token)
  begin
    JWT.decode(token, nil, true, jwt_verification_options)
  rescue JWT::DecodeError => e
    Rails.logger.error "Token verification failed: #{e.message}"
    nil
  end
end

This setup automatically:

  • Fetches the JWKS from your identity server’s endpoint
  • Caches the public keys to avoid repeated network calls
  • Looks up the correct public key using the kid value in the JWT header
  • Handles key rotation (if your identity server updates its keys)

Option 2: Manual Implementation (For Full Control)

If you prefer not to add another gem, you can implement the logic yourself by leveraging the OIDC discovery endpoint and JWKS parsing.

Step 1: Fetch OIDC Discovery Document & JWKS

require 'jwt'
require 'net/http'
require 'json'

# Cache storage for public keys and last fetch time
$jwt_public_keys = {}
$jwks_last_fetched = nil
JWKS_CACHE_TTL = 3600 # 1 hour cache

def fetch_jwks_uri(authority)
  # Get the OIDC discovery document
  discovery_uri = URI("#{authority}/.well-known/openid-configuration")
  response = Net::HTTP.get(discovery_uri)
  JSON.parse(response)['jwks_uri']
end

def refresh_jwks_cache(jwks_uri)
  # Skip refresh if cache is still valid
  return if $jwks_last_fetched && (Time.now - $jwks_last_fetched) < JWKS_CACHE_TTL

  # Fetch and parse JWKS
  response = Net::HTTP.get(URI(jwks_uri))
  jwks = JSON.parse(response)
  
  # Convert JWKS keys to RSA public keys and cache
  $jwt_public_keys = jwks['keys'].each_with_object({}) do |key, hash|
    rsa_key = OpenSSL::PKey::RSA.new(JWT::JWK.new(key).to_pem)
    hash[key['kid']] = rsa_key
  end
  $jwks_last_fetched = Time.now
end

def verify_jwt(token, authority)
  begin
    # Get JWKS URI from discovery endpoint
    jwks_uri = fetch_jwks_uri(authority)
    # Refresh cache if needed
    refresh_jwks_cache(jwks_uri)
    
    # Extract kid from JWT header to find matching public key
    header = JWT.decode(token, nil, false)[1]
    public_key = $jwt_public_keys[header['kid']]
    
    raise "No public key found for kid: #{header['kid']}" unless public_key
    
    # Verify the token
    JWT.decode(token, public_key, true, { algorithm: 'RS256', iss: authority })
  rescue => e
    puts "Token verification error: #{e.message}"
    nil
  end
end

Key Notes for Manual Implementation:

  • Caching is critical: Avoid fetching JWKS on every token verification to prevent performance hits and rate-limiting issues.
  • Handle errors: Add error handling for network failures, invalid discovery documents, or missing keys.
  • Key rotation: The cache TTL ensures you’ll pick up new keys when your identity server rotates them.

Final Recommendations

Stick with the jwks-rsa gem for most cases—it’s maintained, handles edge cases you might miss (like key rotation and cache invalidation), and keeps your code clean. Only opt for the manual approach if you need full control over every step of the process.

内容的提问来源于stack exchange,提问作者user888734

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 21:52:33