
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@startupkit/auth
Advanced tools
Lightweight Authentication and Authorization components and hooks for StartupKit projects.
Part of StartupKit - The Zero to One Startup Framework.
pnpm add @startupkit/auth better-auth
Or use the StartupKit CLI to get started with a complete monorepo setup:
npx startupkit init
This package provides lightweight wrappers around Better Auth:
AuthProvider - React context provider for client componentsuseAuth() - Authentication hook for client componentsYou call betterAuth() directly in your project for full control over configuration.
Create your auth configuration with Better Auth directly:
// lib/auth.ts
import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { nextCookies } from "better-auth/next-js"
import { emailOTP } from "better-auth/plugins"
import { db } from "@/lib/db"
export const auth = betterAuth({
basePath: "/auth",
database: drizzleAdapter(db, {
provider: "pg"
}),
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24 // Refresh every 24 hours
},
plugins: [
emailOTP({
sendVerificationOTP: async ({ email, otp }) => {
// Send OTP via your email provider
await sendEmail({
to: email,
subject: "Your verification code",
template: "otp",
data: { code: otp }
})
}
}),
nextCookies()
],
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
}
}
})
Create the auth API route at app/auth/[...all]/route.ts:
// app/auth/[...all]/route.ts
import { auth } from "@/lib/auth"
import { toNextJsHandler } from "better-auth/next-js"
export const { GET, POST } = toNextJsHandler(auth.handler)
Create your auth client using Better Auth:
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react"
import { emailOTPClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
basePath: "/auth",
plugins: [emailOTPClient()]
})
Wrap your app with the AuthProvider from @startupkit/auth:
// app/providers.tsx
"use client"
import { AuthProvider } from "@startupkit/auth"
import { authClient } from "@/lib/auth-client"
interface ProvidersProps {
children: React.ReactNode
user?: User
}
export function Providers({ children, user }: ProvidersProps) {
return (
<AuthProvider
authClient={authClient}
user={user}
onIdentify={(user) => {
// Optional: Track user identification (e.g., analytics)
console.log("User identified:", user)
}}
onReset={() => {
// Optional: Reset state on logout (e.g., clear analytics)
console.log("User logged out")
}}
>
{children}
</AuthProvider>
)
}
Pass the user from your root layout:
// app/layout.tsx
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { Providers } from "./providers"
export default async function RootLayout({ children }) {
const session = await auth.api.getSession({
headers: await headers()
})
return (
<html>
<body>
<Providers user={session?.user}>
{children}
</Providers>
</body>
</html>
)
}
Access authentication state and methods with the useAuth hook:
"use client"
import { useAuth } from "@startupkit/auth"
export function UserProfile() {
const { isAuthenticated, isLoading, user, logout } = useAuth()
if (isLoading) {
return <div>Loading...</div>
}
if (!isAuthenticated) {
return <a href="/sign-in">Sign In</a>
}
return (
<div>
<p>Welcome, {user.name}</p>
<button onClick={logout}>Sign Out</button>
</div>
)
}
"use client"
import { useAuth } from "@startupkit/auth"
import { useState } from "react"
export function EmailSignIn() {
const { sendAuthCode, verifyAuthCode } = useAuth()
const [email, setEmail] = useState("")
const [code, setCode] = useState("")
const [step, setStep] = useState<"email" | "code">("email")
const handleSendCode = async (e: React.FormEvent) => {
e.preventDefault()
await sendAuthCode(email)
setStep("code")
}
const handleVerifyCode = async (e: React.FormEvent) => {
e.preventDefault()
await verifyAuthCode(email, code)
// User is now authenticated
}
if (step === "email") {
return (
<form onSubmit={handleSendCode}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
required
/>
<button type="submit">Send Code</button>
</form>
)
}
return (
<form onSubmit={handleVerifyCode}>
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter verification code"
required
/>
<button type="submit">Verify</button>
</form>
)
}
"use client"
import { useAuth } from "@startupkit/auth"
export function GoogleSignIn() {
const { googleAuth } = useAuth()
return (
<button onClick={googleAuth}>
Sign in with Google
</button>
)
}
"use client"
import { useAuth } from "@startupkit/auth"
export function SignOutButton() {
const { logout } = useAuth()
return (
<button onClick={logout}>
Sign Out
</button>
)
}
// app/dashboard/page.tsx
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { redirect } from "next/navigation"
export default async function DashboardPage() {
const session = await auth.api.getSession({
headers: await headers()
})
if (!session) {
redirect("/sign-in")
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome, {session.user.name}</p>
</div>
)
}
// app/api/profile/route.ts
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { NextResponse } from "next/server"
export async function GET() {
const session = await auth.api.getSession({
headers: await headers()
})
if (!session) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401 }
)
}
return NextResponse.json({
user: session.user
})
}
This package is a lightweight wrapper around Better Auth, providing:
AuthProviderReact context provider wrapping Better Auth client.
interface AuthProviderProps {
children: React.ReactNode
user?: User
authClient: BetterAuthClient
onIdentify?: (user: User) => void
onReset?: () => void
}
useAuth()Hook to access authentication state and methods.
const {
isAuthenticated, // boolean - Is user authenticated?
isLoading, // boolean - Is session loading?
user, // User | null | undefined - Current user
logout, // () => Promise<void> - Sign out user
sendAuthCode, // (email: string) => Promise<void> - Send OTP
verifyAuthCode, // (email: string, code: string) => Promise<void> - Verify OTP
googleAuth // () => Promise<void> - Sign in with Google
} = useAuth()
AuthContextReact context (if you need direct access).
import { AuthContext } from "@startupkit/auth"
All authentication configuration happens in Better Auth directly. See the Better Auth documentation for complete configuration options including:
Required for Google OAuth:
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
Having issues? Open an issue on GitHub
ISC © 2025 01 Studio
FAQs
Auth package for StartupKit
We found that @startupkit/auth demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.