AppsScriptPulse

Resolve Google Chat Space User IDs to Emails: Least Privilege Approach with Service Accounts and Custom Admin Roles

If you’ve built a Google Chat bot, you’ve likely hit this wall: the API sends you a membership event with a User ID… but omits the email field entirely.

For Google Workspace developers, this limitation in the Chat API is a frequent stumbling block. As detailed in the User resource documentation, while you can use an email address as an alias in requests (e.g., referencing users/[email protected]), the API insists on returning only the canonical resource name (e.g., users/123456789). This ID corresponds to the user’s profile in the People API or Admin SDK, but the email itself is stripped from the response, forcing developers to perform a secondary lookup.

Handling User ID Conversions

I’ve explored this lookup logic before in Beyond the Limits: Automating Google Chat Space Archives. In that solution, I used a helper function to convert the users/{id} format to people/{id} for use with the Advanced People Service.

/**
 * Fetches user details from the People API.
 * @private
 * @param {string} userResourceName The "users/{user}" string from the Chat API.
 * @return {{name: string, displayName: string, email: string|null}} An object with user details.
 */
function getUserDetails_(userResourceName) {
  const defaultUserDetails = {
    name: userResourceName,
    displayName: userResourceName.replace('users/', 'Unknown User '),
    email: null,
  };

  // Fail fast for app users or invalid formats
  if (!userResourceName.startsWith('users/') || userResourceName === 'users/app') {
    return defaultUserDetails;
  }
  
  try {
    const peopleApiResourceName = userResourceName.replace(/^users\//, 'people/');
    
    const person = People.People.get(peopleApiResourceName, {
      personFields: 'names,emailAddresses',
    });
    
    const displayName = person.names?.[0]?.displayName ?? defaultUserDetails.displayName;
    const email = person.emailAddresses?.[0]?.value ?? null;

    return {
      name: userResourceName,
      displayName: displayName,
      email: email,
    };
    
  } catch (e) {
    console.warn(`Could not fetch details for ${userResourceName} from People API: ${e.message}`);
    return defaultUserDetails;
  }
}

However, bridging this gap for a Service Account leads to a security dilemma. The People API often returns empty fields because the Service Account lacks a contact list. You might find yourself reaching for Domain-Wide Delegation to impersonate an admin—effectively using a sledgehammer to crack a nut.

In a recent guide, Justin Poehnelt outlines a more secure strategy that avoids granting blanket domain access. By assigning a custom “Users > Read” Admin Role directly to a Service Account, developers can resolve emails securely without the risks associated with full impersonation.

The Strategy at a Glance

  • Custom Roles: Create a strictly read-only role in the Admin Console.
  • Direct Assignment: Assign this role specifically to the Service Account’s email.
  • No Long-Lived Keys: Use Application Default Credentials (ADC) in production and Service Account Impersonation for local development.

This approach ensures your bot has just enough permission to identify users, keeping your security team happy and your audit logs clean. For more information into the implementation, including the specific configuration steps, I encourage you to read Justin’s full post linked below.

Source: Resolve Chat User IDs to Emails: Least Privilege

A comprehensive look at key value store options in Google Apps Script

A comparison of key-value store options in Google Apps Script, including PropertiesService, CacheService, Firestore, and Sheet Developer Metadata.

Justin Poehnelt has shared a comprehensive look at key-value store options in Google Apps Script. Key-value stores are useful for storing data like user preferences or frequently accessed data, which can also be shared between script executions.

As part of Justin’s post as well as looking at the built-in store options, PropertiesService and CacheService, there is also a look at scenarios where you might want to consider alternatives like Firestore.

The choice of key-value store depends on factors like expiration needs, number of items, value size, access control requirements, latency sensitivity, and cost considerations. Justin’s post provides a thorough analysis of each factor, including latency comparisons.

Head over to the source link to get all the details!

Source: Key Value Store Options in Google Apps Script | Justin Poehnelt