独立造物局登录
认证18 分钟

如何接入 Google 登录并关联 Supabase

从 Google Cloud 创建 OAuth 客户端,到 Supabase 启用 Google Provider,再到本地代码接入和验证的傻瓜式流程。

结论

这篇教程只做一件事:让网站支持 使用 Google 登录,并把登录后的用户保存到 Supabase Auth。

最终效果:

  1. 用户点击 使用 Google 登录
  2. 页面跳转到 Google 账号选择/授权页。
  3. Google 授权后跳回 Supabase。
  4. Supabase 创建或登录用户。
  5. 网站通过 /auth/callback 写入登录 Cookie。
  6. 用户回到网站的用户中心。

不包含:Google Drive、Gmail、Calendar 等额外权限;也不包含支付、会员系统和后台管理系统。

第 1 步:确认你已经有 Supabase 项目

先打开 Supabase Dashboard:

txt
https://supabase.com/dashboard

进入你的项目后,左侧菜单找到:

txt
Authentication -> Sign In / Providers

这里能看到所有登录方式。后面我们会启用其中的 Google

Supabase Auth Providers 列表

第 2 步:复制 Supabase 的 OAuth Callback URL

Sign In / Providers 页面里找到 Google,点击这一行。

右侧会打开 Google Provider 配置面板。先不用填 Client ID 和 Secret,先找到:

txt
Callback URL (for OAuth)

这个地址长得像这样:

txt
https://你的项目ref.supabase.co/auth/v1/callback

点击旁边的 Copy 复制它。这个地址后面要填到 Google Cloud 的 Authorized redirect URIs

Supabase Google Provider 配置面板

注意:这个回调地址不是你网站里的 /auth/callback
Google OAuth 第一跳必须先回到 Supabase 的:

txt
https://xxx.supabase.co/auth/v1/callback

然后 Supabase 才会再跳回你代码里配置的:

txt
http://localhost:3000/auth/callback

第 3 步:配置 Supabase 的站点跳转地址

进入:

txt
Authentication -> URL Configuration

本地开发时先这样填:

txt
Site URL
http://localhost:3000

Redirect URLs
http://localhost:3000/auth/callback

Supabase URL Configuration

上线后还要追加正式域名,例如:

txt
Site URL
https://your-domain.com

Redirect URLs
https://your-domain.com/auth/callback

不要删掉本地地址。开发环境和线上环境可以同时保留。

第 4 步:打开 Google Auth Platform

打开 Google Cloud Console:

txt
https://console.cloud.google.com

选择或创建一个 Google Cloud 项目,然后进入:

txt
Google Auth Platform

左侧会看到这些菜单:

txt
概览
品牌塑造
目标对象
客户端
数据访问
验证中心
设置

Google Auth Platform 概览

如果是第一次使用,Google 会要求你先配置应用信息。

第 5 步:配置 Google 授权页面

进入:

txt
Google Auth Platform -> 品牌塑造

按提示填写:

txt
应用名称
你的网站或产品名

用户支持电子邮件
选择你的 Google 邮箱

开发者联系信息
填写你的邮箱

然后进入:

txt
Google Auth Platform -> 目标对象

如果你的网站面向普通用户,选择:

txt
External

如果还没有发布,可以保持测试状态。测试状态下,只有测试用户可以登录。

第 6 步:创建 OAuth 客户端

进入:

txt
Google Auth Platform -> 客户端

点击:

txt
创建客户端

Google OAuth 客户端列表

在创建页面里,应用类型 选择:

txt
Web 应用

Google 创建 OAuth 客户端

然后填写客户端名称,例如:

txt
独立站 Web Client

这个名称只在 Google Cloud 控制台里显示,用户不会看到。

第 7 步:填写 Authorized JavaScript origins

在 Google OAuth Client 表单里找到:

txt
已获授权的 JavaScript 来源

点击 添加 URI,本地开发填:

txt
http://localhost:3000

注意:

  1. 这里不要加路径。
  2. 不要写成 http://localhost:3000/auth/callback
  3. 本地开发是 http,不是 https
  4. 端口必须和你的本地项目端口一致。

Google OAuth JavaScript 来源

上线后再追加正式域名:

txt
https://your-domain.com

第 8 步:填写 Authorized redirect URIs

继续往下找到:

txt
已获授权的重定向 URI

点击 添加 URI,填第 2 步从 Supabase 复制的 Callback URL:

txt
https://你的项目ref.supabase.co/auth/v1/callback

示例:

txt
https://abcdefghijklmnop.supabase.co/auth/v1/callback

Google OAuth Redirect URI

这里最容易填错。重点是:

  1. 这是 Supabase 的回调地址,不是你网站的 /auth/callback
  2. 必须是 https://xxx.supabase.co/auth/v1/callback
  3. 末尾不要多加 /
  4. 项目 ref 必须和你的 Supabase 项目一致。

