Retrieving Google User Photo
Asked Answered
C

2

10

I am able to retrieve the thumbnailPhotoUrl from the user.list api of the google admin SDK. However, whenever I try to render the image, Google is redirecting to a static silhouette image. The URL that is retrieved via the API looks like

https://plus.google.com/_/focus/photos/private/AIbEiAIA.... 

As mentioned, this ends up getting redirected to:

https://ssl.gstatic.com/s2/profiles/images/silhouette200.png

However, with a little bit of reverse engineering, I can see the photo by adding /u/1/ to the beginning of the URL path, like this:

https://plus.google.com/u/1/_/focus/photos/private/AIbEiAIA...

From my research the /u/1 has something to do with multiple google accounts, so I'm afraid I wouldn't be able to rely on this method. Can anyone help me understand what's happening here?

Classical answered 23/8, 2014 at 23:40 Comment(0)
P
6

Out of my own experience I figured out that if the thumbnailPhotoUrl has private on the URL then the photo is not public i.e. not viewable outside the domain, it could also be that the user hasn't activated their Google+ profile, which I believe makes their photo public anyway.

Best to avoid using the thumbnailPhotoUrl if the URL has a private path on it. I think it's more reliable to retrieve the photo as Web-safe base64 data using the Users.Photo API then encode it as an inline base64 CSS image.

This is the code snippet I usually use:

    import com.google.common.io.BaseEncoding;
    import com.google.api.services.admin.directory.model.User;
    import com.google.api.services.admin.directory.model.UserPhoto;

public class PhotoUtils {

    public void loadPhoto() {
            // if the user have not signed up for Google+ yet then their thumbnail is private
            String thumbnailUrl = user.getThumbnailPhotoUrl();
            String photoData = "";
            if((thumbnailUrl != null) && (thumbnailUrl.indexOf("private") > -1)) {

                    UserPhoto photo = getUserPhoto(user.getId());
                    if(photo != null) {
                        photoData = getBase64CssImage(photo.getPhotoData(), photo.getMimeType());
                    }
            }

    }

        public static String getBase64CssImage(String urlSafeBase64Data, String mimeType) {
            urlSafeBase64Data = new String(BaseEncoding.base64().encode(
                      BaseEncoding.base64Url().decode(urlSafeBase64Data)));
            return "data:" + mimeType + ";base64," + urlSafeBase64Data;
        }

         public UserPhoto getUserPhoto(String userId) throws IOException {
            UserPhoto photo = null;
            try {
                photo = this.getClient().users().photos().get(userId).execute();

            } catch(GoogleJsonResponseException e) {
                if(e.getMessage().indexOf(NOT_FOUND) == 0) {
                    log.warning("No photo is found for user: " + userId);

                } else {
                    throw e;
                }
            }
            return photo;
        }
}
Pocket answered 26/8, 2014 at 12:30 Comment(0)
O
2

Here are my 10 cents...

Recently I've been working on building a G Suite User Picker for Angular and I ran into the same problem. Nonetheless, I kept testing and researching and in one of my tests I decided to call the directory api users list endpoint with a NON-ADMIN user. (Head Explodes 🤯) To my surprise, google permits this type of requests, although it provides non super admin data but enough to get a private thumbnail url for the user. At first I thought it was an unintended behaviour, some kind of bug if you will. That is when I ran into this documentation.

While user accounts can only be modified by administrators, any user on the domain can read user profiles. A non-admin user can make a users.get or users.list request with the viewType parameter equal to domain_public to retrieve a user's public profile. The scope https://www.googleapis.com/auth/admin.directory.user.readonly is ideal for this use case.

So it is an intended behaviour. That is when it hit me. The private url generated when retrieving the user data is intended to be used only by the requesting user. If you provide the url to other user, it will redirect to the anonymous silhouette.

From this I was able to conclude that if I need to retrieve a list of users and need the ability to view their photos, I need to call the directory api endpoint using the credentials of the user that needs to view the respective data. This serves my purpose with the G Suite User Picker widget I am building.

Ozieozkum answered 1/6, 2020 at 0:20 Comment(3)
Just having the same issue, interesting solution but it appears to require the non-admin users to have the admin.directory read scope else the request is 403.Honeysuckle
@DavidThomas Indeed, but you should grant the read only scope that way its safe.Ozieozkum
Yes, I had that scope granted for a sample user but was still unable to request the photo, only got the private photo URL. I ended up using the other user photos api to get the required data developers.google.com/admin-sdk/directory/v1/reference/users/…Honeysuckle

© 2022 - 2024 — McMap. All rights reserved.