Dnishe/blueprints/auth.py
2026-06-08 21:56:14 +00:00

62 lines
2.7 KiB
Python

"""Authentication routes (register, login)."""
import secrets
import sqlite3
from flask import Blueprint, jsonify, make_response, request, current_app
from werkzeug.security import check_password_hash, generate_password_hash
from database import get_db
from decorators import rate_limit
auth_bp = Blueprint('auth', __name__, url_prefix='/api')
@auth_bp.route('/register', methods=['POST'])
@rate_limit(max_attempts=5, window=3600) # 5 attempts per hour
def register():
"""Register a new user with invite code."""
data = request.json
username = data.get('username')
password = data.get('password')
invite_code = data.get('invite_code')
if not username or not password or not invite_code:
return jsonify({'error': 'Username, password, and invite_code required'}), 400
db = get_db(current_app)
code_row = db.execute('SELECT id FROM registration_codes WHERE code = ? AND is_used = 0', (invite_code,)).fetchone()
if not code_row:
return jsonify({'error': 'Invalid or already used invite code'}), 400
hashed_password = generate_password_hash(password)
token = secrets.token_hex(32)
try:
cur = db.execute('INSERT INTO users (username, password, token, avatar_url, description, pronouns) VALUES (?, ?, ?, ?, ?, ?)',
(username, hashed_password, token, '', '', ''))
db.execute('UPDATE registration_codes SET is_used = 1 WHERE code = ?', (invite_code,))
db.commit()
resp = make_response(jsonify({'token': token, 'user_id': cur.lastrowid, 'username': username}), 201)
resp.set_cookie('auth_token', token, httponly=True, samesite='Lax', max_age=86400)
return resp
except sqlite3.IntegrityError:
db.rollback()
return jsonify({'error': 'Username already exists'}), 409
@auth_bp.route('/login', methods=['POST'])
@rate_limit(max_attempts=10, window=3600) # 10 attempts per hour
def login():
"""Log in a user."""
data = request.json
username = data.get('username')
password = data.get('password')
if not username or not password:
return jsonify({'error': 'Username and password required'}), 400
user = get_db(current_app).execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()
if not user or not check_password_hash(user['password'], password):
return jsonify({'error': 'Invalid credentials'}), 401
if user['is_banned'] == 1:
return jsonify({'error': 'Account banned'}), 403
resp = make_response(jsonify({'token': user['token'], 'user_id': user['id'], 'username': user['username']}), 200)
resp.set_cookie('auth_token', user['token'], httponly=True, samesite='Lax', max_age=86400)
return resp