带有 WebView 的 Flutter 应用中的 Firebase 身份验证

Posted

技术标签:

【中文标题】带有 WebView 的 Flutter 应用中的 Firebase 身份验证【英文标题】:Firebase Auth in a Flutter app with WebView 【发布时间】:2020-03-02 18:35:22 【问题描述】:

我正在开发一个使用 Firebase Auth 处理身份验证的 Flutter 应用。但是,应用程序的某些部分使用 WebView 显示来自网络版本的内容(也使用 Firebase 身份验证)。我的问题是如何确保已登录应用的用户也在 WebView 中登录。

【问题讨论】:

【参考方案1】:

Firebase 中没有内置任何东西来自动将身份验证状态从本机代码同步到从本机代码打开的网络视图中。

应该可以将 ID 令牌从本机代码传递到 Web 视图并在那里使用它,但我自己从未尝试过。

我找到的一些相关链接:

How to pass Firebase Auth token to webView and register for notifications on android(描述了同样的问题,但随后使用 Android - 不幸的是没有答案) Is there a way to keep the user signed in between native code and a WebView using Firebase Auth on Android?(很遗憾也没有答案) Webviews and social authentication with React Native(博客文章描述了 Facebook 登录和反应原生问题的解决方法) How to do Authentication on native and pass to webView?(也使用 React Native,但 this answer 看起来很有希望) capacitor-firebase-auth npm module(用于将令牌从本机代码传播到 Web 视图的 Capacitor 框架插件)

这些都不是 Flutter + WebView 的预构建解决方案,但我希望将它们结合起来可以让您自己构建一些东西。如果你这样做:请分享它! :)

【讨论】:

感谢您的回答!将研究您的建议并发布我最终的做法。 @Alex -- 你是怎么做到的?【参考方案2】:

这是在 React Native 中使用 WebView 进行 Firebase 身份验证的解决方案:

import React from 'react'
import WebView from 'react-native-webview'

export default function HomeScreen(props) 
  // props.user represents firebase user
  const apiKey = props.user.toJSON().apiKey
  const authJS = `
        if (!("indexedDB" in window)) 
          alert("This browser doesn't support IndexedDB")
         else 
          let indexdb = window.indexedDB.open('firebaseLocalStorageDb', 1)
          indexdb.onsuccess = function() 
            let db = indexdb.result
            let transaction = db.transaction('firebaseLocalStorage', 'readwrite')
            let storage = transaction.objectStore('firebaseLocalStorage')
            const request = storage.put(
              fbase_key: "firebase:authUser:$apiKey:[DEFAULT]",
              value: $JSON.stringify(props.user.toJSON())
            );
          
          
      `
  return <WebView
    injectedjavascriptBeforeContentLoaded=authJS
    source=
      uri: 'http://192.168.1.102:3000',
      baseUrl: 'http://192.168.1.102:3000',
    
  />

Flutter 中可能需要类似的逻辑(JS 注入)。

【讨论】:

【参考方案3】:

高级

    从 Flutter 移动客户端登录 Firebase

    为登录用户生成一个唯一的 Firestore 文档,设置您需要通过来自 webView 的调用查找的任何身份验证数据 - 例如,uidemail

    doc.id 传递给 webView,并使用该 token 值 作为从 webView 调用云函数的参数,这需要登录的用户数据

代码

在 Firebase 云和浏览器之间实现需要 5 个小 JS 块:

    从 Flutter 移动客户端,调用云函数给你一个唯一的令牌,其中令牌将是一个文档 ID,数据将具有 Auth User uid
exports.getWebAppUserToken = functions.https.onCall(async (data, context) => 

    let docRef = await firestore.collection('webTokens')
                                .add(uid : context.auth['uid']);

    return 'webToken' : docRef.id;
);
    将token传入调用打开webview的url中,例如:http://app.com/appPage/&lt;token&gt;,然后在浏览器中提取token:
getValidationToken() 
  let href = window.location.href;
  let lastIdx = href.lastIndexOf('/');

  return href.substr(lastIdx + 1).trim();

    现在您可以在浏览器中使用令牌调用云函数:
const authFuncCalledFromWeb = 
      firebase.functions().httpsCallable('authFuncCalledFromWeb');

const result = await authFuncCalledFromWeb(uiValidationToken);
    使用webToken 为请求获取uid 的云函数:
exports.authFuncCalledFromWeb = functions.https.onCall(async (data, context) => 
    let webToken = data; 
    let uid = await getWebTokenUid(webToken);

    // >>> do stuff that requires uid
);
    帮助查找webToken:
getWebTokenUid = async function (webToken) 

    let webTokenDoc = await firestore.collection(appData.Collctn.webTokens)
                                     .doc(webToken).get();

    let webTokenDocData = webTokenDoc.data();

    return webTokenDocData['uid'];

==================

如果您想考虑使令牌过期,这里有一个变体:

<!-- begin snippet: js hide: true -->
let EXPIRES_INTERVAL = 1000 * 60 * 20;

exports.getWebAppUserToken = functions.https.onCall(async (data, context) => 

    logr.enter(`getWebAppUserToken`);

    const uid = appConfig.getLoggedInUid(context);
    logr.i(`uid: $uid`);

    // For Field.expires, consider that webToken will not be
    //      looked up until user clicks html submit action.
    //      So whatever interval we give, we should check in client
    //      On the other hand, user can only get this token through
    //      the app in a cloud func, so expires may not be nec.
    let expiresTimestamp = dateUtil.getNowNumericTimestamp() + EXPIRES_INTERVAL;

    let webTokenProfile = 
        [Field._created] : dateUtil.getNowReadableTimestampPST(),
        [Field.expires]  : expiresTimestamp,
        [Field.uid]      : uid,
    

    let docRef = await firestore.collection('webTokens')
                                .add(webTokenProfile);

    let webToken = docRef.id;

    return 'data' : webToken;
);

【讨论】:

以上是关于带有 WebView 的 Flutter 应用中的 Firebase 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

带有地理位置网站的 Flutter iOS webview 应用程序不会询问位置权限,即使 info.plist 已调整

ios Flutter应用程序的webview中的键盘不显示

带有圆角的 Flutter WebView

Flutter webview强制暗模式解决方案?

webview_flutter滑动存在卡顿问题的完美解决方法

带有多个链接的 Flutter webview