64 lines
1.9 KiB
Python
64 lines
1.9 KiB
Python
|
|
"""Authentication routes — token issuance only.
|
||
|
|
|
||
|
|
POST /auth/token — exchange username + password for a JWT.
|
||
|
|
GET /auth/me — return the current user identity (requires token).
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||
|
|
from fastapi.security import OAuth2PasswordRequestForm
|
||
|
|
from pydantic import BaseModel
|
||
|
|
|
||
|
|
from app.api.dependencies.auth import get_current_user
|
||
|
|
from app.config.settings import settings
|
||
|
|
from app.domain.auth.models import UserClaims
|
||
|
|
from app.shared.bootstrap import get_jwt_handler, get_user_store
|
||
|
|
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/auth", tags=["认证"])
|
||
|
|
|
||
|
|
|
||
|
|
class TokenResponse(BaseModel):
|
||
|
|
"""JWT token response body."""
|
||
|
|
|
||
|
|
access_token: str
|
||
|
|
token_type: str = "bearer"
|
||
|
|
expires_in: int
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/token", response_model=TokenResponse)
|
||
|
|
async def login(form: OAuth2PasswordRequestForm = Depends()):
|
||
|
|
"""Issue a JWT for valid username + password credentials.
|
||
|
|
|
||
|
|
Uses standard OAuth2 password grant form fields — compatible with
|
||
|
|
Swagger UI Authorize button.
|
||
|
|
"""
|
||
|
|
user = get_user_store().authenticate(form.username, form.password)
|
||
|
|
if user is None:
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail="Incorrect username or password",
|
||
|
|
headers={"WWW-Authenticate": "Bearer"},
|
||
|
|
)
|
||
|
|
token = get_jwt_handler().create_access_token(
|
||
|
|
user_id=user.id,
|
||
|
|
username=user.username,
|
||
|
|
role=user.role,
|
||
|
|
)
|
||
|
|
return TokenResponse(
|
||
|
|
access_token=token,
|
||
|
|
token_type="bearer",
|
||
|
|
expires_in=settings.auth_token_expire_minutes * 60,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/me")
|
||
|
|
async def get_me(current_user: UserClaims = Depends(get_current_user)):
|
||
|
|
"""Return the identity of the currently authenticated user."""
|
||
|
|
return {
|
||
|
|
"user_id": current_user.user_id,
|
||
|
|
"username": current_user.username,
|
||
|
|
"role": current_user.role.value,
|
||
|
|
}
|