From 76c711a58b84c86679deb443f3735b9db658430f Mon Sep 17 00:00:00 2001 From: raphael Date: Thu, 2 Apr 2020 12:22:59 +0000 Subject: [PATCH] added second test login, moved into seperate folder --- login/.app.py.swp | Bin 0 -> 24576 bytes login/app.py | 274 ++++++++++++++++++ .../client_secret.json | 0 login/db.py | 38 +++ login/schema.sql | 6 + login/sqlite_db | Bin 0 -> 16384 bytes login/user.py | 34 +++ test1/client_secret.json | 1 + index.html => test1/index.html | 0 index.js => test1/index.js | 0 server.py => test1/server.py | 38 ++- website.py => test1/website.py | 0 12 files changed, 384 insertions(+), 7 deletions(-) create mode 100644 login/.app.py.swp create mode 100644 login/app.py rename client_secret.json => login/client_secret.json (100%) create mode 100644 login/db.py create mode 100644 login/schema.sql create mode 100644 login/sqlite_db create mode 100644 login/user.py create mode 100644 test1/client_secret.json rename index.html => test1/index.html (100%) rename index.js => test1/index.js (100%) rename server.py => test1/server.py (84%) rename website.py => test1/website.py (100%) diff --git a/login/.app.py.swp b/login/.app.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..28cf93ce7c0616619a11a9721a13e0787e53788d GIT binary patch literal 24576 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 literal 0 HcmV?d00001 diff --git a/login/app.py b/login/app.py new file mode 100644 index 0000000..30f6928 --- /dev/null +++ b/login/app.py @@ -0,0 +1,274 @@ +# 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 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" +) + +API_SERVICE_NAME = 'calendar' +API_VERSION = 'v3' + +# 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']) + + service = googleapiclient.discovery.build( + API_SERVICE_NAME, API_VERSION, credentials=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("{}") + +@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(): + print("in 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() + print(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_=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) + 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) + + + diff --git a/client_secret.json b/login/client_secret.json similarity index 100% rename from client_secret.json rename to login/client_secret.json diff --git a/login/db.py b/login/db.py new file mode 100644 index 0000000..60e4af5 --- /dev/null +++ b/login/db.py @@ -0,0 +1,38 @@ +# http://flask.pocoo.org/docs/1.0/tutorial/database/ +import sqlite3 + +import click +from flask import current_app, g +from flask.cli import with_appcontext + +def get_db(): + if "db" not in g: + g.db = sqlite3.connect( + "sqlite_db", detect_types=sqlite3.PARSE_DECLTYPES + ) + g.db.row_factory = sqlite3.Row + + return g.db + +def close_db(e=None): + db = g.pop("db", None) + + if db is not None: + db.close() + +def init_db(): + db = get_db() + + with current_app.open_resource("schema.sql") as f: + db.executescript(f.read().decode("utf8")) + +@click.command("init-db") +@with_appcontext +def init_db_command(): + """Clear the existing data and create new tables.""" + init_db() + click.echo("Initialized the database.") + +def init_app(app): + app.teardown_appcontext(close_db) + app.cli.add_command(init_db_command) diff --git a/login/schema.sql b/login/schema.sql new file mode 100644 index 0000000..5c71573 --- /dev/null +++ b/login/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE user ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + email TEXT UNIQUE NOT NULL, + profile_pic TEXT NOT NULL +); diff --git a/login/sqlite_db b/login/sqlite_db new file mode 100644 index 0000000000000000000000000000000000000000..42d1c9401ef2711a33ba22fe94b97319ab7f996c GIT binary patch literal 16384 zcmeI&&rZ}p90%}eiwGoGAwe{nI5#wr?RML3K`#`nSaw-tw+PE+Q`y;VvH!N)ML4bx z-QMte-V5oBVozwszT7AccGHr^T;C5NC-vD}EB+UA7f*2tWV=5P$##AOHafKmY;| zfWW^L7|w9l=jZ0Qx7SkVmCpu!mgJpZtDA;xQrlRqnlz8l2UC=KZEBl)HhodIHjR3d zR?Oz3EF3xk8!gnfY+Bo?R`Vze9M2zxcWT!5j!Dn2#!1xieAbG+gP$Lrdbl8r&p(;t zh!?imalhwhC40*mq|x{LR-R0&l#gQz{9iZ9`Nq7qpU73Gv(Df6K*U-$pg{lv5P$## zAOHafKmY;|fB*!puE21DAJ^~7!?Ik|mvvcJ%gd^&X-l$NEb8UD6T1%clS1II&}S>% zyaq2EM1h;8asRO-`L0svMp1WE>K{a5%EC0?AUTVYvE|C@x?7R$6RW-cytmcfZ}wW% z{gcpc?=@BJu$CrA#`a)!x3jxvSwo2%*XPY)KT9P`MwXS5rk8YWS<@6nX{79k1zzX{ zj^EGcZ=$T8PcBMHy(Fc@W4H8jbMxtX)UzbxM>{_^B%|`ISPs_)`ccD8 zLFX again, you should go back to the auth flow.' + '') +def getCalendars(service): + page_token = None + calendars = [] + while True: + calendar_list = service.calendarList().list(pageToken=page_token).execute() + for calendar in calendar_list['items']: + calendars.append(Calendar(calendar['summary'], calendar['id'], calendar['colorId'])) + page_token = calendar_list.get('nextPageToken') + if not page_token: + break + + print(calendars) + if __name__ == '__main__': # When running locally, disable OAuthlib's HTTPs verification. diff --git a/website.py b/test1/website.py similarity index 100% rename from website.py rename to test1/website.py