Kayan
A headless identity and access management library for Go: authentication, sessions, authorization, SSO and multi-tenancy, without taking over your router, schema or database.

The use case
Almost every backend needs identity: sign-up and login, sessions, roles, maybe single sign-on for a company customer, maybe separate tenants. Kayan is a Go library that provides that whole layer (authentication, sessions, authorization, federation, provisioning, audit and multi-tenancy) as code you import, not a service you run beside your app.
It is what signs people in to GDGoC Admin: Google login, whitelist-gated registration and per-chapter roles for the organisers of 20+ chapters.
The problem
Identity libraries tend to force one of two bad trades.
Frameworks do everything, but on their terms: you adopt their router, their user table, their session model and often their database. Fitting one into an existing service means reshaping the service around it.
Toolkits hand you primitives, such as a JWT library, a hash function or a SAML parser, and leave the assembly to you. That assembly is exactly where authentication bypasses come from: the signature checked over one part of a document while claims are read from another, a token algorithm taken from the token itself, a redirect URL matched by prefix.
Kayan takes a third position: the security decisions live inside the library; the architectural decisions stay in your application.
What it does
- Authentication: password, magic link, email and SMS one-time codes, TOTP, WebAuthn passkeys, API keys, recovery codes, LDAP, and social login over OIDC, with step-up, rate limiting and lockout.
- Sessions: stateless JWT or revocable database-backed sessions.
- Authorization: role-based access with role inheritance and wildcard permissions (
users:*matchesusers:delete), attribute-based and relationship-based access. - Federation and provisioning: acting as an OAuth 2.0 / OpenID Connect provider, SAML 2.0 as both service provider and identity provider, and SCIM 2.0 user provisioning.
- Multi-tenancy: eight ways to work out which tenant a request belongs to, with isolation enforced in the storage layer.
- Operations: audit logging, consent, health checks and OpenTelemetry metrics.
How it is built
Three rules shape the whole API.
- Headless. Kayan has no router and never writes an HTTP response. It does own parsing and validation:
ParseAuthorizeRequestenforces the exact-matchredirect_uriallowlist and the PKCE policy itself, because a caller who parses the query string by hand has to reimplement both, and that is where open redirects come from. - Secure default, real seam. Every algorithm, hash, store and clock sits behind an interface with a safe default. RS256 signing can become Ed25519, or a signer whose key never leaves a KMS; bcrypt can become argon2id in one line. PKCE is required unless you explicitly turn it off, and each escape hatch documents what it gives up.
- Bring your own schema. No base struct to embed, no reserved columns, no generics. Your
Usertype stays yours. Generics were ruled out on purpose: a type parameter spreads to every manager, strategy and hook, and a service with two identity models would end up with two copies of the library.
The code is split into eight Go modules (core, plus GORM, Redis, OIDC provider, SAML, SCIM, LDAP and a testing kit), so a service that only needs password login compiles no OAuth, SAML or SCIM code. core never imports a sibling module, and CI enforces it. Checked-in API baselines flag any accidental change to the public surface.
Security by construction
The defences are built so they cannot be skipped by accident:
- SAML signature wrapping is prevented by the types. Verification returns only the element the signature covers; the unverified document is never carried alongside it, so there is nothing unverified to read claims from.
- JWT algorithm confusion is prevented by routing every token parse through one function that pins the expected algorithm, so an attacker cannot re-sign a token as HS256 using the published public key.
- Stolen refresh tokens are caught: spent tokens are kept, not deleted, so a replay is recognised and the whole token family is revoked.
- Account enumeration by timing is closed: a login for an unknown account still pays for a hash comparison, so it takes as long as a real one.
- Tenant isolation fails closed: a scoped query with no tenant in context is an error, never an unscoped read and never a silently empty result.
Testing is adversarial. Security tests are tables of hostile inputs (fourteen malicious redirect_uri variants, signature-wrapping layouts, algorithm confusion on every parse path), and each asserts the specific error rather than just "an error". Nine fuzz targets cover the parsers that read untrusted bytes, and every security fix was verified by reverting it and watching its test fail.
Status and results
Kayan is open source under Apache 2.0, at v0.3.0 (September 2026), with eleven runnable example backends and a tested PostgreSQL + Redis production reference. It runs in production as the identity layer for GDGoC Admin.
It is deliberately honest about being pre-1.0: the README lists its known gaps (for example, no OAuth device flow or dynamic client registration yet) and the public API can still change. Protocol certification and an independent security review are the next milestones.