如果这里填错,登录时通常会看到:

txt
redirect_uri_mismatch

第 9 步:创建并保存 Client ID / Client Secret

点击 Google 页面底部的:

txt
创建

创建成功后,Google 会给你:

txt
Client ID
Client Secret

你需要立刻保存这两个值。

注意:

  1. Client ID 可以再次看到。
  2. Client Secret 创建后可能只能看一次。
  3. 不要把 Client Secret 写进前端代码。
  4. 不要把 Client Secret 提交到 Git。
  5. 如果 Secret 丢了,就在 Google 客户端详情页新增一个 Secret,然后把新的 Secret 填回 Supabase。

第 10 步:回到 Supabase 启用 Google Provider

回到 Supabase:

txt
Authentication -> Sign In / Providers -> Google

打开:

txt
Enable Sign in with Google

填写:

txt
Client IDs
填 Google Cloud 生成的 Client ID

Client Secret (for OAuth)
填 Google Cloud 生成的 Client Secret

然后点击:

txt
Save

保存成功后,Provider 列表里的 Google 会显示:

txt
Enabled

第 11 步:安装 Supabase 客户端依赖

如果项目还没安装 Supabase,执行:

bash
npm install @supabase/supabase-js @supabase/ssr

如果已经安装,可以跳过。

第 12 步:配置环境变量

在项目根目录创建或修改:

txt
.env.local

写入:

bash
NEXT_PUBLIC_SUPABASE_URL=https://你的项目ref.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=你的anon public key

这两个值在 Supabase 后台这里找:

txt
Project Settings -> API

注意:NEXT_PUBLIC_SUPABASE_ANON_KEY 是前端可用的匿名公钥,不是数据库密码,也不是 service role key。

第 13 步:创建浏览器端 Supabase Client

新建:

txt
src/lib/supabase/client.ts

代码:

ts
"use client";

import { createBrowserClient } from "@supabase/ssr";

export function createClientSupabase() {
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
  const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

  if (!url || !anonKey) {
    return null;
  }

  return createBrowserClient(url, anonKey);
}

第 14 步:创建服务端 Supabase Client

新建:

txt
src/lib/supabase/server.ts

代码:

ts
import { cookies } from "next/headers";
import { createServerClient } from "@supabase/ssr";

export async function createServerSupabase() {
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
  const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

  if (!url || !anonKey) {
    return null;
  }

  const cookieStore = await cookies();

  return createServerClient(url, anonKey, {
    cookies: {
      getAll() {
        return cookieStore.getAll();
      },
      setAll(cookiesToSet) {
        try {
          cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options));
        } catch {
          // Server Components cannot set cookies; auth routes and actions can.
        }
      }
    }
  });
}

第 15 步:登录按钮调用 Google OAuth

在登录组件里添加 Google 登录按钮。

核心代码:

tsx
"use client";

import { createClientSupabase } from "@/lib/supabase/client";

export function GoogleLoginButton() {
  const supabase = createClientSupabase();

  async function handleGoogleLogin() {
    if (!supabase) return;

    await supabase.auth.signInWithOAuth({
      provider: "google",
      options: {
        redirectTo: `${location.origin}/auth/callback?next=/account`
      }
    });
  }

  return (
    <button onClick={handleGoogleLogin} type="button">
      使用 Google 登录
    </button>
  );
}

这里的 redirectTo 是登录完成后,Supabase 再跳回你网站的地址。

本地开发时实际会是:

txt
http://localhost:3000/auth/callback?next=/account

第 16 步:创建 Auth Callback 路由

新建:

txt
src/app/auth/callback/route.ts

代码:

ts
import { NextRequest, NextResponse } from "next/server";
import { createServerSupabase } from "@/lib/supabase/server";

export async function GET(request: NextRequest) {
  const requestUrl = new URL(request.url);
  const code = requestUrl.searchParams.get("code");
  const nextParam = requestUrl.searchParams.get("next") || "/account";
  const next = nextParam.startsWith("/") && !nextParam.startsWith("//") ? nextParam : "/account";

  if (!code) {
    return NextResponse.redirect(new URL("/login?error=missing_code", requestUrl.origin));
  }

  const supabase = await createServerSupabase();
  if (!supabase) {
    return NextResponse.redirect(new URL("/login?error=supabase_not_configured", requestUrl.origin));
  }

  const { error } = await supabase.auth.exchangeCodeForSession(code);
  if (error) {
    return NextResponse.redirect(new URL(`/login?error=${encodeURIComponent(error.message)}`, requestUrl.origin));
  }

  return NextResponse.redirect(new URL(next, requestUrl.origin));
}

这一步非常关键。没有 exchangeCodeForSession(code),数据库里可能已经有用户,但你的网站读不到登录态。

第 17 步:创建登录后的账号页

新建:

txt
src/app/account/page.tsx

代码:

tsx
import { redirect } from "next/navigation";
import { createServerSupabase } from "@/lib/supabase/server";

export default async function AccountPage() {
  const supabase = await createServerSupabase();
  if (!supabase) redirect("/login");

  const {
    data: { user }
  } = await supabase.auth.getUser();

  if (!user) redirect("/login");

  return (
    <main>
      <h1>用户中心</h1>
      <p>{user.email}</p>
    </main>
  );
}

登录成功后,如果这里能显示邮箱,说明网站已经能从 Supabase session 里读到当前用户。

第 18 步:本地启动测试

启动项目:

bash
npm run dev

打开:

txt
http://localhost:3000/login

点击:

txt
使用 Google 登录

正常流程:

  1. 跳到 Google。
  2. 选择 Google 账号。
  3. 第一次使用时确认授权。
  4. 回到你的网站 /auth/callback
  5. 自动跳到 /account
  6. 页面显示用户邮箱。

第 19 步:去 Supabase 验证用户是否创建

登录成功后,回到 Supabase:

txt
Authentication -> Users

你应该能看到一条新用户记录。

如果你创建了 profiles 表和 handle_new_user trigger,也可以进入:

txt
Table Editor -> profiles

检查是否同步了:

txt
email
display_name
avatar_url

第 20 步:理解 Google 登录的注册逻辑

Google 登录没有严格区分“登录”和“注册”。

默认逻辑是:

  1. 如果 Supabase 已经有这个 Google 用户,就是登录。
  2. 如果 Supabase 没有这个 Google 用户,并且允许新用户注册,就是自动注册并登录。

所以用户第一次点 使用 Google 登录 时,不需要先走一个单独的注册表单。

如果你删除了 Supabase 里的用户,用户下次用同一个 Google 账号登录时,Supabase 会重新创建一条用户记录。

第 21 步:为什么第二次 Google 登录不再弹授权页

第一次授权后,Google 会记住这个账号已经允许你的应用读取基础信息。

你删除 Supabase 用户,不会删除 Google 账号里的授权记录。

如果想重新看到授权确认页,需要到 Google 账号里移除授权:

txt
https://myaccount.google.com/connections

路径:

txt
Google 账号 -> 安全性 -> 你与第三方应用和服务的关联 -> 找到应用 -> 移除访问权限

移除后,再次点击 Google 登录,Google 才可能重新展示授权确认。

常见错误 1:redirect_uri_mismatch

原因:Google Cloud 里的重定向 URI 填错。

检查 Google Cloud:

txt
Google Auth Platform -> 客户端 -> 你的 Web Client -> 已获授权的重定向 URI

必须填 Supabase Callback URL:

txt
https://你的项目ref.supabase.co/auth/v1/callback

不是:

txt
http://localhost:3000/auth/callback

常见错误 2:数据库里有用户,但页面还在登录页

原因通常是网站没有把 OAuth code 换成 Supabase session。

检查:

  1. 是否存在 src/app/auth/callback/route.ts
  2. 是否调用了 exchangeCodeForSession(code)
  3. Supabase Redirect URLs 是否包含 http://localhost:3000/auth/callback
  4. 登录按钮的 redirectTo 是否是 ${location.origin}/auth/callback

常见错误 3:Google Provider 已启用,但点击登录报错

检查 Supabase:

txt
Authentication -> Sign In / Providers -> Google

确认:

txt
Enable Sign in with Google: 开启
Client IDs: 已填写
Client Secret: 已填写

如果 Client Secret 丢了,不要猜,也不要用尾号。去 Google Cloud 客户端详情页新增一个 Secret,再填回 Supabase。

常见错误 4:测试用户不能登录

如果 Google Auth Platform 还处于测试状态,只有测试用户可以登录。

检查:

txt
Google Auth Platform -> 目标对象

如果是测试模式,需要把测试账号加入测试用户列表。

正式上线后,再按 Google 要求补齐应用信息和验证流程。

上线前检查清单

上线前至少检查这些:

  1. Supabase Site URL 改成正式域名。
  2. Supabase Redirect URLs 增加正式回调地址。
  3. Google Authorized JavaScript origins 增加正式域名。
  4. Google Authorized redirect URIs 保留 Supabase callback URL。
  5. .env.local 不提交到 Git。
  6. 线上环境变量里配置 Supabase URL 和 anon key。
  7. 不把 Google Client Secret 写进前端代码。
  8. 本地和线上都测试一次完整 Google 登录。

最小链路图

txt
用户点击 Google 登录
  -> Supabase signInWithOAuth("google")
  -> Google 账号选择/授权
  -> https://你的项目ref.supabase.co/auth/v1/callback
  -> http://localhost:3000/auth/callback
  -> exchangeCodeForSession(code)
  -> /account

记住一句话:Google Cloud 配 Supabase callback,Supabase 配你网站 callback,代码里负责把 code 换成 session。

下一步工作流