简体   繁体   中英

Firebase user's context.auth is present still allAuthenticatedUsers secured Google function error with UNAUTHENTICATED

I wrote a simple cloud function:

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,
            };
        }    }
);

And a corresponding Flutter Client app:

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);
                  }
                },
              ),
            ],
          ),
        ));
  }    }

and I can see in both server logs and client side that Auth is passed correctly.

But when I add allAuthenticatedUsers and remove allUsers to 'Cloud Functions Invoker' Role from the function , the App starts getting PlatformException with UNAUTHENTICATED code.

Here is how setting looks before the change:

变更前

And after adding allAuthenticatedUsers :

添加 allAuthenticatedUsers

After removing allUsers :

最终角色

Then when Flutter invokes the function (when the user is logged in), Flutter errors with

PlatformException(functionsError, Cloud function failed with exception., {message: UNAUTHENTICATED, details: null, code: UNAUTHENTICATED })

It should show an error only when the user is not logged in, but it is showing in either cases.

The cloud configuration for allAuthenticatedUsers is not in any way related to Firebase, and what it does for authentication for callable type functions. Firebase callable functions handle their own authentication separately from the configuration you changed. You should change it back if you want callables to work properly.

The allUsers permission that you removed was responsible for making your function accessible to the public, including your app. When you removed that, you effectively removed the ability for anyone on the internet to be able to invoke the function (which is very much required for callable functions to operate as designed).

When you added allAuthenticatedUsers, what you did was require only callers that authenticate themselves with a Google service account. Again, this is not related to Firebase or Firebase Auth. It's a Google Cloud IAM concept. It's not applicable for callable functions, so you shouldn't use it. It's not clear to me why you thought this was a good configuration to make.

If you want Firebase callable functions to be invoked normally from apps, you should leave the permission at the default allUsers, and let the Firebase SDK handle the authentication of the end user.

Read more about Google Cloud access control lists to understand the cloud configurations that you were changing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM