fix(auth): split login and signup into separate 1Password-compatible forms
1Password showed "No items to display" on the signup password field because login and registration shared a single form whose password input switched autocomplete between current-password and new-password, and the display-name field was dynamically added via v-if. Per 1Password compatible website design, unrelated flows must be separate forms with stable fields. Changes: - Split into two distinct <form> elements: login (autocomplete current-password) and signup (autocomplete new-password), each with unique field ids so 1Password does not cache the field as a login field - Add passwordrules, minlength=8, and maxlength=128 to the signup password input so 1Password can generate a password matching the server rules (min 8 chars) - Remove the dynamic autocomplete computed and the v-if display-name field; each form now has a static, complete field set - Add .fields form styling (flex column, gap) now that the forms sit inside the card container
This commit is contained in:
parent
f97b4f92f0
commit
cf63f27839
1 changed files with 51 additions and 24 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { login, oidcLogin, register } from '@/game/auth'
|
import { login, oidcLogin, register } from '@/game/auth'
|
||||||
import { store } from '@/game/store'
|
import { store } from '@/game/store'
|
||||||
|
|
||||||
|
|
@ -10,11 +10,6 @@ const displayName = ref('')
|
||||||
const busy = ref(false)
|
const busy = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
|
||||||
// Password managers (1Password, Bitwarden, …) rely on distinct autocomplete
|
|
||||||
// hints: "current-password" for login, "new-password" so a strong password is
|
|
||||||
// suggested/saved during registration.
|
|
||||||
const passwordAutocomplete = computed(() => (mode.value === 'login' ? 'current-password' : 'new-password'))
|
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
store.auth.showAuth = false
|
store.auth.showAuth = false
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
|
@ -38,7 +33,7 @@ async function submit(): Promise<void> {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="store.auth.showAuth" class="backdrop" @click.self="close">
|
<div v-if="store.auth.showAuth" class="backdrop" @click.self="close">
|
||||||
<form class="card" @submit.prevent="submit">
|
<div class="card">
|
||||||
<button type="button" class="close" @click="close">✕</button>
|
<button type="button" class="close" @click="close">✕</button>
|
||||||
<h2>{{ mode === 'login' ? 'Anmelden' : 'Konto erstellen' }}</h2>
|
<h2>{{ mode === 'login' ? 'Anmelden' : 'Konto erstellen' }}</h2>
|
||||||
<p class="sub">Speichere Kristalle, Forschung & Statistiken über alle Runden hinweg.</p>
|
<p class="sub">Speichere Kristalle, Forschung & Statistiken über alle Runden hinweg.</p>
|
||||||
|
|
@ -48,30 +43,56 @@ async function submit(): Promise<void> {
|
||||||
<button type="button" :class="{ active: mode === 'register' }" @click="mode = 'register'">Registrieren</button>
|
<button type="button" :class="{ active: mode === 'register' }" @click="mode = 'register'">Registrieren</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Anmeldung: separates Formular mit current-password -->
|
||||||
|
<form v-if="mode === 'login'" class="fields" @submit.prevent="submit">
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<span>Benutzername</span>
|
<span>Benutzername</span>
|
||||||
<input id="username" name="username" v-model="username" maxlength="16" autocomplete="username" />
|
<input id="login-username" name="username" v-model="username" maxlength="16" autocomplete="username" />
|
||||||
</label>
|
</label>
|
||||||
<label v-if="mode === 'register'" class="field">
|
<label class="field">
|
||||||
|
<span>Passwort</span>
|
||||||
|
<input id="login-password" name="password" v-model="password" type="password" autocomplete="current-password" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<p v-if="error" class="error">{{ error }}</p>
|
||||||
|
|
||||||
|
<button type="submit" class="primary" :disabled="busy">{{ busy ? '…' : '▶ Anmelden' }}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Registrierung: separates Formular mit new-password + Generierungs-Regeln -->
|
||||||
|
<form v-else class="fields" @submit.prevent="submit">
|
||||||
|
<label class="field">
|
||||||
|
<span>Benutzername</span>
|
||||||
|
<input id="register-username" name="username" v-model="username" maxlength="16" autocomplete="username" />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
<span>Anzeigename (optional)</span>
|
<span>Anzeigename (optional)</span>
|
||||||
<input id="displayName" name="displayName" v-model="displayName" maxlength="24" autocomplete="nickname" />
|
<input id="displayName" name="displayName" v-model="displayName" maxlength="24" autocomplete="nickname" />
|
||||||
</label>
|
</label>
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<span>Passwort</span>
|
<span>Passwort</span>
|
||||||
<input id="password" name="password" v-model="password" type="password" :autocomplete="passwordAutocomplete" />
|
<input
|
||||||
|
id="register-password"
|
||||||
|
name="password"
|
||||||
|
v-model="password"
|
||||||
|
type="password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
minlength="8"
|
||||||
|
maxlength="128"
|
||||||
|
passwordrules="minlength: 8; maxlength: 128;"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<p v-if="error" class="error">{{ error }}</p>
|
<p v-if="error" class="error">{{ error }}</p>
|
||||||
|
|
||||||
<button type="submit" class="primary" :disabled="busy">
|
<button type="submit" class="primary" :disabled="busy">{{ busy ? '…' : '▶ Konto erstellen' }}</button>
|
||||||
{{ busy ? '…' : mode === 'login' ? '▶ Anmelden' : '▶ Konto erstellen' }}
|
</form>
|
||||||
</button>
|
|
||||||
|
|
||||||
<div v-if="store.auth.oidcEnabled" class="divider"><span>oder</span></div>
|
<div v-if="store.auth.oidcEnabled" class="divider"><span>oder</span></div>
|
||||||
<button type="button" v-if="store.auth.oidcEnabled" class="oidc" @click="oidcLogin">🔑 {{ store.auth.oidcLabel }}</button>
|
<button type="button" v-if="store.auth.oidcEnabled" class="oidc" @click="oidcLogin">🔑 {{ store.auth.oidcLabel }}</button>
|
||||||
|
|
||||||
<p class="hint">Als Gast spielen? Einfach schließen – Fortschritt wird lokal gespeichert.</p>
|
<p class="hint">Als Gast spielen? Einfach schließen – Fortschritt wird lokal gespeichert.</p>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -138,6 +159,12 @@ h2 {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
.fields {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
.field {
|
.field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue