Enable mail forwarding using a Google Apps Service Account
V

2

2

On April 20, 2015, several Google Apps APIs are being discontinued, including the Provisioning API(gdata).
In my Python scripts, I am using a Service Account and OAuth 2.0, instead of ClientLogin, and the replacement API: Directory API.
However, I am unable to find a way to enable mail forwarding using the new API, and all of Google's Python documentation for mail forwarding explains how to do it using ClientLogin, which is also being discontinued on April 20th.

Relevant Info:
I have a service account and authorized it appropriately following this guide: https://developers.google.com/api-client-library/python/auth/service-accounts
All of my other functionality is working with the new API!
I have thoroughly searched the Directory API documentation(though I don't rule out the chance that I missed something): https://developers.google.com/resources/api-libraries/documentation/admin/directory_v1/python/latest/index.html
Google's only documentation(that I found) about implementing mail forwarding with Python suggests using ClientLogin as mentioned above: https://developers.google.com/admin-sdk/email-settings/#manage_forwarding_settings

My existing working code for mail forwarding(based on that documentation):

client = gdata.apps.emailsettings.client.EmailSettingsClient(domain='mydomain.co')
client.ClientLogin(email=adminEmail, password=adminPass, source='apps')
client.UpdateForwarding(username=username, enable=True, 
    forward_to=forwardTo, action='ARCHIVE')

My updated code based on Jay Lee's answer:

credentials = SignedJwtAssertionCredentials(serviceEmail, key, sub=adminEmail, 
    scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/ '+'other scopes')
client = gdata.apps.emailsettings.client.EmailSettingsClient(domain='mydomain.co')
client.additional_headers = {'Authorization': 'Bearer %s' % credentials.access_token}
client.UpdateForwarding(username=username, enable=True, 
    forward_to=forwardTo, action='ARCHIVE')

And I added the new scope to my service account in:
Admin Console->Security->Advanced Settings->Manage API Client Access(under Authentication)
*Note: If you are using other scopes, you need to type all of them because it replaces your previous settings.

Update:
I thought I had it was working, but I may have had a line commented out or ignored. When I tried my code later in the day, with everything line being executed properly, it was still giving me a gdata.client.Unauthorized error. I have tried restarting my server so the credentials would be created again, but it has not helped. The error occurs when I try to make the update forwarding call.
I confirmed that the access_token is the same as the one that is working for my Directory API calls, and that "client" is in fact a emailSettingsClient object.
The full error I receive is:

Unauthorized error message

Another attempt based on the following:
http://www.worldofchris.com/blog/2012/12/27/fun-with-oauth-gdata-google-apis-client-library-python/
https://groups.google.com/forum/m/#!msg/google-apps-developer-blog/1pGRCivuSUI/3EAIioKp0-wJ How do I authorize a gdata client without using the gdata oauth2 workflow?

credentials = SignedJwtAssertionCredentials(serviceEmail, key, sub=adminEmail, 
    scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/ '+'other scopes')
auth = gdata.gauth.OAuth2Token(serviceEmail, key, 
    scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/',
    access_token=credentials.access_token,
    refresh_token=credentials.refresh_token,
    user_agent='emailsettings/2.0')#I do not really understand this param
client = gdata.apps.emailsettings.client.EmailSettingsClient(domain='mydomain.co')
#Also tried with (domain='mydomain.co', auth_token = credentials.access_token)
client.additional_headers = {'Authorization': 'Bearer %s' % credentials.access_token}
auth.authorize(client)
client.UpdateForwarding(username=username, enable=True, 
    forward_to=forwardTo, action='ARCHIVE')
Vamoose answered 13/4, 2015 at 15:50 Comment(0)
B
3

This should be the proper way to use the credentials object directly:

import gdata.gauth

credentials = SignedJwtAssertionCredentials(serviceEmail,
                                            key, 
                                            sub=adminEmail,
                                            scope=scopes)
client = gdata.apps.emailsettings.client.EmailSettingsClient(domain='mydomain.co')
client.auth_token = gdata.gauth.OAuth2TokenFromCredentials(credentials)
client.UpdateForwarding(username=username, enable=True, 
forward_to=forwardTo, action='ARCHIVE')
Bilbe answered 13/4, 2015 at 18:4 Comment(17)
I added the Email Settings API scope: apps-apis.google.com/a/feeds/emailsettings/2.0 that I found here: developers.google.com/admin-sdk/email-settings/auth and made the other change you mentioned, and got a 401 "AccessTokenRefreshError - access_denied: Requested client not authorized". I am using sub=superAdminEmail as well.Vamoose
Should I update my question with more of my code, or is my other comment enough info for you to help me with this error?Vamoose
You added the scope to the list of authorized scopes in the Control Panel as well as adding it as one of the requested scopes in your service account code?Bilbe
No I did not, only the code so far. Which APIs do I need to add to my service account(in google developers console) or should I be using a different scope?Vamoose
Email Settings API doesn't need to be enabled in dev console. Did you authorize the scope in the Control Panel and add it to your list of scopes in the code?Bilbe
Sorry, I didn't read your earlier comment carefully enough and mistook Control Panel for Devolopers Console instead of Admin Panel. I just added the scope in the Control(Admin) Panel, and this time I got a: "gdata.client.Unauthorized" errorVamoose
I accidentally replaced my other scopes in the Admin Console with just the new scope, but after I added them back in with the new scope, it worked correctly, thank you! Also, just for confirmation, do you know if gdata will still be supported after April 20th even though it is one of the older APIs?Vamoose
Email settings API is not part of Apr 20 deprecation and will continue to work.Bilbe
It was working for a while, then started giving me the gdata.client.Unauthorized error again. I have tried implementing a token refresh, and restarting my server so the credentials would be created again, but it has not helped. The error occurs when I try to make the update forwarding call.Vamoose
Service accounts are not refreshed via a refresh token. When the access token expires, simply rebuild the credentials object exactly as you did the first time.Bilbe
Thank you for clarifying that. But I don't think that the token is expiring, since that isn't the error, and I have tried putting the UpdateForwarding immediately after credentials creation(as seen in the updated code block in my question).Vamoose
The fact that it was working and stopped indicates something changed. Go back and check your authorization and code.Bilbe
I'm sorry for the confusion, but I don't think it was actually working, but instead I had something commented out or returning prematurely. I just edited my original question for clarification, and added a picture of the error I am getting. My credentials variable works for all Directory API calls I make, but not the emailClientSettings calls. Do I need to use credentials.authorize() on anything for gdata, similar to authorizing the http object I use in my Directory API calls, or is it possible that my requests require user approval?Vamoose
Hey Jay, sorry to keep adding more comments, but I am still having no luck making any EmailClientSettings calls without ClientLogin. I have tried implementing the following examples using gdata.gauth.OAuth2Token and also had no luck (though I am also not sure what the user_agent parameter is). I will add my new code attempts to the original question for clarity. Examples: worldofchris.com/blog/2012/12/27/… groups.google.com/forum/m/#!msg/google-apps-developer-blog/…Vamoose
Apologies for the confusion, I've done this in the past for GData Python Library "service" APIs but not a client API like you're using. The rewritten answer should provide you the quickest way to take a credential object and apply it to your emailsettings client.Bilbe
This works and is simpler than creating a separate gauth object. Thank you for posting a condensed solution!Vamoose
How do I get the key?Negotiate
V
0

There were a few things I needed to change to my last update in order for my code to work: I needed to use user_agent=credentials.user_agent, remove the client.additional_headers, and I used the client_id and client_secret from credentials instead of passing them in myself(not sure if that was a variable type issue).
Final working code:

credentials = SignedJwtAssertionCredentials(serviceEmail, key, sub=adminEmail, 
    scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/ '+'other scopes')
auth = gdata.gauth.OAuth2Token(
    credentials.client_id,#serviceEmail
    credentials.client_secret,#private key 
    scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/',
    access_token=credentials.access_token,
    refresh_token=credentials.refresh_token,
    user_agent=credentials.user_agent)
client = gdata.apps.emailsettings.client.EmailSettingsClient(domain='mydomain.co')
auth.authorize(client)
client.UpdateForwarding(username=username, enable=True, 
    forward_to=forwardTo, action='ARCHIVE')
Vamoose answered 17/4, 2015 at 14:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.