feat: agent-02-kiosk Phase 2A - Auth endpoints (PIN/NFC/QR/List)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import uuid as _uuid
|
||||
from datetime import datetime, timezone
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.database import get_db
|
||||
@@ -18,7 +19,17 @@ from app.schemas.kiosk import (
|
||||
KioskDeviceOut,
|
||||
KioskDeviceUpdate,
|
||||
)
|
||||
from app.schemas.kiosk_auth import (
|
||||
KioskNfcLoginRequest,
|
||||
KioskPinLoginRequest,
|
||||
KioskQrLoginRequest,
|
||||
KioskSessionResponse,
|
||||
KioskUserListEntry,
|
||||
KioskUserListResponse,
|
||||
)
|
||||
from app.services.kiosk_auth_service import _display_name, kiosk_auth_service
|
||||
from app.services.kiosk_service import kiosk_service
|
||||
from app.services.kiosk_session_service import SESSION_TTL_SECONDS, kiosk_session_service
|
||||
|
||||
router = APIRouter(prefix="/kiosk", tags=["Kiosk"])
|
||||
|
||||
@@ -126,6 +137,123 @@ async def revoke_device(
|
||||
)
|
||||
|
||||
|
||||
# ── Kiosk-User-Auth (signierte Requests via Ed25519) ─────────────────────────
|
||||
|
||||
|
||||
@router.post("/auth/pin", response_model=KioskSessionResponse)
|
||||
async def kiosk_login_pin(
|
||||
data: KioskPinLoginRequest,
|
||||
device: KioskDevice = Depends(verify_kiosk_request),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""PIN-Login: Personalnummer + 4-16-stellige PIN."""
|
||||
user, session_token = await kiosk_auth_service.login_pin(
|
||||
personnel_number=data.personnel_number,
|
||||
pin=data.pin,
|
||||
company_id=device.company_id,
|
||||
device_id=device.id,
|
||||
db=db,
|
||||
)
|
||||
return KioskSessionResponse(
|
||||
session_token=session_token,
|
||||
user_id=user.id,
|
||||
user_name=_display_name(user),
|
||||
expires_in_seconds=SESSION_TTL_SECONDS,
|
||||
auth_method="pin",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/auth/nfc", response_model=KioskSessionResponse)
|
||||
async def kiosk_login_nfc(
|
||||
data: KioskNfcLoginRequest,
|
||||
device: KioskDevice = Depends(verify_kiosk_request),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""NFC-Login: NFC-UID der Karte."""
|
||||
user, session_token = await kiosk_auth_service.login_nfc(
|
||||
nfc_uid=data.nfc_uid,
|
||||
company_id=device.company_id,
|
||||
device_id=device.id,
|
||||
db=db,
|
||||
)
|
||||
return KioskSessionResponse(
|
||||
session_token=session_token,
|
||||
user_id=user.id,
|
||||
user_name=_display_name(user),
|
||||
expires_in_seconds=SESSION_TTL_SECONDS,
|
||||
auth_method="nfc",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/auth/qr", response_model=KioskSessionResponse)
|
||||
async def kiosk_login_qr(
|
||||
data: KioskQrLoginRequest,
|
||||
device: KioskDevice = Depends(verify_kiosk_request),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""QR-Login: QR-Token (aus Web-App gescannt)."""
|
||||
user, session_token = await kiosk_auth_service.login_qr(
|
||||
qr_token=data.qr_token,
|
||||
company_id=device.company_id,
|
||||
device_id=device.id,
|
||||
db=db,
|
||||
)
|
||||
return KioskSessionResponse(
|
||||
session_token=session_token,
|
||||
user_id=user.id,
|
||||
user_name=_display_name(user),
|
||||
expires_in_seconds=SESSION_TTL_SECONDS,
|
||||
auth_method="qr",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/auth/list", response_model=KioskSessionResponse)
|
||||
async def kiosk_login_list(
|
||||
user_id: _uuid.UUID = Body(..., embed=True),
|
||||
device: KioskDevice = Depends(verify_kiosk_request),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""List-Login: User wählt sich aus Mitarbeiterliste aus (kein Passwort)."""
|
||||
user, session_token = await kiosk_auth_service.login_list(
|
||||
user_id=user_id,
|
||||
company_id=device.company_id,
|
||||
device_id=device.id,
|
||||
db=db,
|
||||
)
|
||||
return KioskSessionResponse(
|
||||
session_token=session_token,
|
||||
user_id=user.id,
|
||||
user_name=_display_name(user),
|
||||
expires_in_seconds=SESSION_TTL_SECONDS,
|
||||
auth_method="list",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/auth/users", response_model=KioskUserListResponse)
|
||||
async def kiosk_user_list(
|
||||
device: KioskDevice = Depends(verify_kiosk_request),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Mitarbeiterliste für Kiosk-Auswahl."""
|
||||
users = await kiosk_auth_service.get_user_list(device.company_id, db)
|
||||
return KioskUserListResponse(
|
||||
users=[
|
||||
KioskUserListEntry(id=u.id, display_name=_display_name(u))
|
||||
for u in users
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@router.post("/auth/logout")
|
||||
async def kiosk_logout(
|
||||
session_token: str = Body(..., embed=True),
|
||||
device: KioskDevice = Depends(verify_kiosk_request),
|
||||
):
|
||||
"""Session invalidieren."""
|
||||
await kiosk_session_service.invalidate_session(session_token)
|
||||
return {"message": "Abgemeldet."}
|
||||
|
||||
|
||||
# ── Kiosk-Endpunkte (signierte Requests via Ed25519) ──────────────────────────
|
||||
|
||||
@router.post("/heartbeat", response_model=HeartbeatResponse)
|
||||
|
||||
Reference in New Issue
Block a user