Fix QA review #2: backup code flow, audit filters, schema hardening
C-01: verifyTotp now sends backup_code field when in backup mode C-02: Backup code input filter allows alphanumeric chars (not digits only) W-01: Audit log ACTION_TYPES aligned with actual backend action strings W-02: Added extra="forbid" to SetupRequest and LoginRequest schemas Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
72ac1d53fb
commit
72e00f3a69
@ -32,6 +32,8 @@ def _validate_username(v: str) -> str:
|
||||
|
||||
|
||||
class SetupRequest(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
username: str
|
||||
password: str
|
||||
|
||||
@ -69,6 +71,8 @@ class RegisterRequest(BaseModel):
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
@ -16,21 +16,20 @@ import { cn } from '@/lib/utils';
|
||||
import { actionColor } from './shared';
|
||||
|
||||
const ACTION_TYPES = [
|
||||
'user.create',
|
||||
'user.login',
|
||||
'user.logout',
|
||||
'user.login_failed',
|
||||
'user.locked',
|
||||
'user.unlocked',
|
||||
'user.role_changed',
|
||||
'user.disabled',
|
||||
'user.enabled',
|
||||
'user.password_reset',
|
||||
'user.totp_disabled',
|
||||
'user.mfa_enforced',
|
||||
'user.mfa_enforcement_removed',
|
||||
'user.sessions_revoked',
|
||||
'config.updated',
|
||||
'admin.user_created',
|
||||
'admin.role_changed',
|
||||
'admin.password_reset',
|
||||
'admin.mfa_disabled',
|
||||
'admin.mfa_enforce_toggled',
|
||||
'admin.user_deactivated',
|
||||
'admin.user_activated',
|
||||
'admin.sessions_revoked',
|
||||
'admin.config_updated',
|
||||
'auth.login_success',
|
||||
'auth.login_failed',
|
||||
'auth.setup_complete',
|
||||
'auth.registration',
|
||||
'auth.mfa_enforce_prompted',
|
||||
];
|
||||
|
||||
function actionLabel(action: string): string {
|
||||
|
||||
@ -145,7 +145,7 @@ export default function LockScreen() {
|
||||
const handleTotpSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await verifyTotp(totpCode);
|
||||
await verifyTotp({ code: totpCode, isBackup: useBackupCode });
|
||||
} catch (error) {
|
||||
toast.error(getErrorMessage(error, 'Invalid verification code'));
|
||||
setTotpCode('');
|
||||
@ -257,7 +257,7 @@ export default function LockScreen() {
|
||||
onChange={(e) =>
|
||||
setTotpCode(
|
||||
useBackupCode
|
||||
? e.target.value.replace(/[^0-9-]/g, '')
|
||||
? e.target.value.replace(/[^A-Za-z0-9-]/g, '').toUpperCase()
|
||||
: e.target.value.replace(/\D/g, '')
|
||||
)
|
||||
}
|
||||
|
||||
@ -57,11 +57,14 @@ export function useAuth() {
|
||||
});
|
||||
|
||||
const totpVerifyMutation = useMutation({
|
||||
mutationFn: async (code: string) => {
|
||||
const { data } = await api.post('/auth/totp-verify', {
|
||||
mfa_token: mfaToken,
|
||||
code,
|
||||
});
|
||||
mutationFn: async ({ code, isBackup }: { code: string; isBackup: boolean }) => {
|
||||
const payload: Record<string, string> = { mfa_token: mfaToken! };
|
||||
if (isBackup) {
|
||||
payload.backup_code = code;
|
||||
} else {
|
||||
payload.code = code;
|
||||
}
|
||||
const { data } = await api.post('/auth/totp-verify', payload);
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user