How to get Google profile info including custom fields from an Apps Domain user?
Asked Answered
S

3

3

Using the user.profile and user.email scope and the /oauth2/v2/userinfo feed doesn't seem to return any custom fields (in my case Department) or phone numbers. These fields show up in the Domain Shared Contacts directory.

Is there perhaps an Apps Domain specific feed URL something like /oauth2/{DOMAIN}/v2/userinfo ?

Does the API/Service not support any custom fields yet?

Is there a way to fudge this into working?

Read access to your own Apps Domain Shared Contacts profile that's connected to your account shouldn't be so difficult.

I'd prefer a non-admin solution because my domain uses Common Access Cards w/ SAML authentication so I can't just store admin credentials (user : password) in an App Engine app and access the /m8/ feed. If there's a flow to access Domain Shared Contacts (with custom fields) with a beforehand authorized consumer key and secret I'd be interested in the instructions for getting that to work.

EDIT Jay Lee nailed it "https://www.google.com/m8/feeds/gal/{domain}/full"

Here's the proof of concept script using Google Apps Script (I'll add the final OAuth2 version when I finish it)

function getGal(email, passwd, domain) {
  var res = UrlFetchApp.fetch("https://www.google.com/accounts/ClientLogin", {
    contentType: "application/x-www-form-urlencoded",
    method: "post",
    payload: { "Email": email, "Passwd": passwd, "accountType": "HOSTED", "service":"cp" }
  });
  var auth = res.getContentText().match(/Auth=(.*)/i)[1];
  Logger.log("Auth: " + auth);
  res = UrlFetchApp.fetch("https://www.google.com/m8/feeds/gal/" + domain + "/full", {
    method: "get",
    headers: { "Authorization": "GoogleLogin auth=" + auth, "GData-Version": "1.0" }
  });
  Logger.log(res.getHeaders());
  Logger.log(res.getContentText());
}

EDIT 2 OAuth version that returns JSON and only the info for the user accessing the script.

function googleOAuthM8() {
  var oAuthConfig = UrlFetchApp.addOAuthService("m8");
  oAuthConfig.setRequestTokenUrl('https://www.google.com/accounts/OAuthGetRequestToken?scope=https://www.google.com/m8/feeds/');
  oAuthConfig.setAuthorizationUrl('https://www.google.com/accounts/OAuthAuthorizeToken');
  oAuthConfig.setAccessTokenUrl('https://www.google.com/accounts/OAuthGetAccessToken');
  oAuthConfig.setConsumerKey('anonymous');
  oAuthConfig.setConsumerSecret('anonymous');
  return {oAuthServiceName:"m8", oAuthUseToken:'always'};
}
function getGal(domain) {
  res = UrlFetchApp.fetch("https://www.google.com/m8/feeds/gal/" + domain + "/full?alt=json&q=" + Session.getActiveUser().getEmail(), googleOAuthM8());
  Logger.log(res.getHeaders());
  Logger.log(res.getContentText());
}
Subcritical answered 29/4, 2013 at 14:33 Comment(4)
Do you have to use Google Data APIs?Palatalized
@Palatalized - If you're inferring I do something like push the user data directly from LDAP into a Google Doc and reference that doc in my apps that's not a viable solution for me (unfortunately).Subcritical
Something like that might have been a solution.Palatalized
@Palatalized - It's really counter intuitive that there's no non-admin way to programmatically read profile data that's linked to your account. I guess the Domain Shared Contacts were kind of an after thought for Business and Education Apps Domains.Subcritical
Y
10

Any non-admin user can access the GAL programmatically, see:

https://github.com/google/gfw-deployments/blob/master/apps/shell/gal/gal_feed.sh

