[next-auth]네이버 로그인

코드왕·2025년 1월 10일
0
  1. 이게 베스트인듯

https://powerku.tistory.com/312

큰 틀에서 정리하자면 next-auth를 이용해서 session으로 네이버 로그인을 하고 슈파베이스 profiles 테이블에 이메일이 없다면 회원 가입을 시켜서 로그인을 한다. 그리고 로그인 된거를 가지고 슈파베이스 AUTH 처리를 한다. 만약 profiles내에 이메일이 있다면 supabase 일반 계정으로 해서 defaultPassword로 로그인을 시켜서 사용하면 된다.

  1. 환경변수추가
NEXTAUTH_SECRET=234234234
이거 꼭 넣어주자
  1. 네이버 로그인 타계정 로그인

일단 멤버 관리에서 테스트용으로 아이디를 등록해둬야한다.

검수를 꼭 받아야한다.

검수 받으려면 신청해야할 내용은 아래 링크를 참조하자

https://support.cafe24.com/hc/ko/articles/16697872835993-%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C%EB%8F%84-%EC%8B%9C-%EA%B0%9C%EB%B0%9C-%EC%A4%91-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C%EB%8A%94-%EB%93%B1%EB%A1%9D%EB%90%9C-%EC%95%84%EC%9D%B4%EB%94%94%EB%A7%8C-%EC%95%8C%EB%9F%BF%EC%9D%B4-%EB%85%B8%EC%B6%9C%EB%90%98%EB%A9%B0-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%B4-%EB%B6%88%EA%B0%80%ED%95%A9%EB%8B%88%EB%8B%A4

  1. 위 대로 수정하고 middleware가서 getToken을 이용해서 이중 확인
  1. User Management Starter를 한다

  1. handle_new_user를 수정한다

아래 내용 중에 new.email을 주목하라

begin
  insert into public.profiles (id, full_name, avatar_url,email)
  values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url',new.email);
  return new;
end;

이렇게 profiles에 email이 있는 지를 본다.

next-auth callback을 할떄 profiles에서 email이 있는지를 보고 없을때만 가입시킨다.

// app/api/auth/[...nextauth]/route.js
import NextAuth from "next-auth";
import NaverProvider from "next-auth/providers/naver";
import { createClient } from '@supabase/supabase-js';

// Initialize Supabase client with service_role key
const supabaseAdmin = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY, {
    auth: {
        autoRefreshToken: false,
        persistSession: false
    }
});
const supabase = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY);


export const authOptions = {
    providers: [
        NaverProvider({
            clientId: process.env.NEXT_PUBLIC_NAVER_CLIENT_ID ?? "",
            clientSecret: process.env.NEXT_PUBLIC_NAVER_CLIENT_SECRET ?? "",
        })
    ],
    callbacks: {
        async signIn({ user, account, profile }) {
            const { email } = user;
            // profiles 테이블에서 사용자 조회
            const { data, error } = await supabaseAdmin
                .from('profiles')
                .select('*')
                .eq('email', email)
                .single();

            let userId;

            if (error) {
                // 사용자가 없는 경우, 새로운 사용자 생성
                console.log('사용자가 없는 경우, 새로운 사용자 생성')
                const { data: newUser, error: createError } = await supabaseAdmin.auth.admin.createUser({
                    email,
                    password: 'defaultPassword',
                    email_confirm: true
                });

                if (createError) {
                    console.error('Error creating user:', createError);
                    return false;
                }

                userId = newUser.user.id;
                console.log('새로운 사용자 생성:', userId)
                // profiles 테이블 업데이트
                const { error: profileError } = await supabaseAdmin
                    .from('profiles')
                    .upsert({ id: userId, email: email,naverLogin:true }, {
                        onConflict: 'id'
                    });


                if (profileError) {
                    console.error('Error updating profile:', profileError);
                    return false;
                }
            } else {
                userId = data.id;
            }

            // 세션 생성 및 쿠키 설정을 위한 수정된 로그인 처리
            const { data: loginData, error: loginError } = await supabase.auth.signInWithPassword({
                email,
                password: 'defaultPassword'
            });

            if (loginError) {
                console.error('Login error:', loginError);
                return false;
            }

            return true;
        },
        async redirect({ url, baseUrl }) {
            // Redirect to /protected after login
            return '/';
        }
    }
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
//api/auth/supabase-login.js

import { NextResponse } from "next/server";

import { createClient } from "@/utils/supabase/server";

export async function POST(request) {
    const supabase = await createClient();
    const { email } = await request.json();
    const password = `defaultPassword`; // 강제 비밀번호 설정
    console.log("로긴하자")
    const { data, error } = await supabase.auth.signInWithPassword({
        email,
        password,
    });
    console.log("data2323:", data);

    if (error) {
        return NextResponse.json({ error: error.message }, { status: 400 });
    }

    return NextResponse.json({ session: data }, { status: 200 });
}
'use client'
import { signIn, useSession } from "next-auth/react";
import { useEffect } from "react";

export default function LoginPage() {
  const { data: session } = useSession();

  useEffect(() => {
    if (session?.user?.email) {
      fetch("/api/auth/supabase-login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email: session.user.email }),
      })
        .then(async (res) => {
          if (!res.ok) {
            throw new Error(`HTTP error! status: ${res.status}`);
          }
          const contentType = res.headers.get("content-type");
          if (contentType && contentType.includes("application/json")) {
            return res.json();
          }
          throw new TypeError("응답이 JSON 형식이 아닙니다!");
        })
        .then((data) => {
          console.log("Supabase 로그인 성공:", data);
        })
        .catch((err) => {
          console.error("Supabase 로그인 실패:", err.message);
        });
    }
  }, [session]);
  console.log("sessionCheck:", session);

  return (
    <div>
      <button onClick={() => signIn("naver")}>네이버 로그인</button>
    </div>
  );
}
//signOutButton.js
  "use client";

import { signOutAction } from "@/app/actions";
import { Button } from "./ui/button";
import { signOut } from "next-auth/react";

export default function SignOutButton() {
  const handleSignOut = async () => {
    // 클라이언트 측에서 signOut 호출
    await signOut();
  };

  return (
    <form action={signOutAction} onSubmit={handleSignOut}>
      <Button type="submit" variant={"outline"}>
        Sign out
      </Button>
    </form>
  );
}
profile
CODE DIVE!

0개의 댓글