@zero.sc/auth-client
v0.0.7
Published
Zero ID client — OAuth routes, a single session check and a local-development opening.
Maintainers
Readme
The problem this solves
Authentication is the part of an app most likely to be written five times and reviewed once. Each product grows its own callback handler, its own cookie flags, its own refresh timing — and the differences between them are invisible until one of them is wrong.
They are also the differences that matter. A cookie without httpOnly is readable by any
script on the page. A refresh without a lock stampedes when four tabs wake together. A state
parameter that is generated but never compared makes the whole redirect dance decorative.
This package is that flow written once. It is not a framework for authentication; it speaks to exactly one identity provider and does so completely.
Install
npm install @zero.sc/auth-clientnext 16 and react 19 are peers. jose does the token verification.
Sixty seconds
One route file handles the entire round trip.
// app/api/auth/[...zero]/route.ts
import { createAuthRoutes } from '@zero.sc/auth-client/server';
export const { GET, POST } = createAuthRoutes();Guard the pages that need a session.
// middleware.ts
import { authMiddleware } from '@zero.sc/auth-client/server';
export const middleware = authMiddleware();
export const config = { matcher: ['/console/:path*', '/admin/:path*'] };Read the user in a component.
'use client';
import { useUser } from '@zero.sc/auth-client';
export function Greeting() {
const { user, loading } = useUser();
if (loading) return null;
return <span>{user ? user.email : 'Signed out'}</span>;
}Pass initialUser to AuthProvider from a server component and the first paint is already
correct — no flash of a signed-out header on a signed-in page.
What's inside
| Module | Responsibility |
|--------|----------------|
| flow | Authorisation request, PKCE challenge, state, the code exchange |
| tokens | Verification, claims, expiry, refresh with a single-flight lock |
| session | The cookie — httpOnly, secure, sameSite, chunked when it must be |
| routes | /login, /callback, /logout, /session as one handler pair |
| gate | Middleware that redirects rather than rendering a broken page |
| redirect | Return-path validation — an allowlist, not a regex |
| dev-auth | Local development without a network round trip |
| client | AuthProvider, useUser |
The contract it keeps
- Tokens live in
httpOnlycookies. NotlocalStorage. A token a script can read is a token an injected script can take. - PKCE is not optional. The verifier stays on your server, so an intercepted code cannot be redeemed by whoever intercepted it.
- Return paths are validated against an allowlist. Open redirects begin as a convenient
?next=parameter, every time. - Refresh is single-flight. Concurrent requests wait on one refresh instead of racing to spend the same token, which is how a fleet invalidates its own session.
- One provider, on purpose. Not a pluggable strategy layer with Zero ID as one option. Every branch a plug-in architecture would add is a branch nobody tests.
Local development
Signing in through a hosted provider while offline is friction with no security value. Local
mode grants a dev@localhost identity — behind three conditions, all of which must hold,
and which fail closed if any one is missing: an exact NODE_ENV match, an explicit opt-in
environment variable, and a loopback address. A deployed surface satisfies none of them.
Honest limits
- Next.js App Router only. The route handlers and middleware are Next-shaped. The token and flow modules underneath are not, but no adapters are published for anything else.
- One identity provider. By design — see above. If you need a second, this is not it.
- Silent SSO is opt-in and asks first. It is only attempted where the server has agreed to understand the request. A provider that treats it as an ordinary login shows a login page to someone who merely opened the landing page.
- No role or permission model. You get a verified identity and claims. Authorisation is yours.
- This is 0.0.x. The cookie and route shapes are settled; the client-side surface is the part still moving.
Around it
| | |
|---|---|
| Docs | kit.zero.sc |
| Calling services | @zero.sc/sdk |
| Components | @zero.sc/ui |
| A whole app, already wired | @zero.sc/create |
| Everything | @zero.sc |
License
MIT OR Zero License v1.0 — take whichever you prefer. Choosing MIT is enough; nothing further is required of you.
The Zero name, marks and logos are not covered — build anything you like with this code, just don't present it as a Zero product.
Copyright (c) 2026 Zero. Source Code begins at Zero.
