Firebase 用户的 context.auth 仍然存在 allAuthenticatedUsers secure Google function error with UNAUTHENTICAT

Posted

技术标签:

【中文标题】Firebase 用户的 context.auth 仍然存在 allAuthenticatedUsers secure Google function error with UNAUTHENTICATED【英文标题】:Firebase user's context.auth is present still allAuthenticatedUsers secured Google function error with UNAUTHENTICATED 【发布时间】:2020-06-03 04:27:07 【问题描述】:

我写了一个简单的cloud函数:

import * as functions from 'firebase-functions';

export const repeat = functions.https.onCall( function (data, context) 
    // Authentication user information is automatically added to the request.
    if (context.auth) 
        console.log(' context.auth is defined ');
        console.log(' uid is ' + context.auth.uid);
     else 
        console.log(' context.auth undefine. ');
    
        if (context.auth) 
            return 
                repeat_message: context.auth.uid + ' ' + data.message,
                repeat_count: data.count + 1,
            ;
         else 
            return 
                repeat_message: ' noUID ' + data.message,
                repeat_count: data.count + 1,
            ;
            
);

以及对应的 Flutter Client 应用:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:cloud_functions/cloud_functions.dart';

final FirebaseAuth _fAuth = FirebaseAuth.instance;

final GoogleSignIn _googleSignIn = GoogleSignIn(scopes: ['email'], signInOption: SignInOption.standard);

FirebaseUser _firebaseUser;
GoogleSignInAccount _googleSignInAccount;

void main() 
  runApp(new MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      theme: ThemeData(
        textTheme: TextTheme(
          caption: TextStyle(
              fontSize: 20.0
          ),
          body1: TextStyle(
              fontSize: 20.0
          ),
        ),
      ),
      title: 'calling function',
      debugShowCheckedModeBanner: false,
      home: LaunchScreen(),
    );
  


class LaunchScreen extends StatefulWidget 
  @override
  _LaunchScreenState createState() 
   return _LaunchScreenState();
  
 // LoggingOptions  to _LaunchScreenState

class _LaunchScreenState extends State<LaunchScreen> 
  String _response = 'no response';
  int _responseCount = 1;
  final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(functionName: 'repeat')
    ..timeout = const Duration(seconds: 90);

  Future<FirebaseUser> _handleSignIn() async 
    try 
      GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
      _googleSignInAccount = googleSignInAccount;

      GoogleSignInAuthentication authentication = await googleSignInAccount.authentication;

      final AuthCredential credential = GoogleAuthProvider.getCredential(
        accessToken: authentication.accessToken, idToken: authentication.idToken,
      );
      AuthResult authResult;
      authResult = await _fAuth.signInWithCredential( credential);

      _firebaseUser = authResult.user;
      setState(() );
      return _firebaseUser;
     catch (e) 
      print(e.toString());
    
    return null;
  

  Future<void> _handleSignOut() async 
    FirebaseAuth.instance.signOut();
    _googleSignIn.signOut();

    setState(() 
      _firebaseUser =null;
      _googleSignInAccount= null;
    );
  

  @override
  Widget build(BuildContext context) 

    return Scaffold(
        appBar: AppBar( title: const Text('Sample Code'),),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              MaterialButton(
                  child: const Text( 'Sign in with Google', style: TextStyle(fontSize: 16.0),),
                  onPressed: () 
                    _handleSignIn().then((user) 
                      // logggedIn
                      debugPrint('user ' + user.toString());
                    
                        );
                  ,
                  ),

                 MaterialButton(
                  child: const Text( 'Sign out with Google', style: TextStyle(fontSize: 16.0),),
                  onPressed: () 
                    _handleSignOut();
                  ,
                ),

              Text( _firebaseUser != null ? _firebaseUser.uid : 'user logged off'),

              Text('FromServer $_responseCount: $_response'),
              MaterialButton(
                child: const Text('SEND REQUEST', style: TextStyle(fontSize: 18.0),),
                onPressed: () async 
                  try 
                    final HttpsCallableResult result = await callable.call(
                      <String, dynamic>
                        'message': 'hello',
                        'count': _responseCount,
                      
                      ,
                    );
                    print(result.data);
                    setState(() 
                      _response = result.data['repeat_message'];
                      _responseCount = result.data['repeat_count'];
                    );
                   on CloudFunctionsException catch (e) 
                    print('caught Firebase functions exception');
                    print(e.code);
                    print(e.message);
                    print(e.details);
                   catch (e) 
                    print('caught generic exception');
                    print(e);
                  
                ,
              ),
            ],
          ),
        ));
      

