Use Cases

Claude Code के साथ Implementing Social Login

Claude Code का उपयोग करके implementing social login सीखें। Practical code examples शामिल हैं।

ソーシャルlogインको Claude Code सेimplement करना

ソーシャルlogインをintroductionすると、userの登録ハードルが大幅にनीचेがり है।Google、GitHub、X(旧Twitter) आदि複数のproviderーにsupportするのはsettingsがcomplexですが、Claude Code का उपयोग करके一貫したimplementationでefficientlybuild किया जा सकता है。

NextAuth.jsによるマルチproviderーsettings

> NextAuth.jsでソーシャルlogインをimplement करो。
> Google、GitHub、Xのतीनのproviderーにsupportして。
> 既存アカウントとのlinkfeaturesも含めて。
// src/lib/auth.ts
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';
import GitHub from 'next-auth/providers/github';
import Twitter from 'next-auth/providers/twitter';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from './prisma';

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      authorization: {
        params: {
          prompt: 'consent',
          access_type: 'offline',
          response_type: 'code',
        },
      },
    }),
    GitHub({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
    Twitter({
      clientId: process.env.TWITTER_CLIENT_ID!,
      clientSecret: process.env.TWITTER_CLIENT_SECRET!,
      version: '2.0',
    }),
  ],
  callbacks: {
    async signIn({ user, account, profile }) {
      // 既存user के case मेंはアカウントをlink
      if (account && user.email) {
        const existingUser = await prisma.user.findUnique({
          where: { email: user.email },
        });
        if (existingUser) {
          await prisma.account.upsert({
            where: {
              provider_providerAccountId: {
                provider: account.provider,
                providerAccountId: account.providerAccountId,
              },
            },
            update: {},
            create: {
              userId: existingUser.id,
              provider: account.provider,
              providerAccountId: account.providerAccountId,
              type: account.type,
              access_token: account.access_token,
              refresh_token: account.refresh_token,
            },
          });
        }
      }
      return true;
    },
    async session({ session, user }) {
      session.user.id = user.id;
      return session;
    },
  },
  pages: {
    signIn: '/login',
    error: '/login?error=auth',
  },
});

logインpageのUI

// src/app/(auth)/login/page.tsx
'use client';
import { signIn } from 'next-auth/react';

const providers = [
  { id: 'google', name: 'Google', icon: '🔍', bg: 'bg-white border hover:bg-gray-50', text: 'text-gray-700' },
  { id: 'github', name: 'GitHub', icon: '🐙', bg: 'bg-gray-900 hover:bg-gray-800', text: 'text-white' },
  { id: 'twitter', name: 'X (Twitter)', icon: '𝕏', bg: 'bg-black hover:bg-gray-900', text: 'text-white' },
];

export default function LoginPage() {
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
      <div className="bg-white dark:bg-gray-800 p-8 rounded-2xl shadow-lg w-full max-w-md">
        <h1 className="text-2xl font-bold text-center mb-2 dark:text-white">logイン</h1>
        <p className="text-gray-500 text-center mb-8">アカウントでlogインしてください</p>
        <div className="space-y-3">
          {providers.map((provider) => (
            <button
              key={provider.id}
              onClick={() => signIn(provider.id, { callbackUrl: '/dashboard' })}
              className={`w-full flex items-center justify-center gap-3 px-4 py-3 rounded-lg font-medium transition ${provider.bg} ${provider.text}`}
            >
              <span className="text-xl">{provider.icon}</span>
              {provider.name}でlogイン
            </button>
          ))}
        </div>
        <div className="mt-6 text-center text-sm text-gray-500">
          logインする बातで
          <a href="/terms" className="text-blue-600 hover:underline">利用規約</a>と
          <a href="/privacy" className="text-blue-600 hover:underline">プライバシーポリシー</a>に同意します
        </div>
      </div>
    </div>
  );
}

アカウントlinkmanagement

// src/components/LinkedAccounts.tsx
'use client';
import { useState, useEffect } from 'react';

interface LinkedAccount {
  provider: string;
  providerAccountId: string;
  linkedAt: string;
}

export function LinkedAccounts() {
  const [accounts, setAccounts] = useState<LinkedAccount[]>([]);

  useEffect(() => {
    fetch('/api/user/linked-accounts')
      .then((res) => res.json())
      .then(setAccounts);
  }, []);

  const unlinkAccount = async (provider: string) => {
    if (accounts.length <= 1) {
      alert('最低एकのlogイン方法がज़रूरीです');
      return;
    }
    await fetch(`/api/user/linked-accounts/${provider}`, { method: 'DELETE' });
    setAccounts(accounts.filter((a) => a.provider !== provider));
  };

  return (
    <div className="space-y-4">
      <h3 className="font-semibold dark:text-white">integration済みアカウント</h3>
      {accounts.map((account) => (
        <div key={account.provider} className="flex items-center justify-between p-3 border rounded-lg dark:border-gray-700">
          <span className="capitalize dark:text-gray-300">{account.provider}</span>
          <button
            onClick={() => unlinkAccount(account.provider)}
            className="text-red-500 text-sm hover:underline"
          >
            解除
          </button>
        </div>
      ))}
    </div>
  );
}

securityのポイント

ソーシャルlogインではCSRF対策とstateparameterの検証がimportant है।NextAuth.jsはこれらをデフォルトでprocessingしますが、カスタムimplement करनाcase मेंClaude Codeに「OAuthのsecurityベストプラクティスに沿ってimplement करो」と指示 करें।

関連記事

OAuth全般のimplementationpatternはOAuthimplementationガイド、JWTauthentication के साथ combineるcase मेंJWTauthenticationのimplementationभी reference के लिएなり है।

NextAuth.jsのofficial documentation(next-auth.js.org)でproviderーごとの詳細settings confirmでき है।

#Claude Code #social login #OAuth #NextAuth.js #authentication