Quickstart
Get your first login working in under five minutes. You need Node.js 20+ and a Postgres database.
Install
npm install own-authSet Your Environment Variables
Add your Postgres connection string and token pepper to your environment.
DATABASE_URL=postgres://user:password@localhost:5432/myapp
OWN_AUTH_TOKEN_PEPPER=your-random-secret-stringThe token pepper adds an extra layer of protection to hashed tokens in your database. Generate a long random string and keep it secret.
Any Postgres database works: local, hosted, Supabase, Neon, Railway, or RDS. Own Auth creates its own tables and does not modify yours.
Run Migrations
npx own-auth migrateThis creates the tables Own Auth needs: users, sessions, tokens, organisations, API keys, and audit logs. Your existing tables are not modified.
Run this once during setup.
Verify the database connection and migration version:
npx own-auth statusCreate Your Auth Instance
Create an auth.ts file in your project. This is the single entry point for all auth operations.
import { createOwnAuth } from "own-auth";
export const auth = createOwnAuth({
tokenPepper: process.env.OWN_AUTH_TOKEN_PEPPER,
session: {
ttlMs: 30 * 24 * 60 * 60 * 1000, // 30 days
},
});That is your auth layer. Every function in this guide is called on this auth instance. Own Auth reads DATABASE_URL automatically.
Sign Up A User
const { user, session, sessionToken } = await auth.signUpEmailPassword({
email: "alice@example.com",
password: "her-secret-password",
name: "Alice",
});
// user.id -> "usr_a1b2c3..."
// user.email -> "alice@example.com"
// user.name -> "Alice"
// sessionToken -> send this to the client securely
// session.expiresAt -> 2026-08-09T...The password is hashed before it is stored. Own Auth never saves plain-text passwords. The session is created automatically, so the user is signed in as soon as they sign up.
If the email is already taken, signUpEmailPassword throws a typed error you can catch and handle:
import { isAuthError } from "own-auth";
try {
const { user, session, sessionToken } = await auth.signUpEmailPassword({
email,
password,
name,
});
} catch (error) {
if (isAuthError(error) && error.code === "email_already_exists") {
// Handle the duplicate email.
}
throw error;
}Use isAuthError() in catch blocks. It remains reliable when a server bundle contains more than one copy of own-auth.
Sign In
const result = await auth.signInEmailPassword({
email: "alice@example.com",
password: "her-secret-password",
});
if (result.status === "mfa_required") {
// Show one of result.methods and complete the second factor.
} else {
// result.sessionToken -> send this to the client securely
// result.session.userId -> "usr_a1b2c3..."
// result.session.expiresAt -> 2026-08-09T...
}For users without MFA, sign-in completes immediately and the session token identifies the user on future requests. Send it to the client as a cookie, a header, or however your application handles tokens. Users with MFA receive status: "mfa_required" and no session until they complete a second factor.
If the credentials are wrong, signInEmailPassword throws AuthError with the code invalid_credentials. The error deliberately does not reveal whether the email or password was wrong.
Verify A Session
On every authenticated request, verify the session token to identify the user.
const result = await auth.getCurrentSession(sessionToken);
if (!result) {
// Not signed in. Return 401.
}
// result.session.userId -> "usr_a1b2c3..."
// result.user -> { id, email, name, ... }getCurrentSession checks the token against the database. If the session is expired or has been revoked, it returns null.
Sign Out
await auth.signOut(sessionToken);The session is revoked immediately. The token stops working on the next request.
Full Example
Here is everything together in a minimal script. No framework, just plain TypeScript. Swap in Express, Hono, Fastify, or whatever you use.
import { auth } from "./auth";
// Sign up.
const { sessionToken } = await auth.signUpEmailPassword({
email: "alice@example.com",
password: "her-secret-password",
name: "Alice",
});
// Send sessionToken to the client as a cookie, header, or another secure value.
// Later, verify the session on an incoming request.
const result = await auth.getCurrentSession(sessionToken);
if (result) {
console.log("Signed in as", result.user.email);
}
// Sign out.
await auth.signOut(sessionToken);That is it. Users, passwords, and sessions are working. Everything is in your Postgres database, under your control.
Use The HTTP Handler
Applications that want ready-made auth endpoints can mount one framework-neutral Web handler:
import { createOwnAuthHandler } from "own-auth/http";
import { auth } from "./auth";
export const authHandler = createOwnAuthHandler(auth);The handler provides signup, signin, sessions, signout, password flows, magic links, email verification, SMS verification, invitation acceptance, OAuth, SAML SSO, MFA, and passkeys under /api/auth. It sets secure HttpOnly cookies, checks browser request origins, and returns one documented error format.
Browser TypeScript can use the matching client:
import { createOwnAuthClient } from "own-auth/client";
export const authClient = createOwnAuthClient();React applications can use the matching client and session hook from own-auth/react:
import { createOwnAuthReactClient } from "own-auth/react";
export const authClient = createOwnAuthReactClient();
// Inside a component:
// const { data, isPending, error } = authClient.useSession();See the HTTP handler and TypeScript client guides for the complete route and error contract.
What's Next
You have basic email/password auth. Here is where to go next:
Auth methods: Add passwordless login with magic links, or phone-based login with SMS verification.
OAuth: Add Google, GitHub, or Apple through OAuth and external providers.
OAuth server: Let other applications authorize users through the OAuth and OpenID Connect server, including device authorization for command-line tools and input-limited devices.
Enterprise SSO: Connect an organisation identity provider through SAML SSO.
Enterprise provisioning: Provision organisation users and memberships through SCIM.
MFA and passkeys: Add TOTP and recovery codes, or use passkeys for sign-in and MFA.
Sessions: Learn how session management works, including revoking sessions across devices.
Organisations: Add teams, roles, and invitations for multi-tenant applications.
API access for integrations: Issue scoped application API keys for programmatic access to your application's API.
Email delivery: Set up your own email provider, or use Own Auth Delivery to send magic links, verification emails, and invitations without configuring SMTP.
Security: Read the security model to understand hashing, token expiry, rate limiting, and audit logs.
Extensions: Add namespaced behavior through the public plugin contract.
Framework guides: See integration guides for Next.js, Express, Hono, and Fastify.