我可以在服务器日志和客户端看到Auth 被正确传递。

但是当我将allAuthenticatedUsers 添加到“Cloud Functions Invoker”角色from the function 并删除allUsers 时,应用程序开始使用未经身份验证的代码获取PlatformException

以下是更改前的设置:

加上allAuthenticatedUsers之后:

删除allUsers后:

那么当Flutter调用函数时(当用户登录时),Flutter报错与

PlatformException(functionsError, Cloud 函数失败 异常。,消息:未经身份验证,详细信息:空,代码: 未经身份验证

它应该只在用户未登录时显示错误,但在任何一种情况下都会显示。

【问题讨论】:

【参考方案1】:

allAuthenticatedUsers 的云配置与 Firebase 以及它对可调用类型函数的身份验证没有任何关系。 Firebase 可调用函数与您更改的配置分开处理自己的身份验证。如果您希望可调用对象正常工作,则应将其改回。

您删除的 allUsers 权限负责让公众(包括您的应用)可以访问您的函数。当您删除它时,您实际上删除了互联网上任何人都能够调用该函数的能力(这是可调用函数按设计运行所必需的)。

当您添加 allAuthenticatedUsers 时,您所做的只是要求调用者使用 Google 服务帐户进行身份验证。同样,这与 Firebase 或 Firebase Auth 无关。这是一个 Google Cloud IAM 概念。它不适用于可调用函数,因此您不应该使用它。我不清楚你为什么认为这是一个很好的配置。

如果您希望应用程序正常调用 Firebase 可调用函数,则应将权限保留为默认 allUsers,并让 Firebase SDK 处理最终用户的身份验证。

阅读有关Google Cloud access control lists 的更多信息,以了解您正在更改的云配置。

【讨论】:

它按设计工作。如果您不希望未经身份验证的 Firebase Auth 用户调用它,如果未定义 context.auth,则应尽早退出该函数。这是您唯一的选择。您无法阻止互联网上的任何人调用该函数,但您可以阻止他们在没有身份验证的情况下运行您的代码。 这个 callable 可以被调用 curl -X POST -d ' "data": "count":2,"message":"any message"' 'us-central1-xyz.cloudfunctions.net/repeat ' -H "Content-Type:application/json" 这就是我想要避免的 我知道 - 我理解您删除的最后一条评论,我是说无法完全切断未经身份验证的用户的访问。如果用户通过身份验证,您必须签入您的代码,如果没有,则提前返回。如果您想要不同的行为,请直接联系 Firebase 支持以提交功能请求。 support.google.com/firebase/contact/support 使用 allUsers 是一个 BAD 解决方案。 firebase express 功能的解决方案是什么? (不能context.auth)。我正在通过 Flutter 使用短信验证

以上是关于Firebase 用户的 context.auth 仍然存在 allAuthenticatedUsers secure Google function error with UNAUTHENTICAT的主要内容,如果未能解决你的问题,请参考以下文章

在 Firebase 云函数中批量处理 Promise

171.补充-在模板中添加权限控制

django 中没有 TEMPLATE_CONTEXT_PROCESSORS

向模板添加请求对象信息

django在模板中使用权限

Firebase:相同的用户名