Flask-Mail not Authenticating with Gmail and App password - python-2.7

So I am getting an SMTPSenderRefused error when I try to use the flask app I built to send an email. Let me start buy saying this worked previously but now it has stopped. I am at a loss and I have spent many hours testing and tweaking and reading online and NOTHING has given me an answer.
I keep getting the following error.
[2019-01-21 03:07:51,954] ERROR in app: Exception on /register/ [POST]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "MyFlaskApp.py", line 131, in register
mail.send(msg)
File "/usr/local/lib/python2.7/site-packages/flask_mail.py", line 492, in send
message.send(connection)
File "/usr/local/lib/python2.7/site-packages/flask_mail.py", line 427, in send
connection.send(self)
File "/usr/local/lib/python2.7/site-packages/flask_mail.py", line 192, in send
message.rcpt_options)
File "/usr/lib64/python2.7/smtplib.py", line 737, in sendmail
raise SMTPSenderRefused(code, resp, from_addr)
SMTPSenderRefused: (530, '5.5.1 Authentication Required. Learn more at\n5.5.1 https://support.google.com/mail/?p=WantAuthError u186sm20332024pfu.51 - gsmtp', u'myemail#gmail.com')
IP.XX.XXX.XXX - - [21/Jan/2019 03:07:51] "POST /register/ HTTP/1.1" 500 -
My gmail account is configured with 2 step verification and then an application password that this Flask App uses. I have tried every combination and it still will not work. This is my code that I am using.
app = Flask(__name__, static_url_path='/static')
app.config.update(
DEBUG = False,
MAIL_SERVER = 'smtp.gmail.com',
MAIL_PORT = 465,
MAIL_USE_SSL = True,
MAIL_USERNAME = 'myemail#gmail.com',
MAIL_DEFAULT_SENDER = 'myemail#gmail.com',
MAIL_PASSWORD = 'GmailApplicationPassword',
)
mail = Mail(app)
The lines to actually send the message (inside the POST):
msg = Message("Welcome",
sender = 'myemail#gmail.com',
recipients = [request.form["email"]])
msg.body = "Welcome! \n\n Congratulations on your successful registration. \n\n Cheers!"
mail.send(msg)
Some additional information that might be helpful. This exact code used to work when I was running my application locally. Then I deployed my code to my AWS EC2 instance and had it working on there. It was working until I did the following.
I associated a Elastic IP address with the site and then pointed my DNS to that IPv4. I have also added AWS Public Certificate.
Can someone please help me understand why I am unable to send emails. I have tried everything I can find as solutions online.
Additional Details:
$ pip freeze
Flask-Mail==0.9.1
Thanks in advance and I will help provide any answers if more clarification is needed so please ask.

This is the solution that I found to work best. (I am not saying this is the best method but it works) It involves the use of a few different key concepts that I would Highly Recommend using. It involves the use of secrets, which you can follow this guide here for more information on this (as it is not the heart of this question I will only leave a link here). It also required the use of Boto3 to be able to read the variables. The AWS documentation will be helpful here so again I am not including it.
My method was to set up AWS Simple Email Service (SES). This makes it such that the application using Flask-Mail accesses AWS SES to send the email which then talks to gmail to actually send out the email. A few important notes here are that the SES is not in every region but presently (June 2019) it is not required that your EC2 instance be hosted in the same region that you are using SES in. Additionally, if you want to be able to send emails to outside addresses you MUST ensure that you have the email inbox configured in accordance with and not violate AWS terms of use. And Finally if you are on the free tier, you must submit a support ticket to get a rate increase and it will also allow you to send emails externally. Now here is the bit of code that I use:
app = Flask(__name__, static_url_path='/static')
app.config.update(
DEBUG = False,
MAIL_SERVER = params.getParameter("SES_SERVER"),
MAIL_PORT = 587,
MAIL_USE_TLS = True,
MAIL_USERNAME = params.getParameter("SES_USERNAME"),
MAIL_PASSWORD = params.getParameter("SES_PASSWORD"),
)
mail = Mail(app)
The sending code remains unchanged.
I did need to add the an import for Boto3 and I created a new file that is called params.py and is where I built my getParameter method to access the variables. If you want you could hard code the values above but I would not recommend it, especially if you are storing your code in a cloud repository.
I am not 100% sure but I suspect that gmail prohibits a IP address or domain from AWS accessing its servers and thus you cannot send emails. This is my suspicion but I could not find a way to prove it.

