0

I have a Google+ app that is successfully writing app activity for certain test accounts, yet others return a 401 Unauthorized error:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "unauthorized",
    "message": "Unauthorized"
   }
  ],
  "code": 401,
  "message": "Unauthorized"
 }
}

I also noticed this in the response header:

WWW-AuthenticateBearer realm="https://www.google.com/accounts/AuthSubRequest", error=invalid_token

This seems to indicate an invalid token, but I'm not sure how I'm using gapi.auth.authorize incorrectly... especially because the script works perfectly using certain test accounts and writes moments to G+ without issue. If anyone could suggest any reasons certain test accounts might be unable to authenticate for writing app activity (or anything wrong with the code below), please let me know!

// first call gapi.auth.authorize with immediate:true:
 _checkAuth = function _checkAuth(){
        gapi.auth.authorize({
            client_id : 'XXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com',
            scope : 'https://www.googleapis.com/auth/plus.login',
            request_visible_actions : 'http://schemas.google.com/CreateActivity',
            immediate : true
        }, function(authResult){
            if(!authResult || authResult.error){
               _signIn();
            }else{
                _performAction();
            }
        });
    },

// if not logged in, call gapi.auth.authorize with immediate:false:
_signIn = function _signIn(){
        gapi.auth.authorize({
            client_id : 'XXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com',
            scope : 'https://www.googleapis.com/auth/plus.login',
            request_visible_actions : 'http://schemas.google.com/CreateActivity',
            immediate : false
        }, function(token){
            gapi.auth.setToken(token);
           _performAction();
        });
    },

// create activity
_performAction = function _performAction(){
        gapi.client.load('plus','v1', function(){
            gapi.client.setApiKey('XXXXXXXXXXXXXXXXXXXXXXX');
            var payload = {
                "type" : 'http://schemas.google.com/CreateActivity'
            };
            payload.target = {
                "id" : "myappid",
                "image" : "http://www.example.com/xxxxxxxxxxx.jpg",
                "type" : 'http://schema.org/CreativeWork',
                "description" : "description of activity",
                "name" : "name of activity"
            };
            var args = {
                'path' : '/plus/v1/people/me/moments/vault',
                'method' : 'POST',
                'body' : JSON.stringify(payload),
                'callback' : function(response) {
                    console.log(response); // error
                }
            };
            // triggers 401 error for some accounts
            gapi.client.request(args);
        });
},

1 Answers1

1

A couple ideas for causes:

  • Are some of the accounts upgraded to Google+ and others not? This could be a bug on the Google+ side but if an account hasn't yet been upgraded, the notion of connecting apps and so on could be interpreted as an error.
  • Is it possible that you are trying to write different types of app activities or is this the only code writing app activities?
  • Are you storing access tokens? These expire after 3600 seconds. You can verify your tokens here.
  • Why are you calling gapi.client.setApiKey? This should be used for unauthenticated API calls while writing app activities requires authorized credentials. The client library will use the credentials that are captured after the user authorizes your app.
  • Why are you not using the Google+ API client library? I'll give you an example of writing app activities using the client library, hopefully this helps.

An example button requesting write access to app activities used in the demo:

<button class="g-signin"
    data-scope="https://www.googleapis.com/auth/plus.login"
    data-requestvisibleactions=
    "http://schemas.google.com/AddActivity http://schemas.google.com/ListenActivity"
    data-clientId="268858962829.apps.googleusercontent.com"
    data-callback="onSignInCallback"
    data-theme="dark"
    data-cookiepolicy="single_host_origin">
</button>

An example of using the client library for writing app activities.

    writeAddActivity: function(url){
      var payload = {
        "type":"http:\/\/schemas.google.com\/AddActivity",
        "startDate": "2012-10-31T23:59:59.999Z"
      };
      if (url != undefined){
        payload.target = {
          'url' : 'https://developers.google.com/+/plugins/snippet/examples/thing'
        };
      }else{
        payload.target = {
          "id" : "replacewithuniqueidforaddtarget",
          "image" : "http:\/\/www.google.com\/s2\/static\/images\/GoogleyEyes.png",
          "type" : "http:\/\/schema.org\/CreativeWork",
          "description" : "The description for the activity",
          "name":"An example of AddActivity"
        };
      }
      this.writeAppActivity(payload);
    },
    writeAppActivity: function(payload){

      gapi.client.plus.moments.insert(
          {  'userId' : 'me',
             'collection' : 'vault',
             'resource' : payload
          }).execute(function(result){
              console.log(result);
          });
    }

Working demo at:

http://wheresgus.com/appactivitiesdemo/

class
  • 8,621
  • 29
  • 30
  • Thanks so much for your reply. Your demo works with the test account I was having issues with, it writes app activity without any issue... however, next I copied the source code of that over to my environment and changed the client_id and payload target id, and once again am getting the 401 error. I'm looking in the Developer Console and everything appears to be set correctly (Javascript Origins, etc), but I'm wondering if there's a setting that needs to be tweaked somewhere. – L Ron Hubbard Jan 16 '14 at 22:13
  • Are all of the actions you are using set in request_visible_actions? I gave an example button in the answer - you will know that you're correctly setting them because the activity types will appear in the consent dialog. – class Jan 17 '14 at 00:21
  • @class What are do you repave "replacewithuniqueidforaddtarget" with. Where do you get this info? – Supertecnoboff Jun 05 '14 at 10:57