Open source authentication framework. Read the docs
The most comprehensive authentication framework for TypeScript
Quick Setup
Get authentication running in minutes
Answer a few prompts and Keyloom scaffolds routes, providers, and environment variables for you.
CLI flow~5 min setup
Next steps: configure keyloom.config, set env vars, run migrations1
Install the CLI
Add the dev-only CLI and run the interactive init to detect your framework, install deps, and scaffold config.
pnpm add -g @keyloom/cli
2
Install Hooks
Lets you access auth state and user info on the client.
pnpm add @keyloom/react
3
Install UI
Don't want the hastle of building your own auth UI? Use our UI library.
pnpm add @keyloom/ui
Prefer to follow along?
Watch the full walkthrough to see environment syncing, provider setup, and deployment in real time.
Open the guided tutorialPowerful primitives
Build faster with focused components
Before / AfterKeyloom VS Next-Auth
before
import { NextAuthOptions } from 'next-auth';
import GithubProvider from 'next-auth/providers/github';
import GoogleProvider from 'next-auth/providers/google';
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import { prisma } from './prisma';
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GithubProvider({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
callbacks: {
session: async ({ session, token }) => {
if (token.sub) {
const user = await prisma.user.findUnique({
where: { id: token.sub },
include: { accounts: true, sessions: true }
});
return {
...session,
user: { ...session.user, id: token.sub, role: user?.role }
};
}
return session;
},
jwt: ({ token, user, account }) => {
if (user) {
token.sub = user.id;
token.role = user.role;
}
return token;
},
signIn: async ({ user, account, profile }) => {
try {
if (account?.provider === 'github' || account?.provider === 'google') {
const existingUser = await prisma.user.findUnique({
where: { email: user.email! }
});
if (!existingUser) {
await prisma.user.create({
data: {
email: user.email!,
name: user.name,
image: user.image,
role: 'USER'
}
});
}
}
return true;
} catch (error) {
console.error('Sign in error:', error);
return false;
}
},
},
pages: {
signIn: '/auth/signin',
error: '/auth/error',
signOut: '/auth/signout'
},
session: { strategy: 'jwt', maxAge: 30 * 24 * 60 * 60 },
events: {
signIn: async ({ user }) => {
await prisma.user.update({
where: { id: user.id },
data: { lastLogin: new Date() }
});
}
}
};after
import { defineKeyloom } from '@keyloom/core';
import { PrismaAdapter } from '@keyloom/adapters';
import { PrismaClient } from '@prisma/client';
const db = new PrismaClient();
import github from '@keyloom/providers/github';
import google from '@keyloom/providers/google';
export default defineKeyloom({
baseUrl: process.env.NEXT_PUBLIC_APP_URL!,
session: { strategy: 'database', ttlMinutes: 60, rolling: true },
adapter: PrismaAdapter(db),
providers: [
github({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!
}),
google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
}),
],
secrets: { authSecret: process.env.AUTH_SECRET! },
});