15

I'm trying to migrate my code from using API keys stored in the .env file to using Google Cloud Platform Secrets Manager. I've followed the instructions here but I encounter an error saying that I don't have permissions to access the secret.

import * as admin from "firebase-admin"
import { SecretManagerServiceClient } from "@google-cloud/secret-manager"

admin.initializeApp()
const secretClient = new SecretManagerServiceClient()

async function main() {
  async function getSecret(): Promise<string | null | undefined> {
    const [version] = await secretClient.accessSecretVersion({ name: "TELEGRAM_TOKEN" })

    return version.payload?.data?.toString()
  }

  const TELEGRAM_TOKEN = await getSecret()
  console.log(TELEGRAM_TOKEN)
}

main().catch(console.error)

And that's the error I get:

> node lib/app.js --telegram

{ Error: 7 PERMISSION_DENIED: Permission denied on resource project TELEGRAM_TOKEN.
    at Object.callErrorFromStatus (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/call.js:30:26)
    at Object.onReceiveStatus (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/client.js:174:52)
    at Object.onReceiveStatus (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:340:141)
    at Object.onReceiveStatus (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:303:181)
    at Http2CallStream.outputStatus (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/call-stream.js:114:27)
    at Http2CallStream.maybeOutputStatus (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/call-stream.js:153:22)
    at Http2CallStream.endCall (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/call-stream.js:140:18)
    at Http2CallStream.handleTrailers (/Users/bartekpacia/dev/node/telegram-lang-enforcer/node_modules/@grpc/grpc-js/build/src/call-stream.js:262:14)
    at ClientHttp2Stream.emit (events.js:198:13)
    at emit (internal/http2/core.js:265:8)
  code: 7,
  details: 'Permission denied on resource project TELEGRAM_TOKEN.',
  metadata:
   Metadata {
     internalRepr:
      Map {
        'google.rpc.help-bin' => [Array],
        'grpc-status-details-bin' => [Array],
        'grpc-server-stats-bin' => [Array] },
     options: {} },
  note:
   'Exception occurred in retry method that was not classified as transient' }

I did create a Service Account with "Owner" permissions, downloaded it and made export GOOGLE_APPLICATION_CREDENTIALS=/Users/.... My service account .json file location is correctly displayed when I execute echo $GOOGLE_APPLICATION_CREDENTIALS.

I have really no idea what I'm doing wrong.

Bartek Pacia
  • 1,085
  • 3
  • 15
  • 37

5 Answers5

25

When you access a secret, you need to specify the project:

await secretClient.accessSecretVersion({ name: "TELEGRAM_TOKEN" })

should be

await secretClient.accessSecretVersion({ name: "projects/my-project/secrets/TELEGRAM_TOKEN/versions/latest" })
sethvargo
  • 26,739
  • 10
  • 86
  • 156
  • 1
    Obvious now looking at the docs, but doesn't help that the 'name' parameter etc are in comments and so blend in and you don't notice them in the code sample! – TommyBs Jun 01 '20 at 21:09
  • What does this directory refer too? It's GCloud based? – Oliver Dixon Aug 07 '20 at 10:04
  • 3
    For anyone else who was lost like me, it's on the secrets manager page, under the title of the 'secret' there is a path. Such a weird way to add secrets. – Oliver Dixon Aug 07 '20 at 10:07
  • now I'm being hit with Error: 3 INVALID_ARGUMENT: Resource ID [projects/123123123123/secrets/mongo_uri] is not in a valid format. – SebastianG Mar 09 '21 at 09:42
  • do I understand correctly that if I pass secret version (number) instead of "latest" I'll get proper secret version? – TheTanadu Jul 25 '22 at 13:19
  • 1
    @TheTanadu yes, passing in a number returns that version. – sethvargo Jul 25 '22 at 19:20
  • @keet-sugathadasa got me the rest of the way there. I opted to write a little helper class that handles the boiler plate for me, using the `GOOGLE_CLOUD_PROJECT` environment variable to get the project ID: https://gist.github.com/battis/7ad8550cb157d7b7f320c7608099ebaf (this lets me go around writing things like `Secrets::get('API_TOKEN')` or `Secrets::get('MOTHERS_MAIDEN_NAME', 12)` – Seth Battis Nov 22 '22 at 16:13
8

I just encountered the same problem and I personally had to add /versions/latest after specifying the project name in the secret name.

await secretClient.accessSecretVersion({
  name: "projects/my-project/secrets/TELEGRAM_TOKEN/versions/latest"
})
someRandomDev
  • 561
  • 6
  • 15
6

These answers guided me, but it took a long time for me to get this working. You need to enter the PROJECT_ID and not the Project-Name.

Find your Project ID:

The second column here shows the Project ID:

enter image description here

Now use that and run the script

await secretClient.accessSecretVersion({
  name: "projects/PROJECT_ID/secrets/SECRET_NAME/versions/latest"
})
Keet Sugathadasa
  • 11,595
  • 6
  • 65
  • 80
0

You can use the PROJECT_NUMBER also. After updating the PROJECT_NUMBER permission issue is resolved for me. You may also try.

"projects/<PROJECT_NUMBER>/secrets/TELEGRAM_TOKEN/versions/latest"

Singh5025
  • 1
  • 1
-1

If you want to access GCP Secret Manager values using SDK or code, then you can follow the below steps:

Step 1: Download GCP CLI

Step 2: Authenticate GCP CLI on the local machine

Step 3: Create a service using the following codes:

import { Injectable } from '@nestjs/common';
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager').v1;


@Injectable()
export class AppService {
  async getHello(): Promise<string> {
    try {
      const secretmanagerClient = new SecretManagerServiceClient();
      const request = {
        name: 'projects/<YOUR_PROJECT_CODE>/secrets/<YOUR_SECRET_NAME>/versions/1',
      };
      let response = await secretmanagerClient.accessSecretVersion(request);
      response = response[0].payload.data.toString('utf8')
      console.log(response);
      return response;
    }
    catch (err) {
      console.log('err:', err)
    }

  }
}

Step 4: Run your code and call the service method.

Shubham Verma
  • 8,783
  • 6
  • 58
  • 79