feat(auth): public self-service registration (ALLOW_REGISTRATION)
Add POST /auth/register: creates a non-superuser then auto-logs in, returning the same TokenResponse as login. Gated by the new allow_registration setting (env ALLOW_REGISTRATION, default true); when disabled it raises PermissionDeniedError (403). Accounts remain admin-only for superusers. Tests cover create+login, duplicate (409), short password (422), and the disabled (403) path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,11 @@ class LoginRequest(BaseModel):
|
||||
password: str = Field(min_length=1)
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
username: str = Field(min_length=1, max_length=64)
|
||||
password: str = Field(min_length=8)
|
||||
|
||||
|
||||
class RefreshRequest(BaseModel):
|
||||
refresh_token: str
|
||||
|
||||
|
||||
+25
-2
@@ -2,9 +2,16 @@
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
|
||||
from app.api.deps import AuthServiceDep, CurrentUser
|
||||
from app.api.schemas.auth import LoginRequest, RefreshRequest, TokenResponse
|
||||
from app.api.deps import AuthServiceDep, CurrentUser, UserServiceDep
|
||||
from app.api.schemas.auth import (
|
||||
LoginRequest,
|
||||
RefreshRequest,
|
||||
RegisterRequest,
|
||||
TokenResponse,
|
||||
)
|
||||
from app.api.schemas.user import UserResponse
|
||||
from app.core.config import get_settings
|
||||
from app.domain.errors import PermissionDeniedError
|
||||
from app.domain.tokens import TokenPair
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||
@@ -23,6 +30,22 @@ async def login(body: LoginRequest, auth: AuthServiceDep) -> TokenResponse:
|
||||
return _to_token_response(pair)
|
||||
|
||||
|
||||
@router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def register(
|
||||
body: RegisterRequest, users: UserServiceDep, auth: AuthServiceDep
|
||||
) -> TokenResponse:
|
||||
"""Public self-service sign-up (gated by ``ALLOW_REGISTRATION``).
|
||||
|
||||
Registered accounts are always regular users — superusers are created
|
||||
admin-only. On success the new account is logged straight in.
|
||||
"""
|
||||
if not get_settings().allow_registration:
|
||||
raise PermissionDeniedError("Registration is disabled on this instance.")
|
||||
await users.create_user(username=body.username, password=body.password, is_superuser=False)
|
||||
pair = await auth.login(body.username, body.password)
|
||||
return _to_token_response(pair)
|
||||
|
||||
|
||||
@router.post("/refresh", response_model=TokenResponse)
|
||||
async def refresh(body: RefreshRequest, auth: AuthServiceDep) -> TokenResponse:
|
||||
pair = await auth.refresh(body.refresh_token)
|
||||
|
||||
Reference in New Issue
Block a user