For gmail accounts for which 2-Step verification is activated, allowing less secure app is disabled. In that case to access App Password need to be generated.
This is app config code written in flask app.
`app = Flask(__name__)
app.config.update(
MAIL_SERVER = "smtp.gmail.com",
MAIL_PORT="465",
MAIL_USE_SSL=True,
MAIL_USERNAME=params['gmail-user'],
MAIL_PASSWORD=params['gmail-pass']
)
mail=Mail(app)`
Created "config.json" to define the parameter, which will be used above. For username and password check "gmail-user" and "gmail-pass"
{"params":
{ "local_server":"True",
"local_uri":"mysql+pymysql://root:#localhost/jbh_datasc_blog",
"prod_uri": "mysql+pymysql://root:#localhost/jbh_datasc_blog",
"fb_url":"https://facebook.com/<give the name of extension>",
"tw_url":"https://twitter.com/<give the name of extension>",
"gh_url":"https://github.com/<give the name of extension>",
"blog_head":"Data Science Digital",
"sub_head":"Innovating Future",
"gmail-user":"xxxxxxxx#gmail.com",
"gmail-pass":"xxxxxxxxxxxxxxxx"
}
}
Go the your gmail account and in the search bar type App Password.
It will ask about the name of app and computer from where r u accessing, provide it.
Google will generate a 16 digit password.
Use this password to fill in the 'gmail-pass'
Save and execute the code.

Related

Twilio Unable to send fax

Hope you are having a nice day.
I am getting following error on twillion fax api
HTTP Error Your request was:
POST /Faxes
Twilio returned the following information:
Unable to create record: The requested resource /Faxes was not found
Here is the code .
def __init__(self):
self.client = Client(
settings.TWILIO_ACCOUNT_SID,
settings.TWILIO_AUTH_TOKEN,
)
self.sender = settings.FAX_SENDER
self.receiver = settings.FAX_RECEIVER
self.callback_url = settings.TWILIO_CALLBACK_URL
i am sending a fax in function with this code
fax = self.client.fax.faxes.create(
from_=self.sender,
to=self.receiver,
media_url=pdf_url,
status_callback=self.callback_url,
)
Here is the trackback
fax = client.fax.faxes.create(from_= sender,to=receiver,media_url = 'https://mysite.ca/media/7ad4a93f0e7641b58e078da171b3651a.pdf',status_callback = callback_url,)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/var/www/vhosts/site/httpdocs/venv/lib64/python3.6/site-packages/twilio/rest/fax/v1/fax/__init__.py", line 186, in create
payload = self._version.create(method='POST', uri=self._uri, data=data, )
File "/var/www/vhosts/site/httpdocs/venv/lib64/python3.6/site-packages/twilio/base/version.py", line 209, in create
raise self.exception(method, uri, response, 'Unable to create record')
twilio.base.exceptions.TwilioRestException:
HTTP Error Your request was:
POST /Faxes
Twilio returned the following information:
Unable to create record: The requested resource /Faxes was not found
More information may be available here:
https://www.twilio.com/docs/errors/20404
also, I confirmed that my Twilio account is working and active.
It seems like the Faxes API has been deprecated. If you go to https://www.twilio.com/fax you'll see a message.
We are no longer supporting Programmable Fax as of December 17, 2021.
For new and inactive accounts, access to Programmable Fax is disabled effective immediately. So if your account are new, you basically have no options except switching the provider.
Thank you for the help, Twilio blocked my account, however they enabled it since it was active before the cut off date.

httplib2.ServerNotFoundError: Unable to find the server at www.googleapis.com

When i try to fetch the details of gmail using google-api's (googleapiclient,oauth2client), i am getting below error:
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/google_api_python_client-1.7.8-py2.7.egg/googleapiclient/_helpers.py", line 130, in positional_wrapper
File "/usr/local/lib/python2.7/site-packages/google_api_python_client-1.7.8-py2.7.egg/googleapiclient/discovery.py", line 224, in build
File "/usr/local/lib/python2.7/site-packages/google_api_python_client-1.7.8-py2.7.egg/googleapiclient/discovery.py", line 274, in _retrieve_discovery_doc
File "/usr/local/lib/python2.7/site-packages/httplib2-0.12.1-py2.7.egg/httplib2/__init__.py", line 2135, in request
cachekey,
File "/usr/local/lib/python2.7/site-packages/httplib2-0.12.1-py2.7.egg/httplib2/__init__.py", line 1796, in _request
conn, request_uri, method, body, headers
File "/usr/local/lib/python2.7/site-packages/httplib2-0.12.1-py2.7.egg/httplib2/__init__.py", line 1707, in _conn_request
raise ServerNotFoundError("Unable to find the server at %s" % conn.host)
httplib2.ServerNotFoundError: Unable to find the server at www.googleapis.com
but it is working fine in my pc but not from remote location.
code:
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
credentials = ServiceAccountCredentials.from_json_keyfile_name(
"quickstart-1551349397232-e8bcb3368ae1.json", scopes=
['https://www.googleapis.com/auth/admin.directory.group', 'https://www.googleapis.com/auth/admin.directory.user', 'https://www.googleapis.com/auth/admin.directory.domain', 'https://www.googleapis.com/auth/gmail.readonly'])
delegated_credentials = credentials.create_delegated('jango#carbonitedepot.com')
DIRECOTORY = build('admin', 'directory_v1', credentials=delegated_credentials)
try:
results = DIRECOTORY.users().list(customer='my_customer').execute()
users = results.get('users', [])
res = []
for info in users:
print(info)
res.append(info.get("primaryEmail"))
print(res)
except Exception as e:
print(e)
Any help would be much appreciated.
Thanks in advance.
I had the same issue and I searched a lot to get it fixed, turns out I was at fault here.
I don't know from where there was podman installed instead of docker and that caused the problem.
I suggest you check your docker version and make sure the latest is running at the server, otherwise, the library works fine! Please let me know if you still face this issue, would like to dig deep!
The httplib2 is likely scanning through network interface DHCP DNS nameservers (in your registry or visible to docker) and then trying to connect through a stale DNS or possible an IPv6. Patching your socket in the main routine may solve it:
# Monkey patch to force IPv4, since FB seems to hang on IPv6
import socket
old_getaddrinfo = socket.getaddrinfo
def new_getaddrinfo(*args, **kwargs):
responses = old_getaddrinfo(*args, **kwargs)
return [response
for response in responses
if response[0] == socket.AF_INET]
socket.getaddrinfo = new_getaddrinfo

How to use service account with private key file and impersonated user (DWD) in Google App Engine and local development server?

Application: Google App Engine Python standard environment
Purpose: Access Google APIs (not Cloud APIs) through the google-api-python-client, e.g. Sheets API v4, by using a service account and impersonate a user, because the app is supposed to act on behalf of this user. (2-legged auth, the user won't be asked to grant access)
I've got a setup running in production environment, but it runs only on the local development server (dev_appserver.py) for testing if a certain environment variable would be removed. I'm looking for a solution that would work without adding/removing the environment variable.
The service account was created for the app and configured with domain-wide delegation DWD in Admin Console. Sheets API is turned on for this project.
Of the many quick-starts, samples, and references available, it was only after reading the Google Auth Library for Python documentation (google-auth) that I've noticed the missing parts (an environment variable and the SSL library) and finally got the code running on production.
The app code will use the private key JSON file that was downloaded from Cloud Console IAM.
requirements.txt
# as suggested by almost all docs, but this isn't everything we need:
google-api-python-client==1.6.5
google-auth==1.4.0
google-auth-httplib2==0.0.3
app.yaml
env_variables:
# enable socket support of paid app, needed for OAuth2 service-accounts
# see google-auth documentation, v1.4.1, chapter 1.2.4
GAE_USE_SOCKETS_HTTPLIB : true
# some other stuff
libraries:
# to make HTTPS calls to other services, needed for OAuth2 service-accounts
# see google-auth documentation, v1.4.1, chapter 1.2.4
- name: ssl
version: latest
appengine_config.py (partial sample for Sheets API v4 access)
from google.oauth2 import service_account
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
APP_ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
SERVICE_ACCOUNT_FILE = "service-account-private-key.json"
import googleapiclient.discovery
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
# impersonate as user#example.com (G Suite domain account)
credentials = credentials.with_subject('user#example.com')
service = googleapiclient.discovery.build('sheets', 'v4', credentials=credentials)
# until here, the code works in production and local dev server
result = service.spreadsheets().values().get(spreadsheetId="DOC-ID-HERE", range="A1:C5").execute()
# execute() will work only in production,
# on local dev, it will raise an ResponseNotReady exception
traceback
ERROR 2018-03-05 16:32:03,183 wsgi.py:263]
Traceback (most recent call last):
File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 240, in Handle
handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/api/lib_config.py", line 351, in __getattr__
self._update_configs()
File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/api/lib_config.py", line 287, in _update_configs
self._registry.initialize()
File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/api/lib_config.py", line 160, in initialize
import_func(self._modname)
File "/Users/user/git/project/gae/appengine_config.py", line 143, in <module>
spreadsheetId=spreadsheetId, range=rangeName).execute()
File "/Users/user/git/project/gae/_lib/oauth2client/_helpers.py", line 133, in positional_wrapper
return wrapped(*args, **kwargs)
File "/Users/user/git/project/gae/_lib/googleapiclient/http.py", line 839, in execute
method=str(self.method), body=self.body, headers=self.headers)
File "/Users/user/git/project/gae/_lib/googleapiclient/http.py", line 166, in _retry_request
resp, content = http.request(uri, method, *args, **kwargs)
File "/Users/user/git/project/gae/_lib/google_auth_httplib2.py", line 187, in request
self._request, method, uri, request_headers)
File "/Users/user/git/project/gae/_lib/google/auth/credentials.py", line 121, in before_request
self.refresh(request)
File "/Users/user/git/project/gae/_lib/google/oauth2/service_account.py", line 322, in refresh
request, self._token_uri, assertion)
File "/Users/user/git/project/gae/_lib/google/oauth2/_client.py", line 145, in jwt_grant
response_data = _token_endpoint_request(request, token_uri, body)
File "/Users/user/git/project/gae/_lib/google/oauth2/_client.py", line 106, in _token_endpoint_request
method='POST', url=token_uri, headers=headers, body=body)
File "/Users/user/git/project/gae/_lib/google_auth_httplib2.py", line 116, in __call__
url, method=method, body=body, headers=headers, **kwargs)
File "/Users/user/git/project/gae/_lib/httplib2/__init__.py", line 1659, in request
(response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
File "/Users/user/git/project/gae/_lib/httplib2/__init__.py", line 1399, in _request
(response, content) = self._conn_request(conn, request_uri, method, body, headers)
File "/Users/user/git/project/gae/_lib/httplib2/__init__.py", line 1355, in _conn_request
response = conn.getresponse()
File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/dist27/python_std_lib/httplib.py", line 1121, in getresponse
raise ResponseNotReady()
I have figured out that if I delete GAE_USE_SOCKETS_HTTPLIB from app.yaml's env_variables list, the code will work on local development server (but not in production anymore).
Am I doing something wrong here? Could I use the same code (maybe with a small switch) for both environments, without manually adding/removing the variable from app.yaml?
Purpose: Access Google APIs (not Cloud APIs) through the google-api-python-client, e.g. Sheets API v4, ….
Here they explain that:
Private, broadcast, multicast, and Google IP ranges (except those whitelisted below), are blocked:
Google Public DNS: 8.8.8.8, 8.8.4.4, 2001:4860:4860::8888, 2001:4860:4860::8844 port 53
Gmail SMTPS: smtp.gmail.com port 465 and 587
Gmail POP3S: pop.gmail.com port 995
Gmail IMAPS: imap.gmail.com port 993
I have figured out that if I delete GAE_USE_SOCKETS_HTTPLIB from app.yaml's env_variables list, the code will work on local development server (but not in production anymore).
This is explained here:
Using sockets with the development server
You can run and test code
using sockets on the development server, without using any special
command line parameters.
Finally, this question and the accepted answer describe a similar scenario.
Hope this helps you :-)

Setting Spotify credentials using Spotipy

I am trying out spotipy with python 2.7.10 preinstalled on my mac 10.10, specifically [add_a_saved_track.py][1] Here is the code as copied from github:
# Add tracks to 'Your Collection' of saved tracks
import pprint
import sys
import spotipy
import spotipy.util as util
scope = 'user-library-modify'
if len(sys.argv) > 2:
username = sys.argv[1]
tids = sys.argv[2:]
else:
print("Usage: %s username track-id ..." % (sys.argv[0],))
sys.exit()
token = util.prompt_for_user_token(username, scope)
if token:
sp = spotipy.Spotify(auth=token)
sp.trace = False
results = sp.current_user_saved_tracks_add(tracks=tids)
pprint.pprint(results)
else:
print("Can't get token for", username)
I registered the application with developer.spotify.com/my-applications and received client_id and client_secret. I am a bit unclear about selection of redirect_uri so I set that to 'https://play.spotify.com/collection/songs'
Running this from terminal I get an error that says:
You need to set your Spotify API credentials. You can do this by
setting environment variables like so:
export SPOTIPY_CLIENT_ID='your-spotify-client-id'
export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
I put that into my code with the id, secret, and url as strings, just following the imports but above the util.prompt_for_user_token method.
That caused a traceback:
File "add-track.py", line 8
export SPOTIPY_CLIENT_ID='4f...6'
^
SyntaxError: invalid syntax
I noticed that Text Wrangler does not recognize 'export' as a special word. And I searched docs.python.org for 'export' and came up with nothing helpful. What is export? How am I using it incorrectly?
I next tried passing the client_id, client_secret, and redirect_uri as arguments in the util.prompt_for_user_token method like so:
util.prompt_for_user_token(username,scope,client_id='4f...6',client_secret='xxx...123',redirect_uri='https://play.spotify.com/collection/songs')
When I tried that, this is what happens in terminal:
User authentication requires interaction with your
web browser. Once you enter your credentials and
give authorization, you will be redirected to
a url. Paste that url you were directed to to
complete the authorization.
Opening https://accounts.spotify.com/authorize?scope=user-library-modify&redirect_uri=https%3A%2F%2Fplay.spotify.com%2Fcollection%2Fsongs&response_type=code&client_id=4f...6 in your browser
Enter the URL you were redirected to:
I entered https://play.spotify.com/collection/songs and then got this traceback:
Traceback (most recent call last):
File "add-track.py", line 21, in <module>
token = util.prompt_for_user_token(username, scope, client_id='4f...6', client_secret='xxx...123', redirect_uri='https://play.spotify.com/collection/songs')
File "/Library/Python/2.7/site-packages/spotipy/util.py", line 86, in prompt_for_user_token
token_info = sp_oauth.get_access_token(code)
File "/Library/Python/2.7/site-packages/spotipy/oauth2.py", line 210, in get_access_token
raise SpotifyOauthError(response.reason)
spotipy.oauth2.SpotifyOauthError: Bad Request
It seems like I am missing something, perhaps another part of Spotipy needs to be imported, or some other python module. It seems I am missing the piece that sets client credentials. How do I do that? I am fairly new at this (if that wasn't obvious). Please help.
UPDATE: I changed redirect_uri to localhost:8888/callback. That causes a Firefox tab to open with an error -- "unable to connect to server." (Since I do not have a server running. I thought about installing node.js as in the Spotify Web API tutorial, but I have not yet). The python script then asks me to copy and paste the URL I was redirected to. Even though FF could not open a page, I got this to work by copying the entire URL including the "code=BG..." that follows localhost:8888/callback? I am not sure this is an ideal setup, but at least it works.
Does it matter if I set up node.js or not?
The process you've followed (including your update) is exactly as the example intends and you are not missing anything! Obviously, it is a fairly simple tutorial, but it sets you up with a token and you should be able to get the information you need.
For the credentials, you can set these directly in your Terminal by running each of the export commands. Read more about EXPORT here: https://www.cyberciti.biz/faq/linux-unix-shell-export-command/

Google Python Admin SDK using Oauth2 for a Service Account (Education Edition)-"oauth2client.client.AccessTokenRefreshError: access_denied" exception

I have been trying to get the Service Account authentication working for the Google Admin SDK for a few days now to no avail. I am using the google-api-python-client-1.2 library freshly installed from Google.
I have been following Google's documentation on the topic. Links are here:
htps://developers.google.com/accounts/docs/OAuth2ServiceAccount
htps://developers.google.com/api-client-library/python/guide/aaa_oauth
htp://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.client.SignedJwtAssertionCredentials-class.html
And have the tasks.py Service Account example working which you can be found here:
htp://code.google.com/p/google-api-python-client/source/browse/samples/service_account/tasks.py?r=c21573904a2df1334d13b4380f63463c94c8d0e8
And have been closely studying these two Stack Overflow threads on a related topic here:
google admin sdk directory api 403 python
Google Admin API using Oauth2 for a Service Account (Education Edition) - 403 Error
And have studied the relevant code in gam.py (Dito GAM).
Yet I'm still missing something as I am getting an 'oauth2client.client.AccessTokenRefreshError: access_denied' exception in nearly every test case I write.
Here is a concise example of a test authentication:
import httplib2
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
f = file('myKey.p12', 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(
'myServiceAdmin#developer.gserviceaccount.com',
key,
sub='myAdminUser#my.googleDomain.edu',
scope = ['https://www.googleapis.com/auth/admin.directory.user',])
http = httplib2.Http()
http = credentials.authorize(http)
service = build('admin', 'directory_v1', http=http)
When I run the above code I get this stack dump and exception:
Traceback (most recent call last):
File "./test.py", line 17, in <module>
service = build('admin', 'directory_v1', http=http)
File "/usr/lib/python2.7/dist-packages/oauth2client/util.py", line 132, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/apiclient/discovery.py", line 192, in build resp, content = http.request(requested_url)
File "/usr/lib/python2.7/dist-packages/oauth2client/util.py", line 132, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/oauth2client/client.py", line 475, in new_request
self._refresh(request_orig)
File "/usr/lib/python2.7/dist-packages/oauth2client/client.py", line 653, in _refresh
self._do_refresh_request(http_request)
File "/usr/lib/python2.7/dist-packages/oauth2client/client.py", line 710, in _do_refresh_request
raise AccessTokenRefreshError(error_msg)
oauth2client.client.AccessTokenRefreshError: access_denied
I've tried multiple super user accounts, service accounts, and keys and always end up with the same exception. If I add sub to the tasks.py example I end up with the same error. Replacing sub with prn also generates this exception and adding private_key_password='notasecret' does nothing (it is the default). The Admin SDK is activated in the Google Developers Console and the target accounts have super user privileges. This makes me think something is missing on the Google domain side but I cannot think of anything else to check.
Any one have an idea what I am doing wrong?
Have you granted the third party client access in your Admin Console for your service account?
My to go instruction when it comes to setting up Service Account is the instruction Google has for Drive Api.
https://developers.google.com/drive/web/delegation
Take a look at the "Delegate domain-wide authority to your service account" part and see if you have completed those steps.
Maybe not OP's problem, but I had this same error and my issue was that I was setting the sub field in the credentials object
credentials = SignedJwtAssertionCredentials(SERVICE_ACCOUNT_EMAIL, key,
scope=SCOPES, sub=**<DON'T SET ME>**)
If you're using domain-wide delegation, you need to not set a sub (because your "user" is the domain administrator.) The docs are a bit confusing on this point. I just removed the sub field and it worked for me.