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
相关产品推荐
相关产品推荐

