Zig 0.17.0

clerk

On this page:

spider.clerk

Clerk login (spider.clerk): the token check of spider.jwks configured from a Clerk publishable key, plus the OAuth login redirect and the callback that sets the session cookie.

spider.clerk.ClerkConfig

zig
pub const ClerkConfig = struct

What Clerk.init takes. Nothing is read from the environment: the app fills it.

Fields

  • publishable_key: []const u8: The instance's publishable key (pk_test_... or pk_live_...). The issuer and the JWKS URL are derived from it (see Clerk.init); it is also sent as the OAuth client_id. No default.
  • secret_key: []const u8: Sent as the OAuth client_secret when the callback exchanges the code. No default.
  • redirect_uri: []const u8 = "http://localhost:3000/auth/callback": The full URL of the app's callback route (callbackHandler()).
  • login_path: []const u8 = "/login": Where a request without a token is redirected.
  • after_callback_path: []const u8 = "/": Where the callback redirects once the session cookie is set.
  • roles_claim: ?[]const u8 = null: Where the session token carries roles (what .roles checks); Clerk has none by default — add one with a custom session claim, e.g. {"roles": "{{user.public_metadata.roles}}"} and roles_claim = "roles". The active organization's role (what .org_roles checks) is read without configuration. See JwksConfig.roles_claim.
  • map_claims: ?*const fn (c: *Ctx, claims: std.json.ObjectMap) anyerror!void = null: See JwksConfig.map_claims.
  • verify_state: bool = true: Check the OAuth state on the callback against the cookie loginHandler() set, so that nobody can finish a login of their own in a visitor's browser. Every login must then start at loginHandler() (or login): the callback of a hand-built URL has no matching cookie and is sent back to login_path.
  • state_cookie_name: []const u8 = "__clerk_oauth_state": Cookie that keeps the state (HttpOnly, SameSite=Lax, 10 minutes; Secure unless redirect_uri is plain http, like the session cookie).

spider.clerk.AuthUrlOptions

zig
pub const AuthUrlOptions = struct

Options of Clerk.authUrlWith.

Fields

  • state: ?[]const u8 = null: Sent to Clerk and given back to the redirect address as ?state=.

spider.clerk.Clerk

zig
pub const Clerk = struct

One Clerk instance for the app. The middleware and the callback handler keep a pointer to this value: it must not move or be freed while the server runs, and a process can use one.

zig
var clerk = try spider.clerk.Clerk.init(allocator, io, .{
    .publishable_key = publishable_key,
    .secret_key = secret_key,
});
defer clerk.deinit();
server
    .use(clerk.middleware())
    .get("/login", clerk.loginHandler(), .{ .public = true })
    .get("/auth/callback", clerk.callbackHandler(), .{ .public = true });

Fields

  • jwks: JwksAuth: The token verifier (spider.jwks.JwksAuth), with .org_claims = .clerk.
  • config: ClerkConfig: The config given to init. Its strings are not copied.
  • domain: []const u8: The issuer decoded from the publishable key: the base of the JWKS and OAuth URLs.

clerk.Clerk.init

zig
pub fn init(allocator: std.mem.Allocator, io: std.Io, config: ClerkConfig) !Clerk

Decodes the issuer from the publishable key and downloads the signing keys from {issuer}/.well-known/jwks.json. What follows the key's pk_live_ / pk_test_ prefix is base64 of the host of the instance's Frontend API followed by a $ (example.accounts.dev$): the issuer is that host over https. (A key that decodes to JSON with an issuer field is used as that issuer, scheme included: for tests against a server of your own.)

Errors: error.InvalidClerkKey (no such prefix, not base64, or it decodes to nothing), error.JwksFetchFailed when the address does not answer with a key set, or the HTTP client's error when it cannot be reached.

clerk.Clerk.deinit

zig
pub fn deinit(self: *Clerk) void

Frees the cached keys and the two addresses init built.

clerk.Clerk.authUrl

zig
pub fn authUrl(self: *const Clerk, arena: std.mem.Allocator) ![]u8

The URL that starts the OAuth flow ({issuer}/oauth/authorize), without a state. Allocated in arena; the values are URL-encoded. A login route calls login (or mounts loginHandler()), which adds the state the callback checks.

clerk.Clerk.authUrlWith

zig
pub fn authUrlWith(self: *const Clerk, arena: std.mem.Allocator, opts: AuthUrlOptions) ![]u8

authUrl with a state, URL-encoded like the rest.

clerk.Clerk.loginHandler

zig
pub fn loginHandler(self: *Clerk) Handler

The handler for the login route (login_path): login.

clerk.Clerk.login

zig
pub fn login(self: *Clerk, c: *Ctx) !Response

Redirects (302) the visitor to Clerk with a random state, also kept in a cookie of this browser; callbackHandler() compares the two.

clerk.Clerk.middleware

zig
pub fn middleware(self: *Clerk) MiddlewareFn

The middleware that checks the __session token of every request (see JwksAuth.middleware): server.use(clerk.middleware()). Only routes marked .public pass without a token (there is no list of skipped paths), an expired token is a 401 (there is no refresh route), and the token's audience is not checked.

clerk.Clerk.callbackHandler

zig
pub fn callbackHandler(self: *Clerk) Handler

The handler for the route named by redirect_uri: checks the state against the cookie login set (when it does not match: redirect to login_path, and Clerk is not asked anything), exchanges code for tokens, sets the __session cookie (the ID token, or the access token when there is none; 7 days), removes the state cookie and redirects to after_callback_path. 400 without code; 502 when Clerk returns no token. Mark the route .public, or the middleware stops the callback before it gets here.