Clowk Ruby SDK

Clowk is the Ruby gem for integrating Clowk authentication into Rails applications.

It focuses on a small client-side surface:

  • redirect users to Clowk
  • verify the JWT returned by Clowk
  • expose Rails-friendly auth helpers
  • provide a minimal HTTP client for the Clowk API

Product domains

Clowk uses different domains for different concerns:

  • clowk.in: public product site
  • app.clowk.in: dashboard used to manage apps and instances
  • *.clowk.dev: per-instance auth domain used by your end users

For a Rails app using this gem, the important part is the instance auth domain. Your app redirects users there, Clowk authenticates them, then redirects back with a signed JWT.

Install

# Gemfile
gem 'clowk'

Quick start

# config/initializers/clowk.rb
Clowk.configure do |config|
  config.publishable_key = ENV['CLOWK_PUBLISHABLE_KEY']
  config.secret_key = ENV['CLOWK_SECRET_KEY']
end
# config/routes.rb
Rails.application.routes.draw do
  mount Clowk::Engine => '/clowk'
end
class ApplicationController < ActionController::Base
  include Clowk::Authenticable
end
class DashboardController < ApplicationController
  before_action :authenticate_clowk!

  def index
    @user = current_clowk
  end
end

Authentication flow

  1. Your app redirects the user to *.clowk.dev.
  2. Clowk authenticates the user.
  3. Clowk redirects back to your callback URL with token and state.
  4. The gem validates state, verifies the JWT, stores the authenticated session, and redirects back to a safe internal path.

The callback flow includes:

  • session-backed state validation
  • JWT verification with your instance secret_key
  • internal-only redirect sanitization
  • session reset before persisting the authenticated user
  • httponly cookie persistence with SameSite=Lax

Configuration

Clowk.configure do |config|
  config.secret_key = ENV['CLOWK_SECRET_KEY']
  config.publishable_key = ENV['CLOWK_PUBLISHABLE_KEY']
  config.subdomain_url = 'https://acme.clowk.dev'
  config.prefix_by = :clowk

  config. = '/'
  config.after_sign_out_path = '/'

  config.api_base_url = 'https://api.clowk.dev/api/v1'
  config.callback_path = '/clowk/oauth/callback'
  config.mount_path = '/clowk'

  config.http_open_timeout = 5
  config.http_read_timeout = 10
  config.http_write_timeout = 10
  config.http_retry_attempts = 2
  config.http_retry_interval = 0.05
  config.http_logger = Rails.logger
end

Important settings:

Setting Purpose
secret_key Required. Used to verify JWT signatures.
publishable_key Preferred for auth URL resolution. The gem resolves the latest instance URL from it before sign in/sign up.
subdomain_url Fallback auth domain when you do not want publishable-key-based resolution.
prefix_by Prefix used to generate helper names. Default: :clowk.
mount_path Local mount prefix used by helper path generation. Default: /clowk.
callback_path Callback route Clowk redirects back to. Default: /clowk/oauth/callback.
http_logger Optional logger used by Clowk::Http.

Auth URL resolution priority:

  1. publishable_key
  2. subdomain_url

When publishable_key is present, the gem resolves the current auth base URL first and caches it briefly in memory. The lookup endpoint returns the full instance JSON payload, and the gem derives the final auth URL from that data (including subdomain). This keeps dashboard subdomain changes visible without redeploying the client app. If you do not want that lookup, configure only subdomain_url.

Internally, that lookup is done through Clowk::SDK::Client, via client.subdomains.find_by_pk('pk_...').

If you mount the engine under a different prefix, keep mount_path and callback_path aligned with that choice.

Example:

Clowk.configure do |config|
  config.mount_path = '/auth'
  config.callback_path = '/auth/oauth/callback'
end

Rails.application.routes.draw do
  mount Clowk::Engine => '/auth'
end

Generated helpers

With the default prefix_by = :clowk, the concern exposes:

  • current_clowk
  • authenticate_clowk!
  • clowk_signed_in?

You can change the prefix to avoid collisions with another auth system.

Clowk.configure do |config|
  config.prefix_by = :member
end

That generates:

  • current_member
  • authenticate_member!
  • member_signed_in?

Current subject

current_clowk returns a Clowk::Current object.

current_clowk.id
current_clowk.email
current_clowk.name
current_clowk.avatar_url
current_clowk.provider
current_clowk.instance_id
current_clowk.app_id

