AgeOnce Docs
Integration

Ruby

AgeOnce integration with Ruby (Ruby on Rails, Sinatra)

Ruby Integration

Complete AgeOnce integration example with Ruby.

Install dependencies

# Gemfile
gem 'httparty'
gem 'jwt'
bundle install

AgeOnce client

# lib/ageonce.rb
require 'httparty'
require 'securerandom'

class AgeOnce
  include HTTParty
  
  def initialize
    @client_id = ENV['AGEONCE_CLIENT_ID']
    @client_secret = ENV['AGEONCE_CLIENT_SECRET']
    @redirect_uri = ENV['AGEONCE_REDIRECT_URI']
    @api_url = ENV['AGEONCE_API_URL'] || 'https://app.ageonce.com'
  end
  
  def generate_verify_url
    state = SecureRandom.hex(16)
    
    params = {
      client_id: @client_id,
      redirect_uri: @redirect_uri,
      state: state
    }
    
    url = "#{@api_url}/verify?#{URI.encode_www_form(params)}"
    
    { url: url, state: state }
  end
  
  def exchange_code_for_token(code)
    response = self.class.post(
      "#{@api_url}/api/oauth/token",
      headers: { 'Content-Type' => 'application/json' },
      body: {
        client_id: @client_id,
        client_secret: @client_secret,
        code: code,
        redirect_uri: @redirect_uri
      }.to_json
    )
    
    raise "Token exchange failed: #{response['error_description']}" unless response.success?
    
    response.parsed_response
  end
  
  def validate_token(token)
    response = self.class.post(
      "#{@api_url}/api/oauth/validate",
      headers: { 'Content-Type' => 'application/json' },
      body: { token: token }.to_json
    )
    
    return { 'valid' => false } unless response.success?
    
    response.parsed_response
  end
end

Usage examples

# config/routes.rb
Rails.application.routes.draw do
  get '/verify', to: 'ageonce#verify'
  get '/callback', to: 'ageonce#callback'
  get '/protected', to: 'ageonce#protected'
end

# app/controllers/ageonce_controller.rb
class AgeonceController < ApplicationController
  before_action :require_age_verification, only: [:protected]
  
  def verify
    ageonce = AgeOnce.new
    data = ageonce.generate_verify_url
    
    session[:oauth_state] = data[:state]
    
    redirect_to data[:url], allow_other_host: true
  end
  
  def callback
    if params[:state] != session[:oauth_state]
      render plain: 'Invalid state', status: :bad_request
      return
    end
    
    begin
      ageonce = AgeOnce.new
      token_data = ageonce.exchange_code_for_token(params[:code])
      result = ageonce.validate_token(token_data['age_token'])
      
      if result['valid'] && result.dig('payload', 'age_verified')
        session[:age_verified] = true
        session[:min_age] = result.dig('payload', 'min_age')
        redirect_to '/protected'
      else
        render plain: 'Age verification failed', status: :forbidden
      end
    rescue => e
      render plain: "Error: #{e.message}", status: :internal_server_error
    end
  end
  
  def protected
    render plain: 'Welcome to age-restricted content!'
  end
  
  private
  
  def require_age_verification
    redirect_to '/verify' unless session[:age_verified]
  end
end

# app/controllers/concerns/age_verification.rb
module AgeVerification
  extend ActiveSupport::Concern
  
  included do
    before_action :require_age_verification
  end
  
  private
  
  def require_age_verification
    redirect_to '/verify' unless session[:age_verified]
  end
end

# Usage in controller:
# class ProtectedController < ApplicationController
#   include AgeVerification
# end
# app.rb
require 'sinatra'
require 'sinatra/cookies'
require_relative 'lib/ageonce'

enable :sessions
set :session_secret, 'your-secret-key'

ageonce = AgeOnce.new

get '/verify' do
  data = ageonce.generate_verify_url
  session[:oauth_state] = data[:state]
  redirect data[:url]
end

get '/callback' do
  if params[:state] != session[:oauth_state]
    halt 400, 'Invalid state'
  end
  
  begin
    token_data = ageonce.exchange_code_for_token(params[:code])
    result = ageonce.validate_token(token_data['age_token'])
    
    if result['valid'] && result.dig('payload', 'age_verified')
      session[:age_verified] = true
      redirect '/protected'
    else
      halt 403, 'Age verification failed'
    end
  rescue => e
    halt 500, "Error: #{e.message}"
  end
end

def require_age_verification
  redirect '/verify' unless session[:age_verified]
end

get '/protected' do
  require_age_verification
  'Welcome to age-restricted content!'
end

Local JWT validation

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

class AgeOnceJwtValidator
  def initialize
    @api_url = ENV['AGEONCE_API_URL'] || 'https://app.ageonce.com'
    @public_keys = nil
  end
  
  def validate(token)
    keys = fetch_public_keys
    
    decoded = JWT.decode(
      token,
      nil,
      true,
      {
        algorithms: ['RS256'],
        iss: 'ageonce',
        jwks: { keys: keys }
      }
    )
    
    { valid: true, payload: decoded.first }
  rescue JWT::DecodeError => e
    { valid: false, error: e.message }
  end
  
  private
  
  def fetch_public_keys
    return @public_keys if @public_keys
    
    uri = URI("#{@api_url}/api/oauth/jwks")
    response = Net::HTTP.get(uri)
    jwks = JSON.parse(response)
    
    @public_keys = jwks['keys']
  end
end

# Usage
validator = AgeOnceJwtValidator.new
result = validator.validate(age_token)

if result[:valid] && result[:payload]['age_verified']
  # Grant access
end

Ruby provides elegant syntax for working with API and JWT.

On this page