halkyon/packages/halkyon_base_server/migrations/20261020111123780-passwords/migration.sql

148 lines
4.1 KiB
MySQL
Raw Permalink Normal View History

feat(auth): a password with TOTP on the register, and resets The register's side of signing in with an address, a password that never leaves the device (SRP-6a) and a code from an authenticator (ADR 0017), with members forwarding every step through AccountRegister: - password.set: salt, Argon2id's costs, SRP's verifier, the public keys and the private keys wrapped twice, all made on the device; with the address a mailed code proves, an anonymous person becomes identified under their ID. It signs in once an authenticator is confirmed. - passwordSignIn.begin and complete: SRP's two steps with a TOTP or recovery code. An address nobody has is answered from a decoy key as an account would be; every wrong credential is refused alike. - authenticator: secrets sealed under totpKey (XChaCha20-Poly1305, bound to their row), each code accepted once, ten recovery codes with the first, the last one kept. - accountRecovery: a lost authenticator reset 72 hours after the password and a mailed code or the recovery phrase ask, a lost password set anew with the phrase's signature, or the account reset with new keys 72 hours after a mailed code asks; a mail at each start, a sign-in calling it off. - An account with a password no longer signs in by a mailed code or GitHub; no new GitHub account is linked where passwords are offered, and from GITHUB_SIGN_IN_ENDS GitHub signs nobody in and its IDs go. Five tables, added by the module's migration; deletePerson deletes their rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 13:32:12 +02:00
BEGIN;
--
-- Function: gen_random_uuid_v7()
-- Source: https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74
-- License: MIT (copyright notice included on the generator source code).
--
create or replace function gen_random_uuid_v7()
returns uuid
as $$
begin
-- use random v4 uuid as starting point (which has the same variant we need)
-- then overlay timestamp
-- then set version 7 by flipping the 2 and 1 bit in the version 4 string
return encode(
set_bit(
set_bit(
overlay(uuid_send(gen_random_uuid())
placing substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3)
from 1 for 6
),
52, 1
),
53, 1
),
'hex')::uuid;
end
$$
language plpgsql
volatile;
--
-- ACTION CREATE TABLE
--
CREATE TABLE "account_password" (
"id" bigserial PRIMARY KEY,
"authUserId" uuid NOT NULL,
"salt" bytea NOT NULL,
"kdfAlgorithm" text NOT NULL,
"kdfOpsLimit" bigint NOT NULL,
"kdfMemLimit" bigint NOT NULL,
"verifier" bytea NOT NULL,
"encryptionPublicKey" bytea NOT NULL,
"signingPublicKey" bytea NOT NULL,
"passwordKeysNonce" bytea NOT NULL,
"passwordKeysCipherText" bytea NOT NULL,
"recoveryKeysNonce" bytea NOT NULL,
"recoveryKeysCipherText" bytea NOT NULL,
"createdAt" timestamp without time zone NOT NULL,
"updatedAt" timestamp without time zone NOT NULL
);
-- Indexes
CREATE UNIQUE INDEX "account_password_person_idx" ON "account_password" USING btree ("authUserId");
--
-- ACTION CREATE TABLE
--
CREATE TABLE "account_reset" (
"id" bigserial PRIMARY KEY,
"authUserId" uuid NOT NULL,
"kind" text NOT NULL,
"takesEffectAt" timestamp without time zone NOT NULL,
"createdAt" timestamp without time zone NOT NULL
);
-- Indexes
CREATE UNIQUE INDEX "account_reset_person_kind_idx" ON "account_reset" USING btree ("authUserId", "kind");
--
-- ACTION CREATE TABLE
--
CREATE TABLE "recovery_code" (
"id" bigserial PRIMARY KEY,
"authUserId" uuid NOT NULL,
"hash" text NOT NULL
);
-- Indexes
CREATE INDEX "recovery_code_person_idx" ON "recovery_code" USING btree ("authUserId");
--
-- ACTION CREATE TABLE
--
CREATE TABLE "sign_in_challenge" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
"authUserId" uuid,
"purpose" text NOT NULL,
"addressHash" text NOT NULL,
"secret" text NOT NULL,
"serverValue" text,
"createdAt" timestamp without time zone NOT NULL
);
-- Indexes
CREATE INDEX "sign_in_challenge_created_idx" ON "sign_in_challenge" USING btree ("createdAt");
--
-- ACTION CREATE TABLE
--
CREATE TABLE "totp_authenticator" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
"authUserId" uuid NOT NULL,
"name" text NOT NULL,
"secret" bytea NOT NULL,
"confirmedAt" timestamp without time zone,
"lastStep" bigint,
"createdAt" timestamp without time zone NOT NULL
);
-- Indexes
CREATE INDEX "totp_authenticator_person_idx" ON "totp_authenticator" USING btree ("authUserId");
--
-- MIGRATION VERSION FOR halkyon_base
--
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
VALUES ('halkyon_base', '20261020111123780-passwords', now())
ON CONFLICT ("module")
DO UPDATE SET "version" = '20261020111123780-passwords', "timestamp" = now();
--
-- MIGRATION VERSION FOR serverpod
--
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
VALUES ('serverpod', '20260824182259319', now())
ON CONFLICT ("module")
DO UPDATE SET "version" = '20260824182259319', "timestamp" = now();
--
-- MIGRATION VERSION FOR serverpod_auth_idp
--
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
VALUES ('serverpod_auth_idp', '20260924105404509', now())
ON CONFLICT ("module")
DO UPDATE SET "version" = '20260924105404509', "timestamp" = now();
--
-- MIGRATION VERSION FOR serverpod_auth_core
--
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
VALUES ('serverpod_auth_core', '20260924105232991', now())
ON CONFLICT ("module")
DO UPDATE SET "version" = '20260924105232991', "timestamp" = now();
COMMIT;