05/18 Enhanced codes and functionalities 11

This commit is contained in:
2026-05-18 22:00:42 -04:00
parent 78a96e092f
commit 6177b72ef8
5 changed files with 54 additions and 9 deletions
+14 -1
View File
@@ -78,11 +78,24 @@ def register_begin():
"""
Generate PublicKeyCredentialCreationOptions for passkey registration.
Requires an active JWT session (user must be logged in).
Optional JSON body:
attachment: "platform" (default) | "cross-platform"
"platform" → device biometrics (Touch ID, Face ID, Windows Hello)
"cross-platform" → roaming authenticators (YubiKey, phone-as-key via QR)
"""
user = db.session.get(User, g.current_user_id)
if not user:
return jsonify({'error': 'User not found'}), 404
data = request.get_json(silent=True) or {}
attachment_str = (data.get('attachment') or 'platform').strip().lower()
if attachment_str == 'cross-platform':
authenticator_attachment = AuthenticatorAttachment.CROSS_PLATFORM
else:
authenticator_attachment = AuthenticatorAttachment.PLATFORM
# Collect existing credential IDs to exclude (prevent re-registering same key).
existing = WebAuthnCredential.query.filter_by(user_id=user.id).all()
exclude_credentials = [
@@ -102,7 +115,7 @@ def register_begin():
authenticator_selection=AuthenticatorSelectionCriteria(
resident_key=ResidentKeyRequirement.PREFERRED,
user_verification=UserVerificationRequirement.PREFERRED,
authenticator_attachment=AuthenticatorAttachment.PLATFORM,
authenticator_attachment=authenticator_attachment,
),
exclude_credentials=exclude_credentials,
)
+18
View File
@@ -2550,3 +2550,21 @@ html.sidebar-open {
.health-banner-dismiss:hover {
opacity: 1;
}
/* Passkey attachment type selector */
.passkey-attachment-select {
padding: 7px 10px;
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 13px;
color: var(--text);
background: var(--surface);
cursor: pointer;
flex-shrink: 0;
}
.passkey-attachment-select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.15);
}
+4 -2
View File
@@ -523,9 +523,10 @@ const PasskeyAuth = (() => {
/**
* Register a new passkey for the currently logged-in user.
* Requires an active access_token in sessionStorage (set by vault.js on login).
* @param {string} name User-friendly name for the passkey (e.g. "iPhone 15").
* @param {string} name User-friendly name (e.g. "iPhone 15").
* @param {string} attachment "platform" (default) | "cross-platform"
*/
async function registerPasskey(name) {
async function registerPasskey(name, attachment = "platform") {
if (!window.PublicKeyCredential) {
return { error: 'Passkeys are not supported in this browser.' };
}
@@ -540,6 +541,7 @@ const PasskeyAuth = (() => {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ attachment }),
});
if (!beginRes.ok) {
const d = await beginRes.json().catch(() => ({}));
+10 -2
View File
@@ -2442,10 +2442,16 @@ const Vault = (() => {
const lastUsed = c.last_used_at
? `Last used ${new Date(c.last_used_at).toLocaleDateString()}`
: "Never used";
const transports = c.transports || [];
const typeLabel = transports.includes("internal")
? "📱 Device"
: transports.some((t) => ["usb", "nfc", "ble", "smart-card"].includes(t))
? "🔑 Security key"
: "🔑 Passkey";
return `<div class="passkey-item" data-cred-id="${c.id}">
<div class="passkey-info">
<span class="passkey-name">${escHtml(c.name)}</span>
<span class="passkey-meta">${lastUsed} · Added ${escHtml(created)}</span>
<span class="passkey-meta">${typeLabel} · ${lastUsed} · Added ${escHtml(created)}</span>
</div>
<div class="passkey-actions">
<button class="btn-text btn-sm btn-rename-passkey" data-id="${c.id}" data-name="${escHtml(c.name)}">Rename</button>
@@ -2498,13 +2504,15 @@ const Vault = (() => {
registerBtn.dataset.bound = "1";
registerBtn.addEventListener("click", async () => {
const nameInput = document.getElementById("passkey-name-input");
const attachSel = document.getElementById("passkey-attachment-select");
const name = (nameInput?.value || "").trim() || "Passkey";
const attachment = attachSel?.value || "platform";
const errEl = document.getElementById("passkeys-error");
errEl?.classList.add("hidden");
registerBtn.disabled = true;
registerBtn.textContent = "Waiting…";
const result = await PasskeyAuth.registerPasskey(name);
const result = await PasskeyAuth.registerPasskey(name, attachment);
registerBtn.disabled = false;
registerBtn.textContent = "+ Add passkey";
+4
View File
@@ -977,6 +977,10 @@
maxlength="128"
class="passkey-name-input"
/>
<select id="passkey-attachment-select" class="passkey-attachment-select">
<option value="platform">Device (biometric)</option>
<option value="cross-platform">Security key / cross-device</option>
</select>
<button class="btn-secondary" id="btn-register-passkey">
+ Add passkey
</button>