Skip to content

Configuration

Configure the ByteAuth Next.js SDK through environment variables and the handler options.

All sensitive configuration should be in your .env.local file:

# Required
BYTEAUTH_DOMAIN=yourdomain.com
BYTEAUTH_API_KEY=ba_live_xxxxxxxxxxxxx
# Recommended
BYTEAUTH_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
# NextAuth.js
NEXTAUTH_URL=https://yourdomain.com
NEXTAUTH_SECRET=your-random-secret-here
# Optional
BYTEAUTH_SESSION_LIFETIME=3600
BYTEAUTH_CHALLENGE_LIFETIME=30

Configure the ByteAuthHandler with options:

app/api/byteauth/[...byteauth]/route.ts
import { ByteAuthHandler } from '@bytefederal/byteauth-nextjs';
import type { ByteAuthConfig } from '@bytefederal/byteauth-nextjs';
const config: ByteAuthConfig = {
// Domain registered with ByteAuth
domain: process.env.BYTEAUTH_DOMAIN!,
// API credentials
apiKey: process.env.BYTEAUTH_API_KEY!,
webhookSecret: process.env.BYTEAUTH_WEBHOOK_SECRET,
// Session configuration
session: {
lifetime: 3600, // 1 hour in seconds
cookieName: 'byteauth-session',
secure: process.env.NODE_ENV === 'production',
},
// Challenge configuration
challenge: {
lifetime: 30, // seconds
refreshInterval: 30, // seconds
},
// Callbacks
callbacks: {
async onLogin(user) {
console.log('User logged in:', user.publicKey);
// Return user data to include in session
return {
id: user.id,
publicKey: user.publicKey,
name: user.name,
};
},
async onRegister(user) {
console.log('New user registered:', user.publicKey);
// Create user in your database
const newUser = await createUser({
publicKey: user.publicKey,
});
return newUser;
},
async findUser(publicKey) {
// Find user by public key in your database
return await db.user.findUnique({
where: { publicKey },
});
},
async createUser(data) {
// Create new user in your database
return await db.user.create({
data: {
publicKey: data.publicKey,
name: 'ByteAuth User',
},
});
},
},
// Custom routes (optional)
routes: {
qrCode: '/api/byteauth/qr',
webhook: '/api/byteauth/webhook',
check: '/api/byteauth/check',
},
// Redirects
redirects: {
afterLogin: '/dashboard',
afterRegister: '/onboarding',
onError: '/login?error=auth_failed',
},
};
export const { GET, POST } = ByteAuthHandler(config);

The SDK provides full TypeScript support:

import type {
ByteAuthConfig,
ByteAuthUser,
ByteAuthSession,
ByteAuthCallbacks,
WebhookPayload,
} from '@bytefederal/byteauth-nextjs';
// User type
interface ByteAuthUser {
id: string;
publicKey: string;
name?: string;
email?: string;
createdAt: Date;
}
// Session type
interface ByteAuthSession {
id: string;
userId: string;
challenge: string;
expiresAt: Date;
authenticatedAt?: Date;
}
// Webhook payload
interface WebhookPayload {
publicKey: string;
signature: string;
challenge: string;
timestamp: number;
deviceInfo?: {
platform: 'ios' | 'android';
version: string;
};
}
prisma/schema.prisma
model User {
id String @id @default(cuid())
publicKey String @unique
name String?
email String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ByteAuthSession {
id String @id @default(cuid())
userId String?
challenge String @unique
expiresAt DateTime
authenticatedAt DateTime?
createdAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id])
}
lib/byteauth.ts
import { prisma } from './prisma';
import type { ByteAuthCallbacks } from '@bytefederal/byteauth-nextjs';
export const callbacks: ByteAuthCallbacks = {
async findUser(publicKey) {
return await prisma.user.findUnique({
where: { publicKey },
});
},
async createUser(data) {
return await prisma.user.create({
data: {
publicKey: data.publicKey,
},
});
},
async onLogin(user) {
await prisma.user.update({
where: { id: user.id },
data: { lastLoginAt: new Date() },
});
return user;
},
};
db/schema.ts
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: text('id').primaryKey(),
publicKey: text('public_key').unique().notNull(),
name: text('name'),
createdAt: timestamp('created_at').defaultNow(),
});
export const byteauthSessions = pgTable('byteauth_sessions', {
id: text('id').primaryKey(),
userId: text('user_id').references(() => users.id),
challenge: text('challenge').unique().notNull(),
expiresAt: timestamp('expires_at').notNull(),
authenticatedAt: timestamp('authenticated_at'),
});

Full NextAuth.js integration with ByteAuth:

app/api/auth/[...nextauth]/route.ts
import NextAuth, { type NextAuthOptions } from 'next-auth';
import { ByteAuthProvider } from '@bytefederal/byteauth-nextjs/providers';
export const authOptions: NextAuthOptions = {
providers: [
ByteAuthProvider({
domain: process.env.BYTEAUTH_DOMAIN!,
apiKey: process.env.BYTEAUTH_API_KEY!,
}),
],
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, // 30 days
},
callbacks: {
async jwt({ token, user, account }) {
if (account && user) {
token.publicKey = user.publicKey;
token.provider = account.provider;
}
return token;
},
async session({ session, token }) {
return {
...session,
user: {
...session.user,
publicKey: token.publicKey as string,
},
};
},
async signIn({ user, account }) {
// Custom sign-in logic
console.log('User signed in:', user.publicKey);
return true;
},
},
events: {
async signIn({ user }) {
// Track login events
await analytics.track('login', {
userId: user.id,
method: 'byteauth',
});
},
},
pages: {
signIn: '/login',
error: '/login?error=AuthError',
},
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

Protect routes with middleware:

middleware.ts
import { withByteAuth } from '@bytefederal/byteauth-nextjs/middleware';
export default withByteAuth({
// Pages that require authentication
protectedPages: ['/dashboard', '/settings', '/account'],
// Pages to skip (public)
publicPages: ['/', '/login', '/register', '/about'],
// Redirect to login if not authenticated
loginPage: '/login',
});
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};