フルスタックチャンネル
サインアップサインアップ
ログインログイン
利用規約プライバシーポリシーお問い合わせ
Copyright © All rights reserved | FullStackChannel
解決済
Next.js 13新機能のApp DirectoryとSupabaseでブログ構築にGoogle認証を追加したい
Next.js
React
API
nekomusume
2023/07/05 12:01

実現したいこと

「Next.js 13新機能のApp DirectoryとSupabaseでブログ構築」のサインイン画面にGoogle認証ボタンを追加したい。

発生している問題

profilesテーブルを見てもGoogle認証したユーザーが追加されない。
(「プロフィール」ページを見るとGoogleのEmailアドレスのみ表示されている。)

ソースコード

はる先生のソースコードに下記のようにGoogle認証ボタン(handleClick)を追加しました。

utils/SignInResponse.ts(新規追加)

export interface SignInResponse {
  data: {
    user: {
      name: string;
      email: string;
      picture: string;
    };
  };
  error: any;
}

app/components/auth/signup.tsx

'use client'

import { useRef, FormEvent, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useSupabase } from '../supabase-provider'
import { FcGoogle } from "react-icons/fc";

import Link from 'next/link'
import Loading from '../../loading'
import type { SignInResponse } from '../../../utils/SignInResponse.types'

// サインアップ
const Singup = () => {
  const { supabase } = useSupabase()
  const router = useRouter()
  const nameRef = useRef<HTMLInputElement>(null)
  const emailRef = useRef<HTMLInputElement>(null)
  const passwordRef = useRef<HTMLInputElement>(null)
  const [loading, setLoading] = useState(false)


  //ソーシャルサインイン
  const handleClick= async () => {

    try {
      const { data, error } = await supabase.auth.signInWithOAuth({
        provider: 'google'
      }) as SignInResponse;
      if (!error) {
        console.log("Login successful", data);

        const useremail = data.User.email;
        const userName = data.User.name;
        const userAvatar = data.User.picture;

        // プロフィールの名前を更新
        const { error: updateError } = await supabase
        .from('profiles')
        .update({ name: userName, avatar_url:userAvatar })
        .eq('email', useremail)

      } else {
        console.error("Login failed", error);
        setLoading(false)
        return
      }
    } catch (err) {
      console.error("An error occurred", err);
      setLoading(false)
      return
    }


  }

  // 送信
  const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    setLoading(true)

    // supabaseサインアップ
    const { error: signupError } = await supabase.auth.signUp({
      email: emailRef.current!.value,
      password: passwordRef.current!.value,
    })


    if (signupError) {
      alert(signupError.message)
      setLoading(false)
      return
    }

    // プロフィールの名前を更新
    const { error: updateError } = await supabase
      .from('profiles')
      .update({ name: nameRef.current!.value })
      .eq('email', emailRef.current!.value)

    if (updateError) {
      alert(updateError.message)
      setLoading(false)
      return
    }

    // トップページに遷移
    router.push('/')
    setLoading(false)
  }

  return (
    <div className="max-w-sm mx-auto">


      <form onSubmit={onSubmit}>
        <div className="mb-5">
          <div className="text-sm mb-1">名前</div>
          <input
            className="w-full bg-gray-100 rounded border py-1 px-3 outline-none focus:bg-transparent focus:ring-2 focus:ring-yellow-500"
            ref={nameRef}
            type="text"
            id="name"
            placeholder="Name"
            required
          />
        </div>
        <div className="mb-5">
          <div className="text-sm mb-1">メールアドレス</div>
          <input
            className="w-full bg-gray-100 rounded border py-1 px-3 outline-none focus:bg-transparent focus:ring-2 focus:ring-yellow-500"
            ref={emailRef}
            type="email"
            id="email"
            placeholder="Email"
            required
          />
        </div>
        <div className="mb-5">
          <div className="text-sm mb-1">パスワード</div>
          <input
            className="w-full bg-gray-100 rounded border py-1 px-3 outline-none focus:bg-transparent focus:ring-2 focus:ring-yellow-500"
            ref={passwordRef}
            type="password"
            id="password"
            placeholder="Password"
            required
          />
        </div>
        <div className="text-center mb-5">
          {loading ? (
            <Loading />
          ) : (
            <button
              type="submit"
              className="w-full text-white bg-yellow-500 hover:brightness-110 rounded py-1 px-8"
            >
              サインアップ
            </button>
          )}
        </div>
        
      </form>

      <div className="text-center mb-5">
          {loading ? (
            <Loading />
          ) : (
            <button
              className="flex justify-center  items-center font-bold rounded py-1 px-8 w-full border border-gray-400"
              onClick={handleClick}
            >
              <span><FcGoogle /></span>
              <span className='ml-1'>Sign in With Google</span>
            </button>           
          )}
        </div>

      <div className="text-center">アカウントをお持ちですか?</div>

      <div className="text-center">
        <Link href="/auth/login" className="cursor-pointer font-bold">
          ログイン
        </Link>
      </div>
    </div>


  )
}

export default Singup

自分で試したこと

公式ドキュメントを参考に実装してみました。

https://supabase.com/docs/guides/auth/social-login/auth-google

補足情報

Google OAuth Keys とsupabaseの連携登録は行っております。

回答 4件
login
回答するにはログインが必要です
はる@講師
2年以上前

handleClick関数は、signInWithOAuthをコールするだけにします。

ここではプロフィールは更新できません。

//ソーシャルサインイン
const handleClick= async () => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'google'
  })
}

supabase-listener.tsxのリスナー関数で、プロフィールテーブルを更新するとよいかと思います。

  useEffect(() => {
    // セッション情報取得
    const getSession = async () => {
      const { data } = await supabase.auth.getSession()

      if (data.session) {
        const id = data.session.user.id
        const email = data.session.user.email
        const name = data.session.user.user_metadata.name
        const avatar_url = data.session.user.user_metadata.avatar_url

        // ユーザーIDにとメールアドレスを状態管理に設定
        setUser({
          id,
          email,
        })

        // プロフィールチェック
        const { data: userData } = await supabase.from('profiles').select().eq('id', id).single()

        // 名前とアバターが空のとき
        if (!userData?.name && !userData?.avatar_url) {
          // プロフィールの名前を更新
          await supabase
            .from('profiles')
            .update({ name: name, avatar_url: avatar_url })
            .eq('email', email)
        }
      }
    }
nekomusume
2年以上前

ありがとうございます!
どうりで更新できないわけですね!
参考になりました。

はる@講師
2年以上前

Google認証ですね。

こちらでも試してみます。

少々お待ちください。

1
nekomusume
2年以上前

はる先生

このソースコードで実行しますと「if (!error) {...」の部分は通りますのでGoogle認証は出来ているようです。

しかし、この後の処理で何か足りないようでsupabaseのデータベースに保存が出来ていないようです。

お手数ですがよろしくお願いいたします。

訂正です。
ユーザー登録とprofile登録はこのソースで出来ていました。
登録出来ないのはGoogleからのレスポンスデータからname(ユーザー名)とavater_url(アイコンURL)への登録です。