Private
Public Access
Require password confirmation on account creation (#40)
Both account-creation surfaces now require the password twice: - Web signup (invite-based self-service): SignupComplete gains a password_confirm field with a model_validator backstop server-side, plus a client-side match check in SignupPage.tsx for immediate feedback -- the client check is the primary UX, the server check is defense in depth so the guarantee doesn't rely on the client alone. - CLI (python -m app.cli create-user): password is now an optional positional argument. If omitted, prompts interactively via getpass (hidden input) twice, retrying on mismatch -- matching what "entered twice and verified" actually means for a human typing blind. Passing the password directly as before still works unchanged, for scripted/ automated provisioning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,9 +9,10 @@ export function completeSignup(
|
||||
token: string,
|
||||
username: string,
|
||||
password: string,
|
||||
passwordConfirm: string,
|
||||
): Promise<User> {
|
||||
return apiFetch<User>('/api/signup', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ token, username, password }),
|
||||
body: JSON.stringify({ token, username, password, password_confirm: passwordConfirm }),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export function SignupPage() {
|
||||
|
||||
const [username, setUsername] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [passwordConfirm, setPasswordConfirm] = useState('')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
|
||||
@@ -40,9 +41,13 @@ export function SignupPage() {
|
||||
async function handleSubmit(e: FormEvent) {
|
||||
e.preventDefault()
|
||||
setError(null)
|
||||
if (password !== passwordConfirm) {
|
||||
setError("Passwords don't match")
|
||||
return
|
||||
}
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const newUser = await completeSignup(token, username, password)
|
||||
const newUser = await completeSignup(token, username, password, passwordConfirm)
|
||||
updateUser(newUser)
|
||||
navigate('/rooms')
|
||||
} catch (err) {
|
||||
@@ -92,6 +97,18 @@ export function SignupPage() {
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
minLength={8}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Confirm password
|
||||
<input
|
||||
type="password"
|
||||
value={passwordConfirm}
|
||||
onChange={(e) => setPasswordConfirm(e.target.value)}
|
||||
required
|
||||
minLength={8}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</label>
|
||||
{error && <p className="login-error">{error}</p>}
|
||||
|
||||
Reference in New Issue
Block a user