Frauthy
Getting Started022026

Getting Started

Ship it.

From zero to a published, discoverable OAuth provider — on whatever backend you run — and a safe integration with someone else's.

Page 01 covered the theory: flows, tokens, OIDC, and where fine-grained authorization decides what a token may do. This page is hands-on. You'll scaffold a project with the CLI, meet the three SDK legs, publish to the discovery registry, connect to another provider, and wire up authorization. Frauthy is a general wrapper: it speaks to Better Auth and Ory through a common adapter, and integrates with anything that speaks OIDC.

00

Prerequisites

Before you begin

Three things, two of them you already have.

Approved private-beta participants receive the supported toolchain and setup procedure with their invitation. Fine-grained authorization can be introduced when you reach Step 05; this public guide focuses on the architecture and workflow rather than executable setup.

terminal — check your toolchain

# Approved participant setup
[PRIVATE_BETA_INSTALL_INSTRUCTIONS_REDACTED]
01

Install & init

Scaffold

frauthy init is an interactive flow. It asks a handful of questions and writes a working project — no boilerplate to copy.

frauthy init

  ┌─────────────────────────────┐
  │  ╔═╗ Ⓕ  F R A U T H Y       │
  │  ╚═╝  trust, in transit     │
  └─────────────────────────────┘

◇  What is your provider called?
│  Alice Identity
│
◇  Registry handle (slug)
│  alice
│
◇  Provider domain (issuer URL)
│  https://auth.alice.com
│
◇  Which provider backend does Frauthy wrap?
│  ● Ory — Hydra + Kratos, self-host or Ory Network
│
◇  Discovery visibility
│  ● Unlisted — hidden, resolvable by exact handle
│
◇  Which SDK legs do you need?
│  ◼ provider  ◼ connect  ◼ authz
│
◇  Which authorization backend?
│  ● Ory Keto — Zanzibar, pairs with Ory
│
◆  Created 7 files
│
└  Frauthy ready. ory provider, unlisted in discovery.frauthy.dev

The result is a small, legible project. Each module you picked drops in one example file you own and edit:

alice/ — scaffolded project

alice/
├─ frauthy.config.ts        ← single source of truth
├─ package.json
├─ .env.example
├─ README.md
└─ src/
   ├─ provider.ts            ← wraps your backend
   ├─ connect.ts            ← connect
   └─ authz.ts              ← authz

Your whole configuration lives in one typed file. The CLI and every SDK read from it, so there's one place to change a domain, a registry, or your visibility.

frauthy.config.ts

import { defineConfig } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";

export default defineConfig({
  service: {
    slug: "alice",
    name: "Alice Identity",
    issuer: "https://auth.alice.com",
    visibility: "unlisted",
  },
  // swap kind for "better-auth" or "oidc" — nothing else changes
  provider: { kind: "ory", sdkUrl: process.env.ORY_SDK_URL!, apiKey: process.env.ORY_API_KEY },
  registry: { url: process.env.FRAUTHY_REGISTRY_URL!, apiKey: process.env.FRAUTHY_API_KEY },
  authz: { kind: "keto", readUrl: process.env.KETO_READ_URL!, writeUrl: process.env.KETO_WRITE_URL! },
  modules: ["provider", "connect", "authz"],
});
02

The three legs

The SDKs

Frauthy's SDK is split by which side of a connection you're on. Approved participants enable only the capabilities they need.

[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]

Run your auth

Register OAuth clients, manage user sessions, and emit lifecycle events — through an adapter for Better Auth or Ory.

[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]

Connect to others

Discover a provider, register a client dynamically, and run PKCE, refresh, and client-credentials flows. Pure OIDC, so it works with any backend.

[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]

Decide permissions

Object-level check()s and delegation against SpiceDB or Ory Keto — one interface, both Zanzibar engines.

frauthy.ts — create the application SDK contract

import { createFrauthy } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";

export const frauthy = createFrauthy({
  provider: { issuer: "https://auth.alice.com", audience: "alice-web" },
  store: {
    kind: "spicedb",
    endpoint: process.env.SPICEDB_ENDPOINT!,
    token: process.env.SPICEDB_TOKEN!,
  },
});

route.ts — verify a session and decide access

import { resource } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";
import { frauthy } from "./frauthy";

const session = await frauthy.session(request);
const portal = resource("portal", "alice");
if (!(await session.can("access", portal))) {
  return new Response("Forbidden", { status: 403 });
}

Site UI SDK — documentation bridge

import { DocumentationBridge } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";

<DocumentationBridge
  heading="Go from example to contract"
  sdk={sdkReference}
  ui={uiCatalog}
  note="Runtime SDKs and UI SDKs solve different problems."
/>

One adapter contract sits under the provider leg. Pick a backend; everything above it — publishing, lifecycle, connect — is identical.

Better Auth

Embedded in your app

The OIDC plugin issues tokens; Frauthy registers clients and manages sessions through it.

Ory

Hydra + Kratos

Clients map to Hydra's /admin/clients; identities and sessions to Kratos. Self-host or Ory Network.

Other OIDC

Discovery-only

Anything that serves a well-known document can be published and connected to — admin ops stay with that system.

