Platform overview
The Vendidit auth platform is one identity service (Go) plus a family of consumer SDKs that let any browser app or backend service authenticate users against it. Tokens are short-lived HS256 JWTs that downstream services verify locally — no per-request network hop on the hot path.
Hosted services
| URL | What it is |
|---|---|
new-auth.vendidit.com | Production auth-server (Go). API base: /api/v1. Health: /health. |
auth-demo.vendidit.com | Live demo + visual feature catalog. Every atom, form, and flow the browser SDK ships has a working page; the source under auth-client-demo/ is the canonical “how to integrate” reference. |
The auth-server is the single source of truth for users, organizations, roles, permissions, sessions, and refresh tokens. Every SDK is either a typed wrapper around its HTTP surface or a way to validate the JWTs it issues.
Three layers, four runtimes
- The service —
auth-server. Owns Postgres, Redis, the JWT signing secrets, the SSO state store, the audit log, EventBridge schedulers. Everything else is a client. - Browser side —
auth-client(vanilla TS) + framework adapters (./react,./preact,./solid,./vue,./astro) + UI components (./preact/ui/{atoms,forms,flows}). - Backend side — backends pick one of the
auth-server-*packages. TypeScript backends pickauth-server-nest(NestJS module) orauth-server-ts(vanilla TS core). PHP backends pickauth-server-laravel(Laravel adapter) orauth-server-php(vanilla PHP core).
All four TS packages share one type contract via auth-shared — a
source-only package that mirrors the auth-server’s exact wire shape.
All nine packages
The Go identity service. Owns users, orgs, roles, sessions, JWT signing. Single source of truth.
Source-only TS package — JWT claims, principals, wire DTOs, error codes. Mirrors the server’s wire shape.
Browser SDK — login, refresh-rotation, SSO/PKCE, 2FA, magic links. Framework adapters for React, Preact, Solid, Vue, Astro.
Live integration reference at auth-demo.vendidit.com. 45 catalog pages plus full auth routes.
Framework-agnostic TS backend core. AuthClient facade, Flows, JwtValidator, HttpTransport, typed exception hierarchy.
NestJS adapter — module, guards, decorators. Wraps auth-server-ts. Ships Axios + Redis adapters out of the box.
Framework-agnostic PHP 8.1+ core. PSR-18 transport, PSR-16 cache, PSR-3 logger. Mirrors auth-server-ts.
How a request flows
Browser sign-in → backend API call
- Browser app loads
auth-client(configured withapiBaseUrl= the auth-server, and anappCoderegistered for that app). - User submits
<LoginForm>→auth-clientPOSTs/auth/login→ server returns{ access_token, refresh_token, user, organizations, ... }. - Tokens stored via the SDK’s
TokenStoreport (default:localStorage). - Browser makes a request to its own backend API with
Authorization: Bearer <access_token>. - Backend (NestJS / Laravel / whatever) validates the JWT locally with
one of the
auth-server-*SDKs — checks signature against the sharedJWT_ACCESS_SECRET, audience, issuer, expiry, and the per-user token-version cached in Redis. No round-trip to the auth-server on the hot path. - Backend services consume the decoded principal (
AuthenticatedUserorServicePrincipal) — same shape across every SDK because they all import fromauth-shared.
Service-to-service (m2m)
- Service A is registered with the auth-server as an m2m client via the
/admin/m2m-clientsroute, given aclient_id+client_secret. - Service A calls
/oauth/tokenwithgrant_type=client_credentials→ gets a service-scoped access token (token_type: "service"). - Service A calls Service B with that bearer; Service B validates
exactly the same way as a user token. The
ServicePrincipalshape tells it this is an m2m call. ServiceAuthClient(in both TS + PHP cores) handles fetching + caching the m2m token automatically.
Refresh-token rotation
auth-client (browser) and the auth-server-* SDKs (backend) handle
this automatically. Every refresh creates a new refresh-token row
inheriting the parent’s family_id and revokes the parent. If a revoked
token is ever presented again, the auth-server revokes the entire family
(RFC 6819 §5.2.2.3 reuse detection) — the legitimate user has to re-auth.
Cross-cutting conventions
- JWT secret:
JWT_ACCESS_SECRETshared between the auth-server and every backend SDK consumer. Rotation supported viaJWT_ACCESS_SECRET_PREVIOUS— validators try active first, fall back to previous on signature mismatch only. - Wire format: all timestamps are RFC 3339 ISO strings (e.g.
"2026-05-15T17:52:56Z"), not Unix seconds. Money isdecimal(10,2). Snake_case on the wire, camelCase only on normalized principals (AuthenticatedUser,ServicePrincipal). - App scoping: every login request carries an
app_codeidentifying which consuming app. Tokens carryapp_id+app_codeclaims. Backend SDKs can gate requests by app via middleware (vauth.app:auctions). - Permissions:
resource:actionstrings (e.g.users:read,auctions:create). Each consuming service registers its permission manifest on boot viaFlows.registerPermissions()/PermissionRegistrar. - Roles:
system_admin(platform owner, bypasses every gate),super_admin(cross-org data ops),org_admin/org_manager/seller/buyer/org_member(org-scoped),base_user(anyone signed in).
Adding a new consumer
- Browser app:
npm install @vendidit/auth-client, wrap your app in<AuthProvider>, drop in<CompleteLoginFlow>or compose your own UI from theatoms/formsexports. See Browser quickstart. - NestJS backend:
npm install @vendidit/auth-server-nest, registerAuthClientModule.forRoot(...)in yourAppModule. See NestJS quickstart. - Laravel backend:
composer require vendidit/auth-server-laravel, publish config, set the guard, applyvauthmiddleware. See Laravel quickstart. - Anything else: depend on the framework-agnostic core
(
auth-server-tsfor TS,auth-server-phpfor PHP) and build a thin adapter for your framework.
Next reads
- Architecture — package dependency diagram, runtime × language view, secret topology.
- App registration — the one-time onboarding step every consumer goes through.
auth-serveroverview — the Go service.auth-clientoverview — the browser SDK.