You can also access raw claims:

current_clowk[:sub]
current_clowk.to_h

Route protection

class AdminController < ApplicationController
  before_action :authenticate_clowk!
end

Use authenticate_clowk! for protected pages. Use current_clowk when the route can be public but should still know who is authenticated.

View and URL helpers

Local mounted routes:

<%= link_to 'Sign in', clowk_sign_in_path(return_to: dashboard_path) %>
<%= link_to 'Sign up', clowk_sign_up_path(return_to: dashboard_path) %>
<%= link_to 'Sign out', clowk_sign_out_path %>

Direct remote URLs:

<%= link_to 'Direct sign in', clowk_sign_in_url(redirect_to: dashboard_url) %>
<%= link_to 'Direct sign up', clowk_sign_up_url(redirect_to: dashboard_url) %>

When publishable_key is configured, these helpers resolve the latest instance URL before building the final sign-in or sign-up destination. When it is absent, they use subdomain_url directly.

Mounted routes exposed by the engine:

  • /clowk/sign_in
  • /clowk/sign_up
  • /clowk/sign_out
  • /clowk/oauth/callback

When you mount the engine elsewhere, the same route set is exposed under your chosen prefix.

Token sources

The concern can read the token from:

  • params[:token]
  • cookies
  • Authorization: Bearer <token>

That keeps the integration usable for callback routes, regular controllers, and API-style endpoints.

SDK Client

The gem includes a resource-oriented SDK for the Clowk API.

client = Clowk::SDK::Client.new

The client uses your global configuration by default. You can also pass options explicitly:

client = Clowk::SDK::Client.new(
  secret_key: 'sk_live_xxx',
  publishable_key: 'pk_live_xxx'
)

Resources

Each resource is accessible as a method on the client:

client.users
client.sessions
client.subdomains

These return Clowk::SDK::Resource subclasses with a standard CRUD interface:

client.users.list
client.users.find('user_123')
client.users.show('user_123')
client.users.destroy('user_123')

Token verification

response = client.tokens.verify(token: params[:token])

response.status       # => 200
response.success?     # => true
response.body_parsed  # => { 'valid' => true }

Subdomain resolution

response = client.subdomains.find_by_pk('pk_live_xxx')

Search operators

The search method uses a Zendesk-style query syntax. You can use keyword arguments or a raw string for advanced operators.

Keywords:

client.users.search(email: "user@example.com")
# GET /users/search?query=email%3Auser%40example.com

client.users.search(status: "active", role: "admin")
# GET /users/search?query=status%3Aactive+role%3Aadmin

Raw string for custom operators like >, <, >=:

client.users.search("email:user@example.com active:true created_at>2026-01-01")
# GET /users/search?query=email%3Auser%40example.com+active%3Atrue+created_at%3E2026-01-01

The raw string is sent as-is, giving full control over the query syntax. The API backend parses and applies the operators.

Raw HTTP methods

The client also exposes raw HTTP methods for custom requests:

client.get('custom/endpoint')
client.post('custom/endpoint', { key: 'value' })
client.put('custom/endpoint', { key: 'value' })
client.patch('custom/endpoint', { key: 'value' })
client.delete('custom/endpoint')
client.head('custom/endpoint')
client.options('custom/endpoint')

Clowk::Http::Response

HTTP responses are returned as Clowk::Http::Response objects.

response = client.get('users/user_123')

response.status       # => 200
response.success?     # => true
response.body         # => '{"id":"user_123"}'
response.body_parsed  # => { 'id' => 'user_123' }
response.headers      # => { 'content-type' => ['application/json'] }

For compatibility, the response also supports:

response[:status]
response[:success?]
response.to_h

HTTP middleware

Clowk::Http uses a small internal middleware stack built on Net::HTTP.

Included behavior:

  • request and response logging
  • open, read, and write timeouts
  • retry on retryable network errors
  • automatic JSON request encoding
  • automatic body_parsed JSON decoding when possible

Scope

This gem is intentionally narrow. It does not try to replace your entire app session architecture or act as an auth server.

Its job is to make the Rails side of Clowk integration predictable:

  • start authentication
  • validate callbacks safely
  • expose a clean authenticated subject
  • make future Clowk API access straightforward

License

AGPL-3.0. See LICENSE.