Himayah LogoHimayah

Getting Started

Install Himayah, define your schema, mount the handler, and have a working auth system in under 10 minutes.

Getting Started

This guide walks you through installing Himayah, connecting it to your database, and mounting it in your framework of choice.

[!TIP] Supercharge Your Setup: Skip the manual setup entirely! You can get a production-ready authentication configuration in seconds by running the interactive scaffolding CLI:

npx himayah init

This tool dynamically configures your choice of 8 frameworks, 3 ORMs, 3 databases, and handles schemas, route handlers, and configurations automatically.

Himayah requires TypeScript 5+ and a runtime that supports the Web Crypto API (Node.js 18+, Cloudflare Workers, Deno, Bun, Vercel Edge, etc.).


Install Packages

Install the core packages first. You'll always need @himayah/core and @himayah/session:

pnpm add @himayah/core @himayah/session

Then install the database adapter that matches your ORM:

pnpm add @himayah/adapter-drizzle

And any auth plugins you want:

# Password credentials (email + password)
pnpm add @himayah/plugin-password
 
# OAuth (GitHub, Google, any OIDC provider)
pnpm add @himayah/plugin-oauth
 
# Passwordless email magic links
pnpm add @himayah/plugin-magic-link
 
# One-time passcodes via SMS or email
pnpm add @himayah/plugin-otp

Define Your Schema

Himayah is schema-first: you define the tables. Here's a minimal working schema:

If using Drizzle ORM, you can bypass writing 100+ lines of table schema code. Simply use the programmatic schema builder inside your schema definition file:

schema.ts
import { pgTable } from "drizzle-orm/pg-core";
import { createPGSchemas } from "@himayah/adapter-drizzle";
 
// Generates all required auth tables programmatically (users, sessions, passwords, verificationTokens, etc.)
export const authSchemas = createPGSchemas(pgTable);
 
// Export individual tables if needed
export const {
  users,
  sessions,
  passwords,
  verificationTokens,
  accounts,
  orgs,
  members,
  invitations,
  rateLimits,
} = authSchemas;

(Note: Dialect-specific builders createSQLiteSchemas and createMySQLSchemas are also available inside @himayah/adapter-drizzle!)

Initialize Himayah

Create a file — typically lib/auth.ts — and initialize the createAuth coordinator:

lib/auth.ts
import { createAuth } from "@himayah/core";
import { createJWTSessionStore } from "@himayah/session";
import { drizzleAdapter } from "@himayah/adapter-drizzle";
import { passwordPlugin } from "@himayah/plugin-password";
import { db } from "./db";
import {
  users,
  accounts,
  sessions,
  verificationTokens,
  passwords,
} from "./schema";
import { eq } from "drizzle-orm";
 
export const auth = createAuth({
  // 1. Connect your database via the adapter
  adapter: drizzleAdapter(db, {
    users,
    accounts,
    sessions,          // omit if using stateless sessions
    verificationTokens,
  }),
 
  // 2. Configure the JWE session store
  //    Use a 32-byte+ secret — set in env variables, never hardcode in production
  sessionStore: createJWTSessionStore({
    secret: process.env.AUTH_SECRET!,
    maxAge: 30 * 24 * 60 * 60, // 30 days in seconds
  }),
 
  // 3. Add your plugins
  plugins: [
    passwordPlugin({
      async getPasswordHash(userId) {
        const pw = await db.query.passwords.findFirst({
          where: eq(passwords.userId, userId),
        });
        return pw?.hash ?? null;
      },
      async setPasswordHash(userId, hash) {
        await db
          .insert(passwords)
          .values({ userId, hash })
          .onConflictDoUpdate({ target: passwords.userId, set: { hash } });
      },
    }),
  ],
 
  // 4. Set the canonical base URL (used for OAuth callbacks and magic links)
  baseUrl: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
 
  // 5. Cookie config
  cookieName: "himayah.sid",
  csrf: true, // Double-submit CSRF protection (recommended)
});

Set AUTH_SECRET to a cryptographically random 32+ character string. Generate one with:

openssl rand -base64 32

Mount the Handler

Himayah exposes a single auth.handleRequest(req) handler that maps to your framework's routing:

Create a single-line catch-all route using the specialized Next.js SDK wrapper:

app/api/auth/[[...route]]/route.ts
import { auth } from "@/lib/auth";
import { createNextHandler } from "@himayah/next";
 
// Mounts GET, POST, PUT, DELETE, PATCH, and OPTIONS handlers automatically in 1 line
export const { GET, POST, PUT, DELETE, PATCH, OPTIONS } = createNextHandler(auth);

This maps and handles all routes under /api/auth/* dynamically (sign-in, sign-up, OAuth callbacks, magic links, etc.).

Use the Client

Install the type-safe browser client and call auth endpoints from your frontend:

pnpm add @himayah/client
lib/auth-client.ts
import { createAuthClient } from "@himayah/client";
 
export const authClient = createAuthClient({
  baseUrl: "/api/auth",
});

Then use it in your components:

// Sign up a new user
const result = await authClient.password.signUp({
  email: "user@example.com",
  password: "hunter2",
  name: "Jane Doe",
});
 
// Sign in an existing user
const session = await authClient.password.signIn({
  email: "user@example.com",
  password: "hunter2",
});
 
// Get the current session
const { data: session } = await authClient.getSession();
 
// Sign out
await authClient.signOut();

Reading Sessions on the Server

You can read the current user's session on any server-side code:

Retrieve user sessions inside Next.js Server Components, Server Actions, or API Routes without manually building standard request headers:

app/dashboard/page.tsx
import { auth } from "@/lib/auth";
import { getServerSession } from "@himayah/next";
import { redirect } from "next/navigation";
 
export default async function DashboardPage() {
  // Automatically extracts Next.js request headers and cookies asynchronously
  const session = await getServerSession(auth);
 
  if (!session.ok) {
    redirect("/login");
  }
 
  return <div>Welcome, {session.data.userId}</div>;
}

Environment Variables Reference

.env
# Required — generate with: openssl rand -base64 32
AUTH_SECRET="your-super-secret-32-char-minimum-key"
 
# Required — your app's canonical public URL
NEXT_PUBLIC_APP_URL="https://yourapp.com"
 
# Required if using OAuth providers
GITHUB_CLIENT_ID="..."
GITHUB_CLIENT_SECRET="..."
GOOGLE_CLIENT_ID="..."
GOOGLE_CLIENT_SECRET="..."
 
# Required if using Redis rate limiting
REDIS_URL="redis://localhost:6379"

On this page