122 lines
4.2 KiB
Python
122 lines
4.2 KiB
Python
"""Authentication routes."""
|
|
from flask import Blueprint, request, session
|
|
from flask_restx import Api, Resource, fields
|
|
from models.user import User
|
|
from services.auth_service import authenticate_user, register_user
|
|
from typing import Dict, Any
|
|
|
|
auth_bp = Blueprint('auth', __name__, url_prefix='/api')
|
|
auth_api = Api(auth_bp, doc=False, prefix='/auth')
|
|
|
|
# Request/Response models for Swagger
|
|
login_model = auth_api.model('Login', {
|
|
'username': fields.String(required=True, description='Username'),
|
|
'password': fields.String(required=True, description='Password')
|
|
})
|
|
|
|
user_response_model = auth_api.model('UserResponse', {
|
|
'id': fields.Integer(description='User ID'),
|
|
'username': fields.String(description='Username'),
|
|
'email': fields.String(description='Email'),
|
|
'role': fields.String(description='Fellowship member role'),
|
|
'gold': fields.Integer(description='Current gold balance'),
|
|
})
|
|
|
|
login_response_model = auth_api.model('LoginResponse', {
|
|
'message': fields.String(description='Success message'),
|
|
'user': fields.Nested(user_response_model, description='User information')
|
|
})
|
|
|
|
signup_model = auth_api.model('Signup', {
|
|
'username': fields.String(required=True, description='Desired username (minimum 3 characters)'),
|
|
'password': fields.String(required=True, description='Desired password (minimum 8 characters and at least one number)'),
|
|
'email': fields.String(required=False, description='Optional email address')
|
|
})
|
|
|
|
|
|
@auth_api.route('/signup')
|
|
class Signup(Resource):
|
|
"""Public signup endpoint for open SUT registration."""
|
|
|
|
@auth_api.expect(signup_model, validate=False)
|
|
@auth_api.response(201, 'Signup successful', login_response_model)
|
|
@auth_api.response(400, 'Validation error')
|
|
@auth_api.doc(description='Register user and create session')
|
|
def post(self) -> tuple[Dict[str, Any], int]:
|
|
"""Register a user and immediately log them in."""
|
|
data = request.get_json() or {}
|
|
|
|
username = data.get('username')
|
|
password = data.get('password')
|
|
email = data.get('email')
|
|
|
|
try:
|
|
user = register_user(username=username, password=password, email=email)
|
|
except ValueError as error:
|
|
return {'error': str(error)}, 400
|
|
|
|
session['user_id'] = user.id
|
|
session['username'] = user.username
|
|
|
|
return {
|
|
'message': 'Signup successful',
|
|
'user': user.to_dict(),
|
|
}, 201
|
|
|
|
@auth_api.route('/login')
|
|
class Login(Resource):
|
|
"""User login endpoint."""
|
|
|
|
@auth_api.expect(login_model)
|
|
@auth_api.marshal_with(login_response_model)
|
|
@auth_api.doc(description='Authenticate user and create session')
|
|
def post(self) -> tuple[Dict[str, Any], int]:
|
|
"""Login user."""
|
|
data = request.get_json()
|
|
username = data.get('username')
|
|
password = data.get('password')
|
|
|
|
if not username or not password:
|
|
return {'error': 'Username and password are required'}, 400
|
|
|
|
user = authenticate_user(username, password)
|
|
if not user:
|
|
return {'error': 'Invalid credentials'}, 401
|
|
|
|
# Create session
|
|
session['user_id'] = user.id
|
|
session['username'] = user.username
|
|
|
|
return {
|
|
'message': 'Login successful',
|
|
'user': user.to_dict()
|
|
}, 200
|
|
|
|
@auth_api.route('/logout')
|
|
class Logout(Resource):
|
|
"""User logout endpoint."""
|
|
|
|
@auth_api.doc(description='Logout user and destroy session')
|
|
def post(self) -> tuple[Dict[str, Any], int]:
|
|
"""Logout user."""
|
|
session.clear()
|
|
return {'message': 'Logout successful'}, 200
|
|
|
|
@auth_api.route('/me')
|
|
class CurrentUser(Resource):
|
|
"""Get current authenticated user."""
|
|
|
|
@auth_api.marshal_with(user_response_model)
|
|
@auth_api.doc(description='Get current authenticated user information')
|
|
def get(self) -> tuple[Dict[str, Any], int]:
|
|
"""Get current user."""
|
|
user_id = session.get('user_id')
|
|
if not user_id:
|
|
return {'error': 'Not authenticated'}, 401
|
|
|
|
user = User.query.get(user_id)
|
|
if not user:
|
|
return {'error': 'User not found'}, 404
|
|
|
|
return user.to_dict(), 200
|