Question

I am in the process of upgrading my Marketplace applications to support the new marketplace api and OAUTH 2.

I have managed to migrate most APIs but am stuck on the contacts api. With the the previous marketplace version we used 2LO and client key/client secret to authenticate across the Google Apps domain. My understanding is that the only way to do this in current version is with Service Accounts and OAuth 2.

Based on the V3 calendar API I'm assuming something like this (although the contacts API does not support it from what I can see) -

        ServiceAccountCredential credential = new ServiceAccountCredential(
           new ServiceAccountCredential.Initializer(serviceAccountEmail)
           {
               Scopes = new[] { "https://www.google.com/m8/feeds" },
               User = "admin@domain.co"
           }.FromCertificate(certificate));

        var service = new ContactsService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "Contact API Sample",
        });

If anybody has done this, your advice will be appreciated!

Was it helpful?

Solution 2

Well I got no response anywhere on this so I can only assume that the standard client libraries don't have support for this.

I have come up with a workaround based on the following post for email access.

http://www.limilabs.com/blog/oauth2-gmail-imap-service-account

You will need to follow his post and note the following:

  • this must use the DotNetOpenAuth version that he specifies. The latest version will not work.
  • You will only need his AssertionFlowClient classes which can be added to the below code I have provided here:

        X509Certificate2 certificate = new X509Certificate2(
            serviceAccountCertPath,
            serviceAccountCertPassword,
            X509KeyStorageFlags.Exportable);
    
        AuthorizationServerDescription server = new AuthorizationServerDescription
        {
            AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
            TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
            ProtocolVersion = ProtocolVersion.V20,
        };
    
        AssertionFlowClient provider = new AssertionFlowClient(server, certificate)
        {
            ServiceAccountId = serviceAccountEmail,
            Scope = string.Join(" ", new[] { "https://mail.google.com/", "https://www.google.com/m8/feeds/" }),
            ServiceAccountUser = userEmail,
        };
    
        IAuthorizationState grantedAccess = AssertionFlowClient.GetState(provider);
    
        RequestSettings rs = new RequestSettings("iLink");
        rs.OAuth2Parameters = new OAuth2Parameters();
        rs.OAuth2Parameters.AccessToken = grantedAccess.AccessToken;
        rs.OAuth2Parameters.RefreshToken = null;
        rs.OAuth2Parameters.ClientId = null;
        rs.OAuth2Parameters.ClientSecret = null;
        rs.OAuth2Parameters.RedirectUri = null;
    
        ContactsRequest cr = new ContactsRequest(rs);
        Feed<Contact> f = cr.GetContacts();
        foreach (var c in f.Entries)
        {   
            Response.Write(c.Name.FullName);
        }
    

OTHER TIPS

I know this question has been answered, but adding an alternative that doesn't require the AssertionFlowClient or a specific version of DotNetOpenAuth. This allows you to use the same ServiceAccountCredential code from your original question (ie, using the same method to query a user's gmail)

Thank you for following up on your own post, it helped me find this solution!

    private static ServiceAccountCredential GenerateCred(IEnumerable<string> scopes, string delegationUser)
    {
        var certificate = new X509Certificate2(certLocation, certPassword, X509KeyStorageFlags.Exportable);
        var credential = new ServiceAccountCredential(
            new ServiceAccountCredential.Initializer(serviceAccountEmail)
            {
                Scopes = scopes,
                User = delegationUser
            }.FromCertificate(certificate));
        return credential;
    }

        var credential = GenerateCred(new[] { "https://www.googleapis.com/auth/contacts.readonly" }, userToDelegate);

        //Get the token for this scope and user
        await credential.RequestAccessTokenAsync(new CancellationToken());

        //use the token to initalize the request
        var rs = new RequestSettings(projectName)
        {
            OAuth2Parameters = new OAuth2Parameters()
            {
                AccessToken = credential.Token.AccessToken
            }
        };

        var request = new ContactsRequest(rs);
        Feed<Contact> f = request.GetContacts();
        foreach (var c in f.Entries)
        {
            //process each contact
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top