|
#!/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() |