0

NOTE: Using the Sign-in button is NOT an option

A year ago I was having a problem creating a moment. Back then I was using version 1.2 of the Google+ API .Net client. As I described in this post, I had it working although the code failed to insert a moment from time to time. I was hoping that the process is more stable and easier to implement now, and it seems like it as can be seen in the example that you can download here - the current version as of this writing is v1.8. So I created a simple project following the SimpleOAuth2 sample in the download, but implementing Google+. This is the code I came up:

public partial class _Default : System.Web.UI.Page
{        
    private PlusService service;

// Application logic should manage users authentication. 
// This sample works with only one user. You can change
// it by retrieving data from the session.
private const string UserId = "user-id";

protected void Page_Load(object sender, EventArgs e)
{
    GoogleAuthorizationCodeFlow flow;
    var assembly = Assembly.GetExecutingAssembly();
    using (var stream = assembly.GetManifestResourceStream(
          "GPlusSample.client_secrets.json"))
    {
        flow = new GoogleAuthorizationCodeFlow(
                   new GoogleAuthorizationCodeFlow.Initializer
        {
            DataStore = new FileDataStore("GPlusSample.Store"),
            ClientSecretsStream = stream,
            // 
            // Tried only this scope but it did not work
            //Scopes = new[] { PlusService.Scope.PlusMe }
            //
            // I tried the following: but did not work either
            //Scopes = new[] { PlusService.Scope.PlusMe, 
            //            "https://www.googleapis.com/auth/plus.moments.write" }
            //
            // I tried this as well and it failed
            //Scopes = new[] { PlusService.Scope.PlusLogin }
            //
            // Maybe this... but still no joy
            Scopes = new[] { PlusService.Scope.PlusLogin, 
                             PlusService.Scope.PlusMe }
        });
    }

    var uri = Request.Url.ToString();
    var code = Request["code"];
    if (code != null)
    {
        var token = flow.ExchangeCodeForTokenAsync(UserId, code,
            uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;

        // Extract the right state.
        var oauthState = AuthWebUtility.ExtracRedirectFromState(
            flow.DataStore, UserId, Request["state"]).Result;
        Response.Redirect(oauthState);
    }
    else
    {
        var result = new AuthorizationCodeWebApp(flow, uri, uri)
            .AuthorizeAsync(UserId, CancellationToken.None).Result;
        if (result.RedirectUri != null)
        {
            // Redirect the user to the authorization server.
            Response.Redirect(result.RedirectUri);
        }
        else
        {
            // The data store contains the user credential, 
            // so the user has been already authenticated.
            service = new PlusService(new BaseClientService.Initializer
            {
                ApplicationName = "Plus API Sample",
                HttpClientInitializer = result.Credential
            });
        }
    }
}

/// <summary>Gets the TasksLists of the user.</summary>
public async System.Threading.Tasks.Task InsertMoment()
{
    try
    {                
        var me = service.People.Get("me").Execute();

        var request = service.Moments.Insert(new Moment()
        {
            Target = new ItemScope { 
                Id=Guid.NewGuid().ToString(),
                Image="http://www.google.com/s2/static/images/GoogleyEyes.png",
                Type="",
                Name = "test message",
                Description="test",
                Text="test message",                        
            },
            Type = "http://schemas.google.com/AddActivity",                    
        }, me.Id, MomentsResource.InsertRequest.CollectionEnum.Vault);
        var response =await request.ExecuteAsync();
        output.Text = "<h1>" + response.Id + "</h1>";
    }
    catch (Exception ex)
    {
        var str = ex.ToString();
        str = str.Replace(Environment.NewLine, Environment.NewLine + "<br/>");
        str = str.Replace("  ", " &nbsp;");
        output.Text = string.Format("<font color=\"red\">{0}</font>", str);
    }
} 

protected async void createMomentButton_Click(object sender, EventArgs e)
{
    await InsertMoment();
}
}

That code always give me a 401 Unauthorized error, even if I have the Google+ API turned on for my project. Here's the actual error I got:

The service plus has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError Unauthorized [401] Errors [ Message[Unauthorized] Location[ - ] Reason[unauthorized] Domain[global] ]

It's interesting to see that the insert moment is failing even though the call to People.Get("me") works - get("me") works with all of the scope combinations I listed above. It's important to note that each time I try a new scope, I first log out of my Google account and delete the access token that is stored in GPlusSample.Store.

EDIT

I tried setting just the Url instead of individual items as suggested by Ian and I got the exact same error.

var request = service.Moments.Insert(new Moment()
{
    Target = new ItemScope {
        Url = "https://developers.google.com/+/web/snippet/examples/thing"
        },
    Type = "http://schemas.google.com/AddActivity",
}, me.Id, MomentsResource.InsertRequest.CollectionEnum.Vault);
var response =await request.ExecuteAsync();
Community
  • 1
  • 1
von v.
  • 16,868
  • 4
  • 60
  • 84

1 Answers1

0

https://www.googleapis.com/auth/plus.login is the right scope for writing moments, but you need to have requested the specific app activity types you want to write as well. The parameter for this is request_visible_actions, and it takes a space separated list of arguments of the types (Listed on https://developers.google.com/+/api/moment-types/ - e.g. http://schemas.google.com/AddActivity).

The client library may not have a method for adding request_visible_actions, so you may have to add it on to the auth URL you redirect the user to manually (remember to URLencode the app activity type URLs!)

Ian Barber
  • 19,765
  • 3
  • 58
  • 58
  • Thanks for the reply Ian. I have considered that already based on my experience last year. I have tried adding that parameter but it did not help. Also, I think the client library should have taken care of that. What's more interesting is that it worked for a few times, in that our app was able to post on my Google+. But after a restart of the app, creating moments stopped working - no changed in code, hmm... – von v. Mar 18 '14 at 04:05
  • If you had a user that had previously granted access to the write types it would have worked. Make sure to revoke the token for your user before testing again - you can do that and see which types have been granted on plus.google. com/apps – Ian Barber Mar 18 '14 at 09:10
  • And why the sudden stop I wonder... To make a clean test, I created a new Google account and used that with our app and auth failed for that new user - who has not used our app before. Btw, removing the token that was saved in the DataStore revokes the permission, as the (test) user is asked to authorize our app each time I remove it. So,hmm... – von v. Mar 18 '14 at 10:02
  • Removing the token doesn't revoke access - it just means you need to sign in again. So if a user grants A and B, you delete your local token with A, but then request B, Google still has a grant on record for B. Only calling the token revoke endpoint wipes out those grants. – Ian Barber Mar 18 '14 at 11:57
  • Cool thanks for clarifying that. Although there's still the problem, like I mentioned, I tested with a new user and auth failed the first time. At this point, it would help if there's a running sample for inserting moments. I can read the user's profile as well as read the moments but insert fails. – von v. Mar 18 '14 at 12:11
  • Can you try the insert with a URL rather than the individual pieces of data e.g. on the target itemscope, just set the URL property to "https://developers.google.com/+/plugins/snippet/examples/thing" and try that? – Ian Barber Mar 18 '14 at 14:38
  • I tired that and got the same result. Please see my edit in my post. Thanks. – von v. Mar 19 '14 at 01:26
  • @vonv. did you manage to get it working. I am having same issue and dont know what to you. I have google+ api enabled – zish Jul 16 '14 at 22:13