import os from flask import Flask, render_template, request, redirect, abort, url_for, session import requests from datetime import datetime def get_env_variable(var_name): value = os.environ.get(var_name) if not value: raise ValueError(f"Missing required environment variable: {var_name}") return value app = Flask(__name__) # Read environment variables outside the route function client_id = get_env_variable('CLIENT_ID') redirect_uri = get_env_variable('REDIRECT_URI') optional_scopes = get_env_variable('OPTIONAL_SCOPES') database_url = get_env_variable('DATABASE_URL') csrf_protection_string = None @app.route('/') def home(): if is_logged_in(): resp_json = requests.get(f'{database_url}/token/latest').json() access_token = resp_json['token']['access_token'] # set session token id session['token_id'] = resp_json['token']['id'] user_info = requests.get('https://api.github.com/user', headers={ 'Authorization': f'Bearer {access_token}' }).json() return render_template('success.html', user_info=user_info) # Generate a CSRF protection string global csrf_protection_string csrf_protection_string = os.urandom(16).hex() # Pass dynamic variables to the template return render_template('index.html', client_id=client_id, redirect_uri=redirect_uri, optional_scopes=optional_scopes, csrf_protection_string=csrf_protection_string) @app.route('/oauth-redirect') def oauth_redirect(): auth_code = request.args.get('code') csrf_token = request.args.get('state') # Verify the CSRF protection string if csrf_token != csrf_protection_string: abort(400, 'Invalid CSRF token. Please try again.') # Exchange authorization code for access and refresh tokens # response = requests.post( # 'https://www.inoreader.com/oauth2/token', # headers={ # 'Content-Type': 'application/x-www-form-urlencoded', # }, # data={ # 'code': auth_code, # 'redirect_uri': get_env_variable('REDIRECT_URI'), # 'client_id': get_env_variable('CLIENT_ID'), # 'client_secret': get_env_variable('CLIENT_SECRET'), # 'scope': '', # 'grant_type': 'authorization_code' # } # ) # TEST: Github OAuth - REMOVE response = requests.post( 'https://github.com/login/oauth/access_token', headers={ 'Accept': 'application/json' }, data={ 'code': auth_code, 'redirect_uri': get_env_variable('REDIRECT_URI'), 'client_id': get_env_variable('CLIENT_ID'), 'client_secret': get_env_variable('CLIENT_SECRET') } ) response.raise_for_status() tokens = response.json() # TEST: Github OAuth - REMOVE tokens['refresh_token'] = 'N/A' tokens['expires_in'] = 36000 # Save tokens for later use save_tokens(tokens['access_token'], tokens['refresh_token'], tokens['expires_in']) return redirect(url_for('home')) # logout @app.route('/logout') def logout(): token_id = session.get('token_id') if not token_id: return redirect(url_for('home')) # remove token_id from session session.pop('token_id', None) response = requests.put(f'{database_url}/token/{token_id}', headers={ 'Content-Type': 'application/json' }, json={ 'is_logged_in': False }) response.raise_for_status() return redirect(url_for('home')) def is_logged_in(): response = requests.get(f'{database_url}/token/latest') response.raise_for_status() if response.status_code == 204: return False elif response.status_code == 200: resp_json = response.json() return resp_json['token']['is_logged_in'] or False return False def save_tokens(access_token, refresh_token, expiration_seconds): response = requests.post( f'{database_url}/token', headers={ 'Content-Type': 'application/json' }, json={ 'access_token': access_token, 'refresh_token': refresh_token, 'expiration_seconds': expiration_seconds } ) response.raise_for_status() if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=5000)