auth.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. """
  2. Authentication API endpoints
  3. Provides login, logout, token refresh, and current user endpoints.
  4. """
  5. from flask import jsonify, request
  6. from app.api import api_bp
  7. from app.services import AuthService, login_required, get_current_user_from_context
  8. from app.errors import ValidationError
  9. @api_bp.route('/auth/login', methods=['POST'])
  10. def login():
  11. """
  12. User login endpoint
  13. Request body:
  14. {
  15. "username": "string",
  16. "password": "string"
  17. }
  18. Returns:
  19. {
  20. "token": "access_token",
  21. "refresh_token": "refresh_token",
  22. "user": { user object }
  23. }
  24. """
  25. data = request.get_json()
  26. if not data:
  27. raise ValidationError(
  28. message="Request body is required",
  29. details={"reason": "missing_body"}
  30. )
  31. username = data.get('username')
  32. password = data.get('password')
  33. if not username or not password:
  34. raise ValidationError(
  35. message="Username and password are required",
  36. details={
  37. "missing_fields": [
  38. field for field in ['username', 'password']
  39. if not data.get(field)
  40. ]
  41. }
  42. )
  43. user, tokens = AuthService.authenticate(username, password)
  44. return jsonify({
  45. 'token': tokens['access_token'],
  46. 'refresh_token': tokens['refresh_token'],
  47. 'user': user.to_dict()
  48. }), 200
  49. @api_bp.route('/auth/logout', methods=['POST'])
  50. @login_required
  51. def logout():
  52. """
  53. User logout endpoint
  54. Note: Since JWT is stateless, logout is handled client-side by removing the token.
  55. This endpoint exists for API consistency and can be extended for token blacklisting.
  56. Returns:
  57. { "message": "Logged out successfully" }
  58. """
  59. # In a stateless JWT system, logout is handled client-side
  60. # This endpoint can be extended to implement token blacklisting if needed
  61. return jsonify({
  62. 'message': 'Logged out successfully'
  63. }), 200
  64. @api_bp.route('/auth/me', methods=['GET'])
  65. @login_required
  66. def get_current_user():
  67. """
  68. Get current authenticated user information
  69. Returns:
  70. { user object }
  71. """
  72. user = get_current_user_from_context()
  73. return jsonify(user.to_dict()), 200
  74. @api_bp.route('/auth/refresh', methods=['POST'])
  75. def refresh_token():
  76. """
  77. Refresh access token using refresh token
  78. Request body:
  79. {
  80. "refresh_token": "string"
  81. }
  82. Returns:
  83. {
  84. "access_token": "new_access_token"
  85. }
  86. """
  87. data = request.get_json()
  88. if not data:
  89. raise ValidationError(
  90. message="Request body is required",
  91. details={"reason": "missing_body"}
  92. )
  93. refresh_token_value = data.get('refresh_token')
  94. if not refresh_token_value:
  95. raise ValidationError(
  96. message="Refresh token is required",
  97. details={"missing_fields": ["refresh_token"]}
  98. )
  99. tokens = AuthService.refresh_access_token(refresh_token_value)
  100. return jsonify({
  101. 'access_token': tokens['access_token']
  102. }), 200