Skip to content

Instantly share code, notes, and snippets.

@mattrasband
Last active June 12, 2017 00:45
Show Gist options
  • Save mattrasband/93571aebc0e50a12b5532bd5c6cca591 to your computer and use it in GitHub Desktop.
Save mattrasband/93571aebc0e50a12b5532bd5c6cca591 to your computer and use it in GitHub Desktop.
OAuth2 Client Flow Samples: Authorization Code
#!/usr/bin/env python3
from urllib.parse import urlencode
import requests
from flask import abort, current_app, Flask, redirect, render_template_string, request, url_for
app = Flask(__name__)
app.config.update(
client_id='',
client_secret='',
authorization_uri='https://accounts.google.com/o/oauth2/v2/auth',
token_uri='https://www.googleapis.com/oauth2/v4/token',
scopes='email profile',
)
@app.route('/')
def index():
return render_template_string("""
<html>
<head><title>Index</title></head>
<body>
{% if user is none %}
<a href="{{ url_for('login_google') }}">Login</a>
{% else %}
<h3>Hello {{ user.email }}</h3>
{% endif %}
</body>
</html>
""", user=None)
@app.route('/login/google')
def login_google():
# Simply need to redirect to the oauth2 provider with
# the required minimum info, as a query string. Some
# providers will add some extra attributes on, but this
# is the base set:
opts = dict(
client_id=current_app.config['client_id'],
# Note: this url must be registered with the oauth2 provider
redirect_uri=url_for('.login_google_callback', external_=True),
# always "code" for the authorization code flow
response_type='code',
# valid attributes for this is up to the provider, this simply
# allows the user to limit what access a client may have.
scope=current_app.config['scopes'],
# You can use this to keep state for your app after the oauth2 flow
state='',
)
# The options are a query string, on redirect the user will authenticate
# with the remote provider directly.
return redirect(current_app.config['authorization_uri'] + '?' + urlencode(opts))
@app.route('/login/google/callback')
def login_google_callback():
# In most cases in which there is an error, you will get an `error` query
# argument back and needs to be handled.
if request.args.get('error'):
abort(401)
# Otherwise you get a short lived code, which needs to then be exchanged
# remotely for the actual access token.
r = requests.post(current_app.config['token_uri'], data=dict(
code=request.args.get('code'),
client_id=current_app.config['client_id'],
client_secret=current_app.config['client_secret'],
redirect_url=url_for('.login_google_callback', external_=True),
grant_type='authorization_code',
))
r.raise_for_status()
# Per the spec, you will get at least: access_token, expires_in, and token_type
# in the response. In some cases you can also get a refresh_token, which can be
# used to update the access_token, which will expire.
body = r.json()
# You can now use this token to access something for the user:
r = requests.get('https://www.googleapis.com/plus/v1/people/userId', headers={
'Authorization': 'Bearer ' + body['access_token'],
})
r.raise_for_status()
print('Google User Details:', r.json())
return redirect(url_for('.index'))
if __name__ == '__main__':
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment