import os from flask import Blueprint, render_template, redirect, url_for, flash, request, session from flask_login import login_user, logout_user, login_required, current_user from app import db, oauth from app.models import User from app.forms import LoginForm, RegisterForm, ChangePasswordForm from werkzeug.security import generate_password_hash auth_bp = Blueprint('auth', __name__) google = None github = None if os.getenv('GOOGLE_CLIENT_ID') and os.getenv('GOOGLE_CLIENT_SECRET') and \ os.getenv('GOOGLE_CLIENT_ID') != 'your-google-client-id' and \ os.getenv('GOOGLE_CLIENT_SECRET') != 'your-google-client-secret': google = oauth.remote_app( 'google', consumer_key=os.getenv('GOOGLE_CLIENT_ID'), consumer_secret=os.getenv('GOOGLE_CLIENT_SECRET'), request_token_params={'scope': 'email profile'}, base_url='https://www.googleapis.com/oauth2/v1/', request_token_url=None, access_token_method='POST', access_token_url='https://accounts.google.com/o/oauth2/token', authorize_url='https://accounts.google.com/o/oauth2/auth', ) if os.getenv('GITHUB_CLIENT_ID') and os.getenv('GITHUB_CLIENT_SECRET'): github = oauth.remote_app( 'github', consumer_key=os.getenv('GITHUB_CLIENT_ID'), consumer_secret=os.getenv('GITHUB_CLIENT_SECRET'), request_token_params={'scope': 'user:email'}, base_url='https://api.github.com/', request_token_url=None, access_token_method='POST', access_token_url='https://github.com/login/oauth/access_token', authorize_url='https://github.com/login/oauth/authorize', ) @auth_bp.route('/login', methods=['GET', 'POST']) def login(): if current_user.is_authenticated: if current_user.force_password_change: return redirect(url_for('auth.change_password')) return redirect(url_for('gallery.landing')) form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user and user.check_password(form.password.data): login_user(user) if user.force_password_change: flash('Please change your password before continuing.', 'warning') return redirect(url_for('auth.change_password')) flash('Logged in successfully!', 'success') return redirect(url_for('gallery.landing')) flash('Invalid email or password.', 'danger') return render_template('login.html', form=form) @auth_bp.route('/register', methods=['GET', 'POST']) def register(): if current_user.is_authenticated: if current_user.force_password_change: return redirect(url_for('auth.change_password')) return redirect(url_for('gallery.landing')) form = RegisterForm() if form.validate_on_submit(): if User.query.filter_by(email=form.email.data).first(): flash('Email already registered.', 'danger') return render_template('register_user.html', form=form) if User.query.filter_by(username=form.username.data).first(): flash('Username already taken.', 'danger') return render_template('register_user.html', form=form) user = User( username=form.username.data, email=form.email.data, is_admin=False, force_password_change=False ) user.set_password(form.password.data) db.session.add(user) db.session.commit() login_user(user) flash('Registration successful! You are now logged in.', 'success') return redirect(url_for('gallery.landing')) return render_template('register_user.html', form=form) @auth_bp.route('/login/google') def login_google(): if not google: flash('Google login is not configured.', 'danger') return redirect(url_for('auth.login')) return google.authorize(callback=url_for('auth.google_authorized', _external=True)) @auth_bp.route('/login/google/authorized') def google_authorized(): if not google: flash('Google login is not configured.', 'danger') return redirect(url_for('auth.login')) resp = google.authorized_response() if resp is None or resp.get('access_token') is None: flash('Google login failed.', 'danger') return redirect(url_for('auth.login')) session['google_token'] = (resp['access_token'], '') user_info = google.get('userinfo').data user = User.query.filter_by(oauth_provider='google', oauth_id=user_info['id']).first() if not user: email = user_info.get('email') if not email: flash('Google account has no verified email.', 'danger') return redirect(url_for('auth.login')) user = User( username=email.split('@')[0], email=email, oauth_provider='google', oauth_id=user_info['id'], is_admin=False, force_password_change=False ) db.session.add(user) db.session.commit() login_user(user) if user.force_password_change: flash('Please change your password before continuing.', 'warning') return redirect(url_for('auth.change_password')) flash('Logged in with Google!', 'success') return redirect(url_for('gallery.landing')) @auth_bp.route('/login/github') def login_github(): if not github: flash('GitHub login is not configured.', 'danger') return redirect(url_for('auth.login')) return github.authorize(callback=url_for('auth.github_authorized', _external=True)) @auth_bp.route('/login/github/authorized') def github_authorized(): if not github: flash('GitHub login is not configured.', 'danger') return redirect(url_for('auth.login')) resp = github.authorized_response() if resp is None or resp.get('access_token') is None: flash('GitHub login failed.', 'danger') return redirect(url_for('auth.login')) session['github_token'] = (resp['access_token'], '') user_info = github.get('user').data emails = github.get('user/emails').data primary_email = None for email in emails: if email.get('primary') and email.get('verified'): primary_email = email['email'] break if not primary_email: primary_email = f"{user_info['login']}@github.com" user = User.query.filter_by(oauth_provider='github', oauth_id=str(user_info['id'])).first() if not user: user = User( username=user_info['login'], email=primary_email, oauth_provider='github', oauth_id=str(user_info['id']), is_admin=False, force_password_change=False ) db.session.add(user) db.session.commit() login_user(user) if user.force_password_change: flash('Please change your password before continuing.', 'warning') return redirect(url_for('auth.change_password')) flash('Logged in with GitHub!', 'success') return redirect(url_for('gallery.landing')) @auth_bp.route('/change-password', methods=['GET', 'POST']) @login_required def change_password(): form = ChangePasswordForm() if form.validate_on_submit(): if not current_user.check_password(form.current_password.data): flash('Current password is incorrect.', 'danger') return render_template('change_password.html', form=form) current_user.set_password(form.new_password.data) current_user.force_password_change = False db.session.commit() flash('Password changed successfully!', 'success') return redirect(url_for('gallery.landing')) return render_template('change_password.html', form=form) @auth_bp.route('/logout') @login_required def logout(): logout_user() flash('Logged out successfully.', 'success') return redirect(url_for('gallery.landing')) if google: @google.tokengetter def get_google_oauth_token(): return session.get('google_token') if github: @github.tokengetter def get_github_oauth_token(): return session.get('github_token')