Oscar Caicedo
Oscar Caicedo

Reputation: 320

"The caller does not have permission " in Vertex AI for Firebase SDK

I am developing a Flutter application that uses the Vertex AI for Firebase SDK. When I use only a text prompt without any Firebase Storage URL, everything works fine. However, when including a Firebase Storage URL, I receive the following error message: The caller does not have permission.

I found that changing the Firebase Storage security rules to the following solves the problem, but these rules are not secure:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true;
    }
  }
}
Stream<String?> generateTextFromMediaStream(String promptText, List<String>? mediaPaths) async* {
    
      final prompt = TextPart(promptText);
      List mediaPartsFuture = [];
      if (mediaPaths != null) {
        mediaPartsFuture = mediaPaths.map((path)  {
          final mimeType = getMimeType(path);
          return FileData(mimeType, path);

        }).toList();
      
      }

      final response = _model.generateContentStream([
        Content.multi([prompt, ...mediaPartsFuture])
      ]);

      await for (final chunk in response) {
        yield chunk.text;
      }
  }

Error Messages:

I/flutter (12483): The caller does not have permission
I/flutter (12483): #0      parseGenerateContentResponse (package:google_generative_ai/src/api.dart:558:54)
I/flutter (12483): #1      _MapStream._handleData (dart:async/stream_pipe.dart:213:31)
I/flutter (12483): #2      _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
I/flutter (12483): #3      _RootZone.runUnaryGuarded (dart:async/zone.dart:1594:10)
I/flutter (12483): #4      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11)
I/flutter (12483): #5      _BufferingStreamSubscription._add (dart:async/stream_impl.dart:297:7)
I/flutter (12483): #6      _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19)
I/flutter (12483): #7      _StreamController._add (dart:async/stream_controller.dart:658:7)
I/flutter (12483): #8      _StreamController.add (dart:async/stream_controller.dart:606:5)
I/flutter (12483): #9      _AsyncStarStreamController.add (dart:async-patch/async_patch.dart:76:16)
I/flutter (12483): #10     HttpApiClient.streamRequest (package:google_generative_ai/src/client.dart)
I/flutter (12483): <asynchronous suspension>
I/flutter (12483): #11     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:152:3)
I/flutter (12483): <asynchronous suspension>

I see in Google Cloud APIS the following errors:

Firebase ML API:

Additional Context:

Flutter 3.22.0 firebase_vertexai 0.1.0+1

Could someone guide me on what might be causing this permission error and how to resolve it without compromising the security of Firebase Storage rules?

What I've Tried:

  1. I attempted to use the following more restrictive security rules, but they did not work:
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null ||                         request.auth.token.email.matches('.*gserviceaccount.*') ||
                          request.auth.token.email == '[email protected]'||
                          request.auth.token.email == 'service-PROJECT_NUMBER@gcp-sa-aiplatform-cc.iam.gserviceaccount.com'||
                          request.auth.token.email == '[email protected]'||
                          request.auth.token.email == '[email protected]'||
                          request.auth.token.email == '[email protected]' ||
                          request.auth.token.email == '[email protected]' ||
                          request.auth.token.email == 'service-PROJECT_NUMBER@gcp-sa-firebasestorage.iam.gserviceaccount.com';
    }
  }
}

I replaced PROJECT_NUMBER with my project number

  1. I give roles to following rol (Cloud Storage for Firebase Admin ) to service accounts:

Upvotes: 0

Views: 273

Answers (2)

Mohamed Noordin
Mohamed Noordin

Reputation: 173

Probably the Firebase VertexAI SDK uses a service account to access the storage bucket (or not using authentication at all).

Edit: The main solution would be to authenticate the VertexAI service or SDK using the current FirebaseAuth instance to be able to make authenticated requests to the Firebase Cloud Storage.
To authenticate the VertexAI SDK, pass the current FirebaseAuth instance to it when initiating a new VertexAI instance as following:

final vertexAI = FirebaseVertexAI.instanceFor(
  auth: FirebaseAuth.instance, // The existing FirebaseAuth instance if the user is authenticated.
  location: 'location');

print(vertexAI.auth?.currentUser?.email); // Check if authentication was successful.

However, here's a quick and dirty solution that might be relevant to you if the user isn't authenticated or the resources were uploaded recently for example:

rules_version = '2';

// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/path/to/file.txt"
    match /user/{userId}/{allPaths=**} {
      // Allow read if the file was created less than 10 minutes ago
      allow read: if request.time < resource.timeCreated + duration.value(10, 'm');
      // Allow write if authenticated and UID matches or if the user has admin privileges
      allow write: if request.auth != null && (request.auth.uid == userId || request.auth.token.admin == true);
    }
  }
}

Otherwise, you can handle resource download yourself, then pass it to the service as raw bytes.

Upvotes: 1

Ros&#225;rio P. Fernandes
Ros&#225;rio P. Fernandes

Reputation: 11336

You can add Firebase Authentication to your Flutter app and implement one of the sign-in providers offered by the service (Email/Password, Google Sign in, etc).

If you don't wish to ask users for their credentials, you can implement Anonymous sign in.

When you use the Firebase Auth SDK in your app and sign a user in, it ensures that all calls to Vertex AI are authenticated using that user's token. Then you can change your security rules back to the good old request.auth != null:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

If you want more granular control over your security rules (eg. user can only read/write their own file), you can checkout the Firebase documentation about Storage Security Rules.

Upvotes: -1

Related Questions