Expose the full token response in OAuth2Client and OAuth2Decorator.
Reviewed in https://codereview.appspot.com/7301099/.
diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py
index fc148b8..a4738f8 100644
--- a/oauth2client/appengine.py
+++ b/oauth2client/appengine.py
@@ -26,6 +26,8 @@
import os
import pickle
import time
+import urllib
+import urlparse
from google.appengine.api import app_identity
from google.appengine.api import memcache
@@ -34,6 +36,7 @@
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import login_required
from google.appengine.ext.webapp.util import run_wsgi_app
+from apiclient import discovery
from oauth2client import GOOGLE_AUTH_URI
from oauth2client import GOOGLE_REVOKE_URI
from oauth2client import GOOGLE_TOKEN_URI
@@ -55,6 +58,11 @@
except ImportError:
ndb = None
+try:
+ from urlparse import parse_qsl
+except ImportError:
+ from cgi import parse_qsl
+
logger = logging.getLogger(__name__)
OAUTH2CLIENT_NAMESPACE = 'oauth2client#ns'
@@ -570,6 +578,7 @@
user_agent=None,
message=None,
callback_path='/oauth2callback',
+ token_response_param=None,
**kwargs):
"""Constructor for OAuth2Decorator
@@ -592,6 +601,10 @@
callback_path: string, The absolute path to use as the callback URI. Note
that this must match up with the URI given when registering the
application in the APIs Console.
+ token_response_param: string. If provided, the full JSON response
+ to the access token request will be encoded and included in this query
+ parameter in the callback URI. This is useful with providers (e.g.
+ wordpress.com) that include extra fields that the client may want.
**kwargs: dict, Keyword arguments are be passed along as kwargs to the
OAuth2WebServerFlow constructor.
"""
@@ -608,6 +621,7 @@
self._message = message
self._in_error = False
self._callback_path = callback_path
+ self._token_response_param = token_response_param
def _display_error_message(self, request_handler):
request_handler.response.out.write('<html><body>')
@@ -782,6 +796,12 @@
CredentialsModel, user.user_id(), 'credentials').put(credentials)
redirect_uri = _parse_state_value(str(self.request.get('state')),
user)
+
+ if decorator._token_response_param and credentials.token_response:
+ resp_json = simplejson.dumps(credentials.token_response)
+ redirect_uri = discovery._add_query_parameter(
+ redirect_uri, decorator._token_response_param, resp_json)
+
self.redirect(redirect_uri)
return OAuth2Handler
diff --git a/oauth2client/client.py b/oauth2client/client.py
index 918539a..4cc3cda 100644
--- a/oauth2client/client.py
+++ b/oauth2client/client.py
@@ -393,7 +393,7 @@
@util.positional(8)
def __init__(self, access_token, client_id, client_secret, refresh_token,
token_expiry, token_uri, user_agent, revoke_uri=None,
- id_token=None):
+ id_token=None, token_response=None):
"""Create an instance of OAuth2Credentials.
This constructor is not usually called by the user, instead
@@ -410,6 +410,9 @@
revoke_uri: string, URI for revoke endpoint. Defaults to None; a token
can't be revoked if this is None.
id_token: object, The identity of the resource owner.
+ token_response: dict, the decoded response to the token request. None
+ if a token hasn't been requested yet. Stored because some providers
+ (e.g. wordpress.com) include extra fields that clients may want.
Notes:
store: callable, A callable that when passed a Credential
@@ -427,6 +430,7 @@
self.user_agent = user_agent
self.revoke_uri = revoke_uri
self.id_token = id_token
+ self.token_response = token_response
# True if the credentials have been revoked or expired and can't be
# refreshed.
@@ -559,7 +563,8 @@
data['token_uri'],
data['user_agent'],
revoke_uri=data.get('revoke_uri', None),
- id_token=data.get('id_token', None))
+ id_token=data.get('id_token', None),
+ token_response=data.get('token_response', None))
retval.invalid = data['invalid']
return retval
@@ -678,6 +683,7 @@
if resp.status == 200:
# TODO(jcgregorio) Raise an error if loads fails?
d = simplejson.loads(content)
+ self.token_response = d
self.access_token = d['access_token']
self.refresh_token = d.get('refresh_token', self.refresh_token)
if 'expires_in' in d:
@@ -1292,7 +1298,8 @@
self.client_secret, refresh_token, token_expiry,
self.token_uri, self.user_agent,
revoke_uri=self.revoke_uri,
- id_token=d.get('id_token', None))
+ id_token=d.get('id_token', None),
+ token_response=d)
else:
logger.info('Failed to retrieve access token: %s' % content)
if 'error' in d: