Send firebase storage authorization as url parameter from a flutter web app - firebase

I would like to know how to make an authorized request to firebase storage using the user Id Token as a parameter in the url. Right now with a firebase rule of 'request.auth != null' I receive a 403 network error (Failed to load video: You do not have permission to access the requested resource). Here is my GET request url:
https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<folder_name>%2F<video_name>.mp4?alt=media&auth=eyJh...<ID TOKEN>...Ll2un8ng
-WITHOUT the firebase rule in place I'm able to successfully get the asset with this request url https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<folder_name>%2F<video_name>.mp4?alt=media
-also tried token=, token_id=, tokenId=
-the reason for not using the firebase SDK to fetch the file is so that I can use the flutter video_player (https://pub.dev/packages/video_player#-example-tab-) package and use this with files in firebase, I mention this in case theres a better way to use the video_player library in flutter web right now:
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
closedCaptionFile: _loadCaptions(),
);
[EDIT] It appears that it's not possible to pass the auth in as a query parameter. After some exploring, I found an acceptable way to still use the video_player with your firebase assets that are protected (If you're not using rules to protect them, you can directly use the firebase url). I will post some general steps here and some sample code:
Use the Storage Firebase SDK package to get the Uint8List, the uri given by getDownloadURL has the correct header auth, for example
import 'package:firebase/firebase.dart';
final url = await storagePath.getDownloadURL();
final response = await http.get(url);
if (response.statusCode == 200) {
return response.bodyBytes;
}
use the Uint8List buffer to init a Blob object which you'll use to then create an ObjectURL which basically gives you the same interface as a file url to use as the network url for your video player
final blob = html.Blob([data.buffer], 'video/mp4');
final videoUrl = html.Url.createObjectUrl(blob);
videoPlayerController = VideoPlayerController.network(
videoUrl)
..initialize().then((_) {...
That's it.

Firebase Storage REST does not (rightly) support authorization from GET query string as you are trying to do. Instead, it uses the standard Authorization header (see here).
Firebase cloud storage internally uses Google Cloud Storage. Mentioned here
If the library you use doesn't support HTTP headers yet, you must consider an alternative. The issue you mentioned in the comment shows that the feature is still under development, so you can also wait for the library to come out with the support for headers.

Internally all this package does for flutter-web is create an HtmlElementView widget here for which it passes a VideoElement (ref here) from the package dart:html with the provided URL which translates to a <Video> tag inside a shadow dom element in your web page. The error 403 could also mean you are trying to access it from a different origin.
I would suggest following approach.
Check your console for any CORS related errors. If yes, then you will have to whitelist your ip/domain in the firebase storage. Check this post for possible approach and more details here.
Check if you are able to access the URL directly with the authorization token as a query parameter as you suggested. If not then, it is not the correct way to access the object and should be corrected. You could update the question with the exact error details.

Related

Firebase Realtime Database - how to manage database rules using REST API

I would like to get/set realtime database rules using Rest API however no tutorial is working for me. I try to do it like that:
I copied the url to my realtime database which is in Europe https://my-project-database.europe-west1.firebasedatabase.app/
I copied the java code from this tutorial: https://firebase.google.com/docs/database/rest/auth which was supposed to give me the access token
val googleCred = GoogleCredential.fromStream(File("/path/to/my/key.json").inputStream())
val scoped = googleCred.createScoped(
Arrays.asList( // or use firebase.database.readonly for read-only access
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/userinfo.email"
)
)
scoped.refreshToken()
val token = scoped.accessToken
println(token)
However the token looks very strange with a long string of dots at the end like (it's related to: Why am I getting a JWT with a bunch of periods/dots back from Google OAuth?)
ya29.c.Kp8BCgi0lxWtUt-_[Normal JWT stuff, redacted for
security]yVvGk...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
I did HTTP GET to address like https://my-project-database.europe-west1.firebasedatabase.app/.settings/rules.json?access_token=$token and I got 401 UNAUTHORIZED
I assume it's due to the fact that I use the whole of this strange token full of dots as the access_token variable. So now I have questions how to transform it and use as the access_token to make it work
EDIT:
I created this gist although it's in python it has exactly the same problem. How to make it work ?
https://gist.github.com/solveretur/86d53a9c0221f096c38c3ef8f70a8dbd
It works I used wrong database key

Firebase emulator hitting DB via the REST feature

I’m trying to setup the emulator so I can develop the firebase functions safely before deploying them. I just noticed that some REST calls I’m doing now fails - anybody know if it is not possible to use the REST feature of the RealTime DB https://firebase.google.com/docs/reference/rest/database
I'm trying to hit it with this URL
http://localhost:9000/?ns=<PROJECT ID>-default-rtdb/development/DISHES.json
because this is what I set the firebaseConfig.databaseURL to (suggested here by Google)
Bonus info: If I try to do a GET to the URL via postman it creates another database called fake-server (http://localhost:4000/database/fake-server: null) 🤔
According to RFC 3986, the path must come before the query parameters in URLs. Your URL should be instead written as:
http://localhost:9000/development/DISHES.json?ns=<PROJECT ID>-default-rtdb
Note how the corrected URL has the query parameter appended to the very end. (The URL you've provided in the question will be parsed as having one query parameter ns with the value of <PROJECT ID>-default-rtdb/development/DISHES.json, which is not a valid namespace name. That explains the errors you've seen.)
FYI, it looks like you're constructing the URL by concatenating the string databaseURL with the path -- this may lead to surprising results as you've seen above. Considering using a URL parser / formatter in your language / framework of choice instead, which handles URL parts correctly. For example, in JavaScript you can use the snippet below:
const url = new URL(databaseURL); // Parse URL (including query params, if any)
url.pathname = '/development/DISHES.json';
yourFetchingLogic(url.toString()); // Reconstruct the URL with path replaced

Firebase and LinkedIn Auth integration unknown route

I've been examining this code base as an example of how to implement LinkedIn authorization to my project with a Firebase Backend. One thing I'm confused about is these lines:
var code = getURLParameter("code");
var state = getURLParameter("state");
var error = getURLParameter("error");
if (error) {
document.body.innerText = "Error back from the LinkedIn auth page: " + error;
} else if (!code) {
// Start the auth flow.
window.location.href = "/redirect";
}
at window.location.href = '/redirect', I believe it is meant to invoke the cloud function called "redirect". In my code base, it simply goes to an unknown route and triggers my fallback. Am I wrong about the purpose of this line of code? Does anyone know any possible reasons it's not triggering the cloud function (console says 0 invocations)? What other information should I look into to try to debug this?
To provide a bit fuller of an answer:
The example you provided relies on a Firebase.json file. This file provides configuration if (and only if) your application is hosted with Firebase hosting (see docs).
If you expect to host your app elsewhere, you'll need to make sure your /redirect path points to the Firebase function URL itself (probably something like https://us-central1-project-name.cloudfunctions.net/redirect). In the authorization flow, the LinkedIn module in the example repo then will redirect to either a default or a configured callback url.

Getting storage item without CORS configured

I fetched an item from my Firebase storage bucket via this technique (generally):
const url = await firebase.storage().ref('my/ref').getDownloadURL();
const filename = 'filename.ext';
const a = document.getElementById('link');
a.href = url;
a.download = filename;
a.click();
I did it the above way prior to trying the example from the docs:
storageRef.child('images/stars.jpg').getDownloadURL().then(function(url) {
// `url` is the download URL for 'images/stars.jpg'
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
});
When trying it this way, I hit the CORS error. After adding the CORS config to my bucket, it then worked as expected. However, I cannot determine why I was able to successfully fetch it via the first technique prior to configuring CORS.
I tested it again by removing the GET method from my CORS config and uploading the config file again via gsutil. I was still able to successfully obtain the file via the first technique described above.
If this is possible to do without configuring CORS, how can I prevent it to restrict access? Odds are no one will be able to figure out the required ref to build the link, anyways, because the actual ref has multiple unique IDs that will be all but impossible to figure out. This is mainly a question out of curiosity.
I cannot determine why I was able to successfully fetch it via the first technique prior to configuring CORS.
Because same-origin policy doesn't apply when the Javascript can't access the data. In your first example, the JS tweaks the document and the document accesses the data. In the second example, the JS accesses the cross-origin data, and the absence of CORS prevents such access.
If this is possible to do without configuring CORS, how can I prevent it to restrict access?
CORS isn't designed to restrict access. (Wait, what?) CORS is designed to permit access that would otherwise be assumed to be something the user would not want -- for scripts on one page to have access to data from another origin, including, potentially, handing over use of the user's credentials to scripts on the current page when accessing the foreign site. CORS allows site B to tell the browser that it expects to be contacted by scripts from site A, and therefore such access should not be unexpected or assumed unauthorized. It has no impact on requests that don't fall under the same origin policy.
The solution -- and I apologize if I am stating the patently obvious -- is that getDownloadUrl() should not be able to fetch a usable URL for the object, if the object should not in fact be accessible. You can't trust code running on the browser, so whatever credentials are in play here should not be able to be used in this way, if the object is not intended to be accessible... otherwise you have a misconfiguration that is allowing access that should not be allowed.

AWS API Gateway as Serivce proxy for S3 upload

I have been reading about creating an API which can be used to upload objects directly to S3. I have followed the guides from Amazon with little success.
I am currently getting the following error:
{"message":"Missing Authentication Token"}
My API call configuration:
The role ARN assigned is not in the image, but has been set up and assigned.
The "Missing Authentication Token" error can be interpreted as either
Enabling AWS_IAM authentication for your method and making a request to it without signing it with SigV4, or
Hitting a non-existent path in your API.
For 1, if you use the generated SDK the signing is done for you.
For 2, if you're making raw http requests make sure you're making requests to /<stage>/s3/{key}
BTW, the path override for s3 puts needs to be {bucket}/{key}, not just {key}. You may need to create a two-level hierarchy with bucket as the parent, or just hardcode the bucket name in the path override if it will always be the same. See: http://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html

Resources