Quelltext des aktuellen Standes
-- Migration 246: Passkeys im Tresor
--
-- Ein Passkey gehoert zu einem Tresor-Eintrag und teilt dessen Schluessel-
-- kontext (persoenlich = VMK, Team = TVK). Der private Schluessel verlaesst den
-- Server nie: Anlegen und Signieren geschehen serverseitig, die Browser-
-- Erweiterung reicht nur die WebAuthn-Antworten an die Webseite durch.
-- Relying Party und Credential-Kennung liegen im Klartext, weil die Auswahl
-- je Webseite darueber laeuft; sie sind kein Geheimnis.
CREATE TABLE IF NOT EXISTS `vault_passkeys` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`entry_id` BIGINT UNSIGNED NOT NULL,
`rp_id` VARCHAR(253) NOT NULL COMMENT 'Relying Party (Domain), die den Passkey angelegt hat',
`rp_name` VARCHAR(255) NULL,
`credential_id` VARCHAR(128) NOT NULL COMMENT 'Base64URL; identifiziert den Passkey gegenueber der Webseite',
`public_key` BLOB NOT NULL COMMENT 'COSE-kodierter oeffentlicher Schluessel',
`alg` SMALLINT NOT NULL DEFAULT -7 COMMENT 'COSE-Algorithmus (-7 = ES256)',
`privkey_enc` BLOB NOT NULL,
`privkey_nonce` BINARY(12) NOT NULL,
`privkey_tag` BINARY(16) NOT NULL,
`user_handle_enc` BLOB NOT NULL,
`user_handle_nonce` BINARY(12) NOT NULL,
`user_handle_tag` BINARY(16) NOT NULL,
`user_name_enc` BLOB NULL,
`user_name_nonce` BINARY(12) NULL,
`user_name_tag` BINARY(16) NULL,
`user_display_enc` BLOB NULL,
`user_display_nonce` BINARY(12) NULL,
`user_display_tag` BINARY(16) NULL,
`created_by` BIGINT UNSIGNED NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_used_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_vault_passkeys_credential` (`credential_id`),
KEY `idx_vault_passkeys_entry` (`entry_id`),
KEY `idx_vault_passkeys_rp` (`rp_id`),
CONSTRAINT `fk_vault_passkeys_entry`
FOREIGN KEY (`entry_id`) REFERENCES `vault_entries` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_vault_passkeys_user`
FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;