Wocha Docs

Ruby on Rails quickstart

Add Wocha authentication to a Rails application with OmniAuth OIDC for browser login and JWT validation for API mode, plus the Management API for admin tasks.

Add Wocha authentication to a Rails application: OmniAuth OIDC for browser login and JWT validation for API mode, plus the Management API for admin tasks.

Prerequisites

  • Ruby 3.2+
  • Rails 7.1+
  • A Wocha project with OAuth applications (Wocha Console)
  • Confidential client for server-side web login; public or confidential for API token validation

What you'll build

A Rails app with OmniAuth browser login, JWT API validation, and Management API integration.

your-app.local

Welcome back

After sign-in

✓ Session stored securely

✓ User profile available in your app

✓ Organisation context ready for multi-tenant features

Installation

# Gemfile
gem "omniauth"
gem "omniauth-rails_csrf_protection"
gem "omniauth-openid-connect"
gem "jwt"
gem "faraday"
bundle install

Environment variables

# OIDC login (web)
WOCHA_ISSUER=https://your-tenant.auth.wocha.ai
WOCHA_CLIENT_ID=your-client-id
WOCHA_CLIENT_SECRET=your-client-secret
 
# JWT validation (API)
WOCHA_AUDIENCE=your-api-audience          # optional
 
# Management API
WOCHA_API_KEY=wocha_mgmt_...
WOCHA_TENANT=your-tenant-slug

Register redirect URI: http://localhost:3000/auth/wocha/callback

OmniAuth OIDC strategy

Create config/initializers/omniauth.rb:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :openid_connect, {
    name: :wocha,
    issuer: ENV.fetch("WOCHA_ISSUER"),
    discovery: true,
    client_auth_method: :client_secret_basic,
    client_options: {
      identifier: ENV.fetch("WOCHA_CLIENT_ID"),
      secret: ENV.fetch("WOCHA_CLIENT_SECRET"),
      redirect_uri: "#{ENV.fetch('APP_URL', 'http://localhost:3000')}/auth/wocha/callback",
    },
    scope: [:openid, :profile, :email, :offline_access],
    response_type: :code,
    send_scope_to_token_endpoint: true,
  }
end

Add routes in config/routes.rb:

Rails.application.routes.draw do
  get "/auth/:provider/callback", to: "sessions#create"
  get "/login", to: "sessions#new"
  delete "/logout", to: "sessions#destroy"
  get "/dashboard", to: "dashboard#show"
end

Create app/controllers/sessions_controller.rb:

class SessionsController < ApplicationController
  def new
    redirect_to "/auth/wocha", allow_other_host: true
  end
 
  def create
    auth = request.env["omniauth.auth"]
    session[:user_id] = auth.uid
    session[:access_token] = auth.credentials.token
    session[:refresh_token] = auth.credentials.refresh_token
    redirect_to dashboard_path
  end
 
  def destroy
    reset_session
    redirect_to root_path
  end
end

Protect controllers with a before_action:

class DashboardController < ApplicationController
  before_action :require_login
 
  def show
    @user_id = session[:user_id]
  end
 
  private
 
  def require_login
    redirect_to login_path unless session[:user_id]
  end
end

JWT validation for API mode

Create app/services/greet_jwt_validator.rb:

require "jwt"
require "faraday"
require "json"
 
class WochaJwtValidator
  JWKS_CACHE = {}
 
  def self.verify!(token)
    issuer = ENV.fetch("WOCHA_ISSUER").chomp("/")
    jwks = fetch_jwks(issuer)
    algorithms = jwks["keys"].map { |k| k["alg"] }.compact.uniq
 
    decode_options = {
      algorithms: algorithms.presence || ["RS256"],
      iss: issuer,
      verify_iss: true,
      jwks: jwks,
    }
 
    if (aud = ENV["WOCHA_AUDIENCE"]).present?
      decode_options[:aud] = aud
      decode_options[:verify_aud] = true
    end
 
    JWT.decode(token, nil, true, decode_options).first
  end
 
  def self.fetch_jwks(issuer)
    JWKS_CACHE[issuer] ||= begin
      response = Faraday.get("#{issuer}/.well-known/jwks.json")
      JSON.parse(response.body)
    end
  end
end

Create app/controllers/concerns/authenticates_api.rb:

module AuthenticatesApi
  extend ActiveSupport::Concern
 
  included do
    before_action :authenticate_api_request!
  end
 
  private
 
  def authenticate_api_request!
    header = request.headers["Authorization"]
    token = header&.split&.last
    head :unauthorized and return unless token
 
    @current_claims = WochaJwtValidator.verify!(token)
  rescue JWT::DecodeError
    head :unauthorized
  end
 
  def current_user_id
    @current_claims&.dig("sub")
  end
end

Use in an API controller:

class Api::MeController < ApplicationController
  include AuthenticatesApi
 
  def show
    render json: {
      user_id: current_user_id,
      email: @current_claims["email"],
      org_id: @current_claims["org_id"],
    }
  end
end

Management API

Create app/services/greet_management_client.rb:

require "faraday"
require "json"
 
class GreetManagementClient
  BASE_URL = "https://#{ENV.fetch('WOCHA_TENANT')}.api.wocha.ai/v1"
 
  def self.list_users(page_size: 25)
    response = connection.get("users", { page_size: page_size })
    JSON.parse(response.body)
  end
 
  def self.connection
    Faraday.new(url: BASE_URL) do |f|
      f.headers["Authorization"] = "Bearer #{ENV.fetch('WOCHA_API_KEY')}"
      f.headers["Content-Type"] = "application/json"
    end
  end
end

Testing the integration

Web login

  1. Start Rails: bin/rails server
  2. Visit http://localhost:3000/login
  3. Complete login on the hosted Wocha UI
  4. Confirm you reach /dashboard with a session

API mode

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" http://localhost:3000/api/me

Next steps

On this page