Authentication Snippets
Common authentication patterns in ShipKit
Authentication Snippets
Protected Route
// src/app/(app)/dashboard/page.tsx
import { auth } from "@/server/auth";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const session = await auth();
if (!session?.user) {
redirect("/sign-in");
}
return (
<div>
<h1>Welcome {session.user.name}</h1>
</div>
);
}
Sign In Form
// src/components/auth/sign-in-form.tsx
"use client";
import { signIn } from "next-auth/react";
import { useSearchParams } from "next/navigation";
import { useState } from "react";
import { toast } from "sonner";
export function SignInForm() {
const [isLoading, setIsLoading] = useState(false);
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") || "/dashboard";
async function handleSubmit(formData: FormData) {
try {
setIsLoading(true);
const result = await signIn("credentials", {
email: formData.get("email") as string,
password: formData.get("password") as string,
redirect: false,
});
if (result?.error) {
toast.error(result.error);
return;
}
window.location.href = callbackUrl;
} catch (error) {
toast.error("Something went wrong");
console.error(error);
} finally {
setIsLoading(false);
}
}
return (
<form action={handleSubmit}>
<input
type="email"
name="email"
placeholder="Email"
required
/>
<input
type="password"
name="password"
placeholder="Password"
required
/>
<button type="submit" disabled={isLoading}>
{isLoading ? "Signing in..." : "Sign in"}
</button>
</form>
);
}
OAuth Buttons
// src/components/auth/oauth-buttons.tsx
"use client";
import { signIn } from "next-auth/react";
import { Button } from "@/components/ui/button";
import { Github, Mail } from "lucide-react";
export function OAuthButtons() {
return (
<div className="grid gap-2">
<Button
variant="outline"
onClick={() => signIn("github", { callbackUrl: "/dashboard" })}
>
<Github className="mr-2 h-4 w-4" />
Continue with GitHub
</Button>
<Button
variant="outline"
onClick={() => signIn("google", { callbackUrl: "/dashboard" })}
>
<Mail className="mr-2 h-4 w-4" />
Continue with Google
</Button>
</div>
);
}
Protected API Route
// src/app/api/protected/route.ts
import { auth } from "@/server/auth";
import { ErrorService } from "@/server/services/error-service";
import { NextResponse } from "next/server";
export async function GET() {
try {
const session = await auth();
if (!session?.user) {
return ErrorService.throwUnauthorized("Not authenticated");
}
return NextResponse.json({ message: "Success" });
} catch (error) {
const appError = ErrorService.handleError(error);
return NextResponse.json(
{ error: appError },
{ status: appError.code === "UNAUTHORIZED" ? 401 : 500 }
);
}
}