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 siteapp.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
- Your app redirects the user to
*.clowk.dev. - Clowk authenticates the user.
- Clowk redirects back to your callback URL with
tokenandstate. - 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
statevalidation - JWT verification with your instance
secret_key - internal-only redirect sanitization
- session reset before persisting the authenticated user
httponlycookie persistence withSameSite=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.after_sign_in_path = '/'
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:
publishable_keysubdomain_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_clowkauthenticate_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_memberauthenticate_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.('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_parsedJSON 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.