@vela-partners/auth
v0.5.0
Published
Centralized authentication client for Vela apps. Introspects sessions against the landing-page IdP.
Maintainers
Readme
@vela-partners/auth
Centralized single sign-on for every Vela app on *.vela.partners. One package, one env var, drop-in middleware. Users sign in once on landing.vela.partners and are authenticated everywhere — V Platform, Market DJ, admin, any new app.
📖 Full documentation: vela.partners/internal/auth-docs (Vela engineers; requires a @vela.partners sign-in).
Install
pnpm add @vela-partners/authQuick start (Next.js)
The canonical case. Other frameworks (React SPA, SvelteKit, Node, Python) follow the same shape — see the full docs.
1. Point at the IdP — add to .env.local:
NEXT_PUBLIC_LANDING_PAGE_URL=https://vela.partners2. Transpile the package in next.config.mjs:
const nextConfig = {
transpilePackages: ["@vela-partners/auth"],
};3. Add middleware at src/middleware.ts:
import { resolveIssuer } from "@vela-partners/auth";
import { authMiddleware } from "@vela-partners/auth/nextjs";
export default authMiddleware({
issuer: resolveIssuer(process.env.NEXT_PUBLIC_LANDING_PAGE_URL),
// Add `requireWhitelist: true` for LP-gated apps.
});
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|sw.js).*)"],
};4. Wrap your client tree in <AuthProvider>:
"use client";
import { resolveIssuer } from "@vela-partners/auth";
import { AuthProvider } from "@vela-partners/auth/react";
const issuer = resolveIssuer(process.env.NEXT_PUBLIC_LANDING_PAGE_URL);
export const Providers = ({ children }: { children: React.ReactNode }) => (
<AuthProvider issuer={issuer}>{children}</AuthProvider>
);Done. Requests without a session bounce to ${issuer}/auth/signin. After sign-in, the user returns with a cookie scoped to .vela.partners and useAuth() / getSession() work everywhere.
Local development
The session cookie is Domain=.vela.partners; Secure, so it only flows to *.vela.partners hosts over HTTPS — localhost:3000 can't receive it. The package ships a CLI that mints a locally-trusted cert and adds a dev-<app>.vela.partners hosts entry:
npx @vela-partners/auth setup-localThat's it. Re-runs are idempotent. Then point at the real IdP in .env.local and start the dev server:
NEXT_PUBLIC_LANDING_PAGE_URL=https://vela.partners
# or https://staging.thevelapartners.com for staging./dev-<your-app>.shVisit https://dev-<your-app>.vela.partners:3443/ and the full production sign-in flow runs against your real account. See the full docs for the Vite and non-JS-backend setup.
Subpath entries
| Subpath | For |
| ------------------------------- | ---------------------------------------- |
| @vela-partners/auth | Shared types + resolveIssuer |
| @vela-partners/auth/client | Framework-agnostic browser code |
| @vela-partners/auth/nextjs | Next.js middleware + server getSession |
| @vela-partners/auth/react | <AuthProvider>, useAuth |
| @vela-partners/auth/sveltekit | SvelteKit handle + server getSession |
| @vela-partners/auth/svelte | Reactive createAuthStore |
| @vela-partners/auth/node | Express / Fastify requireSession |
Every entry point ultimately calls ${issuer}/api/session. The differences are where the cookie comes from and how the result is delivered.
Reading the session
Server-side (Next.js):
import { resolveIssuer } from "@vela-partners/auth";
import { getSession } from "@vela-partners/auth/nextjs";
export const auth = () =>
getSession({ issuer: resolveIssuer(process.env.NEXT_PUBLIC_LANDING_PAGE_URL) });
// Inside a server component or route handler:
const session = await auth();
if (!session) return <SignInPrompt />;Client-side (React):
"use client";
import { useAuth } from "@vela-partners/auth/react";
const { session, status, signOut } = useAuth();
// status: "loading" | "authenticated" | "unauthenticated"useAuth() refetches on window focus and ~60 s before session expiry, staying in sync with the IdP.
Environment variables
One per consumer:
| Consumer | Variable | Example |
| ------------ | ------------------------------ | -------------------------------------- |
| Next.js | NEXT_PUBLIC_LANDING_PAGE_URL | https://vela.partners |
| Vite SPA | VITE_LANDING_PAGE_URL | passed to <AuthProvider issuer={…}> |
| SvelteKit | PUBLIC_LANDING_PAGE_URL | $env/static/public |
| Node backend | LANDING_PAGE_URL | passed to requireSession({ issuer }) |
resolveIssuer(override?) returns the override if non-empty, otherwise https://vela.partners in production (NODE_ENV === "production") and https://staging.thevelapartners.com everywhere else.
Optional: SUPABASE_JWT_SECRET on a Node backend to enable the zero-network bearer-JWT fast path.
Security
| Property | Value |
| ------------- | ---------------------------------------------------------------------------------------------------------------- |
| Cookie | __Secure-next-auth.session-token on .vela.partners, HttpOnly, SameSite=Lax, Secure |
| Session cache | 30 s in-process |
| CORS | *.vela.partners allow-list (extendable via AUTH_ALLOWED_ORIGINS, supports https://*.example.com wildcards) |
| Sign-out CSRF | X-Auth-Action: signout header check |
Threats addressed: XSS-stealing the cookie (HttpOnly), CSRF on sign-out (header check + strict CORS), cross-domain leak (CORS never returns headers for non-allowed origins), stale revocation (30 s cache TTL).
Versioning
This package follows semver. Breaking changes are flagged in the changelog and in the migration section of the full docs.
Links
- 📖 Full documentation (architecture, recipes, reference, FAQ)
- 🐛 Issues
- 📦 Source
