Improve authentication recovery and mobile gameplay
This commit is contained in:
112
tests/initial-password.test.ts
Normal file
112
tests/initial-password.test.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
authActionSchema,
|
||||
initialPasswordSchema,
|
||||
newPasswordSchema,
|
||||
} from "../shared/account";
|
||||
import {
|
||||
generateInitialPassword,
|
||||
issueInitialPassword,
|
||||
} from "../shared/initial-password";
|
||||
|
||||
test("email-link sign-in is rejected, password login and recovery remain supported", () => {
|
||||
assert.equal(
|
||||
authActionSchema.safeParse({ action: "magic", email: "owner@example.com" })
|
||||
.success,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
authActionSchema.safeParse({
|
||||
action: "recover",
|
||||
email: "owner@example.com",
|
||||
}).success,
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
authActionSchema.safeParse({
|
||||
action: "login",
|
||||
email: "owner@example.com",
|
||||
password: "existing-password",
|
||||
}).success,
|
||||
true,
|
||||
);
|
||||
});
|
||||
test("generated first passwords are strong, unique and cannot target another account", () => {
|
||||
const passwords = new Set(
|
||||
Array.from({ length: 100 }, generateInitialPassword),
|
||||
);
|
||||
assert.equal(passwords.size, 100);
|
||||
for (const password of passwords) {
|
||||
assert.equal(newPasswordSchema.safeParse(password).success, true);
|
||||
assert.ok(
|
||||
/[A-Z]/.test(password) &&
|
||||
/[a-z]/.test(password) &&
|
||||
/\d/.test(password) &&
|
||||
/[!]/.test(password),
|
||||
);
|
||||
}
|
||||
assert.equal(
|
||||
initialPasswordSchema.safeParse({
|
||||
password: generateInitialPassword(),
|
||||
userId: "someone-else",
|
||||
}).success,
|
||||
false,
|
||||
);
|
||||
});
|
||||
test("first password issuance cannot overwrite a password or replay an uncertain Auth update", async () => {
|
||||
let claimed = false,
|
||||
updates = 0;
|
||||
const operations = {
|
||||
claim: async () => {
|
||||
if (claimed) return false;
|
||||
claimed = true;
|
||||
return true;
|
||||
},
|
||||
update: async (id: string) => {
|
||||
assert.equal(id, "verified-owner");
|
||||
updates++;
|
||||
},
|
||||
};
|
||||
await issueInitialPassword(
|
||||
"verified-owner",
|
||||
generateInitialPassword(),
|
||||
operations,
|
||||
);
|
||||
await assert.rejects(
|
||||
issueInitialPassword(
|
||||
"verified-owner",
|
||||
generateInitialPassword(),
|
||||
operations,
|
||||
),
|
||||
/already/,
|
||||
);
|
||||
assert.equal(updates, 1);
|
||||
let uncertainClaimed = false;
|
||||
const uncertain = {
|
||||
claim: async () => {
|
||||
if (uncertainClaimed) return false;
|
||||
uncertainClaimed = true;
|
||||
return true;
|
||||
},
|
||||
update: async () => {
|
||||
throw new Error("network interrupted");
|
||||
},
|
||||
};
|
||||
await assert.rejects(
|
||||
issueInitialPassword(
|
||||
"verified-owner",
|
||||
generateInitialPassword(),
|
||||
uncertain,
|
||||
),
|
||||
/network interrupted/,
|
||||
);
|
||||
await assert.rejects(
|
||||
issueInitialPassword(
|
||||
"verified-owner",
|
||||
generateInitialPassword(),
|
||||
uncertain,
|
||||
),
|
||||
/already/,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user