RDS Supabase第三方认证登录(GitHub)

本文以GitHub为例,演示如何为RDS Supabase实例配置并启用第三方认证登录功能。

背景信息

RDS Supabase支持与 GitHub、Google、微信、支付宝等主流第三方身份提供商(Identity Provider, IdP)集成,以实现OAuth认证登录。此功能允许用户使用其既有的第三方账号登录RDS Supabase应用,从而简化注册流程并提升用户体验。

关于所有支持的提供商及其详细参数配置,请参考阿里云RDS Supabase认证(Authentication)

接下来,以GitHub为例,详细介绍如何为RDS Supabase实例配置并启用第三方认证登录。

前提条件

操作步骤

整个配置流程分为创建GitHub OAuth应用、配置RDS Supabase实例和功能验证三个主要部分。

步骤一:创建并配置GitHub OAuth Application

首先,需要在GitHub上创建一个OAuth Application,以获取用于API认证的Client IDClient Secret。

  1. 登录GitHub,进入开发者设置页面。

  2. 在左侧导航栏选择OAuth Apps,然后单击New OAuth App

  3. 在注册页面,填写应用信息:

    • Application name:根据您的应用实际情况填写。

    • Homepage URL:填写您的应用主页地址。

    • Authorization callback URL:填入您的RDS Supabase实例的外网访问地址,并附加路径/auth/v1/callback。格式:https://<Supabase外网地址>/auth/v1/callback

      说明

      Supabase外网地址不要带端口号。

  4. 单击Register application

  5. 注册完成后,页面会自动跳转到应用详情页。请记录页面上显示的Client ID

  6. 单击Generate a new client secret,生成客户端密钥。

    重要

    GitHubClient Secret仅在生成时完整显示一次,请务必妥善保存,后续配置将会用到。

image.png

步骤二:配置RDS Supabase认证参数

获取到GitHub OAuth App的凭证后,需要在RDS Supabase实例中配置相关参数以启用该认证方式。

  1. 进入RDS控制台首页,在左侧导航栏,单击AI 应用开发。在上方选择地域后,单击项目ID进入RDS Supabase实例详情页。

  2. 在左侧导航栏中,单击Auth配置

  3. 点击身份验证提供商 > GitHub页面,配置以下参数:

    • 是否启用GITHUB登录:点击启用GitHub登录。

    • GitHub OAuth App 的 Client ID:填入步骤一中获取的GitHub OAuth AppClient ID

    • GitHub OAuth AppClient Secret:填入步骤一中获取的GitHub OAuth AppClient Secret

    • 授权回调地址:填入与步骤一中完全相同的Authorization callback URL

  4. 配置完成后,单击确认。实例会自动重启以使配置生效,请耐心等待重启完成。

image

步骤三:配置RDS Supabase白名单

为确保您的应用能够访问RDS Supabase实例,需要将其公网IP地址添加到RDS Supabase的访问白名单中。

  1. 进入RDS控制台首页,在左侧导航栏,单击AI 应用开发

  2. 在上方选择地域后,在RDS Supabase列表中,点击目标的项目ID进入RDS Supabase详情页。

  3. 基本信息页的白名单信息区域,单击添加白名单分组,将目标客户端的IP地址添加到白名单中。

步骤四:允许实例访问公网

为确保RDS Supabase实例能够主动向公网上的GitHub服务器发起API请求,以完成OAuth认证流程,需要打开允许实例访问公网设置。

  1. 点击进入实例详情的基本信息页。

  2. 打开网络信息 > 允许实例访问公网按钮。

    image

说明

若第三方应用在海外地域,为了保证Supabase第三方认证功能的稳定性和良好体验,推荐购买海外地域的RDS Supabase实例。

步骤四:验证GitHub认证功能

