2
votes

I am looking for a way how to change user profile photo programmatically.

Generally, I need to be able to change avatars of domain GSuite users, but if there are API for changing my own avatar is also good.

Had tried already:

There are Contacts API, but I am trying to change my own (or impersonated one) profile image, so I believe this API isn't for my case.

If you can point me in some direction to search I'll be very glad. I just do not believe that there is no way to change avatar, it can't be for real.

Update:

Checked Contact API - also doesn't work, looks like it changes an avatar only on me in my contact list, nobody else doesn't see changes, and the main profile picture remains the same.

Code:

// admin directory way
const { google } = require('googleapis');
const SafeBase64 = require('url-safe-base64');
const GOOGLE = require(process.env.GOOGLEAPIS_VAULT);

const encodedPhoto = SafeBase64.encode('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7');

const JWTClient = new google.auth.JWT(
  GOOGLE.client_email,
  null,
  GOOGLE.private_key,
  [
    'https://www.googleapis.com/auth/admin.directory.user',
  ],
  'gsync@***.com', // sub
);

const directory = google.admin({ version: 'directory_v1', auth: JWTClient });
directory.users.photos.update({}, ctx.params.email);
const res = await directory.users.photos.update({
  userKey: ctx.params.email,
  resource: {
    photoData: encodedPhoto,
  },
});
// Contacts API way
const { google } = require('googleapis');
const SafeBase64 = require('url-safe-base64');
const GOOGLE = require(process.env.GOOGLEAPIS_VAULT);

const JWTClient = new google.auth.JWT(
  GOOGLE.client_email,
  null,
  GOOGLE.private_key,
  [
    'https://www.google.com/m8/feeds/',
  ],
  ctx.params.email, //sub
);

const res1 = await JWTClient.requestAsync({
  headers: {
    'GData-Version': 3.0,
  },
  params: {
    alt: 'json',
    q: 'arsenyp@***.com',
  },
  url: `https://www.google.com/m8/feeds/contacts/${ctx.params.email}/full`,
});

const contactIdFull = res1.data.feed.entry.filter((c) => c.gd$email[0].address === ctx.params.email)[0].id.$t;
const [, contactId] = /\/base\/([a-z0-9]+)$/.exec(contactIdFull);

// https://www.google.com/m8/feeds/contacts/{userEmail}/full/{contactId}

const res2 = await JWTClient.requestAsync({
  headers: {
    'GData-Version': 3.0,
  },
  params: {
    alt: 'json',
  },
  url: `https://www.google.com/m8/feeds/contacts/${ctx.params.email}/full/${contactId}`,
});

const { href: image, gd$etag: etagJ } = res2.data.entry.link.filter((l) => l.rel === 'http://schemas.google.com/contacts/2008/rel#photo')[0];
const res3 = await axios({
  method: 'GET',
  url: image,
  headers: {
    Authorization: `Bearer "${(await JWTClient.getAccessTokenAsync()).token}"`,
  },
  responseType: 'arraybuffer',
});
const etag = JSON.parse(etagJ);

// PUT /m8/feeds/photos/media/default/contactId
// If-match: Etag
// Content-Type: image/*

const res4 = await axios({
  method: 'PUT',
  url: `https://www.google.com/m8/feeds/photos/media/default/${contactId}`,
  headers: {
    Authorization: `Bearer "${(await JWTClient.getAccessTokenAsync()).token}"`,
    'Content-Type': 'image/png',
  },
  // responseType: 'arraybuffer',
  data: Buffer.from('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7', 'base64'),
});
// People API way (won't work, throwing an error)
const userId = '1024471xxxxx251564465';

const JWTClient = new google.auth.JWT(
  GOOGLE.client_email,
  null,
  GOOGLE.private_key,
  [
    'https://www.googleapis.com/auth/contacts',
    'profile',
  ],
  ctx.params.email, // sub
);

const people = google.people({ version: 'v1', auth: JWTClient });

const res = await people.people.updateContactPhoto({
  resourceName: `people/${userId}`,
  resource: {
    photoBytes: SafeBase64.encode('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7'),
    personFields: 'photos',
  },
  sources: [
    'READ_SOURCE_TYPE_PROFILE',
  ],
});
1
"The main profile picture remains the same." Where do you see this?Aerials
I see an unchanged picture, the same as it was before the change.h0x91B
Please share the code you are using in both your try cases.Aerials
Added code samples for all 3 cases.h0x91B
Check this answer stackoverflow.com/a/48444481/12167785 out. It hints that there is no such API.Sudhakar Ramasamy

1 Answers

1
votes

Ok, so as @Sudhakar mention there is no such API.

But I've found that admin directory API actually changes the profile in most places.

The trick in that the user needs manually once remove the avatar from https://aboutme.google.com/ Profile picture because this photo has top priority against admin directories one.

If the user removes the photo from aboutme.google.com then changes it via admin directory v1 API this photo is seen in the calendar, Gmail, contacts...

So check my first code sample:

// admin directory way
const { google } = require('googleapis');
const SafeBase64 = require('url-safe-base64');
const GOOGLE = require(process.env.GOOGLEAPIS_VAULT);

const encodedPhoto = SafeBase64.encode('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7');
const targerUser = 'arsenyp@***.com';

const JWTClient = new google.auth.JWT(
  GOOGLE.client_email,
  null,
  GOOGLE.private_key,
  [
    'https://www.googleapis.com/auth/admin.directory.user',
  ],
  'gsync@***.com', // sub - this is service user with proper permissions, not a target user
);

const directory = google.admin({ version: 'directory_v1', auth: JWTClient });
directory.users.photos.update({}, targerUser);
const res = await directory.users.photos.update({
  userKey: targerUser,
  resource: {
    photoData: encodedPhoto,
  },
});