Question

I am building an app on Google App Engine using Flask. I am implementing Google+ login from the server-side flow described in the Python examples: https://developers.google.com/+/web/signin/server-side-flow and https://github.com/googleplus/gplus-quickstart-python/blob/master/signin.py.

Both of the examples have:

credentials = oauth_flow.step2_exchange(code)

and

session['credentials'] = credentials

storing the credentials object to the Flask session. When I run this code on my Google App Engine project, I get the error:

TypeError: <oauth2client.client.OAuth2Credentials object at 0x7f6c3c953610> is not JSON serializable

As discussed in this issue (marked WontFix), the OAuth2Credentials is not designed to by JSON serializable. It has methods to_json and from_json, which could be used to store it, e.g:

session['credentials'] = credentials.to_json()

However, in the same issue:

Never store a Credentials object in a cookie, it contains the applications
client id and client secret.

Perhaps I misunderstand how a Flask session object works, but from the doc:

... A session basically makes it possible to remember information from one request to another. The way Flask does this is by using a signed cookie. So the user can look at the session contents, but not modify it unless they know the secret key...

And therefore, we should not be storing a credentials object in the session, even if it is a signed cookie.

In my case, I currently only need to re-use the access token for disconnect purposes, so I can just store that.

What is the correct way to deal with this situation? Should the credentials not be stored in the session at all? Should at this point in the examples there be a comment "Securely save credentials here"?

Was it helpful?

Solution

Flask used to use pickle instead of JSON to store values in the session, and the Google example code was written with that in mind. Flask switched to a JSON-based format to reduce the impact of the server-side secret being disclosed (a hacker can hijack your process with pickle, not with JSON).

Store just the access token in your session:

session['credentials'] = credentials.access_token

You can recreate the credentials object with that token, using the AccessTokenCredentials class at a later time, should you need it again:

credentials = AccessTokenCredentials(session['credentials'], 'user-agent-value')

The AccessTokenCredentials object stores just the credentials; because it lacks the client id and client secret it cannot be used to refresh the token, however.

The user agent value is something you get to make up; it can help diagnose problems if you have access to the OAuth server logs; with Google I would not count on that so just make something up here.

OTHER TIPS

"Flask by default uses the Werkzeug provided 'secure cookie' as session system. It works by pickling the session data, compressing it and base64 encoding it." - http://flask.pocoo.org/snippets/51/

In other words, flask is really weird. Anything you put in the session, it gets ciphered with the server key, sent to the client and stored in the client. The server then receives it on each subsequent request and decodes it with the same key. It also means session data will survive server reboots because it's sitting in the client.

To improve this for my app I've used the flask SessionInterface with Couchdb - and now the client only knows a sessionID that is checked against my database where the actual data is stored. Hurray.

Check this out, it has a few approaches to server side sessions depending what db you may be using - http://flask.pocoo.org/snippets/category/sessions/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top