An agent-native, type-safe authorization and authentication library for Next.js applications.
By separating notions of authentication ("who am I?") and authorization ("what can I do?"), we can scale apps to dozens of integrations while maintaining a simple, type-safe API.
- 🔐 Multiple Authentication Methods: OAuth2, Magic Links, and API Key authorization
- 🏗️ Flexible Architecture: Mix and match different providers and database adapters
- 🔒 Type Safety: Full TypeScript support with strict typing
- ⚡ Fast: Built with Bun.js for optimal performance
- 🎯 Next.js Optimized: Built specifically for Next.js with App Router support
- 🔄 Token Refresh: Automatic OAuth2 token refresh handling
- 🗄️ Database Agnostic: Support for Prisma and Drizzle ORMs
bun add @rubriclab/auth
Choose between Prisma or Drizzle:
// Using Prisma
import { prismaAdapter } from '@rubriclab/auth/providers/prisma'
import { PrismaClient } from '@prisma/client'
import { env } from '@/env'
const prisma = new PrismaClient()
const databaseProvider = prismaAdapter(prisma)
// OR using Drizzle
import { drizzleAdapter } from '@rubriclab/auth/providers/drizzle'
import { drizzle } from 'drizzle-orm/neon-serverless'
const db = drizzle(env.DATABASE_URL)
const databaseProvider = drizzleAdapter(db)
// lib/auth/server.ts
import { createAuth } from '@rubriclab/auth'
import { createGithubAuthenticationProvider } from '@rubriclab/auth/providers/github'
import { createGoogleAuthenticationProvider } from '@rubriclab/auth/providers/google'
import { createResendMagicLinkAuthenticationProvider } from '@rubriclab/auth/providers/resend'
export const { routes, actions } = createAuth({
databaseProvider,
authUrl: env.NEXT_PUBLIC_AUTH_URL, // e.g., 'https://yourdomain.com'
// OAuth2 Authentication Providers
oAuth2AuthenticationProviders: {
github: createGithubAuthenticationProvider({
githubClientId: env.GITHUB_CLIENT_ID,
githubClientSecret: env.GITHUB_CLIENT_SECRET,
}),
google: createGoogleAuthenticationProvider({
googleClientId: env.GOOGLE_CLIENT_ID,
googleClientSecret: env.GOOGLE_CLIENT_SECRET,
}),
},
// Magic Link Authentication Providers
magicLinkAuthenticationProviders: {
resend: createResendMagicLinkAuthenticationProvider({
resendApiKey: env.RESEND_API_KEY,
fromEmail: '[email protected]',
subject: 'Sign in to Your App',
html: (url) => `
<h1>Welcome to Your App</h1>
<p>Click the link below to sign in:</p>
<a href="${url}">Sign In</a>
`,
}),
},
})
// lib/auth/actions.ts
'use server'
import { actions } from '@/lib/auth/server'
export const { signIn, signOut, sendMagicLink, getAuthConstants, getSession } = actions
// lib/auth/client.ts
'use client'
import { CreateAuthContext } from '@rubriclab/auth/client'
import type { DrizzleSession } from '@rubriclab/auth/providers/drizzle'
// or (prisma): import type { User } from '@prisma/client'
import type { users } from '@/lib/db/schema/auth'
export const { ClientAuthProvider, useSession } =
CreateAuthContext<DrizzleSession<typeof users.$inferSelect>>()
// or (prisma): CreateAuthContext<User>()
Create an API route handler for authentication:
// app/api/auth/[...auth]/route.ts
import { routes } from '@/lib/auth/server'
export const { GET } = routes
// Server Component
import { getSession } from '@/lib/auth/actions'
export default async function DashboardPage() {
const session = await getSession({
redirectUnauthorized: '/login'
})
return (
<div>
<h1>Welcome, {session.user.email}!</h1>
{/* Your dashboard content */}
</div>
)
}
// Client Component
'use client'
import { CreateAuthContext, useSession } from '@/lib/auth/client'
export function DashboardClient({ session }: { session: typeof session }) {
return (
<ClientAuthProvider session={session}>
<DashboardContent />
</ClientAuthProvider>
)
}
function DashboardContent() {
const session = useSession()
return (
<div>
<h2>Connected Accounts:</h2>
<ul>
{session.user.oAuth2AuthenticationAccounts.map(account => (
<li key={account.accountId}>{account.provider}</li>
))}
</ul>
</div>
)
}
import { signIn, sendMagicLink, signOut } from '@/lib/auth/actions'
// Sign in with OAuth2
await signIn({
provider: 'github',
callbackUrl: '/dashboard'
})
// Send magic link
await sendMagicLink({
provider: 'resend',
email: '[email protected]'
})
// Sign out
await signOut({ redirect: '/' })
# Your application's base URL for auth callbacks
NEXT_PUBLIC_AUTH_URL=https://yourdomain.com
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
RESEND_API_KEY=your_resend_api_key
-
GitHub (
@rubriclab/auth/providers/github
)- Scopes:
read:user
,user:email
(default) - Features: User authentication, email retrieval
- Note: GitHub tokens don't expire by default
- Scopes:
-
Google (
@rubriclab/auth/providers/google
)- Scopes:
userinfo.email
,userinfo.profile
(default) - Features: User authentication, token refresh support
- Additional scopes available for Gmail, Drive, Calendar, etc.
- Scopes:
- Resend (
@rubriclab/auth/providers/resend
)- Features: Email-based magic link authentication
- Customizable email templates
- 24-hour expiration
-
GitHub (
@rubriclab/auth/providers/github
)- Customizable scopes for repository access, organization management, etc.
- Available scopes:
repo
,admin:org
,workflow
,packages
, etc.
-
Google (
@rubriclab/auth/providers/google
)- Customizable scopes for Gmail, Drive, Calendar, etc.
- Available scopes:
gmail.readonly
,drive.file
,calendar.events
, etc.
-
Brex (
@rubriclab/auth/providers/brex
)- Features: API key-based authorization for Brex platform
- Account ID retrieval from Brex API
-
Vercel (
@rubriclab/auth/providers/vercel
)- Features: API key-based authorization for Vercel platform
- Account ID retrieval from Vercel API
-
Prisma (
@rubriclab/auth/providers/prisma
)- Full Prisma ORM support
- Type-safe database operations
- Automatic schema generation
-
Drizzle (
@rubriclab/auth/providers/drizzle
)- Drizzle ORM support with PostgreSQL
- Optimized for Neon serverless
- Lightweight and fast
The library requires the following database tables:
users
- User accountssessions
- User sessionsoauth2_authentication_requests
- OAuth2 authentication flow stateoauth2_authorization_requests
- OAuth2 authorization flow statemagic_link_requests
- Magic link authentication state
oauth2_authentication_accounts
- Connected OAuth2 authentication accountsoauth2_authorization_accounts
- Connected OAuth2 authorization accountsapi_key_authorization_accounts
- Connected API key authorization accounts
import { createOauth2AuthenticationProvider } from '@rubriclab/auth'
const customProvider = createOauth2AuthenticationProvider({
getAuthenticationUrl: async ({ redirectUri, state }) => {
const url = new URL('https://your-provider.com/oauth/authorize')
url.searchParams.set('client_id', process.env.CUSTOM_CLIENT_ID!)
url.searchParams.set('redirect_uri', redirectUri)
url.searchParams.set('state', state)
url.searchParams.set('scope', 'read:user')
return url
},
getToken: async ({ code, redirectUri }) => {
// Implement token exchange
return {
accessToken: 'token',
refreshToken: 'refresh',
expiresAt: new Date(Date.now() + 3600000)
}
},
getUser: async ({ accessToken }) => {
// Implement user info retrieval
return {
accountId: 'user_id',
email: '[email protected]'
}
},
refreshToken: async ({ refreshToken }) => {
// Implement token refresh
return {
accessToken: 'new_token',
refreshToken: 'new_refresh',
expiresAt: new Date(Date.now() + 3600000)
}
}
})
The library provides full TypeScript support with strict typing:
// Session type inference
const session = await auth.actions.getSession({ redirectUnauthorized: '/login' })
// session.user.email is typed as string
// session.user.oAuth2AuthenticationAccounts is typed as array
// Provider type safety
await auth.actions.signIn({
provider: 'github', // TypeScript will ensure this is a valid provider
callbackUrl: '/dashboard'
})