@novahelm/auth
v2026.6.2
Published
NovaAuth — full-stack authentication (argon2 + WebAuthn + JWT) for web and mobile.
Downloads
778
Maintainers
Readme
@novahelm/auth
Drop-in authentication for NovaHelm — NovaAuth, the first-party auth system with support for email/password, OAuth, magic links, passkeys, 2FA, phone/SMS, anonymous auth, enterprise SSO, and organizations. Works across web (Next.js / Vite / any React host) and mobile (Expo/React Native) with cookie-based and Bearer token sessions respectively.
Quick Start
pnpm add @novahelm/auth// server — create the auth instance
import { createAuth } from "@novahelm/auth/server";
const auth = createAuth({
db: drizzleClient,
secret: env.NOVA_AUTH_SECRET,
baseUrl: env.APP_URL,
providers: {
google: { clientId: env.GOOGLE_ID, clientSecret: env.GOOGLE_SECRET },
github: { clientId: env.GITHUB_ID, clientSecret: env.GITHUB_SECRET },
},
enableMagicLink: true,
enableTwoFactor: true,
});// client — create the web auth client
import { createAuthClient } from "@novahelm/auth/client";
const authClient = createAuthClient({
baseUrl: "http://localhost:3000",
});
// Use auth hooks
const { data: session } = authClient.useSession();
await authClient.signIn.email({ email, password });
await authClient.signUp.email({ name, email, password });
await authClient.signOut();Subpath Exports
| Import path | Entry point | Purpose |
|-------------|-------------|---------|
| @novahelm/auth | src/index.ts | Core auth factory, permissions, API key utilities |
| @novahelm/auth/server | src/server.ts | createAuth() — server-side NovaAuth factory with all plugins |
| @novahelm/auth/client | src/client.ts | createAuthClient() and createNativeAuthClient() factories |
| @novahelm/auth/edge | src/edge.ts | getSessionFromCookie() — edge-runtime session parsing (no DB) |
| @novahelm/auth/components | src/components.ts | 16 headless auth UI components |
| @novahelm/auth/react | src/react.tsx | Drop-in React provider, hooks, and pre-built auth components |
| @novahelm/auth/nextjs | src/nextjs.tsx | createAuthPages() — catch-all route factory for Next.js |
| @novahelm/auth/nextjs/middleware | src/nextjs-middleware.ts | withNovaAuth() — edge middleware for route protection |
| @novahelm/auth/native | src/native.tsx | Expo/React Native provider, hooks, and screen wrappers |
Server Setup
createAuth() returns a NovaAuth instance with the database adapter and all configured plugins. Register it in the Nova registry via initNova().
import { createAuth, type AuthConfig } from "@novahelm/auth/server";
const auth = createAuth({
db: drizzleClient,
secret: env.NOVA_AUTH_SECRET,
baseUrl: env.APP_URL,
// OAuth providers (all optional)
providers: {
google: { clientId: "...", clientSecret: "..." },
github: { clientId: "...", clientSecret: "..." },
discord: { clientId: "...", clientSecret: "..." },
apple: { clientId: "...", clientSecret: "..." },
microsoft: { clientId: "...", clientSecret: "...", tenantId: "..." },
facebook: { clientId: "...", clientSecret: "..." },
twitter: { clientId: "...", clientSecret: "..." },
linkedin: { clientId: "...", clientSecret: "..." },
},
// Feature toggles
enableMagicLink: true, // Passwordless magic link (default: true)
enableTwoFactor: true, // TOTP two-factor auth
enablePasskey: true, // WebAuthn / passkey
enableEmailOtp: true, // Email OTP verification
enableAnonymous: true, // Anonymous / guest auth
enablePhoneAuth: true, // Phone / SMS OTP
enableHaveIBeenPwned: true, // Password breach checking
enableSSO: true, // Enterprise SAML 2.0 + OIDC
// Callbacks
onUserCreated: async (user) => {
await welcomeEmail(user.email);
},
// Session config
sessionExpiresIn: 60 * 60 * 24 * 7, // 7 days (default)
});Client Setup
Web (React / Next.js)
import { createAuthClient } from "@novahelm/auth/client";
export const authClient = createAuthClient({
baseUrl: process.env.NEXT_PUBLIC_APP_URL!,
});The client provides hooks: useSession(), signIn.email(), signIn.oauth(), signUp.email(), signOut(), and more.
Mobile (Expo / React Native)
import { createNativeAuthClient } from "@novahelm/auth/client";
import * as SecureStore from "expo-secure-store";
export const authClient = createNativeAuthClient({
baseUrl: "https://api.example.com",
secureStorage: {
getItem: SecureStore.getItemAsync,
setItem: SecureStore.setItemAsync,
removeItem: SecureStore.deleteItemAsync,
},
});Mobile clients use Bearer token auth with secure storage instead of cookies. The storage adapter must implement getItem, setItem, and removeItem.
React Provider & Components
The /react subpath provides a complete drop-in auth UI for web apps.
Provider
import { NovaAuthProvider } from "@novahelm/auth/react";
export function App({ children }) {
return (
<NovaAuthProvider
baseUrl="http://localhost:3000"
oauthProviders={["google", "github"]}
>
{children}
</NovaAuthProvider>
);
}Hooks
import { useAuth } from "@novahelm/auth/react";
function Dashboard() {
const { user, isAuthenticated, isLoading, signOut } = useAuth();
if (isLoading) return <Spinner />;
if (!isAuthenticated) return <Redirect to="/auth/sign-in" />;
return <p>Welcome, {user.name}</p>;
}Pre-built Components
| Component | Description |
|-----------|-------------|
| SignIn | Sign-in form with email/password, OAuth, passkey support. Modes: page or modal |
| SignUp | Sign-up form with name, email, password, and OAuth |
| UserButton | Avatar dropdown with profile link and sign-out |
| UserProfile | Full profile management (info, password, 2FA, sessions, connected accounts, danger zone) |
| SignedIn | Render children only when authenticated |
| SignedOut | Render children only when not authenticated |
| ProtectedRoute | Redirect to sign-in if unauthenticated |
Modal components have .Trigger sub-components for inline use:
import { SignIn, SignedIn, SignedOut, UserButton } from "@novahelm/auth/react";
<SignedOut>
<SignIn mode="page" afterSignInUrl="/dashboard" />
</SignedOut>
<SignedIn>
<UserButton afterSignOutUrl="/" />
</SignedIn>Next.js Integration
Auth Pages (Catch-All Route)
Create a single catch-all route that handles sign-in, sign-up, forgot-password, reset-password, verify-email, and profile pages:
// app/auth/[[...slug]]/page.tsx
import { createAuthPages } from "@novahelm/auth/nextjs";
export default createAuthPages({
afterSignInUrl: "/dashboard",
afterSignUpUrl: "/dashboard",
logo: <MyLogo />,
});This maps URL slugs to pages automatically: /auth/sign-in, /auth/sign-up, /auth/forgot-password, /auth/reset-password, /auth/verify-email, /auth/profile.
Edge Middleware
Protect routes at the edge without a database round-trip:
// middleware.ts
import { withNovaAuth } from "@novahelm/auth/nextjs/middleware";
export default withNovaAuth({
publicRoutes: ["/", "/auth/*", "/api/auth/*", "/pricing"],
signInUrl: "/auth/sign-in",
secret: process.env.NOVA_AUTH_SECRET, // enables HMAC signature validation
});
export const config = { matcher: ["/((?!_next|.*\\..*).*)"] };When secret is provided, the middleware validates the session cookie's HMAC-SHA256 signature and checks expiration using only the Web Crypto API (no DB). Without secret, it falls back to a simple cookie-presence check.
Expo / React Native
import { NovaAuthProvider, useNovaAuth, SignedIn, SignedOut, ProtectedRoute } from "@novahelm/auth/native";
import { SignInScreen, SignUpScreen } from "@novahelm/auth/native";
import * as SecureStore from "expo-secure-store";
const storage = {
getItem: SecureStore.getItemAsync,
setItem: SecureStore.setItemAsync,
removeItem: SecureStore.deleteItemAsync,
};
// Root layout
<NovaAuthProvider
baseUrl="https://api.example.com"
storage={storage}
oauthProviders={["google", "apple"]}
>
<SignedIn>
<HomeScreen />
</SignedIn>
<SignedOut>
<SignInScreen logo={<Logo />} onNavigate={(screen) => router.push(`/${screen}`)} />
</SignedOut>
</NovaAuthProvider>
// Protected route with redirect
<ProtectedRoute redirectTo="/sign-in" fallback={<LoadingSpinner />}>
<DashboardScreen />
</ProtectedRoute>Headless Components
The /components subpath exports 16 unstyled auth components for custom UIs:
| Component | Description |
|-----------|-------------|
| OAuthButtons | Social login button set |
| EmailVerification | Email verification flow |
| ResetPasswordForm | Password reset form |
| ForgotPasswordForm | Forgot password form |
| EmailOtpVerify | Email OTP input |
| PasskeySetup | Passkey registration |
| PasskeySignIn | Passkey authentication |
| PhoneSignIn | Phone/SMS sign-in |
| ProfileForm | User profile editor |
| PasswordChangeForm | Password change form |
| TwoFactorSetup | 2FA TOTP setup with QR code |
| ActiveSessions | Session management list |
| ConnectedAccounts | Linked OAuth account management |
| DataExport | User data export request |
| DangerZone | Account deletion |
| ApiKeyManager | API key CRUD interface |
Permissions & Authorization
The middleware module provides role-based access control and plan gating:
import {
hasPermission,
requirePermission,
requirePlan,
requireFeature,
checkLimit,
} from "@novahelm/auth";
// Check permissions
const canEdit = await hasPermission(userId, "posts.write");
// Guard — throws NovaError("FORBIDDEN") if denied
await requirePermission(userId, "posts.write", "posts.delete");
// Plan gating — throws NovaError("PLAN_REQUIRED") if wrong plan
await requirePlan(userId, "pro", "enterprise");
// Feature gating — checks plan.features array
await requireFeature(userId, "ai-chat");
// Usage limits — returns { allowed, limit } without throwing
const { allowed, limit } = await checkLimit(userId, "api-calls", currentCount);The system.manage permission acts as a wildcard and satisfies any permission check.
API Key Management
Generate, hash, and validate API keys for programmatic access:
import { generateApiKey, validateApiKey, checkApiKeyScope, VALID_API_KEY_SCOPES } from "@novahelm/auth";
// Generate a new key (show the raw key to the user once)
const { key, hash, prefix } = generateApiKey("sk_live");
// key: "sk_live_a1b2c3..." (full key — show once)
// hash: "abc123..." (SHA-256 — store in DB)
// prefix: "sk_live_a1b2" (identification prefix)
// Validate an incoming key (checks DB, updates lastUsedAt)
const ctx = await validateApiKey(incomingKey);
if (!ctx) throw new Error("Invalid API key");
// ctx: { keyId, userId, permissions, rateLimit }
// Check scope
const allowed = checkApiKeyScope("collections:read", ctx.permissions);Available scopes: collections:read, collections:write, media:read, media:upload, schema:read, ai:chat, * (full access).
Edge Session Parsing
Parse NovaAuth session cookies without a database connection — useful for edge middleware and serverless functions:
import { getSessionFromCookie, type EdgeSession } from "@novahelm/auth/edge";
const session = await getSessionFromCookie(
request.headers.get("cookie") ?? "",
process.env.NOVA_AUTH_SECRET!,
);
if (session) {
// session: { userId, expiresAt, role, planId }
}Uses Web Crypto API only (edge-compatible). Validates HMAC-SHA256 signature and checks expiration.
Session Handling
| Platform | Transport | Storage |
|----------|-----------|---------|
| Web (Next.js / React) | HTTP-only cookies | Browser cookie jar |
| Mobile (Expo / React Native) | Authorization: Bearer <token> header | Secure storage (e.g. expo-secure-store) |
Sessions default to 7-day expiration with daily refresh. Cookie caching is enabled with a 5-minute TTL.
API Reference
| Export | Subpath | Description |
|--------|---------|-------------|
| createAuth(config) | /server | Create NovaAuth instance with database adapter |
| createAuthClient(config) | /client | Create web auth client (cookie-based) |
| createNativeAuthClient(config) | /client | Create native/mobile auth client (Bearer token) |
| createAuthContext(req) | . | Extract auth context from request |
| getUserPermissions(userId) | . | Fetch all user permissions |
| hasPermission(userId, ...perms) | . | Check user has all permissions |
| requirePermission(userId, ...perms) | . | Guard — throws if permission missing |
| requirePlan(userId, ...plans) | . | Guard — throws if plan mismatch |
| requireFeature(userId, feature) | . | Guard — throws if feature unavailable |
| checkLimit(userId, resource, count) | . | Check usage limit (non-throwing) |
| generateApiKey(prefix?) | . | Generate API key + hash + prefix |
| hashApiKey(key) | . | SHA-256 hash an API key |
| validateApiKey(key) | . | Validate key against DB |
| checkApiKeyScope(required, granted) | . | Check scope permission |
| getSessionFromCookie(cookie, secret) | /edge | Edge-compatible session parsing |
| withNovaAuth(config) | /nextjs/middleware | Next.js edge middleware factory |
| createAuthPages(config?) | /nextjs | Catch-all auth page route factory |
| NovaAuthProvider | /react | Web auth context provider |
| useAuth() | /react | Web auth state hook |
| SignIn, SignUp, UserButton, UserProfile | /react | Pre-built auth components |
| SignedIn, SignedOut, ProtectedRoute | /react | Conditional rendering helpers |
| NovaAuthProvider | /native | Mobile auth context provider |
| useNovaAuth() | /native | Mobile auth state hook |
| SignInScreen, SignUpScreen | /native | Expo screen wrappers |
