2025-05-11 21:14:27 +10:00

205 lines
8.2 KiB
Python

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')