完成以上配置后,可以搭建一个简单的前端项目来验证GitHub登录流程是否正常工作。

  1. 初始化项目结构

    创建一个项目文件夹,并在其中包含以下文件:

    • index.html: 前端页面结构。

    • main.js: 核心的认证逻辑。

    • supabase-config.js: RDS Supabase连接配置。

    • package.json: 项目依赖和脚本配置。

  2. 编写代码

    在项目中创建以下文件并填入相应内容。

    index.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Supabase GitHub 认证测试</title>
     <style>
     body {
     font-family: Arial, sans-serif;
     max-width: 800px;
     margin: 0 auto;
     padding: 20px;
     background-color: #f5f5f5;
     }
     .container {
     background-color: white;
     padding: 30px;
     border-radius: 10px;
     box-shadow: 0 2px 10px rgba(0,0,0,0.1);
     }
     .hidden {
     display: none;
     }
     button {
     background-color: #333;
     color: white;
     border: none;
     padding: 12px 24px;
     border-radius: 5px;
     cursor: pointer;
     font-size: 16px;
     margin: 10px 0;
     }
     button:hover {
     background-color: #555;
     }
     button.github {
     background-color: #24292e;
     }
     button.github:hover {
     background-color: #333;
     }
     button.logout {
     background-color: #dc3545;
     }
     button.logout:hover {
     background-color: #c82333;
     }
     .user-info {
     background-color: #f8f9fa;
     padding: 20px;
     border-radius: 5px;
     margin: 20px 0;
     border-left: 4px solid #007bff;
     }
     .error {
     color: #dc3545;
     background-color: #f8d7da;
     padding: 10px;
     border-radius: 5px;
     margin: 10px 0;
     }
     .success {
     color: #155724;
     background-color: #d4edda;
     padding: 10px;
     border-radius: 5px;
     margin: 10px 0;
     }
     </style>
    </head>
    <body>
     <div class="container">
     <h1>Supabase GitHub 认证测试</h1>
     
     <!-- 登录前显示 -->
     <div id="login-section">
     <h2>登录</h2>
     <p>点击下面的按钮使用 GitHub 账号登录</p>
     <button id="github-login" class="github">使用 GitHub 登录</button>
     <div id="login-error" class="error hidden"></div>
     </div>
     
     <!-- 登录后显示 -->
     <div id="user-section" class="hidden">
     <h2>用户信息</h2>
     <div class="user-info">
     <p><strong>用户ID:</strong> <span id="user-id"></span></p>
     <p><strong>用户名:</strong> <span id="user-name"></span></p>
     <p><strong>邮箱:</strong> <span id="user-email"></span></p>
     <p><strong>头像:</strong></p>
     <img id="user-avatar" src="" alt="用户头像" style="width: 100px; height: 100px; border-radius: 50%;">
     </div>
     <button id="logout" class="logout">登出</button>
     <div id="logout-error" class="error hidden"></div>
     </div>
     
     <!-- 状态信息 -->
     <div id="status" class="success hidden"></div>
     </div>
     <script type="module" src="/main.js"></script>
    </body>
    </html>

    main.js

    // 导入 Supabase 配置
    import { SUPABASE_CONFIG } from './supabase-config.js';
    // 初始化 Supabase 客户端
    import { createClient } from '@supabase/supabase-js';
    const supabase = createClient(SUPABASE_CONFIG.url, SUPABASE_CONFIG.anonKey);
    // DOM 元素
    const loginSection = document.getElementById('login-section');
    const userSection = document.getElementById('user-section');
    const githubLoginBtn = document.getElementById('github-login');
    const logoutBtn = document.getElementById('logout');
    const loginError = document.getElementById('login-error');
    const logoutError = document.getElementById('logout-error');
    const statusDiv = document.getElementById('status');
    // 检查用户登录状态
    async function checkUser() {
     const { data: { user } } = await supabase.auth.getUser();
     if (user) {
     showUser(user);
     } else {
     showLogin();
     }
    }
    // 显示登录界面
    function showLogin() {
     loginSection.classList.remove('hidden');
     userSection.classList.add('hidden');
    }
    // 显示用户信息
    function showUser(user) {
     document.getElementById('user-id').textContent = user.id;
     document.getElementById('user-name').textContent = user.user_metadata?.name || user.email;
     document.getElementById('user-email').textContent = user.email;
     const avatarUrl = user.user_metadata?.avatar_url || '';
     document.getElementById('user-avatar').src = avatarUrl;
     document.getElementById('user-avatar').style.display = avatarUrl ? 'block' : 'none';
     
     loginSection.classList.add('hidden');
     userSection.classList.remove('hidden');
    }
    // GitHub 登录
    async function loginWithGitHub() {
     loginError.classList.add('hidden');
     try {
     const { data, error } = await supabase.auth.signInWithOAuth({
     provider: 'github',
     options: {
     redirectTo: window.location.origin
     }
     });
     
     if (error) throw error;
     } catch (error) {
     console.error('登录失败:', error);
     loginError.textContent = '登录失败: ' + error.message;
     loginError.classList.remove('hidden');
     }
    }
    // 登出
    async function logout() {
     logoutError.classList.add('hidden');
     try {
     const { error } = await supabase.auth.signOut();
     if (error) throw error;
     showLogin();
     statusDiv.textContent = '已成功登出';
     statusDiv.classList.remove('hidden');
     setTimeout(() => statusDiv.classList.add('hidden'), 3000);
     } catch (error) {
     console.error('登出失败:', error);
     logoutError.textContent = '登出失败: ' + error.message;
     logoutError.classList.remove('hidden');
     }
    }
    // 事件监听器
    githubLoginBtn.addEventListener('click', loginWithGitHub);
    logoutBtn.addEventListener('click', logout);
    // 监听认证状态变化
    supabase.auth.onAuthStateChange((event, session) => {
     console.log('认证状态变化:', event);
     if (event === 'SIGNED_IN') {
     showUser(session.user);
     statusDiv.textContent = '登录成功!';
     statusDiv.classList.remove('hidden');
     setTimeout(() => statusDiv.classList.add('hidden'), 3000);
     } else if (event === 'SIGNED_OUT') {
     showLogin();
     statusDiv.textContent = '已登出';
     statusDiv.classList.remove('hidden');
     setTimeout(() => statusDiv.classList.add('hidden'), 3000);
     }
    });
    // 页面加载时检查用户状态
    checkUser();

    package.json

    package.json
    {
     "name": "supabase-github-auth-test",
     "version": "1.0.0",
     "description": "Test Supabase GitHub authentication",
     "main": "index.js",
     "scripts": {
     "dev": "vite",
     "build": "vite build",
     "preview": "vite preview"
     },
     "keywords": ["supabase", "github", "authentication"],
     "author": "",
     "license": "ISC",
     "dependencies": {
     "@supabase/supabase-js": "^2.38.4"
     },
     "devDependencies": {
     "vite": "^4.5.0"
     }
    }

    supabase-config.js

    // Supabase 配置文件
    // 请将此文件中的占位符替换为您的实际 Supabase 项目信息
    export const SUPABASE_CONFIG = {
     // 您的 Supabase 项目 URL
     url: 'YOUR_SUPABASE_URL',
     
     // 您的 Supabase 匿名密钥 (anon key)
     anonKey: 'YOUR_SUPABASE_ANON_KEY'
    };
  3. 获取并配置Supabase连接信息

    在运行项目前,参考RDS Supabase SDK使用指南获取SupabaseSUPABASE_URLAnon Key,来替换supabase-config.js文件中的urlanonKey的值。

  4. 运行项目并测试

    1. 在项目根目录下打开终端,执行以下命令安装依赖,并启动服务器:

      npm install
      npm run dev
    2. 命令执行成功后,在浏览器中访问 http://localhost:5173

    3. 在页面上单击使用GitHub登录。页面将跳转至GitHub授权页。

      image.png

    4. 授权成功后,页面将自动跳回,并显示从GitHub获取的用户信息(用户ID、用户名、邮箱等),表示认证成功。

      image.png

  5. 验证后台数据

    1. 进入RDS控制台首页,在左侧导航栏,单击AI 应用开发

    2. 在上方选择地域后,在RDS Supabase列表中,点击目标项目ID,进入RDS Supabase详情页。

    3. 点击网络信息 > 外网连接地址,进入Supabase的登录页面。

      说明

      如果您使用与RDS Supabase项目在同一VPC下的ECS进行登录,建议您使用内网连接地址

    4. 点击左侧导航栏的Authentication > Users,查看到刚刚通过GitHub登录的新用户信息,确认用户数据已成功同步至RDS Supabase。

    image.png