Skip to content

Commit 41e353e

Browse files
authored
[PM-21380] Bugfix - Update account creation script for new server versions (#341)
* update account creation script for new server versions * bump self-host image version and add container name to docker-compose file * simplify matching logic
1 parent 961ab43 commit 41e353e

File tree

2 files changed

+89
-30
lines changed

2 files changed

+89
-30
lines changed

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
name: v2025-05-02
12
services:
23
bitwarden:
34
depends_on:
45
- db
56
env_file:
67
- .env
7-
image: ghcr.io/bitwarden/self-host:2025.4.1-beta # https://github.com/bitwarden/self-host/releases
8+
image: ghcr.io/bitwarden/self-host:2025.5.2-beta # https://github.com/bitwarden/self-host/releases
89
restart: always
910
ports:
1011
- ${VAULT_HOST_PORT}:8443

scripts/create-account.ts

Lines changed: 87 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { configDotenv } from "dotenv";
33

44
configDotenv();
55

6-
type AccountCreationResponseData = {
6+
type ResponseData = {
77
captchaBypassToken?: string;
88
message?: string | "The model state is invalid.";
99
validationErrors?: {
@@ -12,12 +12,20 @@ type AccountCreationResponseData = {
1212
exceptionMessage?: string | null;
1313
exceptionStackTrace?: string | null;
1414
innerExceptionMessage?: string | null;
15-
object?: "register" | "error";
15+
object?: "registerFinish" | "error";
1616
};
1717

18+
type PreAccountCreateResponseData = ResponseData | string;
19+
20+
type AccountCreationResponseData = ResponseData;
21+
1822
let failedAttemptsCount = 0;
1923

2024
async function createAccount() {
25+
if (failedAttemptsCount > 60) {
26+
throw new Error("The account was unable to be created.");
27+
}
28+
2129
const {
2230
GENERATED_RSA_KEY_PAIR_PROTECTED_PRIVATE_KEY,
2331
GENERATED_RSA_KEY_PAIR_PUBLIC_KEY,
@@ -31,30 +39,81 @@ async function createAccount() {
3139
const vaultHost = `${VAULT_HOST_URL}:${VAULT_HOST_PORT}`;
3240

3341
try {
34-
const response = await fetch(`${vaultHost}/identity/accounts/register`, {
42+
const requestOptions = {
3543
headers: {
36-
"Content-Type": "application/json",
44+
accept: "application/json",
45+
"content-type": "application/json; charset=utf-8",
3746
},
38-
body: JSON.stringify({
39-
email: `${VAULT_EMAIL}`,
40-
name: null,
41-
masterPasswordHash: `${MASTER_PASSWORD_HASH}`,
42-
key: `${PROTECTED_SYMMETRIC_KEY}`,
43-
kdf: 0,
44-
kdfIterations: `${KDF_ITERATIONS}`,
45-
referenceData: { id: null },
46-
captchaResponse: null,
47-
masterPasswordHint: null,
48-
keys: {
49-
publicKey: `${GENERATED_RSA_KEY_PAIR_PUBLIC_KEY}`,
50-
encryptedPrivateKey: `${GENERATED_RSA_KEY_PAIR_PROTECTED_PRIVATE_KEY}`,
51-
},
52-
}),
5347
method: "POST",
54-
});
48+
};
49+
50+
const preCreationResponse = await fetch(
51+
`${vaultHost}/identity/accounts/register/send-verification-email`,
52+
{
53+
...requestOptions,
54+
body: JSON.stringify({ email: `${VAULT_EMAIL}`, name: "" }),
55+
},
56+
);
57+
58+
const preCreationResponseData =
59+
(await preCreationResponse.json()) as PreAccountCreateResponseData;
60+
61+
if (
62+
typeof preCreationResponseData !== "string" &&
63+
preCreationResponseData.object === "error"
64+
) {
65+
const emailIsTaken = !!preCreationResponseData.message.match(
66+
/^Email .+@.+ is already taken$/g,
67+
)?.length;
68+
69+
if (emailIsTaken) {
70+
emitSuccessMessage(vaultHost);
71+
return;
72+
}
73+
74+
console.log(`Retrying account creation at ${vaultHost}...`);
75+
failedAttemptsCount++;
76+
setTimeout(createAccount, 3000);
77+
return;
78+
} else if (
79+
typeof preCreationResponseData !== "string" ||
80+
!preCreationResponseData.startsWith(
81+
"BwRegistrationEmailVerificationToken_",
82+
)
83+
) {
84+
console.log(
85+
"Unexpected response: expected BwRegistrationEmailVerificationToken",
86+
);
87+
return;
88+
}
89+
90+
const response = await fetch(
91+
`${vaultHost}/identity/accounts/register/finish`,
92+
{
93+
...requestOptions,
94+
body: JSON.stringify({
95+
email: `${VAULT_EMAIL}`,
96+
emailVerificationToken: preCreationResponseData,
97+
masterPasswordHash: `${MASTER_PASSWORD_HASH}`,
98+
kdf: 0,
99+
kdfIterations: KDF_ITERATIONS,
100+
masterPasswordHint: "",
101+
userSymmetricKey: `${PROTECTED_SYMMETRIC_KEY}`,
102+
userAsymmetricKeys: {
103+
publicKey: `${GENERATED_RSA_KEY_PAIR_PUBLIC_KEY}`,
104+
encryptedPrivateKey: `${GENERATED_RSA_KEY_PAIR_PROTECTED_PRIVATE_KEY}`,
105+
},
106+
}),
107+
},
108+
);
55109

56110
const responseData = (await response.json()) as AccountCreationResponseData;
57111

112+
if (responseData.object === "registerFinish") {
113+
emitSuccessMessage(vaultHost);
114+
return;
115+
}
116+
58117
let emailIsTaken = false;
59118

60119
const responseErrors =
@@ -70,25 +129,24 @@ async function createAccount() {
70129
}
71130

72131
if (emailIsTaken) {
73-
console.log(
74-
"\x1b[1m\x1b[32m%s\x1b[0m", // bold, light green foreground
75-
`Account has been created successfully at ${vaultHost}!\n`,
76-
);
132+
emitSuccessMessage(vaultHost);
77133

78134
return;
79135
}
80136
} catch (error) {
81137
// Server isn't ready yet
82138
}
83139

84-
if (failedAttemptsCount > 60) {
85-
throw new Error("The account was unable to be created.");
86-
}
87-
88140
console.log(`Retrying account creation at ${vaultHost}...`);
89-
90141
failedAttemptsCount++;
91142
setTimeout(createAccount, 3000);
92143
}
93144

145+
function emitSuccessMessage(vaultHost: string) {
146+
console.log(
147+
"\x1b[1m\x1b[32m%s\x1b[0m", // bold, light green foreground
148+
`Account has been created successfully at ${vaultHost}!\n`,
149+
);
150+
}
151+
94152
createAccount();

0 commit comments

Comments
 (0)