-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Add web sample for using google-auth for end-user auth #1127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# Copyright 2017 Google Inc. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""An example web application that obtains authorization and credentials from | ||
an end user. | ||
|
||
This sample is used on | ||
https://developers.google.com/identity/protocols/OAuth2WebServer. Please | ||
refer to that page for instructions on using this sample. | ||
|
||
Notably, you'll need to obtain a OAuth2.0 client secrets file and set the | ||
``GOOGLE_CLIENT_SECRETS`` environment variable to point to that file. | ||
""" | ||
|
||
import os | ||
|
||
import flask | ||
import google.oauth2.credentials | ||
import google_auth_oauthlib.flow | ||
import googleapiclient.discovery | ||
|
||
# The path to the client-secrets.json file obtained from the Google API | ||
# Console. You must set this before running this application. | ||
CLIENT_SECRETS_FILENAME = os.environ['GOOGLE_CLIENT_SECRETS'] | ||
# The OAuth 2.0 scopes that this application will ask the user for. In this | ||
# case the application will ask for basic profile information. | ||
SCOPES = ['email', 'profile'] | ||
|
||
app = flask.Flask(__name__) | ||
# TODO: A secret key is included in the sample so that it works but if you | ||
# use this code in your application please replace this with a truly secret | ||
# key. See http://flask.pocoo.org/docs/0.12/quickstart/#sessions. | ||
app.secret_key = 'TODO: replace with a secret value' | ||
|
||
|
||
@app.route('/') | ||
def index(): | ||
if 'credentials' not in flask.session: | ||
return flask.redirect('authorize') | ||
|
||
# Load the credentials from the session. | ||
credentials = google.oauth2.credentials.Credentials( | ||
**flask.session['credentials']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way to check credential validity, either here or after issuing the request if it failed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
# Get the basic user info from the Google OAuth2.0 API. | ||
client = googleapiclient.discovery.build( | ||
'oauth2', 'v2', credentials=credentials) | ||
|
||
response = client.userinfo().v2().me().get().execute() | ||
|
||
return str(response) | ||
|
||
|
||
@app.route('/authorize') | ||
def authorize(): | ||
# Create a flow instance to manage the OAuth 2.0 Authorization Grant Flow | ||
# steps. | ||
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( | ||
CLIENT_SECRETS_FILENAME, scopes=SCOPES) | ||
flow.redirect_uri = flask.url_for('oauth2callback', _external=True) | ||
authorization_url, state = flow.authorization_url( | ||
# This parameter enables offline access which gives your application | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: ", which" |
||
# an access token and a refresh token for the user's credentials. | ||
access_type='offline', | ||
# This parameter enables incremental auth. | ||
include_granted_scopes='true') | ||
|
||
# Store the state in the session so that the callback can verify the | ||
# authorization server response. | ||
flask.session['state'] = state | ||
|
||
return flask.redirect(authorization_url) | ||
|
||
|
||
@app.route('/oauth2callback') | ||
def oauth2callback(): | ||
# Specify the state when creating the flow in the callback so that it can | ||
# verify the authorization server response. | ||
state = flask.session['state'] | ||
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( | ||
CLIENT_SECRETS_FILENAME, scopes=SCOPES, state=state) | ||
flow.redirect_uri = flask.url_for('oauth2callback', _external=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Has to be set for oauthlib to verify the response - oauthlib verifies state, http(s), and makes sure the url base is correct. |
||
|
||
# Use the authorization server's response to fetch the OAuth 2.0 tokens. | ||
authorization_response = flask.request.url | ||
flow.fetch_token(authorization_response=authorization_response) | ||
|
||
# Store the credentials in the session. | ||
credentials = flow.credentials | ||
flask.session['credentials'] = { | ||
'token': credentials.token, | ||
'refresh_token': credentials.refresh_token, | ||
'token_uri': credentials.token_uri, | ||
'client_id': credentials.client_id, | ||
'client_secret': credentials.client_secret, | ||
'scopes': credentials.scopes | ||
} | ||
|
||
return flask.redirect(flask.url_for('index')) | ||
|
||
|
||
if __name__ == '__main__': | ||
# When running locally with Flask's development server this disables | ||
# OAuthlib's HTTPs verification. When running in production with a WSGI | ||
# server such as gunicorn this option will not be set and your application | ||
# *must* use HTTPS. | ||
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' | ||
app.run('localhost', 8080, debug=True) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Copyright 2017 Google Inc. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import pytest | ||
|
||
import main | ||
|
||
# Note: samples that do end-user auth are difficult to test in an automated | ||
# way. These tests are basic sanity checks. | ||
|
||
|
||
@pytest.fixture | ||
def client(): | ||
main.app.testing = True | ||
return main.app.test_client() | ||
|
||
|
||
def test_index_wo_credentials(client): | ||
r = client.get('/') | ||
assert r.status_code == 302 | ||
assert r.headers['location'].endswith('/authorize') | ||
|
||
|
||
def test_authorize(client): | ||
r = client.get('/authorize') | ||
assert r.status_code == 302 | ||
assert r.headers['location'].startswith('https://accounts.google.com') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
google-auth==1.1.0 | ||
google-auth-oauthlib==0.1.1 | ||
google-auth-httplib2==0.0.2 | ||
google-api-python-client==1.6.3 | ||
flask==0.12.2 | ||
requests==2.18.4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: ", please"