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;
}
}
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:
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?
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
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.