Leg one — manage your own provider

src/provider.ts

import { createProvider, oryAdapter } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";
import config from "../frauthy.config";

// One surface, any backend. Swap the adapter and nothing below changes:
//   betterAuthAdapter(auth, { issuer })   ← Better Auth
const provider = createProvider(oryAdapter({
  issuer: config.service.issuer,
  sdkUrl: process.env.ORY_SDK_URL!,   // Hydra admin → /admin/clients
  apiKey: process.env.ORY_API_KEY,
}));

// Register an app that will sign users in through you
const client = await provider.clients.register({
  name: "Alice Web",
  redirectUris: ["https://app.alice.com/callback"],
  scopes: ["openid", "profile", "email"],
});

// React to the whole auth lifecycle (audit, webhooks, authz sync)
provider.lifecycle.on((e) => log.info(e.type));
03

Publish for discovery

Discovery

Publishing reads your provider's live OIDC metadata and registers a descriptor so others can find and integrate with you — at the visibility you choose.

Public

Listed in search

Anyone can find and integrate with you. For open platforms and marketplaces.

Unlisted

Hidden from search

Resolvable only if someone knows your exact handle or issuer. The quiet default.

Private

Owner-only

Resolvable solely with an authorized token. For internal or invite-gated providers.

Approved publication workflow

[PRIVATE_BETA_INSTALL_INSTRUCTIONS_REDACTED]

Approved participants can change visibility through the publication workflow or programmatically through publishService(config, { visibility }). The descriptor is rebuilt from the discovery document every time, so it never drifts from reality.

04

Connect to a provider

Integration

This is the part Frauthy exists for. Resolve another provider by handle, register a client against it automatically, and you have a fully wired connection — no secrets traded over chat.

src/connect.ts

import { discover, connectTo } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";
import config from "../frauthy.config";

// 1 — resolve Bob from the registry (or a direct issuer URL)
const bob = await discover("bob", { registry: config.registry.url });

// 2 — register a client against Bob via dynamic client registration
const client = await connectTo(bob, {
  redirectUri: "https://app.alice.com/cb/bob",
  scopes: ["openid", "read:files"],
});

// 3 — send the user through Code + PKCE (keep the verifier for the callback)
const { url, codeVerifier } = await client.authorizationUrl();
// ...on callback:
const tokens = await client.exchange(code, codeVerifier);

Prefer the terminal? frauthy connect bob resolves the provider and prints exactly this wiring for you to drop in.

05

Enforce permissions

Authorization

A valid token is not a yes. Gate each call on token scope and an object-level check — against SpiceDB or Ory Keto, your choice. The two-gate pattern from Page 01.

src/authz.ts

import { createAuthz, FRAUTHY_BASE_SCHEMA } from "[PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED]";
import config from "../frauthy.config";

// kind: "spicedb" | "keto" — picked from config, same surface either way
const { backend, delegation } = createAuthz(config.authz!);
if (backend.supportsSchemaWrite) await backend.writeSchema(FRAUTHY_BASE_SCHEMA);
// (Keto's model is OPL, uploaded with the Ory CLI — see FRAUTHY_KETO_OPL)

// Gate 2: may THIS user edit THIS document?
const { allowed } = await backend.check({
  resource: { type: "document", id: "q3-plan" },
  permission: "edit",
  subject: { type: "user", id: "alice" },
});
if (!allowed) throw forbidden();           → 403

// Delegate read-only to a service — caveat-boxed on SpiceDB, app-boxed on Keto
await delegation.grant({
  resource: { type: "document", id: "q3-plan" },
  service: "alice-bot", mode: "read",
  expiresAt: new Date(Date.now() + 3_600_000),
});
Keep it in sync — Use the provider lifecycle bus from Step 02 to write relationships — into SpiceDB or Keto — whenever clients or memberships change, so your permission graph always mirrors reality. Delegation is data, not code.
06

Run the stack

Locally

Three processes during development. In production each scales on its own.

terminal — three tabs

# 1 · your provider (Ory — or `pnpm dev` for Better Auth)
$ ory tunnel http://localhost:3000

# 2 · local discovery registry
$ pnpm discovery
▸ Frauthy Discovery listening on http://localhost:8788

# 3 · authz engine — Ory Keto (or `spicedb serve --http-enabled`)
$ keto serve -c keto.yml

# publication access is provided to approved participants
[PRIVATE_BETA_INSTALL_INSTRUCTIONS_REDACTED]

That's the whole loop: init → publish → connect → check. Your provider is live on your domain, listed exactly as visibly as you want, integrating with others through standard OIDC — with object-level authorization on every call.

Two pages, one system.

You've now seen both halves of Frauthy — the theory of delegated access and the tools that make operating it a matter of minutes. Keep them side by side.

Page 01 The Field Manual — OAuth, OIDC & SpiceDB theory
Page 02 Getting Started — CLI, SDKs & discovery (you are here)
Case study GP.Family — domain membership in production
Reference SDK — [PRIVATE_BETA_PACKAGE_COORDINATE_REDACTED] API surface
Frauthy · Getting Started · Rev 2026.06