I don't believe this API call is documented or supported officially but it works even with OAuth authentication rather than the example's ClientLogin (tested on the OAuth 2.0 playground with a non-admin user and the standard https://www.google.com/m8/feeds/ Contacts scope).

Note that the Global Address List is a compilation of user profiles, groups and shared contacts. You'll need to parse it out to find the user(s) you wish to get department information for.

Yocum answered 8/5, 2013 at 16:52 Comment(5)
You're the man Jay, I was sure there was something you could add to the m8 feed to get it to work and "gal" was it. All of the custom fields are there in the response. I'll update my question with a working code snippet, the one in your link uses HTTP instead of HTTPS for the feed URL which no longer works.Subcritical
This may have been taken down. I get "Server responded with: 404, Invalid request URI"Phoebe
@Jay Lee. Is it possible to get users from gmail ??Joyajoyan
@Chandan: your question isn't very clear and it should probably be a new SO question in any case as it sounds like a different scenario from this question.Yocum
Can confirm that this solution still works currently +1Seaton
H
2

I would utilize the Google Apps Profiles API to do this. It'll give you a bunch of meta information, including profile data and even profile photos: https://developers.google.com/google-apps/profiles/

Even if you're using PIV/CAC/SAML, you will be able to auth using Two-Legged-OAuth. https://developers.google.com/accounts/docs/OAuth#GoogleAppsOAuth

Two-legged-oauth is the path of least resistance, but you should also take a look at OAuth2, especially the JWT-signed service accounts portion -- however, it can be a little tricky to get working with the older GData xml apis.

As far as fields available go, you'll have to work with the ones on this page. There are extended properties where you add in arbitrary data, but they don't show up in the Contacts browser with Google Mail itself: https://developers.google.com/gdata/docs/2.0/elements#gdProfileKind

On a sidenote, if you're in an LDAP environment (and since you mentioned CAC, I think you probably are), you should take a look at Google Apps Directory Sync, which can synchronize that profile data with your local AD/LDAP.

source: I deployed Google Apps to large organizations (3000+), public and private.

Hatteras answered 2/5, 2013 at 23:52 Comment(1)
There's no mention of OAuth or OAuth2 in the Profiles API documentation, but it does specifically state an admin username and password are needed. I was under the impression that 2-legged OAuth is deprecated, so perhaps the JWT route would be best but would I be able to delegate it read-only permissions on Domain Shared Profiles? With regards to LDAP Sync (I think my knowledge of it is dated) can it now update app domain user profiles as opposed to just the Domain Shared Contacts profiles there seems to be a disconnect between the two?Subcritical
H
0

I have used the following approach with TwoLeggedOAuthHmacToken: Consumer key and secret can be found in google apps admin dashboard

CONSUMER_KEY = 'domain.com'
CONSUMER_SECRET = 'secret_key'


class ContactClient():
    def __init__(self, username):
        # Contacts Data API Example ====================================================
        self.requestor_id = username + '@' + CONSUMER_KEY
        self.two_legged_oauth_token = gdata.gauth.TwoLeggedOAuthHmacToken(
            CONSUMER_KEY, CONSUMER_SECRET, self.requestor_id)

        self.contacts_client = gdata.contacts.client.ContactsClient(source=SOURCE_APP_NAME)
        self.contacts_client.auth_token = self.two_legged_oauth_token

    def newuser(self, username):
        self.contacts_client.auth_token.requestor_id = username + '@' + CONSUMER_KEY

    def getContacts(self, username=None):
        if username:
            self.newuser(username)
        return self.contacts_client.GetContacts()


class MainPage(webapp2.RequestHandler):
    def get(self):
        contacts = ContactClient(username='username')
        feed = contacts.getContacts()
        output = ""
        if feed:
              for entry in feed.entry:
                if entry.title and entry.title.text:
                    output += entry.title.text + "<br/>"
                for email in entry.email:
                    if email.primary and email.primary == 'true':
                        output += '&nbsp;%s<br/>' % (email.address)
        self.response.headers['Content-Type'] = 'text/html'
        self.response.write('''<h1>Contact Access via GData Client</h1>''' + output)
Hieronymus answered 8/5, 2013 at 10:30 Comment(1)
I want the Domain Shared Contact profile for the current user. The m8 feed seems to only return the account profile not the Domain Shared Contact profile that is linked to the account, thus it's missing custom fields that was sync'd to it using the Google LDAP Sync utility.Subcritical

© 2022 - 2024 — McMap. All rights reserved.