@betterportal/auth-default 10.6.20 → 10.6.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -10
- package/bsb-plugin.json +1 -1
- package/lib/account.d.ts +16 -0
- package/lib/account.d.ts.map +1 -0
- package/lib/account.js +274 -0
- package/lib/account.js.map +1 -0
- package/lib/accountUI.d.ts +5 -0
- package/lib/accountUI.d.ts.map +1 -0
- package/lib/accountUI.js +68 -0
- package/lib/accountUI.js.map +1 -0
- package/lib/bp-contracts/org.betterportal.auth.default.json +2031 -707
- package/lib/clientAddress.d.ts +10 -0
- package/lib/clientAddress.d.ts.map +1 -0
- package/lib/clientAddress.js +35 -0
- package/lib/clientAddress.js.map +1 -0
- package/lib/config.d.ts +3 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +28 -0
- package/lib/config.js.map +1 -0
- package/lib/defaultAuthManifest.d.ts +7 -0
- package/lib/defaultAuthManifest.d.ts.map +1 -1
- package/lib/factors.d.ts +18 -0
- package/lib/factors.d.ts.map +1 -0
- package/lib/factors.js +95 -0
- package/lib/factors.js.map +1 -0
- package/lib/identity.d.ts +106 -0
- package/lib/identity.d.ts.map +1 -0
- package/lib/identity.js +315 -0
- package/lib/identity.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/mail.d.ts +19 -0
- package/lib/mail.d.ts.map +1 -0
- package/lib/mail.js +102 -0
- package/lib/mail.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.d.ts +435 -11
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.js +85 -0
- package/lib/plugins/service-betterportal-auth-default/.bp-generated/registry.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.d.ts +34 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.js +25 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/GET.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.d.ts +42 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.js +27 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/POST.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.d.ts +2 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.js +2 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/GET.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.d.ts +2 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.js +2 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/_renderer.bootstrap5/POST.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.d.ts +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.js +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/account/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_renderer.bootstrap5/GET.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_renderer.bootstrap5/GET.js +11 -8
- package/lib/plugins/service-betterportal-auth-default/bp-routes/login/_renderer.bootstrap5/GET.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.d.ts +3 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.js +10 -23
- package/lib/plugins/service-betterportal-auth-default/bp-routes/refresh/POST.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_renderer.bootstrap5/GET.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_renderer.bootstrap5/GET.js +2 -2
- package/lib/plugins/service-betterportal-auth-default/bp-routes/register/_renderer.bootstrap5/GET.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.d.ts +34 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.js +26 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/GET.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.d.ts +31 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.js +23 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/POST.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.d.ts +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.js +36 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/_renderer.bootstrap5/GET.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.d.ts +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.js +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/social/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.d.ts +32 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.js +39 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/GET.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.d.ts +41 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.js +103 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/POST.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.d.ts +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.js +19 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/_renderer.bootstrap5/GET.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.d.ts +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.d.ts.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.js +3 -0
- package/lib/plugins/service-betterportal-auth-default/bp-routes/users/index.js.map +1 -0
- package/lib/plugins/service-betterportal-auth-default/index.d.ts +41 -4
- package/lib/plugins/service-betterportal-auth-default/index.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/index.js +205 -9
- package/lib/plugins/service-betterportal-auth-default/index.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/loginFlow.d.ts +17 -2
- package/lib/plugins/service-betterportal-auth-default/loginFlow.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/loginFlow.js +83 -62
- package/lib/plugins/service-betterportal-auth-default/loginFlow.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/logoutFlow.js +1 -1
- package/lib/plugins/service-betterportal-auth-default/logoutFlow.js.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/registrationFlow.d.ts +4 -2
- package/lib/plugins/service-betterportal-auth-default/registrationFlow.d.ts.map +1 -1
- package/lib/plugins/service-betterportal-auth-default/registrationFlow.js +15 -17
- package/lib/plugins/service-betterportal-auth-default/registrationFlow.js.map +1 -1
- package/lib/schemas/service-betterportal-auth-default.json +26 -2
- package/lib/schemas/service-betterportal-auth-default.plugin.json +27 -3
- package/lib/social.d.ts +40 -0
- package/lib/social.d.ts.map +1 -0
- package/lib/social.js +178 -0
- package/lib/social.js.map +1 -0
- package/lib/storage.d.ts +72 -0
- package/lib/storage.d.ts.map +1 -0
- package/lib/storage.js +427 -0
- package/lib/storage.js.map +1 -0
- package/package.json +14 -7
package/lib/identity.js
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import argon2 from "argon2";
|
|
3
|
+
import bcrypt from "bcrypt";
|
|
4
|
+
import * as av from "anyvali";
|
|
5
|
+
import { uuidv7 } from "@betterportal/framework";
|
|
6
|
+
export class AuthError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(message, status = 400) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const tenantScope = (tenantId) => ({ tenantId, appId: "" });
|
|
14
|
+
export const secretHash = (value) => createHash("sha256").update(value).digest("hex");
|
|
15
|
+
export const newSecret = () => randomBytes(32).toString("base64url");
|
|
16
|
+
export const normalizeAccountIdentifier = (value) => value.trim().toLowerCase();
|
|
17
|
+
const EmailSchema = av.string().maxLength(254).format("email");
|
|
18
|
+
export function isValidEmail(value) {
|
|
19
|
+
// Bound input before parsing: AnyVali collects issues rather than stopping at maxLength.
|
|
20
|
+
return value.length <= 254 && EmailSchema.safeParse(value).success;
|
|
21
|
+
}
|
|
22
|
+
export function equalSecret(a, b) {
|
|
23
|
+
const aa = Buffer.from(secretHash(a));
|
|
24
|
+
const bb = Buffer.from(secretHash(b));
|
|
25
|
+
return timingSafeEqual(aa, bb);
|
|
26
|
+
}
|
|
27
|
+
export class SecretCipher {
|
|
28
|
+
key;
|
|
29
|
+
constructor(key) {
|
|
30
|
+
this.key = key;
|
|
31
|
+
if (key.length !== 32)
|
|
32
|
+
throw new Error("Invalid auth encryption key");
|
|
33
|
+
}
|
|
34
|
+
encrypt(value) {
|
|
35
|
+
const iv = randomBytes(12);
|
|
36
|
+
const cipher = createCipheriv("aes-256-gcm", this.key, iv);
|
|
37
|
+
const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
38
|
+
return Buffer.concat([iv, cipher.getAuthTag(), encrypted]).toString("base64url");
|
|
39
|
+
}
|
|
40
|
+
decrypt(value) {
|
|
41
|
+
const input = Buffer.from(value, "base64url");
|
|
42
|
+
const decipher = createDecipheriv("aes-256-gcm", this.key, input.subarray(0, 12));
|
|
43
|
+
decipher.setAuthTag(input.subarray(12, 28));
|
|
44
|
+
return Buffer.concat([decipher.update(input.subarray(28)), decipher.final()]).toString("utf8");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function validatePassword(value) {
|
|
48
|
+
if (value.length < 12 || Buffer.byteLength(value, "utf8") > 1024)
|
|
49
|
+
throw new AuthError("Use a password between 12 and 1024 bytes.");
|
|
50
|
+
if (/^(password|qwerty|letmein|welcome|123456|betterportal|admin)/i.test(value))
|
|
51
|
+
throw new AuthError("Choose a less common password.");
|
|
52
|
+
}
|
|
53
|
+
export async function hashPassword(value) { validatePassword(value); return argon2.hash(value, { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1 }); }
|
|
54
|
+
let dummyHash;
|
|
55
|
+
export async function verifyPassword(hash, value) {
|
|
56
|
+
// Legacy bcrypt accepted UTF-8 input beyond its 72-byte effective key length.
|
|
57
|
+
if (hash?.startsWith("$2"))
|
|
58
|
+
return bcrypt.compare(value, hash);
|
|
59
|
+
if (Buffer.byteLength(value, "utf8") > 1024)
|
|
60
|
+
return false;
|
|
61
|
+
if (!hash) {
|
|
62
|
+
dummyHash ??= hashPassword("unavailable account timing check");
|
|
63
|
+
await argon2.verify(await dummyHash, value);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (hash.startsWith("$argon2"))
|
|
67
|
+
return argon2.verify(hash, value);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
export class IdentityService {
|
|
71
|
+
storage;
|
|
72
|
+
cipher;
|
|
73
|
+
constructor(storage, cipher) {
|
|
74
|
+
this.storage = storage;
|
|
75
|
+
this.cipher = cipher;
|
|
76
|
+
}
|
|
77
|
+
async directory(tx, scope, isolation, lock = false) {
|
|
78
|
+
const root = tenantScope(scope.tenantId);
|
|
79
|
+
const settings = await tx.get("tenant", scope.tenantId, root);
|
|
80
|
+
if (settings && settings.isolation !== isolation)
|
|
81
|
+
throw new AuthError("User isolation is locked for this tenant.", 409);
|
|
82
|
+
if (lock) {
|
|
83
|
+
if (this.storage.mode === "simple") {
|
|
84
|
+
const tenants = await tx.list("tenant");
|
|
85
|
+
if (tenants.some(t => t.id !== scope.tenantId))
|
|
86
|
+
throw new AuthError("Simple auth supports one tenant. Upgrade to Advanced.", 426);
|
|
87
|
+
}
|
|
88
|
+
await tx.put("tenant", root, { ...settings, id: scope.tenantId, isolation, lockedAt: settings?.lockedAt ?? Date.now() });
|
|
89
|
+
}
|
|
90
|
+
return { tenantId: scope.tenantId, appId: (settings?.lockedAt ? settings.isolation : isolation) === "tenant" ? "" : scope.appId };
|
|
91
|
+
}
|
|
92
|
+
async effectiveIsolation(tenantId, requested) {
|
|
93
|
+
return this.storage.transaction(tenantScope(tenantId), async (tx) => {
|
|
94
|
+
const settings = await tx.get("tenant", tenantId, tenantScope(tenantId));
|
|
95
|
+
if (settings?.lockedAt && requested && requested !== settings.isolation)
|
|
96
|
+
throw new AuthError("User isolation cannot change after account creation.", 409);
|
|
97
|
+
return requested ?? settings?.isolation ?? "app";
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async hasNoUsers() { return this.storage.transaction(tenantScope(""), async (tx) => !(await tx.list("user")).length); }
|
|
101
|
+
async bootstrapAvailable(scope) {
|
|
102
|
+
return this.storage.transaction(scope, async (tx) => !(await tx.get("tenant", scope.tenantId, tenantScope(scope.tenantId)))?.bootstrapComplete);
|
|
103
|
+
}
|
|
104
|
+
async createUser(scope, policy, input, roles, root = false) {
|
|
105
|
+
const passwordHash = input.password ? await hashPassword(input.password) : undefined;
|
|
106
|
+
return this.storage.transaction(scope, async (tx) => this.createInTransaction(tx, scope, policy, input, passwordHash, roles, root));
|
|
107
|
+
}
|
|
108
|
+
async createInTransaction(tx, scope, policy, input, passwordHash, roles, root = false) {
|
|
109
|
+
const dir = await this.directory(tx, scope, policy.isolation, true);
|
|
110
|
+
if (this.storage.mode === "simple" && (await tx.list("user")).length >= 10)
|
|
111
|
+
throw new AuthError("Simple auth is limited to 10 stored users. Upgrade to Advanced.", 426);
|
|
112
|
+
const username = normalizeAccountIdentifier(input.username);
|
|
113
|
+
const email = input.email ? normalizeAccountIdentifier(input.email) : undefined;
|
|
114
|
+
if (!username || username.length > 254 || (email !== undefined && !isValidEmail(email)))
|
|
115
|
+
throw new AuthError("Enter a valid account identifier and email.");
|
|
116
|
+
for (const id of new Set([username, ...(email ? [email] : [])]))
|
|
117
|
+
if (await tx.get("identifier", id, dir))
|
|
118
|
+
throw new AuthError("This account cannot be registered.", 409);
|
|
119
|
+
if (roles?.includes("*") && !root)
|
|
120
|
+
throw new AuthError("Root cannot be assigned by account provisioning.", 403);
|
|
121
|
+
if (root) {
|
|
122
|
+
if ((await tx.get("tenant", scope.tenantId, tenantScope(scope.tenantId)))?.bootstrapComplete)
|
|
123
|
+
throw new AuthError("Bootstrap already completed.", 409);
|
|
124
|
+
const settings = await tx.get("tenant", scope.tenantId, tenantScope(scope.tenantId));
|
|
125
|
+
await tx.put("tenant", tenantScope(scope.tenantId), { ...settings, bootstrapComplete: true });
|
|
126
|
+
}
|
|
127
|
+
const user = { id: uuidv7(), username, tenantId: dir.tenantId, appId: dir.appId, passwordHash, email, emailVerified: input.verified ?? false, name: input.name, enabled: true, bootstrapAdmin: root, refreshVersion: 0, createdAt: Date.now(), updatedAt: Date.now() };
|
|
128
|
+
await tx.put("user", dir, user);
|
|
129
|
+
for (const id of new Set([username, ...(email ? [email] : [])]))
|
|
130
|
+
await tx.put("identifier", dir, { id, userId: user.id });
|
|
131
|
+
if (roles)
|
|
132
|
+
await tx.put("roles", scope, { id: user.id, roles, initialized: true });
|
|
133
|
+
await this.audit(tx, scope, user.id, "user.created");
|
|
134
|
+
return user;
|
|
135
|
+
}
|
|
136
|
+
async findUser(scope, policy, identifier, byId = false) {
|
|
137
|
+
return this.storage.transaction(scope, async (tx) => {
|
|
138
|
+
const dir = await this.directory(tx, scope, policy.isolation);
|
|
139
|
+
const id = byId ? identifier : (await tx.get("identifier", normalizeAccountIdentifier(identifier), dir))?.userId;
|
|
140
|
+
return id ? tx.get("user", id, dir) : undefined;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async authenticate(scope, policy, username, password) {
|
|
144
|
+
const before = await this.storage.transaction(scope, async (tx) => {
|
|
145
|
+
const dir = await this.directory(tx, scope, policy.isolation);
|
|
146
|
+
// Only password sign-in accepts exact legacy usernames for ambiguous identifiers.
|
|
147
|
+
// Email recovery and all other lookups continue to reject ambiguous ownership.
|
|
148
|
+
const index = await tx.get("legacy-username", username, dir) ?? await tx.get("identifier", normalizeAccountIdentifier(username), dir);
|
|
149
|
+
return typeof index?.userId === "string" ? tx.get("user", index.userId, dir) : undefined;
|
|
150
|
+
});
|
|
151
|
+
const verified = await verifyPassword(before?.passwordHash, password);
|
|
152
|
+
if (!before?.enabled || !verified)
|
|
153
|
+
return undefined;
|
|
154
|
+
const nextHash = before.passwordHash?.startsWith("$2") && Buffer.byteLength(password, "utf8") <= 1024 ? await argon2.hash(password, { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1 }) : undefined;
|
|
155
|
+
return this.storage.transaction(scope, async (tx) => {
|
|
156
|
+
const dir = await this.directory(tx, scope, policy.isolation);
|
|
157
|
+
const current = await tx.get("user", before.id, dir);
|
|
158
|
+
if (!current?.enabled || current.passwordHash !== before.passwordHash || current.refreshVersion !== before.refreshVersion)
|
|
159
|
+
return undefined;
|
|
160
|
+
if (nextHash) {
|
|
161
|
+
current.passwordHash = nextHash;
|
|
162
|
+
await tx.put("user", dir, current);
|
|
163
|
+
}
|
|
164
|
+
return current;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async updateUser(scope, policy, id, update) {
|
|
168
|
+
return this.storage.transaction(scope, async (tx) => {
|
|
169
|
+
const dir = await this.directory(tx, scope, policy.isolation);
|
|
170
|
+
const user = await tx.get("user", id, dir);
|
|
171
|
+
if (!user)
|
|
172
|
+
throw new AuthError("Account unavailable.", 404);
|
|
173
|
+
await update(user, tx);
|
|
174
|
+
user.updatedAt = Date.now();
|
|
175
|
+
await tx.put("user", dir, user);
|
|
176
|
+
return user;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async roles(tx, scope, user, policy, initialize = false) {
|
|
180
|
+
let assignment = await tx.get("roles", user.id, scope);
|
|
181
|
+
if (!assignment?.initialized && initialize) {
|
|
182
|
+
if (policy.defaultRoleIds.some(id => id === "*" || !policy.allowedRoleIds.includes(id)))
|
|
183
|
+
throw new AuthError("Default roles reference unavailable roles.", 503);
|
|
184
|
+
assignment = { id: user.id, roles: [...new Set(policy.defaultRoleIds)], initialized: true };
|
|
185
|
+
await tx.put("roles", scope, assignment);
|
|
186
|
+
await this.audit(tx, scope, user.id, "roles.initialized", { roles: assignment.roles });
|
|
187
|
+
}
|
|
188
|
+
const groups = await tx.list("group", { tenantId: user.tenantId, appId: user.appId });
|
|
189
|
+
const groupRoles = groups.filter(g => g.members.includes(user.id)).flatMap(g => g.appRoles[scope.appId] ?? []);
|
|
190
|
+
return [...new Set([...(assignment?.roles ?? []), ...groupRoles])].filter(id => id === "*" || policy.allowedRoleIds.includes(id));
|
|
191
|
+
}
|
|
192
|
+
async issueSession(scope, policy, user, issuer, refreshSeconds) {
|
|
193
|
+
return this.storage.transaction(scope, async (tx) => {
|
|
194
|
+
const dir = await this.directory(tx, scope, policy.isolation);
|
|
195
|
+
const current = await tx.get("user", user.id, dir);
|
|
196
|
+
if (!current?.enabled || current.refreshVersion !== user.refreshVersion)
|
|
197
|
+
throw new AuthError("Account changed; sign in again.", 401);
|
|
198
|
+
await tx.pruneSessions(scope, Date.now());
|
|
199
|
+
const id = uuidv7();
|
|
200
|
+
const roles = await this.roles(tx, scope, current, policy, true);
|
|
201
|
+
const issued = issuer.issueTokenPair({ sub: user.id, ...scope, roles, sessionId: id, sessionVersion: user.refreshVersion, name: current.name ?? current.username, email: current.email, picture: current.picture, authProvider: "betterportal.default", refreshContext: { sessionId: id, version: user.refreshVersion } });
|
|
202
|
+
await tx.put("session", scope, { id, userId: user.id, userAppId: dir.appId, version: user.refreshVersion, jti: issued.tokenId, previousJtis: [], expiresAt: Date.now() + refreshSeconds * 1000, createdAt: Date.now(), revoked: false });
|
|
203
|
+
await this.audit(tx, scope, user.id, "login.success");
|
|
204
|
+
return issued;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async refresh(scope, policy, claims, issuer) {
|
|
208
|
+
return this.storage.transaction(scope, async (tx) => {
|
|
209
|
+
const id = String(claims.refreshContext?.sessionId ?? "");
|
|
210
|
+
const session = await tx.get("session", id, scope);
|
|
211
|
+
const dir = await this.directory(tx, scope, policy.isolation);
|
|
212
|
+
const user = session ? await tx.get("user", session.userId, dir) : undefined;
|
|
213
|
+
if (!session || !user?.enabled || session.revoked || session.expiresAt <= Date.now() || user.id !== claims.sub || user.refreshVersion !== session.version)
|
|
214
|
+
return undefined;
|
|
215
|
+
if (session.jti !== claims.jti) {
|
|
216
|
+
session.revoked = true;
|
|
217
|
+
await tx.put("session", scope, session);
|
|
218
|
+
await this.audit(tx, scope, user.id, "refresh.replay");
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
const roles = await this.roles(tx, scope, user, policy);
|
|
222
|
+
const issued = issuer.issueTokenPair({ sub: user.id, ...scope, roles, sessionId: id, sessionVersion: user.refreshVersion, name: user.name ?? user.username, email: user.email, picture: user.picture, authProvider: "betterportal.default", refreshContext: { sessionId: id, version: user.refreshVersion } });
|
|
223
|
+
session.previousJtis = [...session.previousJtis.slice(-4), session.jti];
|
|
224
|
+
session.jti = issued.tokenId;
|
|
225
|
+
await tx.put("session", scope, session);
|
|
226
|
+
return issued;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async assertActiveSession(tx, scope, claims, user) {
|
|
230
|
+
const session = await tx.get("session", claims.sessionId ?? "", scope);
|
|
231
|
+
if (!session || session.revoked || session.expiresAt <= Date.now() || session.userId !== user.id || session.version !== user.refreshVersion
|
|
232
|
+
|| claims.sessionVersion !== user.refreshVersion)
|
|
233
|
+
throw new AuthError("Session changed; sign in again.", 401);
|
|
234
|
+
}
|
|
235
|
+
async revokeSession(scope, claims) {
|
|
236
|
+
await this.storage.transaction(scope, async (tx) => {
|
|
237
|
+
const session = await tx.get("session", String(claims.refreshContext?.sessionId ?? ""), scope);
|
|
238
|
+
if (session?.userId === claims.sub) {
|
|
239
|
+
session.revoked = true;
|
|
240
|
+
await tx.put("session", scope, session);
|
|
241
|
+
await this.audit(tx, scope, claims.sub, "session.revoked");
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
async challenge(scope, purpose, data, userId, lifetimeSeconds = 600) {
|
|
246
|
+
return this.storage.transaction(scope, async (tx) => this.challengeInTransaction(tx, scope, purpose, data, userId, lifetimeSeconds));
|
|
247
|
+
}
|
|
248
|
+
async challengeInTransaction(tx, scope, purpose, data, userId, lifetimeSeconds = 600) {
|
|
249
|
+
const id = uuidv7();
|
|
250
|
+
const secret = newSecret();
|
|
251
|
+
const records = await tx.list("challenge", scope);
|
|
252
|
+
for (const record of records)
|
|
253
|
+
if (record.expiresAt <= Date.now())
|
|
254
|
+
await tx.remove("challenge", record.id, scope);
|
|
255
|
+
if (records.filter(c => c.expiresAt > Date.now()).length >= 1000)
|
|
256
|
+
throw new AuthError("Too many pending requests. Try later.", 429);
|
|
257
|
+
await tx.put("challenge", scope, { id, purpose, data, userId, hash: secretHash(secret), attempts: 0, expiresAt: Date.now() + lifetimeSeconds * 1000 });
|
|
258
|
+
return { id, secret };
|
|
259
|
+
}
|
|
260
|
+
async consume(scope, purpose, id, secret, complete) {
|
|
261
|
+
return this.consumeWithPreparation(scope, purpose, id, secret, async () => undefined, complete);
|
|
262
|
+
}
|
|
263
|
+
async consumeWithPreparation(scope, purpose, id, secret, prepare, complete) {
|
|
264
|
+
// Charge the attempt independently. The application transaction must roll back
|
|
265
|
+
// every side effect on failure, while a failed factor still consumes an attempt.
|
|
266
|
+
const accepted = await this.storage.transaction(scope, async (tx) => {
|
|
267
|
+
const challenge = await tx.get("challenge", id, scope);
|
|
268
|
+
if (!challenge || challenge.purpose !== purpose || challenge.expiresAt <= Date.now() || challenge.attempts >= 5)
|
|
269
|
+
return false;
|
|
270
|
+
challenge.attempts++;
|
|
271
|
+
await tx.put("challenge", scope, challenge);
|
|
272
|
+
return equalSecret(challenge.hash, secretHash(secret)) ? challenge : false;
|
|
273
|
+
});
|
|
274
|
+
if (!accepted)
|
|
275
|
+
throw new AuthError("Verification expired or unavailable.");
|
|
276
|
+
// Remote work happens without a store/tenant lock. Revalidate and consume only on commit.
|
|
277
|
+
const prepared = await prepare(accepted);
|
|
278
|
+
return this.storage.transaction(scope, async (tx) => {
|
|
279
|
+
const challenge = await tx.get("challenge", id, scope);
|
|
280
|
+
if (!challenge || challenge.purpose !== purpose || challenge.expiresAt <= Date.now()
|
|
281
|
+
|| !equalSecret(challenge.hash, secretHash(secret)))
|
|
282
|
+
throw new AuthError("Verification expired or unavailable.");
|
|
283
|
+
const value = await complete(challenge, tx, prepared);
|
|
284
|
+
await tx.remove("challenge", id, scope);
|
|
285
|
+
return value;
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async rateLimit(scope, action, actor, max = 10, seconds = 600) {
|
|
289
|
+
const allowed = await this.storage.transaction(scope, async (tx) => {
|
|
290
|
+
const records = await tx.list("limit", scope);
|
|
291
|
+
for (const row of records)
|
|
292
|
+
if (Number(row.reset) <= Date.now())
|
|
293
|
+
await tx.remove("limit", row.id, scope);
|
|
294
|
+
if (records.filter(row => Number(row.reset) > Date.now()).length >= 10000)
|
|
295
|
+
return false;
|
|
296
|
+
const id = secretHash(`${action}:${actor}`);
|
|
297
|
+
const old = await tx.get("limit", id, scope);
|
|
298
|
+
const reset = Number(old?.reset ?? 0) > Date.now() ? Number(old.reset) : Date.now() + seconds * 1000;
|
|
299
|
+
const count = Number(old?.reset ?? 0) > Date.now() ? Number(old?.count ?? 0) + 1 : 1;
|
|
300
|
+
await tx.put("limit", scope, { id, reset, count });
|
|
301
|
+
return count <= max;
|
|
302
|
+
});
|
|
303
|
+
if (!allowed)
|
|
304
|
+
throw new AuthError("Too many attempts. Try again later.", 429);
|
|
305
|
+
}
|
|
306
|
+
async audit(tx, scope, actor, event, data = {}) {
|
|
307
|
+
await tx.put("audit", scope, { id: uuidv7(), actor, event, data, at: Date.now() });
|
|
308
|
+
if (this.storage.mode === "simple") {
|
|
309
|
+
const entries = await tx.list("audit", scope);
|
|
310
|
+
for (const row of entries.slice(0, Math.max(0, entries.length - 1000)))
|
|
311
|
+
await tx.remove("audit", row.id, scope);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from "./defaultAuthManifest.js";
|
|
2
2
|
export * from "./userStore.js";
|
|
3
3
|
export * from "./plugins/service-betterportal-auth-default/index.js";
|
|
4
|
+
export * from "./storage.js";
|
|
5
|
+
export * from "./identity.js";
|
|
6
|
+
export * from "./factors.js";
|
|
7
|
+
export * from "./social.js";
|
|
4
8
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sDAAsD,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sDAAsD,CAAC;AAErE,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from "./defaultAuthManifest.js";
|
|
2
2
|
export * from "./userStore.js";
|
|
3
3
|
export * from "./plugins/service-betterportal-auth-default/index.js";
|
|
4
|
+
export * from "./storage.js";
|
|
5
|
+
export * from "./identity.js";
|
|
6
|
+
export * from "./factors.js";
|
|
7
|
+
export * from "./social.js";
|
|
4
8
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sDAAsD,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sDAAsD,CAAC;AAErE,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
package/lib/mail.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type IdentityService } from "./identity.js";
|
|
2
|
+
import type { AuthTransaction, Scope } from "./storage.js";
|
|
3
|
+
export interface MailConfig {
|
|
4
|
+
transport: "postal" | "http";
|
|
5
|
+
url: string;
|
|
6
|
+
from: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseMailHeaders(configured: unknown): Record<string, string> | undefined;
|
|
11
|
+
export declare function validateMailUrl(value: unknown): void;
|
|
12
|
+
export declare class MailQueue {
|
|
13
|
+
private readonly identity;
|
|
14
|
+
private readonly config;
|
|
15
|
+
constructor(identity: IdentityService, config: (scope: Scope) => MailConfig | undefined);
|
|
16
|
+
enqueue(tx: AuthTransaction, scope: Scope, to: string, subject: string, text: string): Promise<void>;
|
|
17
|
+
drain(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=mail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail.d.ts","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAe,KAAK,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,WAAW,UAAU;IAAG,SAAS,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE;AAE1I,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAUxF;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAKpD;AACD,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAmB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAlD,QAAQ,EAAE,eAAe,EAAmB,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS;IACnH,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAepG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAkC7B"}
|
package/lib/mail.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { uuidv7 } from "@betterportal/framework";
|
|
2
|
+
import * as av from "anyvali";
|
|
3
|
+
import { AuthError } from "./identity.js";
|
|
4
|
+
const MailHeadersSchema = av.record(av.string());
|
|
5
|
+
export function parseMailHeaders(configured) {
|
|
6
|
+
if (configured === undefined || configured === "")
|
|
7
|
+
return undefined;
|
|
8
|
+
let value;
|
|
9
|
+
try {
|
|
10
|
+
value = typeof configured === "string" ? JSON.parse(configured) : configured;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new AuthError("Custom mail headers must be a valid JSON object of strings.", 503);
|
|
14
|
+
}
|
|
15
|
+
const result = MailHeadersSchema.safeParse(value);
|
|
16
|
+
if (!result.success)
|
|
17
|
+
throw new AuthError("Custom mail headers must be a JSON object of strings.", 503);
|
|
18
|
+
try {
|
|
19
|
+
new Headers(result.data);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new AuthError("Custom mail headers contain an invalid HTTP header name or value.", 503);
|
|
23
|
+
}
|
|
24
|
+
return result.data;
|
|
25
|
+
}
|
|
26
|
+
const MailUrlSchema = av.string().format("url");
|
|
27
|
+
export function validateMailUrl(value) {
|
|
28
|
+
const result = MailUrlSchema.safeParse(value);
|
|
29
|
+
if (!result.success)
|
|
30
|
+
throw new AuthError("Email delivery requires a valid absolute URL.", 503);
|
|
31
|
+
const url = new URL(result.data);
|
|
32
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1"].includes(url.hostname)))
|
|
33
|
+
throw new AuthError("Email delivery requires HTTPS.", 503);
|
|
34
|
+
}
|
|
35
|
+
export class MailQueue {
|
|
36
|
+
identity;
|
|
37
|
+
config;
|
|
38
|
+
constructor(identity, config) {
|
|
39
|
+
this.identity = identity;
|
|
40
|
+
this.config = config;
|
|
41
|
+
}
|
|
42
|
+
async enqueue(tx, scope, to, subject, text) {
|
|
43
|
+
const config = this.config(scope);
|
|
44
|
+
if (!config)
|
|
45
|
+
throw new AuthError("Email delivery has not been configured.", 503);
|
|
46
|
+
validateMailUrl(config.url);
|
|
47
|
+
if (this.identity.storage.mode === "simple") {
|
|
48
|
+
const rows = await tx.list("mail", scope);
|
|
49
|
+
if (rows.filter(row => row.state !== "sent").length >= 500)
|
|
50
|
+
throw new AuthError("Email queue is full. Contact your administrator.", 503);
|
|
51
|
+
const sent = rows.filter(row => row.state === "sent");
|
|
52
|
+
for (const row of sent.slice(0, Math.max(0, sent.length - 99)))
|
|
53
|
+
await tx.remove("mail", row.id, scope);
|
|
54
|
+
}
|
|
55
|
+
await tx.put("mail", scope, {
|
|
56
|
+
id: uuidv7(), scope, encrypted: this.identity.cipher.encrypt(JSON.stringify({ to, subject, text })),
|
|
57
|
+
attempts: 0, nextAttempt: Date.now(), state: "pending", updatedAt: Date.now()
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async drain() {
|
|
61
|
+
const rows = await this.identity.storage.transaction({ tenantId: "", appId: "" }, tx => tx.dueMail(Date.now(), 100));
|
|
62
|
+
for (const row of rows) {
|
|
63
|
+
if (row.state === "sent" || row.state === "failed" || row.nextAttempt > Date.now())
|
|
64
|
+
continue;
|
|
65
|
+
const lease = uuidv7();
|
|
66
|
+
const claimed = await this.identity.storage.transaction(row.scope, async (tx) => {
|
|
67
|
+
const current = await tx.get("mail", row.id, row.scope);
|
|
68
|
+
if (!current || current.state === "sent" || current.state === "failed" || current.nextAttempt > Date.now())
|
|
69
|
+
return false;
|
|
70
|
+
await tx.put("mail", row.scope, { ...current, lease, state: "sending", nextAttempt: Date.now() + 60000 });
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
if (!claimed)
|
|
74
|
+
continue;
|
|
75
|
+
let success = false;
|
|
76
|
+
try {
|
|
77
|
+
const config = this.config(row.scope);
|
|
78
|
+
if (!config)
|
|
79
|
+
throw new Error("Mail not configured");
|
|
80
|
+
validateMailUrl(config.url);
|
|
81
|
+
const payload = JSON.parse(this.identity.cipher.decrypt(row.encrypted));
|
|
82
|
+
const body = { from: config.from, to: payload.to, subject: payload.subject, text: payload.text, html: "" };
|
|
83
|
+
const url = config.transport === "postal" ? `${config.url.replace(/\/+$/, "")}/api/v1/send/message` : config.url;
|
|
84
|
+
const response = await fetch(url, {
|
|
85
|
+
method: "POST", redirect: "error", signal: AbortSignal.timeout(15000),
|
|
86
|
+
headers: { ...config.headers, "Content-Type": "application/json", ...(config.transport === "postal" ? { "X-Server-API-Key": config.apiKey ?? "" } : {}) },
|
|
87
|
+
body: JSON.stringify(config.transport === "postal" ? { from: body.from, to: [body.to], subject: body.subject, plain_body: body.text, html_body: body.html } : body)
|
|
88
|
+
});
|
|
89
|
+
success = response.ok && (config.transport !== "postal" || (await response.json()).status === "success");
|
|
90
|
+
}
|
|
91
|
+
catch { /* Persist a bounded delivery status, never provider bodies or secrets. */ }
|
|
92
|
+
await this.identity.storage.transaction(row.scope, async (tx) => {
|
|
93
|
+
const current = await tx.get("mail", row.id, row.scope);
|
|
94
|
+
if (current?.lease !== lease)
|
|
95
|
+
return;
|
|
96
|
+
const attempts = current.attempts + 1;
|
|
97
|
+
await tx.put("mail", row.scope, { ...current, encrypted: success ? "" : current.encrypted, attempts, state: success ? "sent" : attempts >= 5 ? "failed" : "pending", nextAttempt: Date.now() + Math.min(3600000, 30000 * 2 ** attempts), updatedAt: Date.now() });
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=mail.js.map
|
package/lib/mail.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"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"}
|