Reputation: 6346
I try a simple HelloWorld with gdata, appengine and OAuth2. I read this post and the official post from Google.
Problem 1
According to the first post post, my app fails at the part 7 "Use the code to get an access token" :
Traceback (most recent call last):
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/webapp/_webapp25.py", line 701, in __call__
handler.get(*groups)
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/webapp/util.py", line 68, in check_login
handler_method(self, *args)
File "[$PATH]/dev/projets/xxx/main.py", line 76, in get
token.get_access_token(url.query)
File "[$PATH]/dev/projets/xxx/gdata/gauth.py", line 1296, in get_access_token
'redirect_uri': self.redirect_uri,
AttributeError: 'OAuth2Token' object has no attribute 'redirect_uri'
I provide the redirect_uri in the method generate_authorize_url() and i filled 2 "Redirect URIs" on Google APIs console :
Why the redirect_uri is loosed ?
Solution : See @bossylobster answer.
Problem 2 Now, i want to save this new access token like this :
access_token_key = 'access_token_%s' % current_user.user_id()
gdata.gauth.ae_save(token, access_token_key)
Theses lines throw this exception :
Traceback (most recent call last):
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/webapp/_webapp25.py", line 701, in __call__
handler.get(*groups)
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/webapp/util.py", line 68, in check_login
handler_method(self, *args)
File "[$PATH]/dev/projets/xxx/main.py", line 89, in get
gdata.gauth.ae_save(token, access_token_key)
File "[$PATH]/dev/projets/xxx/gdata/gauth.py", line 1593, in ae_save
return gdata.alt.app_engine.set_token(key_name, token_to_blob(token))
File "[$PATH]/dev/projets/xxx/gdata/alt/app_engine.py", line 92, in set_token
if Token(key_name=unique_key, t=token_str).put():
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/db/__init__.py", line 973, in __init__
prop.__set__(self, value)
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/db/__init__.py", line 613, in __set__
value = self.validate(value)
File "[$PATH]/dev/outils/google_appengine/google/appengine/ext/db/__init__.py", line 2779, in validate
(self.name, self.data_type.__name__, err))
BadValueError: Property t must be convertible to a Blob instance (Blob() argument should be str instance, not unicode)
But gdata.gauth.access_token
calls gdata.gauth.upgrade_to_access_token
which return the token with some modification.
If i try with token_to_blob
, i have this exception UnsupportedTokenType: Unable to serialize token of type <type 'unicode'>
** How save the new access Token ?**
main.py :
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app, login_required
from google.appengine.api import users
import gdata.gauth
import atom.http_core
SETTINGS = {
'APP_NAME': 'xxx',
'CLIENT_ID':'xxx.apps.googleusercontent.com',
'CLIENT_SECRET':'xxx',
'SCOPES': ['https://www.google.com/m8/feeds/', 'https://docs.google.com/feeds/', 'https://www.google.com/calendar/feeds/'],
'USER_AGENT' : 'xxxs',
'OAUTH2CALLBACK':'http://localhost:8080/oauth2callback'
#'OAUTH2CALLBACK':'http://example.com/oauth2callback'
}
class Home(webapp.RequestHandler):
def get(self):
"""Home"""
if users.get_current_user():
self.redirect("/step1")
else:
self.response.out.write("<a href='/step1'>Sign in google</a><br />")
class Fetcher(webapp.RequestHandler):
@login_required
def get(self):
"""This handler is responsible for fetching an initial OAuth
request token and redirecting the user to the approval page."""
current_user = users.get_current_user()
#create token
token = gdata.gauth.OAuth2Token(client_id = SETTINGS['CLIENT_ID'],
client_secret = SETTINGS['CLIENT_SECRET'],
scope = ' '.join(SETTINGS['SCOPES']),
user_agent = SETTINGS['USER_AGENT'])
url = token.generate_authorize_url(redirect_uri = SETTINGS['OAUTH2CALLBACK'])
#save token to datastore
gdata.gauth.ae_save(token, current_user.user_id())
message = """<a href="%s">
Request token for the Google Documents Scope</a>"""
self.response.out.write(message % url)
self.response.out.write(" ; redirect uri : %s" % token.redirect_uri)
class RequestTokenCallback(webapp.RequestHandler):
@login_required
def get(self):
"""When the user grants access, they are redirected back to this
handler where their authorized request token is exchanged for a
long-lived access token."""
current_user = users.get_current_user()
#get token from callback uri
url = atom.http_core.Uri.parse_uri(self.request.uri)
# get token from datastore
token = gdata.gauth.ae_load(current_user.user_id())
# SOLUTION 1
token.redirect_uri = SETTINGS['OAUTH2CALLBACK']
if isinstance(token, gdata.gauth.OAuth2Token):
if 'error' in url.query:
pass
else:
token.get_access_token(url.query)
gdata.gauth.ae_save(gdata.gauth.token_to_blob(token), "access_token_" + current_user.user_id())
def main():
application = webapp.WSGIApplication([('/', Home),
('/step1', Fetcher),
('/oauth2callback', RequestTokenCallback)],
debug = True)
run_wsgi_app(application)
if __name__ == '__main__':
main()
app.yaml :
application: xxx
version: 2
runtime: python
api_version: 1
handlers:
- url: .*
script: main.py
Upvotes: 0
Views: 2197
Reputation: 10163
When you call AeLoad
, you need to look at AeSave
. You'll notice in the source code that token_to_blob
is called.
However, in the source for token_to_blob
, the redirect_uri
is not saved to the blob, so you'll need to keep it around and call:
token = gdata.gauth.ae_load(current_user.user_id())
token.redirect_uri = SETTINGS['OAUTH2CALLBACK']
For reference, see another related post.
Answer to Question 2: Read the traceback: Blob()
argument should be str
instance, not unicode
. Which version of Python
are you using locally?
Upvotes: 0