Firebase Functions Version 1.0 now requires a iam.serviceAccounts.signBlob

1,328 views
Skip to first unread message

Doug Thompson

unread,
May 17, 2018, 10:04:00 AM5/17/18
to Firebase Google Group
Previously, using a QR code generator with Firebase functions worked fine.

However the function is now throwing an error which I'm having trouble parsing:

SigningError: Permission iam.serviceAccounts.signBlob is required to perform this operation on service account projects/RAN...@appspot.gserviceaccount.com.
    at /user_code/node_modules/firebase-admin/node_modules/@google-cloud/storage/src/file.js:1715:16
    at Request._callback (/user_code/node_modules/firebase-admin/node_modules/@google-cloud/storage/node_modules/google-auto-auth/index.js:356:9)
    at Request.self.callback (/user_code/node_modules/firebase-admin/node_modules/request/request.js:185:22)
    at emitTwo (events.js:106:13)
    at Request.emit (events.js:191:7)
    at Request.<anonymous> (/user_code/node_modules/firebase-admin/node_modules/request/request.js:1157:10)
    at emitOne (events.js:96:13)
    at Request.emit (events.js:188:7)
    at IncomingMessage.<anonymous> (/user_code/node_modules/firebase-admin/node_modules/request/request.js:1079:12)
    at IncomingMessage.g (events.js:292:16)

It seems to happen when requesting a writeStream, as follows:

const bucket = admin.storage().bucket();
const file = bucket.file(filePath);

var writeStream = file.createWriteStream({

    metadata
: {
      contentType
: 'image/png',
   
}
 
});
codeStream
.pipe(writeStream);
return writeStream.on('finish', function() {
    file
.getSignedUrl({
      action
: 'read',
      expires
: '03-17-2025'
   
}).then(signedUrls => {
      data
.QRCODE = signedUrls[0];
     
// DOES NOT GET HERE

Is there something in how authentication or something has changed in Version 1.0 which is preventing the pipe?

Anyone have similar issues?

I am using a node QR code generator 

"qr-image": "^3.2.0",

Hiranya Jayathilaka

unread,
May 17, 2018, 1:40:23 PM5/17/18
to fireba...@googlegroups.com
This is thrown by the getSignedUrl() method, which relies on the IAM signBlob endpoint to sign the file URL. I believe this endpoint is used only when the SDK is not initialized with a service account credential.This functionality has been around for a while though: https://github.com/googleapis/nodejs-storage/pull/91

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/38e7e296-2f0d-4642-95a2-5353ad106786%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--

Hiranya Jayathilaka | Software Engineer | h...@google.com | 650-203-0128

Doug Thompson

unread,
May 17, 2018, 4:56:35 PM5/17/18
to Firebase Google Group
Yes, solved this by adding GCS to the dependencies, and then requiring it in the function:

const gcs = require('@google-cloud/storage')({keyFilename: 'serviceAccountTester.json'});

I was then able to use getSignedUrl() method successfully.

One thing I can't quite validate, but I needed to generate a credential separately from the one provided on the Firebase console.

Clearly, Firebase Storage includes minimal wrappers around gcs. All good now.

Thanks!

Hiranya Jayathilaka

unread,
May 17, 2018, 6:53:55 PM5/17/18
to fireba...@googlegroups.com
If you have a service account file you can also use it to initialize the Admin SDK (i.e. pass it to admin.initializeApp() wrapped in a cert credential), and then use storage APIs as usual. Your observation regarding permissions is correct. The service account you get from Firebase Console has "Editor" role, which doesn't have the necessary IAM permissions. You need a role like "Service Account Token Creator" for that.


For more options, visit https://groups.google.com/d/optout.

Pat Needham

unread,
May 21, 2018, 3:24:18 PM5/21/18
to Firebase Google Group
Hiranya,

Is there a way to selectively choose which Firebase functions are configured with `admin.initializeApp(<cert credential>)` as opposed to the default `admin.initializeApp()`?

Say I have an `index.js` file set up similar to this:

const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin
.initializeApp()

const twilioModule = require('./src/twilio')
const slackModule = require('./src/slack')
const authModule = require('./src/auth')
const usersModule = require('./src/users')

exports
.twilio = functions.https.onRequest(twilioModule.app)
exports
.slack = functions.https.onRequest(slackModule.app)
exports
.auth = functions.https.onRequest(authModule.app)
exports
.users = functions.https.onRequest(usersModule.app)

...


In that setup above, the admin.initializeApp call takes place for all the functions. If my twilio function requires the cert credential, how could I override that to use a custom cert instead?

Is it possible to do something like this:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
if (process.env.FUNCTION_BEING_EXECUTED == 'twilio') {
 
const customCert = require('/path/to/cert')
  admin
.initializeApp(customCert)
} else {
  admin
.initializeApp()
}



To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/b88b6eeb-b0eb-4c71-8cb4-ce7ce1e561b4%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Hiranya Jayathilaka

unread,
May 21, 2018, 4:16:03 PM5/21/18
to fireba...@googlegroups.com
Not sure I fully follow. But you can create multiple App instances with different names, and then use them for different API calls.

const defaultApp = admin.initializeApp();
const customApp = admin.initializeApp({....}, 'customApp');

admin.auth().createUser(...); // Uses default app
admin.storage(customApp).bucket(); // Uses custom app




--

Hiranya Jayathilaka | Software Engineer | h...@google.com | 650-203-0128

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Doug Stevenson

unread,
May 21, 2018, 5:16:42 PM5/21/18
to Firebase Google Group
Optimally, you don't want to init the admin SDK globally for all your functions if it's not going to be used in all of them, since they all execute using the same global scope.  Instead, you can init only within the function implementations where it's going to be used, and only init if it hasn't been already.  So, something like this within the function callback:

var adminForX

exports.foo = functions.auth().createUser(event => {
    if (!adminForX) {
        adminForX = admin.initializeApp(...)
    }
    // use adminForX
))

Then you can be sure that only function foo is initializing and making use of the admin SDK for whatever configuration you choose, and no other functions (even though they will all see adminForX as undefined).

Doug

Clayton Gomes

unread,
Jun 10, 2018, 8:51:30 PM6/10/18
to Firebase Google Group
try to add `service account token creator` role to `App Engine default service account`. Only `Editor` role is not enough to use the signBlob function.

Screen Shot 2018-06-11 at 11.37.46 AM.png
Reply all
Reply to author
Forward
0 new messages