You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Android端Firebase Functions实现Apple登录跳转异常排查

问题:Flutter Android端Apple登录自定义Firebase Functions回调跳转异常

我在Flutter Android端开发Apple登录功能,目前Firebase Auth原生暂不支持Flutter Android的Apple登录。按照sign_in_with_apple包的官方指南使用Glitch示例回调项目可成功实现Apple登录,但我希望在Firebase Functions上自定义后端回调。

当前遇到的问题:加载Apple登录界面完成登录并点击“继续”后,页面跳转到了Google登录页,而非预期的Flutter应用(Glitch示例项目可正常跳转回应用)。我认为已遵循Firebase和sign_in_with_apple文档的所有步骤,但因对Node.js和Express了解有限,请求帮忙指出代码中的问题。


Firebase Functions代码

const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();

const express = require("express");
const AppleAuth = require("apple-auth");
const jwt = require("jsonwebtoken");
const bodyParser = require("body-parser");
const package_name = "my.package.name";
const team_id = "myteamid";
const service_id = "myserviceid";
const bundle_id = "mybundleid";
const key_id = "mykeyid";
const key_contents = 
`
MIGTAgEAMBMGByqGSM49234CCqGSM49AwEHBHkwdwIBAQQg27klLz6CSd30LJhs
...............................................................
mTfM2jUHZzQGiAySweGo7BmigwasdBvToiLErq4YJldATys1zSRNpWnSB//RAYRa
gyMCp94Y
`;

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));

// make all the files in 'public' available
app.use(express.static("public"));

app.get("/", (request, response) => {
    res.send('Got GET request');
});

// Android回调路由,将Apple返回的参数通过DeepLink传递回应用
app.post("callbacks/sign_in_with_apple", (request, response) => {
  const redirect = `intent://callback?${new URLSearchParams(request.body).toString()}#Intent;package=${package_name};scheme=signinwithapple;end`;

  console.log(`Redirecting to ${redirect}`);

  response.redirect(307, redirect);
});

// 应用端用于交换Apple授权code的接口
app.post("/sign_in_with_apple", async (request, response) => {
  const auth = new AppleAuth(
    {
      client_id:
        request.query.useBundleId === "true"
          ? bundle_id
          : service_id,
      team_id: team_id,
      redirect_uri:
        "https://us-central1-project-id.cloudfunctions.net/callbacks/sign_in_with_apple",
      key_id: key_id
    },
    key_contents.replace(/\|/g, "\n"),
    "text"
  );

  console.log(`request query = ${request.query}`);

  const accessToken = await auth.accessToken(request.query.code);

  const idToken = jwt.decode(accessToken.id_token);

  const userID = idToken.sub;

  console.log(`idToken = ${idToken}`);

  const sessionID = `NEW SESSION ID for ${userID} / ${idToken.email}`;

  console.log(`sessionID = ${sessionID}`);

  response.json({sessionId: sessionID});
});

exports.sign_in_with_apple = functions.https.onRequest(app);

package.json内容

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "eslint",
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "16"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^10.0.2",
    "firebase-functions": "^3.18.0",
    "apple-auth": "1.0.7",
    "body-parser": "1.19.0",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1"
  },
  "devDependencies": {
    "eslint": "^8.9.0",
    "eslint-config-google": "^0.14.0",
    "firebase-functions-test": "^0.2.0"
  },
  "private": true
}

Flutter端代码

Future<void> signInWithApple(BuildContext context) async {
    // 生成随机字符串防止重放攻击,Firebase验证时会匹配rawNonce的sha256哈希值
    final rawNonce = generateNonce();
    final nonce = sha256ofString(rawNonce);

    // 请求Apple账户授权凭证
    final appleCredential = await SignInWithApple.getAppleIDCredential(
      scopes: [
        AppleIDAuthorizationScopes.email,
        AppleIDAuthorizationScopes.fullName,
      ],
      webAuthenticationOptions: WebAuthenticationOptions(
        clientId: 'my.service.id',
        redirectUri: Uri.parse('https://us-central1-project-name.cloudfunctions.net/sign_in_with_apple'),
      ),
      nonce: nonce,
    );

    // 基于Apple返回的凭证创建Firebase OAuth凭证
    final credential = OAuthProvider("apple.com").credential(
      idToken: appleCredential.identityToken,
      rawNonce: rawNonce,
      accessToken: '123'  // 临时占位,对应已知的FlutterFire问题
    );

    debugPrint('Credential : $credential');

    // 使用凭证登录Firebase
    try {
      await _authInstance.signInWithCredential(credential);
    } on FirebaseAuthException catch (error) {
      _showErrorDialog(error, context);
    }
  } 

内容的提问来源于stack exchange,提问作者Vincent Gigandet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 15:40:23