---
title: "Add JWT Authentication to Your undefined API"
description: undefined
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/ruby/hanami/jwt-backend"
framework: undefined
language: undefined
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your undefined API



## How Zuplo Handles It

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

## undefined Backend Code

```ruby
require 'hanami/api'
require 'jwt'
require 'net/http'
require 'uri'
require 'json'

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

  def initialize
    @keys = fetch_keys
  end

  def fetch_keys
    uri = URI(JWKS_URI)
    response = Net::HTTP.get(uri)
    JSON.parse(response)['keys']
  end

  def find_key(kid)
    @keys.find { |key| key['kid'] == kid }
  end

  def get_public_key(kid)
    key_data = find_key(kid)
    return unless key_data

    OpenSSL::X509::Certificate.new(Base64.decode64(key_data['x5c'].first)).public_key
  rescue OpenSSL::OpenSSLError => e
    nil
  end
end

class JWTGuard
  def initialize(app)
    @app = app
    @jwks = JWKS.new
  end

  def call(env)
    token = extract_token(env)

    if token.nil?
      return [401, {'Content-Type' => 'application/json'}, [{ error: 'No token provided' }.to_json]]
    end

    begin
      decoded_token = decode_jwt(token)
      env['user'] = decoded_token
      @app.call(env)
    rescue JWT::DecodeError => e
      [401, {'Content-Type' => 'application/json'}, [{ error: 'Invalid token', details: e.message }.to_json]]
    end
  end

  private

  def extract_token(env)
    auth_header = env['HTTP_AUTHORIZATION']
    auth_header&.gsub(/^Bearer /, '')
  end

  def decode_jwt(token)
    header = JWT.decode(token, nil, false).first
    kid = header['kid']

    public_key = @jwks.get_public_key(kid)
    raise JWT::DecodeError, 'No signing key found' unless public_key

    JWT.decode(token, public_key, true, { iss: JWKS::ISSUER, verify_iss: true, algorithm: 'RS256' }).first
  end
end

class App < Hanami::API
  use JWTGuard

  get '/protected' do
    user = env['user']
    json message: "Access granted", user: user
  end
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)
