One identity.
All your apps.
An open-source OAuth 2.1 and OpenID Connect provider on Cloudflare Workers. Your apps add “Sign in with willy.im” and delete their account code.
{
"iss": "https://idp.willy.im/auth",
"sub": "usr_8f2c1a",
"email": "ada@example.com",
"picture": "https://idp.willy.im/avatar/usr_8f2c1a",
"https://willy.im/workspaces": [
{ "id": "ws_acme", "role": "admin" }
],
"permissions": [
"notes:read",
"notes:write",
"kirby:thread:t_14f451b6"
]
}The ID token Notes gets back. Example values.
Everything an app would otherwise build
Authentication is central. Authorization stays with each app, fed by claims the IdP resolves.
- Sign-in
Magic links, one-time codes and passkeys
No passwords to store. Discord and other identities can be linked to the same account. - One identity
One user store, many apps
A person has a single identity across every app. Each app decides who gets in and what they can do. - Workspaces
Per-app workspaces and roles in the token
The token lists every workspace the user has in the requesting app, and never one from another app. - Permissions
A permission catalog per app
Apps declare their permissions. The IdP resolves each user's grants, down to a single resource like one thread, and emits them as a claim. - API keys
Scoped keys for machines
Keys carry the same permissions model as tokens, so apps can drop their own key systems. - Management API
HTTP, OpenAPI and MCP, built on kit
Every console action is one method, so an agent can provision apps, users and workspaces with a key. - Console
Admin console for apps, users and members
Invitations, secret rotation, impersonation and an audit log of every change. - Custom domains
Your app's own sign-in domain
Serve the IdP from idp.yourapp.com so its session cookie stays first-party.
Add it to an app
@willyim/idp is the client: OIDC, server sessions and React Router guards. Its only runtime dependency is zod, and it runs on Workers, Node and Bun.
import { createIdp } from "@willyim/idp"
import { drizzleSessions } from "@willyim/idp/drizzle"
export const idp = createIdp({
issuer: "https://idp.willy.im/auth",
clientId: env.IDP_CLIENT_ID,
clientSecret: env.IDP_CLIENT_SECRET,
sessions: drizzleSessions(db, schema.idpSession),
session: { secret: env.SESSION_SECRET, expiresIn: "30d" },
})$npm install @willyim/idp
Run your own
No plans, no seats, no tiers. Fork it and it's yours. The whole IdP is one Worker and one D1 database.
Fork the repository
The IdP lives inapps/idp.Create a D1 database
Pointwrangler.jsoncat it, then apply the migrations.wrangler d1 create idp npm run db:migrate-prod --workspace=idpDeploy
npm run deploy --workspace=idp
Roadmap
It already runs sign-in for the apps on willy.im.
Shipped
- Email and passkey sign-in
- OIDC provider and discovery
- Workspaces and role claims
- Permission catalogs and resource grants
- API keys and management keys
- Admin console, audit, impersonation
- Custom domains
In progress
- Per-app user metadata
Later
- Profile editing in one place
- MFA
- Organizations
- SAML and SCIM