11

The background
I'm using the Google Apps Script to create a Gmail Add-on. Via this plugin, I would like to connect to my backend server (a non-Google service) using a REST service request. The request has to be authorised. When authorised, I could then make requests to that server to receive data associated with that user in the database. I'm already using Google sign-in in my webapp to sign in to the backend service - at the front end, I receive the id_token inside of the GoogleUser object in the authorisation response.

The problem
I need this id_token to log in to my backend service when connecting to it via the Gmail plugin. However, I couldn't find a way how to access the token.

The research
I would assume the token must be available through the API in the Apps Script.
In the webapp, I receive the id_token using the Google Auth API like this:

Promise.resolve(this.auth2.signIn())
        .then((googleUser) => {
            let user_token = googleUser.getAuthResponse().id_token; // this is the id_token I need in the Gmail plugin, too

            // send the id_token to the backend service
            ...
        };

In the Google Apps Script API I could only find the OAuth token:

ScriptApp.getOAuthToken();

I assumed the token could also be stored in the session. The Google Apps Script API contains the Session class and that itself contains the getActiveUser method, which returns the User object. The User object, however, only contains the user's email address, no id token (or anything else for that matter):

Session.getActiveUser().getEmail();


The question(s)
Is there a way to obtain the id token?
Am I choosing the right approach to logging in to the backend server using the data of the signed-in user in the Gmail?

Evan
  • 288
  • 1
  • 10
  • 19
  • 1
    For example, when you set ``onTriggerFunction`` to ``buildAddOn`` in ``appsscript.json``, you can retrieve the access token by ``e.messageMetadata.accessToken`` from ``e`` of ``function buildAddOn(e){}``. If this was not what you want, I'm sorry. – Tanaike Sep 18 '18 at 23:11
  • Not quite. This is the access token, I need to acquire the id token. But thank you. – Patrik Chynoranský Sep 24 '18 at 09:04
  • I'm really sorry I misunderstood your question. – Tanaike Sep 24 '18 at 11:38
  • What do you do with the `id_token`? Do you only read `sub`? – TheMaster Dec 14 '18 at 16:57
  • I would like to use the `id_token` to enable my own server to make requests to Gmail on my behalf. The same way you do when you log in to Gmail via Google OAuth login. But in this case I'm already logged in to Gmail (as a user of the Gmail add-on I have to be). And I would like to use that session information to enable my server to access my account data (messages, profile information etc.). – Patrik Chynoranský Dec 19 '18 at 15:02

2 Answers2

5

Method 1: use getIdentityToken()

Gets an OpenID Connect identity token for the effective user:

var idToken = ScriptApp.getIdentityToken();
var body = idToken.split('.')[1];
var decoded = Utilities.newBlob(Utilities.base64Decode(body)).getDataAsString();
var payload = JSON.parse(decoded);
var profileId = payload.sub;
Logger.log('Profile ID: ' + profileId);

Method 2: use Firebase and getOAuthToken()

Steps to get Google ID Token from Apps Script's OAuth token:

  1. Enable Identity Toolkit API for your Apps Script project.
  2. Add new Firebase project to your existing Google Cloud Platform project at https://console.firebase.google.com/
  3. Create Firebase app for platform: Web.
  4. You will get your config data: var firebaseConfig = {apiKey: YOUR_KEY, ...}.
  5. Enable Google sign-in method for your Firebase project at https://console.firebase.google.com/project/PROJECT_ID/authentication/providers.
  6. Use Apps Script function to get ID Token for current user:

function getGoogleIDToken()
{
    // get your configuration from Firebase web app's settings
    var firebaseConfig = {
        apiKey: "***",
        authDomain: "*.firebaseapp.com",
        databaseURL: "https://*.firebaseio.com",
        projectId: "***",
        storageBucket: "***.appspot.com",
        messagingSenderId: "*****",
        appId: "***:web:***"
    };

    var res = UrlFetchApp.fetch('https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key='+firebaseConfig.apiKey, {
        method: 'POST',
        payload: JSON.stringify({
            requestUri: 'https://'+firebaseConfig.authDomain,
            postBody: 'access_token='+ScriptApp.getOAuthToken()+'&providerId=google.com',
            returnSecureToken: true,
            returnIdpCredential: true
        }),
        contentType: 'application/json',
        muteHttpExceptions: true
    });

    var responseData = JSON.parse(res);

    idToken = responseData.idToken;

    Logger.log('Google ID Token: ');
    Logger.log(idToken);

    return idToken;
}

Kudos to Riël Notermans

Kos
  • 4,890
  • 9
  • 38
  • 42
  • In the Method 1 above, once you have the `idToken` in the add-on, what is the right way to use it to authorize with your RESTful backend? i.e. would you have some sort of token exchange endpoint that takes in the `idToken` and returns a bearer token that can be used with your own endpoint? And that exchange endpoint would validate the `idToken`? – Alex Rothberg Jun 22 '23 at 03:33
1

You should enable oAuth scopes, https://developers.google.com/apps-script/concepts/scopes

Macondo
  • 166
  • 6
  • I already did that. I have these enabled: "https://www.googleapis.com/auth/gmail.addons.execute", "https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.addons.current.message.readonly", "https://www.googleapis.com/auth/userianfo.email", "https://www.googleapis.com/auth/script.locale", "https://www.googleapis.com/auth/script.external_request". But I don't see how that gives me the ID token. I'm missing some API method here, not a permission. – Patrik Chynoranský Dec 19 '18 at 14:55
  • The required scope to get the userIdToken is "openid". – CK_One Jan 13 '22 at 22:22