google-cloud-platformgoogle-apigoogle-iam

what (least) roles assign on a IAM policy to access a public shared calendar


I have a snippet perfectly working with an Service Account which has all the roles possible, just to test if the client code was working good. Now, I want to narrow the permissions for a proper go to prod.

const serviceAccountConfig = await Deno.readTextFile(Deno.env.get('GOOGLE_APPLICATION_CREDENTIALS'));
const credentials = JSON.parse(serviceAccountConfig);

const cal_id =  '<a public shared cal id>';


const api = new GoogleAPI({
    email: "<my-serviceacount>@g<blahblah>.iam.gserviceaccount.com",
    scope: ["https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events"],
    key: credentials.private_key,
});

const calget = await api.get(`https://www.googleapis.com/calendar/v3/calendars/${cal_id}/events`);

To authenticate it rightly, I use a JSON key I've generated and imported along with the code. Keep in mind that I've tested the client side with a key that does work, but it has way too many permissions.

As you can see I would like to have the https://www.googleapis.com/auth/calendar scope. To do so, I head to https://console.cloud.google.com/iam-admin/iam and I try to create a new IAM policy, by:

Then, I wait some 5 minutes or so, since according to the documentation is an eventually operation and it could take a bit of time. I try to run my code and here it goes the error:

error: Uncaught (in promise) Error: Error in get token, data: {"error":"invalid_grant","error_description":"Invalid JWT Signature."}
      throw new Error(`Error in get token, data: ${JSON.stringify(data)}`);
            ^
    at GoogleAuth.#authorizeJWTToken (https://deno.land/x/google_deno_integration@v1.1/google_auth.ts:72:13)
    at eventLoopTick (ext:core/01_core.js:183:11)
    at async GoogleAuth.#authenticate (https://deno.land/x/google_deno_integration@v1.1/google_auth.ts:91:19)
    at async GoogleAuth.getToken (https://deno.land/x/google_deno_integration@v1.1/google_auth.ts:35:7)
    at async GoogleAPI.request (https://deno.land/x/google_deno_integration@v1.1/google_api.ts:67:34)
    at async GoogleAPI.get (https://deno.land/x/google_deno_integration@v1.1/google_api.ts:45:12)

I'm wondering what role I should give my policy to make things work


Solution

  • I've found it out: no IAM policies are needed. If I switch from a service account (along with its authentication key) to another, of course I have to change the email in the code too, to make match both the key and the user account email used to access:

    const api = new GoogleAPI({
        // this one 👇
        email: "<my-serviceacount>@g<blahblah>.iam.gserviceaccount.com",
        scope: ["https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events"],
        key: credentials.private_key,
    });
    

    the reason why no IAM is needed in this case, and it could lead to confusion, is explained in a comment below this answer, which I quote here:

    Google services, such as Calendar, use OAuth 2 Scopes to grant permissions. That permission design existed before Google Cloud. Google also used that design in the early days of Google Cloud before IAM was fully developed and there are remnants still to this day. In your case, you are using an identity from Google Cloud (service account email address) and using it outside of Google Cloud. In most cases, the correct scopes to use are cloud-platform plus the Google service specific OAuth Scope (googleapis.com/auth/calendar). IAM roles usually have no impact on Google services.