0

I'm trying to write a Firebase Cloud Function to get an audio file from an API and save it to Firebase Storage. I'm getting this error:

Firebase Storage: User does not have permission to access 'Audio/English/United_States-OED-0/winter.mp3'. (storage/unauthorized)

I'm getting the same error using the emulator and the cloud. No file is written in Storage. The log Uploaded a string!' doesn't appear.

I have a Firebase IAM service account. I know how to import it into a CommonJS module (require) but my functions are in an ES module (import). How do I import my service account? This didn't work because assert only works in Node 17, when Firebase Functions uses Node 16.

import serviceAccount from "./my-awesome-project-firebase-adminsdk-12345.json" assert { type: "json" };

Here's my cloud function:

import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getStorage, ref, uploadString, connectStorageEmulator } from "firebase/storage";

const firebaseConfig = {
    apiKey: ...,
    authDomain: ...,
    databaseURL: ...,
    projectId: ...,
    storageBucket: "my-awesome-project.appspot.com",
    appId: "..."
};

const app = initializeApp(firebaseConfig);

import got from 'got';

export const Oxford_T2S = functions.firestore.document('Users/{userID}/English/OED_T2S_Request').onUpdate((change, context) => {
  const storage = getStorage(app);
  connectStorageEmulator(storage, "localhost", 9199); // comment out to use cloud
  const audioEnglishOEDWinterRef = ref(storage, 'Audio/English/United_States-OED-0/winter.mp3');

  async function getOED() {
    try {
      let file = await got('https://audio.oxforddictionaries.com/en/mp3/winter__us_2.mp3');
      uploadString(audioEnglishOEDWinterRef, file).then((snapshot) => {
          console.log('Uploaded a string!');
      });
    } catch (error) {
        console.error(error);
    }
  }

  return getOED()
});

Here are my cloud rules:

service firebase.storage {
  match /b/{bucket}/o {   
    // anything using request.auth.token relies on the userLogin cloud function.
    
    match /Audio/{shortLangA}/{file=**} {
        // allow writing of audio files if the user is trusted or a teacher of the language, apply recursively
        allow write: if (request.auth.token.trusted || shortLangA in request.auth.token.teaches);
    }
    
     match /Users/{file=**} {
        // allow writing of pronunciation tests
        // this rule is insecure
      // this rule needs to be improved by allowing users to only write to their own folders
      allow read, write: if true;
    }

    match /{allPaths=**} {
      // Only authenticated users can read from the bucket
      allow read: if request.auth != null;
    }
  }
}

I tried spinning up my Angular project and logging in but that didn't help.

Here are my emulator rules. These are the default rules created by firebase init functions:

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

I changed false to true but that didn't help.

Documentation on got is here. That line isn't throwing errors.

Thomas David Kehoe
  • 10,040
  • 14
  • 61
  • 100
  • https://stackoverflow.com/questions/42748738/swift-3-record-audio-and-upload-to-firebase-storage-and-play-back This is how I uploaded audio file to Firebase Storage in iOS, the logic should be similar. – Bryce Chan Nov 10 '22 at 02:57

1 Answers1

3

The Cloud Storage error messages documentation says

storage/unauthorized
User is not authorized to perform the desired action, check your security rules to ensure they are correct.

Looking at the emulator's default storage.rules, nothing can read or write. Gee, thanks for protecting my emulator from Russian hackers. The default should be

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write
    }
  }
}

Even with the new rules the emulator seems to be finicky. Sometimes a file writes, most of the time nothing writes to Storage.

My Cloud Storage rules, particularly request.auth.token.trusted, are set up for calls from my Angular app. If the call isn't coming from a logged in user on the app then the request is stopped. I.e., testing the Cloud Function in the Firebase Console won't write to Storage.

Thomas David Kehoe
  • 10,040
  • 14
  • 61
  • 100