# Python standard libraries import json import os import sqlite3 # Third-party libraries import flask from flask import Flask, redirect, request, url_for from flask_login import ( LoginManager, current_user, login_required, login_user, logout_user, ) from oauthlib.oauth2 import WebApplicationClient import requests # Internal imports from db import init_db_command from user import User import caltojson import google.oauth2.credentials import google_auth_oauthlib.flow import googleapiclient.discovery # Configuration CLIENT_SECRETS_FILE = "client_secret.json" # This OAuth 2.0 access scope allows for full read/write access to the # authenticated user's account and requires requests to use an SSL connection. SCOPES = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/calendar.readonly", "openid"] API_SERVICE_NAME = 'calendar' API_VERSION = 'v3' GOOGLE_CLIENT_ID ="377787187748-shuvi4iq5bi4gdet6q3ioataimobs4lh.apps.googleusercontent.com" GOOGLE_CLIENT_SECRET = "Hu_YWmKsVKUcLwyeINYzdKfZ" GOOGLE_DISCOVERY_URL = ( "https://accounts.google.com/.well-known/openid-configuration" ) # Flask app setup app = Flask(__name__) app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24) # User session management setup # https://flask-login.readthedocs.io/en/latest login_manager = LoginManager() login_manager.init_app(app) # Naive database setup try: init_db_command() except sqlite3.OperationalError: # Assume it's already been created pass # OAuth 2 client setup client = WebApplicationClient(GOOGLE_CLIENT_ID) # Flask-Login helper to retrieve a user from our db @login_manager.user_loader def load_user(user_id): return User.get(user_id) @app.route("/") def index(): if current_user.is_authenticated: return ( "

Hello, {}! You're logged in! Email: {}

" "

Google Profile Picture:

" 'Google profile pic
' 'Logout' 'test API'.format( current_user.name, current_user.email, current_user.profile_pic ) ) else: return 'Google Login' def get_google_provider_cfg(): return requests.get(GOOGLE_DISCOVERY_URL).json() @app.route('/test') def test_api_request(): if 'credentials' not in flask.session: return flask.redirect('login') # Load credentials from the session. credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) todaysCal = caltojson.generateJsonFromCalendarEntries(credentials) # Save credentials back to session in case access token was refreshed. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(todaysCal) @app.route("/login") def login(): ''' # Find out what URL to hit for Google login google_provider_cfg = get_google_provider_cfg() authorization_endpoint = google_provider_cfg["authorization_endpoint"] # Use library to construct the request for Google login and provide # scopes that let you retrieve user's profile from Google request_uri = client.prepare_request_uri( authorization_endpoint, redirect_uri=request.base_url + "/callback", scope=["openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly"], ) return redirect(request_uri) ''' # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = request.base_url + "/callback" authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state print("auth_url: " + authorization_url) print("state: " + state) return flask.redirect(authorization_url) @app.route("/login/callback") def callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = request.base_url # 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 credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials flask.session['credentials'] = credentials_to_dict(credentials) session = flow.authorized_session() userinfo = session.get('https://www.googleapis.com/userinfo/v2/me').json() # Create a user in your db with the information provided # by Google user = User( id_=userinfo['id'], name=userinfo['name'], email=userinfo['email'], profile_pic=userinfo['picture'] ) # Doesn't exist? Add it to the database. if not User.get(user.id): User.create(user.id, user.name, user.email, user.profile_pic) # Begin user session by logging the user in login_user(user) return flask.redirect(flask.url_for('index')) ''' # Get authorization code Google sent back to you code = request.args.get("code") # Find out what URL to hit to get tokens that allow you to ask for # things on behalf of a user google_provider_cfg = get_google_provider_cfg() token_endpoint = google_provider_cfg["token_endpoint"] # Prepare and send a request to get tokens! Yay tokens! token_url, headers, body = client.prepare_token_request( token_endpoint, authorization_response=request.url, redirect_url=request.base_url, code=code ) print("asking for tokens") token_response = requests.post( token_url, headers=headers, data=body, auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET), ) # Parse the tokens! client.parse_request_body_response(json.dumps(token_response.json())) # Now that you have tokens (yay) let's find and hit the URL # from Google that gives you the user's profile information, # including their Google profile image and email userinfo_endpoint = google_provider_cfg["userinfo_endpoint"] uri, headers, body = client.add_token(userinfo_endpoint) userinfo_response = requests.get(uri, headers=headers, data=body) print(userinfo_response.json()) # You want to make sure their email is verified. # The user authenticated with Google, authorized your # app, and now you've verified their email through Google! if userinfo_response.json().get("email_verified"): unique_id = userinfo_response.json()["sub"] users_email = userinfo_response.json()["email"] picture = userinfo_response.json()["picture"] users_name = userinfo_response.json()["given_name"] else: return "User email not available or not verified by Google.", 400 # Create a user in your db with the information provided # by Google user = User( id_=unique_id, name=users_name, email=users_email, profile_pic=picture ) # Doesn't exist? Add it to the database. if not User.get(unique_id): User.create(unique_id, users_name, users_email, picture) # Begin user session by logging the user in login_user(user) # Send user back to homepage return redirect(url_for("index")) ''' @app.route("/logout") @login_required def logout(): logout_user() return redirect(url_for("index")) def credentials_to_dict(credentials): return {'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} if __name__ == "__main__": app.run('192.168.68.103.xip.io', 1234, ssl_context="adhoc", debug=True)