Supabase OAthログイン(github)の設定【Next.js Appルーター】


# はじめに
前回はSupabaseでログインの実装を行う方法を記事で共有しました。

https://qiita.com/acu8/items/e54db39ace273cce3bc7

今回は通常のemailログインに加えて、GithubのOAthログインの実装方法を共有していきたいと思います。

技術スタックは前回と同じです。

```: 開発環境
言語：
Typescript

フレームワーク：
Next.js 14(appルーター使用)

DB:
Supabase
```

# Supabaseの設定

Supabaseの左側メニューからAuthenticationを選択しProvidersをクリックします。
Auth Providersが表示されますので、Githubの部分を選択し「Enabled」に変更します。
![スクリーンショット 2024-11-06 12.50.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825671/1d302a7c-f98a-6dfa-211b-db750525f3fe.png)

その下のClient IDとClient SecretはGithub上の設定をする際に取得できるので一旦空欄で大丈夫です。

さらにその下にある「Callback URL (for OAuth)」はGithubで設定する際に必要になるのでコピーをして次の手順に進んでください。

# Github上の設定
Github右上に表示されている自分のプロフィールアイコンをクリックし、表示されるメニューから「Setting」をクリックします。
Settingの一番下にある「Developer Settings」をクリックしてください。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825671/ffcd3f41-6ad5-e9ac-1a6c-b936e05b48f8.png">

以下の画面が表示されますので、「OAuth Apps」をクリックして先ほどコピーした「Callback URL (for OAuth)」を所定の欄に入力してください。
![スクリーンショット 2024-11-06 9.13.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825671/80ac1f97-911e-258b-0b37-61a0fdc9f7b4.png)

同時にのClient IDとClient Secretを取得して、先ほどのAuth ProvidersページのGithubの部分に添付して、右下のSaveボタンで保存を行います。

ここまででSupabaseとGithubの設定は完了です。
次は実際にアプリ側で設定を進めていきます。

# アプリ側でOAth設定

公式ドキュメントがありますので、そちらを参考にしつつ進めていくことができます。

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

app配下に以下のファイルを作成します。前回のemailログインを実装している場合は同じファイルにコードを追加してください。
```
app/login/page.tsx
app/login/actions.ts
app/error/page.tsx
```



```tsx: app/login/oauth-signin.tsx
"use client";
import { Button } from "@/components/ui/button";
import { Provider } from "@supabase/supabase-js";
import { Github } from "lucide-react";
import { oAuthSignIn } from "./action";

type OAuthProvider = {
  name: Provider;
  displayName: string;
  icon?: JSX.Element;
};

export function OAuthButtons() {
  const oAuthProviders: OAuthProvider[] = [
    {
      name: "github",
      displayName: "GitHub",
      icon: <Github className="size-5" />,
    },
  ];

  return (
    <>
      {oAuthProviders.map((provider) => (
        <Button
          className="w-full flex items-center justify-center gap-2"
          variant="outline"
          onClick={async () => {
            await oAuthSignIn(provider.name);
          }}
        >
          {provider.icon}
          Login with {provider.displayName}
        </Button>
      ))}
    </>
  );
}
```

```tsx: app/login/actions.ts
export async function oAuthSignIn(provider: Provider) {
    if (!provider) {
        return redirect('/login?message=No provider selected')
    }

    const supabase = createClient();
    const redirectUrl = getURL("/auth/callback")
    const { data, error } = await supabase.auth.signInWithOAuth({
        provider,
        options: {
            redirectTo: redirectUrl,
        }
    })

    if (error) {
        redirect('/login?message=Could not authenticate user')
    }

    return redirect(data.url)
}

```

```tsx: app/login/page.tsx
import { OAuthButtons } from "./oauth-signin";

<OAuthButtons />

```

以下のディレクトリにroute.tsを作成します。
```tsx: app/auth/callback/route.ts
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
import { type CookieOptions, createServerClient } from '@supabase/ssr'

export async function GET(request: Request) {
    const { searchParams, origin } = new URL(request.url)
    const code = searchParams.get('code')
    // if "next" is in param, use it as the redirect URL
    const next = searchParams.get('next') ?? '/'

    if (code) {
        const cookieStore = cookies()
        const supabase = createServerClient(
            process.env.NEXT_PUBLIC_SUPABASE_URL!,
            process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
            {
                cookies: {
                    get(name: string) {
                        return cookieStore.get(name)?.value
                    },
                    set(name: string, value: string, options: CookieOptions) {
                        cookieStore.set({ name, value, ...options })
                    },
                    remove(name: string, options: CookieOptions) {
                        cookieStore.delete({ name, ...options })
                    },
                },
            }
        )
        const { error } = await supabase.auth.exchangeCodeForSession(code)
        if (!error) {
            return NextResponse.redirect(`${origin}${next}`)
        }
    }

    // return the user to an error page with instructions
    return NextResponse.redirect(`${origin}/login?message=Could not login with provider`)
}

```

:::note info
**認証コールバックの流れ：**
①認証プロバイダからのコールバックを受け取る
②認証コードを使用してセッションを確立
③ユーザーを適切なページにリダイレクト
:::

上記の`callback/route.ts`の一部を解説します。

```tsx:
const { searchParams, origin } = new URL(request.url)
const code = searchParams.get('code')
const next = searchParams.get('next') ?? '/'
```
URLから認証コードを取得してきます。'next'のパラメータが入っていない場合は、リダイレクト先にHomeの「/」を設定しています。

```tsx:
const { error } = await supabase.auth.exchangeCodeForSession(code)
if (!error) {
    return NextResponse.redirect(`${origin}${next}`)
}
```
取得した認証コードを使用してSupabaseのセッションを確立しています。
もしエラーが出なければ指定したページにリダイレクトをするように設定をしています。



# おわりに
GithubのAuthはたまたま参考に見ていたYoutubeで使用していたので使うことに決めました。
やってみるとユーザー情報をとってきたりする部分の理解が複雑だったりしましたが、案外簡単にできることがわかりました。
今後の個人開発アプリにも取り入れていきたいと思います。
皆様の参考になれば幸いです！

# 参考
本記事で紹介しているコードは以下のチュートリアルを参考にしました。

https://youtu.be/A6-56miVA_0?si=69Sc2BT-VBnLeQF6

https://github.com/jolbol1/supatodo

# JISOUのメンバー募集中！

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
実践的なカリキュラムで、あなたのエンジニアとしてのキャリアを最短で飛躍させましょう！
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください！
▼▼▼

https://projisou.jp/