cachapa / firedart

A dart-native implementation of the Firebase Auth and Firestore SDKs
https://pub.dev/packages/firedart
Apache License 2.0
174 stars 62 forks source link

gRPC Error (13, Error making call: NoSuchMethodError: The getter '_expiry' was called on null. #20

Closed SaadArdati closed 4 years ago

SaadArdati commented 4 years ago

Given the following code:

  void init() async {
    var tokenStore = VolatileStore();
    var auth = await FirebaseAuth('AIzaSyDc4GGbOb-<...>', tokenStore);
    firestore = await Firestore('<...>', auth: auth);
  }

I get the following error when trying to call firestore.document()

ERROR - 2020-05-13 17:55:47.418320
GET /user/get
Error thrown by handler.
gRPC Error (13, Error making call: NoSuchMethodError: The getter '_expiry' was called on null.
Receiver: null
Tried calling: _expiry)
[ERROR] 2020-05-13 17:55:47.040673  0:00:00.355121  GET /user/get
gRPC Error (13, Error making call: NoSuchMethodError: The getter '_expiry' was called on null.
Receiver: null
Tried calling: _expiry)
dart:async                                                                  _StreamController.addError
package:grpc/src/client/call.dart 116:18                                    ClientCall._terminateWithError
package:grpc/src/client/call.dart 158:5                                     ClientCall._onMetadataProviderError
dart:async                                                                  _AsyncAwaitCompleter.completeError
package:firedart/firestore/token_authenticator.dart                         TokenAuthenticator.authenticate

I'm not sure what's going wrong.

cachapa commented 4 years ago

Are you logged in?

SaadArdati commented 4 years ago

@cachapa Firebase supports anonymous api calls. Signing in shouldn't be required.

Either way, I tried logging in and got this:

package:firedart/auth/auth_gateway.dart 48:7  AuthGateway._post
NoSuchMethodError: The method 'close' was called on null.
Receiver: null
Tried calling: close(force: true)
dart:core                       Object.noSuchMethod
test\endpoint_tests.dart 90:20  main.<fn>.<fn>

image

Note that this is on the dart vm. Not flutter.

cachapa commented 4 years ago

You're right, anonymous calls should be supported.

Could you try using the debug client? It might shed some light:

var auth = FirebaseAuth(apiKey, tokenStore, httpClient: VerboseClient());
SaadArdati commented 4 years ago

@cachapa Same expiary error unfortunately.

cachapa commented 4 years ago

Ok thanks for the report. I'll try it here when I find the time.

cachapa commented 4 years ago

Did you end up figuring out what this was? I guess by now you know the codebase as well as anyone.

SaadArdati commented 4 years ago

I didn't. And didn't bother testing since I rely on service accounts :/

SaadArdati commented 4 years ago

I figured this out.

Service account sign in does not provide the TokenStore with a tokenId/expiry/refreshToken/etc, but the UserClient assumed that it does and tried to add the tokenId to the body of any requests going through it.

So if you call getUser but are using a service account for example, the result is the error because the token store has null fields.

This is the solution:

class UserClient extends http.BaseClient {
  final KeyClient client;
  final TokenProvider tokenProvider;

  UserClient(this.client, this.tokenProvider);

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    var body = (request as http.Request).bodyFields;
    request = http.Request(request.method, request.url)
      ..headers['content-type'] = 'application/x-www-form-urlencoded'
      ..bodyFields = {...body, if (!body.containsKey('idToken')) 'idToken': await tokenProvider.idToken};
    return client.send(request);
  }
}

Note that I'm manually passing idToken elsewhere to test this properly.

Of course this is not the ideal solution, I will be diving into this issue to further investigate better solutions.

The other issue is that there seems to be another endpoint for admin REST https://identitytoolkit.googleapis.com/v1/projects/<project-id>/<method> this might be completely unrelated.

cachapa commented 4 years ago

This is a perfect example of why I don't think it's a good idea to implement the admin API in this project. They seem to be close enough that you could reuse your code, but end up running into a lot of these small things which leads to problems that are hard to debug.

I'm closing this since I will be removing admin credentials from the project.

SaadArdati commented 4 years ago

I guess I'll be doing my own work in my fork...

cachapa commented 4 years ago

Sure, no problem. Im curious: why do this yourself instead of just using firebase_admin, in which you're basing a lot of your work anyway?

SaadArdati commented 4 years ago

firebase_admin only provides firebase auth and firebase storage support. Firedart had a much ready implementation for firestore and storage already and needed verifyIdToken to finish it off. As well as that this project stays up to date much more frequently. firebase_admin's last update was in February. EDIT: also seems to be plagued with issues.

But I suppose I was a bit naiive to not realize this was not meant for admin.

cachapa commented 4 years ago

This is partly my fault. Given my lack of time to evaluate things properly I thought the admin portion could be implemented "transparently" but it turns out to cause a lot of issues.

I don't know the other project well enough to comment on it, but if you're right that it has its own issues it might make sense to create a competitor package to it. In that case though it would make sense to have it completely separate to avoid confusion.

SaadArdati commented 4 years ago

Probably for the best...