← 返回文章列表
开发笔记

在 Flutter 中用 Apple 登录接入 Supabase Auth(分步指南)

本文说明如何在 Flutter 应用里使用 Sign in with Apple(Apple 登录),并通过 Supabase Auth 建立会话。在 iOS / macOS 上,推荐用 sign_in_with_apple 走系统原生授权,取得 ID Token 后调用 signInWithIdToken;在 Android、Web、Windows、Linux 等没有原生 Apple 登录的环境,需改用 Supabase 的 signInWithOAuth 浏览器流程,并完成 Apple 侧的 Services ID、密钥与回调 URL 等配置。

官方参考:Supabase · Apple 登录(含 Flutter)sign_in_with_apple

你将完成什么

  • 在 Apple Developer 为 App 开启 Sign in with Apple(必要时配置 Services ID 与密钥,用于 OAuth 流程)
  • 在 Supabase 启用 Apple 提供商,并把 Bundle ID(及多环境变体) 登记到 Client IDs
  • 在 Flutter 中:iOS/macOS 使用 signInWithIdToken;其他平台使用 signInWithOAuth 与深度链接(如需要)
  • (可选)在首次登录时把 Apple 返回的姓名写入用户 metadata(Apple 只在第一次授权时提供全名)

前置条件

  • 已能运行 Flutter 工程,并已集成 supabase_flutterSupabase.initialize(可参考本站《在 Flutter 中接入 Supabase 邮箱登录》中的初始化步骤)。
  • iOS / macOS:Apple Developer 账号;Xcode 中正确配置 Signing & Capabilities
  • 若使用 OAuth / Flutter Web / Android 上的 Apple 登录:需在 Apple Developer 配置 Services ID签名密钥(.p8),并在 Supabase 填写由密钥生成的 Secret;该 Secret 最长约 6 个月需轮换一次,请设日历提醒。
  • 纯原生 iOS/macOS 客户端:按 Supabase 文档说明,可以不配置 OAuth 那一套(Services ID、p8 等),但仍须在控制台 Apple 提供商 中登记 Client IDs(即 Bundle ID) 并启用提供商。

重要概念:两种接入方式

场景做法Supabase / Apple 配置要点
iOS、macOS 原生 Appsign_in_with_applesignInWithIdToken登记 Bundle IDClient IDs;纯原生可不配 OAuth Secret
Android、Web、桌面signInWithOAuth(OAuthProvider.apple, …)Services ID、回调 https://<project-ref>.supabase.co/auth/v1/callback.p8 与 Secret;移动端要配 深度链接

不要在非 Apple 平台上按 sign_in_with_apple 的 README 去配「Android/Web 的 sign_in_with_apple」来接 Supabase;这些平台应走 OAuth

第一步:Apple Developer — App ID 与 Sign in with Apple

  1. 登录 Apple Developer,打开 Certificates, Identifiers & Profiles
  2. Identifiers → 筛选 App IDs,选择或新建你的应用标识(例如 com.example.app)。
  3. Capabilities 中勾选 Sign In with Apple,保存。
  4. Server-to-Server Notification Endpoint:当前 Supabase Auth 不支持该端点配置,相关项可留空(与官方文档一致)。

若你还有 开发 / 预览 / 生产 等多个 Bundle ID,每个要在 Supabase 的 Apple Client IDs 里各加一条(与 Expo 文档同理,Flutter 多 flavor 也一样)。

第二步:Xcode / Flutter iOS 工程

  1. 用 Xcode 打开 ios/Runner.xcworkspace
  2. 选中 Runner target → Signing & Capabilities+ Capability → 添加 Sign In with Apple(若上一步 App ID 已开启,能力与描述文件应能正常生效)。
  3. 确认 Bundle Identifier 与 Apple Developer 里登记的 App ID 完全一致

sign_in_with_apple 的 iOS/macOS 具体 plist、最低系统版本等以 pub.dev 说明 为准。

第三步:Supabase — 启用 Apple 并登记 Client IDs

  1. Supabase 控制台 → AuthenticationProvidersApple
  2. 打开 Enable Sign in with Apple
  3. Client IDs(或控制台中等价字段)中加入你的 Bundle ID(及所有需要登录的变体,如 com.example.app.dev)。
  4. 仅在使用 OAuth时(Web、Android、或 signInWithOAuth):再填写 Services IDSecret Key(由 .p8 等生成的客户端密钥)、Team IDKey ID 等,并按文档把 Apple Services ID 的 Return URLs 配成:
    https://<你的-project-ref>.supabase.co/auth/v1/callback
  5. AuthenticationURL ConfigurationSite URLRedirect URLs 与深度链接 /网站回调一致,避免 OAuth 回调被拒绝。

第四步:Flutter 依赖

pubspec.yaml 中(版本以 pub.dev 为准):

dependencies:
  flutter:
    sdk: flutter
  supabase_flutter: ^2.8.0
  sign_in_with_apple: ^6.1.0
  crypto: ^3.0.0
flutter pub get

crypto 用于对 nonce 做 SHA256,与 sign_in_with_apple 传入的 nonce 及 Supabase 校验一致。

第五步:iOS / macOS — 原生 Apple 登录代码示例

逻辑要点:

  1. 使用 supabase.auth.generateRawNonce() 生成 原始 nonce
  2. 对原始 nonce 做 SHA256 十六进制字符串,传给 SignInWithApple.getAppleIDCredential(nonce: hashedNonce)
  3. 将凭证中的 identityToken原始 nonce 传给 signInWithIdToken(provider: OAuthProvider.apple, …)
  4. givenName / familyName 非空(通常仅首次授权有值),可 updateUser 写入 metadata。
import 'dart:convert';

import 'package:crypto/crypto.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

final supabase = Supabase.instance.client;

/// 在 iOS / macOS 上使用原生 Sign in with Apple,并接入 Supabase 会话。
Future<AuthResponse> signInWithAppleNative() async {
  final rawNonce = supabase.auth.generateRawNonce();
  final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();

  final credential = await SignInWithApple.getAppleIDCredential(
    scopes: [
      AppleIDAuthorizationScopes.email,
      AppleIDAuthorizationScopes.fullName,
    ],
    nonce: hashedNonce,
  );

  final idToken = credential.identityToken;
  if (idToken == null) {
    throw const AuthException('无法从 Apple 凭证中读取 ID Token。');
  }

  final authResponse = await supabase.auth.signInWithIdToken(
    provider: OAuthProvider.apple,
    idToken: idToken,
    nonce: rawNonce,
  );

  if (credential.givenName != null || credential.familyName != null) {
    final parts = <String>[];
    if (credential.givenName != null) parts.add(credential.givenName!);
    if (credential.familyName != null) parts.add(credential.familyName!);
    final fullName = parts.join(' ');

    await supabase.auth.updateUser(
      UserAttributes(
        data: {
          'full_name': fullName,
          if (credential.givenName != null) 'given_name': credential.givenName,
          if (credential.familyName != null) 'family_name': credential.familyName,
        },
      ),
    );
  }

  return authResponse;
}

界面上可用 SignInWithAppleButton 触发上述方法;注意仅在 iOS 13+ / macOS 10.15+ 等支持的环境展示。

第六步:Android / Web / 桌面 — OAuth 流程

使用 signInWithOAuth,并配置 redirectTo(非 Web 时常为自定义 URL Scheme),且完成上文 OAuth 所需的 Apple Services ID + Secret 与 Supabase Redirect URLs

import 'package:flutter/foundation.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> signInWithAppleOAuth() async {
  await Supabase.instance.client.auth.signInWithOAuth(
    OAuthProvider.apple,
    redirectTo: kIsWeb ? null : '你的scheme://你的host', // 与深度链接、控制台 Redirect URLs 一致
    authScreenLaunchMode: kIsWeb
        ? LaunchMode.platformDefault
        : LaunchMode.externalApplication,
  );
}

深度链接与 app_links / uni_links 等配合方式见:Supabase · 原生移动深度链接

第七步:登出与其他 Supabase 登录方式相同,例如:

await Supabase.instance.client.auth.signOut();

常见问题

现象建议排查
signInWithIdToken 报错 / 校验失败Client IDs 是否包含当前 Bundle ID;nonce 是否为「原始值 + SHA256 给 Apple」这一对
OAuth 回调失败Services ID 的 Return URL 是否精确包含 https://<ref>.supabase.co/auth/v1/callback;Supabase Redirect URLs 是否包含你的 redirectTo
生产环境突然全部无法登录 AppleOAuth Secret 是否过期(约 6 个月);.p8 是否泄露需轮换
用户姓名始终为空Apple 只在首次授权通过原生接口返回姓名;需在首次登录时写入 metadata,或引导用户在资料页自行补充

合规与设计提示

  • 若 App 已提供第三方登录(如 Google),在 iOS 上通常须同时提供 Sign in with Apple(以 App Store 审核指南为准)。
  • 用户选择「隐藏邮箱」时,会得到 Apple 中转邮箱;需在产品中说明邮件送达与账号找回方式。

按上述步骤,你可以在 Flutter 中完成 Apple 登录与 Supabase Auth 的串联;以 原生 iOS/macOS 为主时优先 signInWithIdToken,跨平台需求再补 OAuth 与密钥维护流程即可。