Mengimplementasikan Social Login dengan Claude Code: Next.js, Auth.js, Google, dan GitHub OAuth
Panduan praktis social login aman dengan Claude Code, Next.js/Auth.js, Google/GitHub OAuth, kode siap pakai, dan jebakan produksi.
Social login bukan sekadar tombol untuk mempersingkat formulir registrasi. Begitu pengguna masuk lewat Google atau GitHub OAuth, kamu juga sedang mendesain identitas, account linking, session, cookie, CSRF, provider scope, audit log, dan alur support. Jika Claude Code hanya diberi instruksi “buat login”, UI bisa terlihat berjalan, tetapi masih ada risiko: redirect URI salah, akun otomatis terhubung lewat email yang belum diverifikasi, scope terlalu luas, atau client secret masuk ke source code.
Artikel ini memakai Next.js App Router, Auth.js dengan gaya NextAuth v5, TypeScript, dan Prisma Adapter. OAuth authorization code adalah kode sementara yang ditukar server menjadi token. state adalah nilai acak untuk mendeteksi CSRF. Redirect URI adalah URL persis tempat Google atau GitHub mengembalikan browser. Account linking berarti pengguna yang sudah login secara sadar menghubungkan provider lain ke akun yang sama.
Tentukan batas keamanan lebih dulu
Pertanyaan pertama bukan berapa banyak provider yang didukung, tetapi apa yang harus dibuktikan oleh login. Untuk SaaS, halaman pelatihan, tool internal, atau produk developer, Google dan GitHub sering sudah cukup. Google cocok untuk pengguna umum dan email perusahaan. GitHub cocok untuk produk teknis. Meminta Google Drive, Calendar, atau GitHub repo saat login pertama akan menurunkan kepercayaan dan conversion.
Bagi pekerjaan untuk Claude Code menjadi unit kecil:
- Tambahkan provider Auth.js, environment variables, dan callback route.
- Buat halaman login dan halaman yang dilindungi.
- Tambahkan hanya
user.idke session, jangan access token. - Account linking hanya dari halaman settings saat pengguna sudah login.
- Cegah penghapusan metode login terakhir.
- Jangan menulis client secret, refresh token, atau access token di code, log, issue, atau draft artikel.
Pada proyek uji Masa, masalah terbesar bukan code generation, tetapi redirect URI di Google Cloud Console yang tidak sama denganAUTH_URL aplikasi. Claude Code bisa mengubah repository, tetapi tidak bisa memastikan nilai di console eksternal. Karena itu URL, scope, aturan email verification, dan cookie policy perlu ditulis sebelum implementasi.
Alur OAuth dalam diagram
Dokumentasi Google merekomendasikan authorization code flow untuk aplikasi web. Browser menerimacode sementara, lalu server menukarnya dengan provider. Client secret tidak boleh masuk ke kode browser.
sequenceDiagram
participant User as User
participant App as Next.js app
participant Auth as Auth.js route
participant Provider as Google or GitHub
User->>App: Click "Continue with Google"
App->>Auth: signIn("google")
Auth->>Provider: Redirect with client_id, redirect_uri, scope, state
Provider->>User: Consent screen
Provider->>Auth: Redirect back with code and state
Auth->>Provider: Exchange code on the server
Provider->>Auth: Return tokens
Auth->>App: Create session cookie
App->>User: Show dashboard
state memastikan callback berasal dari request login yang dimulai aplikasi. Auth.js menangani ini jika kamu memakai route standar. Jika Claude Code membuat callback custom, validasi state harus disebutkan eksplisit. Dokumentasi GitHub juga menyarankan nilai state yang tidak mudah ditebak dan menghentikan flow bila tidak cocok.
Environment variables dan scope minimum
Konfigurasi ini fokus pada login, bukan mengambil izin API yang belum diperlukan. Google memakaiopenid email profile; GitHub memakairead:user user:email.
npm install next-auth@beta @auth/prisma-adapter prisma @prisma/client
npx prisma init
npm exec auth secret
# .env.local
AUTH_SECRET="nilai dari npm exec auth secret"
AUTH_URL="http://localhost:3000"
AUTH_GOOGLE_ID="client ID dari Google Cloud Console"
AUTH_GOOGLE_SECRET="client secret dari Google Cloud Console"
AUTH_GITHUB_ID="client ID dari GitHub OAuth App"
AUTH_GITHUB_SECRET="client secret dari GitHub OAuth App"
DATABASE_URL="postgresql://user:password@localhost:5432/app"
File contoh hanya berisi key kosong, bukan nilai asli.
# .env.example
AUTH_SECRET=
AUTH_URL=
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=
DATABASE_URL=
Untuk Google, redirect URI lokal adalahhttp://localhost:3000/api/auth/callback/google; produksi adalahhttps://example.com/api/auth/callback/google. Untuk GitHub gunakanhttps://example.com/api/auth/callback/github. Protocol, domain, path, dan nama provider harus sama persis.
Konfigurasi Auth.js
File berikut menolak Google bilaemail_verified tidak true dan menolak GitHub bila email tidak tersedia. Provider token tidak dikirim ke session browser.
// auth.ts
import NextAuth, { type NextAuthConfig } from "next-auth";
import Google from "next-auth/providers/google";
import GitHub from "next-auth/providers/github";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";
type GoogleProfile = {
sub: string;
name?: string;
email: string;
email_verified: boolean;
picture?: string;
};
export const authConfig = {
adapter: PrismaAdapter(prisma),
session: { strategy: "database" },
providers: [
Google({
authorization: {
params: {
scope: "openid email profile",
response_type: "code",
},
},
profile(profile: GoogleProfile) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture,
emailVerified: profile.email_verified ? new Date() : null,
};
},
}),
GitHub({
authorization: {
params: {
scope: "read:user user:email",
},
},
}),
],
callbacks: {
async signIn({ account, profile, user }) {
if (account?.provider === "google") {
const googleProfile = profile as GoogleProfile | undefined;
return Boolean(googleProfile?.email && googleProfile.email_verified);
}
if (account?.provider === "github") {
return Boolean(user.email);
}
return true;
},
async session({ session, user }) {
session.user.id = user.id;
return session;
},
},
pages: {
signIn: "/login",
error: "/login",
},
} satisfies NextAuthConfig;
export const { handlers, auth, signIn, signOut } = NextAuth(authConfig);
// src/types/next-auth.d.ts
import "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: string;
name?: string | null;
email?: string | null;
image?: string | null;
};
}
}
// src/app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;
Jika memakai Prisma Adapter, minta Claude Code menambahkan model standarUser, Account, Session, danVerificationToken. TabelAccount menyimpan provider dan providerAccountId, sehingga account linking bisa diaudit.
Halaman login dan halaman terlindungi
MemanggilsignIn dari Server Action menjaga flow tetap berada di Auth.js. Ini lebih aman daripada merangkai URL provider secara manual.
// src/app/login/page.tsx
import { signIn } from "@/auth";
const providers = [
{ id: "google", label: "Lanjutkan dengan Google" },
{ id: "github", label: "Lanjutkan dengan GitHub" },
] as const;
export default function LoginPage({
searchParams,
}: {
searchParams: { error?: string };
}) {
return (
<main className="mx-auto flex min-h-screen max-w-sm flex-col justify-center gap-6 px-6">
<div>
<h1 className="text-2xl font-bold">Masuk</h1>
<p className="mt-2 text-sm text-gray-600">
Pilih identitas kerja yang akan digunakan untuk aplikasi ini.
</p>
</div>
{searchParams.error ? (
<p className="rounded-md bg-red-50 p-3 text-sm text-red-700">
Login gagal. Coba akun lain atau hubungi support.
</p>
) : null}
<div className="grid gap-3">
{providers.map((provider) => (
<form
key={provider.id}
action={async () => {
"use server";
await signIn(provider.id, { redirectTo: "/dashboard" });
}}
>
<button
type="submit"
className="w-full rounded-md border px-4 py-3 text-sm font-medium hover:bg-gray-50"
>
{provider.label}
</button>
</form>
))}
</div>
</main>
);
}
// src/app/dashboard/page.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const session = await auth();
if (!session?.user) {
redirect("/login");
}
return (
<main className="mx-auto max-w-3xl p-8">
<h1 className="text-2xl font-bold">Dashboard</h1>
<p className="mt-4 text-gray-700">
Signed in as {session.user.email}.
</p>
</main>
);
}
Account linking dan unlink
Jangan otomatis menghubungkan akun hanya karena email sama. Pengguna harus sudah login dan memulai linking dari settings. API unlink memeriksa same-origin dan mencegah penghapusan metode login terakhir.
// src/app/api/settings/linked-accounts/route.ts
import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";
import { NextResponse } from "next/server";
function isSameOrigin(request: Request) {
const origin = request.headers.get("origin");
const host = request.headers.get("host");
if (!origin || !host) return false;
try {
return new URL(origin).host === host;
} catch {
return false;
}
}
export async function DELETE(request: Request) {
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (!isSameOrigin(request)) {
return NextResponse.json({ error: "Bad origin" }, { status: 403 });
}
const body = (await request.json()) as { provider?: string };
const accounts = await prisma.account.findMany({
where: { userId: session.user.id },
select: { provider: true },
});
if (accounts.length <= 1) {
return NextResponse.json(
{ error: "Metode login terakhir tidak boleh dihapus." },
{ status: 400 },
);
}
await prisma.account.deleteMany({
where: { userId: session.user.id, provider: body.provider },
});
return NextResponse.json({ ok: true });
}
Prompt Claude Code dan checklist review
Tambahkan Google/GitHub social login ke aplikasi Next.js App Router ini dengan Auth.js v5.
Kebutuhan:
- client secret hanya dibaca dari .env.local atau deployment secrets
- Google scope hanya openid email profile
- GitHub scope hanya read:user user:email
- Google hanya boleh login bila email_verified true
- Session hanya ditambah user.id, jangan expose access_token ke client
- Account linking hanya dari settings page saat user sudah login
- User tidak boleh menghapus metode login terakhir
- Setelah edit, laporkan lint result dan langkah manual OAuth test
| Area review | Yang harus dicek |
|---|---|
| OAuth flow | Authorization code flow dipakai dan redirect URI cocok dengan provider dashboard |
| state dan CSRF | Route standar Auth.js tetap dipakai, API custom memeriksa same-origin |
| Cookie dan session | Token tetap di server; cookie produksi mempertimbangkan Secure, HttpOnly, SameSite |
| Account linking | Email verified dan aksi eksplisit pengguna sebelum akun dihubungkan |
Contoh use case nyata
Pertama, dashboard admin B2B SaaS. Google menjadi login utama, lalu domain Workspace dan RBAC bisa ditambahkan. GitHub hanya dibuka untuk pengguna teknis.
Kedua, tool untuk developer. GitHub login mempercepat onboarding, sedangkan permission repository diminta nanti di fitur yang benar-benar membutuhkannya.
Ketiga, produk dengan email-password yang ingin menambah social login. Pengguna yang sudah login sebaiknya menghubungkan Google atau GitHub dari settings, bukan otomatis merge berdasarkan email.
Keempat, halaman training atau webinar. Google verified email dapat mengurangi pendaftaran palsu dan biaya konfirmasi manual.
Kegagalan umum di produksi
Redirect URI mismatch adalah masalah paling umum. Jika lokal berhasil tetapi produksi gagal, bandingkanAUTH_URL, proxy Host header, dan callback URL di Google/GitHub.
Automatic linking berdasarkan email itu berisiko. Google menyediakanemail_verified, tetapi tidak semua provider memberi jaminan yang sama. String email yang sama belum cukup sebagai bukti identitas.
Scope berlebihan menurunkan PV dan konsultasi. Pengguna yang hanya ingin login bisa pergi jika melihat permintaan akses repository atau file.
Client secret di code harus menghentikan review. Gunakan.env.local, deployment secrets, atau secret manager.
Refresh token Google perlu desain terpisah. Google bisa mengembalikannya hanya saat consent pertama. Untuk login murni jangan simpan; untuk Google API background buat task token rotation tersendiri.
Referensi resmi dan internal link
Gunakan sumber primer: Auth.js, Google Identity Services OAuth, GitHub OAuth Apps, dan OWASP Authentication Cheat Sheet.
Untuk pendalaman, baca implementasi OAuth dengan Claude Code, JWT authentication, security best practices Claude Code, dan manajemen environment variable.
Konsultasi, training, dan poin verifikasi
ClaudeCodeLab membantu tim mengubah tombol login menjadi alur authentication yang bisa direview: requirement, pembagian tugas Claude Code, code review, secret management, dan manual OAuth testing. Jika targetnya meningkatkan inquiry, mulai dari target user, provider, izin minimum, dan pesan error sebelum implementasi.
Saat mencoba isi artikel ini, pastikan redirect URI development dan production sama persis, Googleemail_verified dicek, GitHub tanpa email publik punya pesan error jelas, state dan cookies tetap mengikuti flow standar Auth.js, metode login terakhir tidak bisa dihapus, dan tidak ada client secret di source code atau log.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.