feat: implement user registration and login functionality with JWT issuance
This commit is contained in:
parent
fd6561205b
commit
41e344f06f
|
|
@ -2,9 +2,15 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from app.api.deps import get_auth_service
|
from app.api.deps import get_auth_service, get_user_repository
|
||||||
|
from app.core.security import password_hasher
|
||||||
|
from app.models.organization import Organization
|
||||||
|
from app.models.organization_member import OrganizationMember, OrganizationRole
|
||||||
from app.models.token import LoginRequest, TokenResponse
|
from app.models.token import LoginRequest, TokenResponse
|
||||||
|
from app.models.user import UserCreate
|
||||||
|
from app.repositories.user_repo import UserRepository
|
||||||
from app.services.auth_service import AuthService, InvalidCredentialsError
|
from app.services.auth_service import AuthService, InvalidCredentialsError
|
||||||
|
|
||||||
from .models import RegisterRequest
|
from .models import RegisterRequest
|
||||||
|
|
@ -12,20 +18,56 @@ from .models import RegisterRequest
|
||||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||||
|
|
||||||
|
|
||||||
def _stub(detail: str) -> dict[str, str]:
|
@router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED)
|
||||||
return {"detail": detail}
|
async def register_user(
|
||||||
|
payload: RegisterRequest,
|
||||||
|
repo: UserRepository = Depends(get_user_repository),
|
||||||
|
auth_service: AuthService = Depends(get_auth_service),
|
||||||
|
) -> TokenResponse:
|
||||||
|
"""Register a new owner along with the first organization and return JWT."""
|
||||||
|
|
||||||
|
existing = await repo.get_by_email(payload.email)
|
||||||
|
if existing is not None:
|
||||||
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="User already exists")
|
||||||
|
|
||||||
|
organization = Organization(name=payload.organization_name)
|
||||||
|
repo.session.add(organization)
|
||||||
|
await repo.session.flush()
|
||||||
|
|
||||||
|
user_data = UserCreate(email=payload.email, password=payload.password, name=payload.name)
|
||||||
|
hashed_password = password_hasher.hash(payload.password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = await repo.create(data=user_data, hashed_password=hashed_password)
|
||||||
|
membership = OrganizationMember(
|
||||||
|
organization_id=organization.id,
|
||||||
|
user_id=user.id,
|
||||||
|
role=OrganizationRole.OWNER,
|
||||||
|
)
|
||||||
|
repo.session.add(membership)
|
||||||
|
await repo.session.commit()
|
||||||
|
except IntegrityError as exc:
|
||||||
|
await repo.session.rollback()
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
|
detail="Organization or user already exists",
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
await repo.session.refresh(user)
|
||||||
|
return auth_service.create_access_token(user)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/register", status_code=status.HTTP_501_NOT_IMPLEMENTED)
|
@router.post("/login", response_model=TokenResponse)
|
||||||
async def register_user(_: RegisterRequest) -> dict[str, str]:
|
async def login(
|
||||||
"""Placeholder for user plus organization registration flow."""
|
credentials: LoginRequest,
|
||||||
return _stub("POST /auth/register is not implemented yet")
|
service: AuthService = Depends(get_auth_service),
|
||||||
|
) -> TokenResponse:
|
||||||
|
"""Authenticate user credentials and issue a JWT."""
|
||||||
@router.post("/login", status_code=status.HTTP_501_NOT_IMPLEMENTED)
|
try:
|
||||||
async def login(_: LoginRequest) -> dict[str, str]:
|
user = await service.authenticate(credentials.email, credentials.password)
|
||||||
"""Placeholder for login shortcut endpoint defined in the spec."""
|
except InvalidCredentialsError as exc: # pragma: no cover - thin API
|
||||||
return _stub("POST /auth/login is not implemented yet")
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
|
||||||
|
return service.create_access_token(user)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/token", response_model=TokenResponse)
|
@router.post("/token", response_model=TokenResponse)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue