najm-auth 2.0.11 → 2.0.13
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 +47 -2
- package/dist/index.d.ts +90 -2
- package/dist/index.js +330 -17
- package/dist/schema/pg.d.ts +328 -1
- package/dist/schema/pg.js +14 -0
- package/dist/schema/sqlite.d.ts +364 -1
- package/dist/schema/sqlite.js +14 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/AuthPlugin.ts
|
|
9
|
-
import { Err as
|
|
9
|
+
import { Err as Err13, plugin } from "najm-core";
|
|
10
10
|
import { cache } from "najm-cache";
|
|
11
11
|
|
|
12
12
|
// src/auth.tokens.ts
|
|
@@ -90,6 +90,18 @@ var tokensTable = pgTable("tokens", {
|
|
|
90
90
|
userIdIdx: index("tokens_user_id_idx").on(table.userId),
|
|
91
91
|
expiresAtIdx: index("tokens_expires_at_idx").on(table.expiresAt)
|
|
92
92
|
}));
|
|
93
|
+
var credentialSetupSessionsTable = pgTable("credential_setup_sessions", {
|
|
94
|
+
...baseFields(16),
|
|
95
|
+
userId: text("user_id").references(() => usersTable.id, { onDelete: "cascade" }).notNull(),
|
|
96
|
+
purpose: text("purpose").notNull(),
|
|
97
|
+
tokenHash: text("token_hash").notNull().unique(),
|
|
98
|
+
expiresAt: timestamp("expires_at", { mode: "string" }).notNull(),
|
|
99
|
+
consumedAt: timestamp("consumed_at", { mode: "string" }),
|
|
100
|
+
revokedAt: timestamp("revoked_at", { mode: "string" })
|
|
101
|
+
}, (table) => ({
|
|
102
|
+
userPurposeIdx: index("credential_setup_sessions_user_purpose_idx").on(table.userId, table.purpose),
|
|
103
|
+
expiresAtIdx: index("credential_setup_sessions_expires_at_idx").on(table.expiresAt)
|
|
104
|
+
}));
|
|
93
105
|
var rolePermissionsTable = pgTable("role_permissions", {
|
|
94
106
|
roleId: text("role_id").notNull().references(() => rolesTable.id, { onDelete: "cascade" }),
|
|
95
107
|
permissionId: text("permission_id").notNull().references(() => permissionsTable.id, { onDelete: "cascade" }),
|
|
@@ -101,6 +113,7 @@ var authSchema = {
|
|
|
101
113
|
users: usersTable,
|
|
102
114
|
oauthAccounts: oauthAccountsTable,
|
|
103
115
|
tokens: tokensTable,
|
|
116
|
+
credentialSetupSessions: credentialSetupSessionsTable,
|
|
104
117
|
roles: rolesTable,
|
|
105
118
|
permissions: permissionsTable,
|
|
106
119
|
rolePermissions: rolePermissionsTable
|
|
@@ -169,6 +182,18 @@ var tokensTable2 = sqliteTable("tokens", {
|
|
|
169
182
|
userIdIdx: index2("tokens_user_id_idx").on(table.userId),
|
|
170
183
|
expiresAtIdx: index2("tokens_expires_at_idx").on(table.expiresAt)
|
|
171
184
|
}));
|
|
185
|
+
var credentialSetupSessionsTable2 = sqliteTable("credential_setup_sessions", {
|
|
186
|
+
...baseFields2(16),
|
|
187
|
+
userId: text2("user_id").references(() => usersTable2.id, { onDelete: "cascade" }).notNull(),
|
|
188
|
+
purpose: text2("purpose").notNull(),
|
|
189
|
+
tokenHash: text2("token_hash").notNull().unique(),
|
|
190
|
+
expiresAt: text2("expires_at").notNull(),
|
|
191
|
+
consumedAt: text2("consumed_at"),
|
|
192
|
+
revokedAt: text2("revoked_at")
|
|
193
|
+
}, (table) => ({
|
|
194
|
+
userPurposeIdx: index2("credential_setup_sessions_user_purpose_idx").on(table.userId, table.purpose),
|
|
195
|
+
expiresAtIdx: index2("credential_setup_sessions_expires_at_idx").on(table.expiresAt)
|
|
196
|
+
}));
|
|
172
197
|
var rolePermissionsTable2 = sqliteTable("role_permissions", {
|
|
173
198
|
...baseFields2(10),
|
|
174
199
|
roleId: text2("role_id").notNull().references(() => rolesTable2.id, { onDelete: "cascade" }),
|
|
@@ -180,6 +205,7 @@ var authSchema2 = {
|
|
|
180
205
|
users: usersTable2,
|
|
181
206
|
oauthAccounts: oauthAccountsTable2,
|
|
182
207
|
tokens: tokensTable2,
|
|
208
|
+
credentialSetupSessions: credentialSetupSessionsTable2,
|
|
183
209
|
roles: rolesTable2,
|
|
184
210
|
permissions: permissionsTable2,
|
|
185
211
|
rolePermissions: rolePermissionsTable2
|
|
@@ -2288,6 +2314,32 @@ var AuthService = class AuthService2 {
|
|
|
2288
2314
|
return this.inviteUser(body);
|
|
2289
2315
|
}
|
|
2290
2316
|
async loginUser(body) {
|
|
2317
|
+
const user = await this.verifyCredentials(body);
|
|
2318
|
+
return this.establishSession(user);
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Verify credentials and account policy without minting access/refresh
|
|
2322
|
+
* tokens or writing normal auth cookies. Sensitive onboarding flows can use
|
|
2323
|
+
* this before issuing a purpose-bound CredentialSetupService session.
|
|
2324
|
+
*/
|
|
2325
|
+
async verifyCredentials(body) {
|
|
2326
|
+
return this.verifyCredentialsForPolicy(body, { kind: "active" });
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Verify a pending, unverified account for one exact application role.
|
|
2330
|
+
* This deliberately does not establish a normal auth session. Applications
|
|
2331
|
+
* should exchange the result for a short-lived, purpose-bound setup session.
|
|
2332
|
+
*/
|
|
2333
|
+
async verifyPendingCredentials(body, expectedRole) {
|
|
2334
|
+
if (!expectedRole.trim()) {
|
|
2335
|
+
Err8(this.t("errors.invalidCredentials"), 401);
|
|
2336
|
+
}
|
|
2337
|
+
return this.verifyCredentialsForPolicy(body, {
|
|
2338
|
+
kind: "pending",
|
|
2339
|
+
expectedRole: expectedRole.trim().toLowerCase()
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
async verifyCredentialsForPolicy(body, policy) {
|
|
2291
2343
|
const password = body.password;
|
|
2292
2344
|
const rawIdentifier = "identifier" in body ? body.identifier : body.email;
|
|
2293
2345
|
const identifier = normalizeAuthIdentifier(rawIdentifier);
|
|
@@ -2318,18 +2370,29 @@ var AuthService = class AuthService2 {
|
|
|
2318
2370
|
}
|
|
2319
2371
|
Err8(this.t("errors.invalidCredentials"), 401);
|
|
2320
2372
|
}
|
|
2321
|
-
if (
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2373
|
+
if (policy.kind === "pending") {
|
|
2374
|
+
const role = typeof user.role === "string" ? user.role.toLowerCase() : "";
|
|
2375
|
+
if (user.status !== "pending" || user.emailVerified || role !== policy.expectedRole) {
|
|
2376
|
+
Err8(this.t("errors.invalidCredentials"), 401);
|
|
2377
|
+
}
|
|
2378
|
+
} else {
|
|
2379
|
+
if (user.status !== "active") {
|
|
2380
|
+
Err8(this.t("errors.accountInactive"), 403);
|
|
2381
|
+
}
|
|
2382
|
+
if (this.config.requireVerifiedEmail && !user.emailVerified) {
|
|
2383
|
+
Err8(this.t("errors.emailNotVerified"), 403);
|
|
2384
|
+
}
|
|
2326
2385
|
}
|
|
2327
2386
|
if ((user.failedLoginAttempts ?? 0) > 0 || user.lockoutUntil) {
|
|
2328
2387
|
await this.userService.resetFailedAttempts(user.id);
|
|
2329
2388
|
}
|
|
2330
2389
|
const { password: _, failedLoginAttempts: __, lockoutUntil: ___, ...sanitized } = user;
|
|
2390
|
+
return sanitized;
|
|
2391
|
+
}
|
|
2392
|
+
/** Establish a complete normal auth session for an already verified user. */
|
|
2393
|
+
async establishSession(user) {
|
|
2331
2394
|
this.authSessionService ??= new AuthSessionService(this.tokenService, this.userService, this.cookieManager);
|
|
2332
|
-
return this.authSessionService.establish(
|
|
2395
|
+
return this.authSessionService.establish(user);
|
|
2333
2396
|
}
|
|
2334
2397
|
async refreshTokens() {
|
|
2335
2398
|
const generated = await this.tokenService.refreshTokens();
|
|
@@ -4215,7 +4278,7 @@ function toSingular(plural) {
|
|
|
4215
4278
|
}
|
|
4216
4279
|
__name(toSingular, "toSingular");
|
|
4217
4280
|
function createResourceGuards(ownershipClass, resourceType, resource, options) {
|
|
4218
|
-
var
|
|
4281
|
+
var _a23, _b13;
|
|
4219
4282
|
const writeGuard = options?.adminGuard ?? isAdmin;
|
|
4220
4283
|
let AccessGuard = class AccessGuard {
|
|
4221
4284
|
static {
|
|
@@ -4236,7 +4299,7 @@ function createResourceGuards(ownershipClass, resourceType, resource, options) {
|
|
|
4236
4299
|
__param10(1, Params4("id")),
|
|
4237
4300
|
__metadata24("design:type", Function),
|
|
4238
4301
|
__metadata24("design:paramtypes", [Object, String]),
|
|
4239
|
-
__metadata24("design:returntype", typeof (
|
|
4302
|
+
__metadata24("design:returntype", typeof (_a23 = typeof Promise !== "undefined" && Promise) === "function" ? _a23 : Object)
|
|
4240
4303
|
], AccessGuard.prototype, "canActivate", null);
|
|
4241
4304
|
AccessGuard = __decorate24([
|
|
4242
4305
|
Injectable12()
|
|
@@ -4259,7 +4322,7 @@ function createResourceGuards(ownershipClass, resourceType, resource, options) {
|
|
|
4259
4322
|
__param10(0, User4()),
|
|
4260
4323
|
__metadata24("design:type", Function),
|
|
4261
4324
|
__metadata24("design:paramtypes", [Object]),
|
|
4262
|
-
__metadata24("design:returntype", typeof (
|
|
4325
|
+
__metadata24("design:returntype", typeof (_b13 = typeof Promise !== "undefined" && Promise) === "function" ? _b13 : Object)
|
|
4263
4326
|
], ListGuard.prototype, "canActivate", null);
|
|
4264
4327
|
ListGuard = __decorate24([
|
|
4265
4328
|
Injectable12()
|
|
@@ -4397,7 +4460,7 @@ function configureOwnership(config) {
|
|
|
4397
4460
|
Injectable12()
|
|
4398
4461
|
], GeneratedOwnershipService);
|
|
4399
4462
|
function bodyGuard(resourceType, bodyField, optional = false) {
|
|
4400
|
-
var
|
|
4463
|
+
var _a23;
|
|
4401
4464
|
let BodyAccessGuard = class BodyAccessGuard {
|
|
4402
4465
|
static {
|
|
4403
4466
|
__name(this, "BodyAccessGuard");
|
|
@@ -4419,7 +4482,7 @@ function configureOwnership(config) {
|
|
|
4419
4482
|
__param10(1, Body5()),
|
|
4420
4483
|
__metadata24("design:type", Function),
|
|
4421
4484
|
__metadata24("design:paramtypes", [Object, Object]),
|
|
4422
|
-
__metadata24("design:returntype", typeof (
|
|
4485
|
+
__metadata24("design:returntype", typeof (_a23 = typeof Promise !== "undefined" && Promise) === "function" ? _a23 : Object)
|
|
4423
4486
|
], BodyAccessGuard.prototype, "canActivate", null);
|
|
4424
4487
|
BodyAccessGuard = __decorate24([
|
|
4425
4488
|
Injectable12()
|
|
@@ -5456,6 +5519,252 @@ var OAUTH_MODULE = [
|
|
|
5456
5519
|
OAuthController
|
|
5457
5520
|
];
|
|
5458
5521
|
|
|
5522
|
+
// src/credentialSetup/CredentialSetupRepository.ts
|
|
5523
|
+
import { and as and5, eq as eq8, gt, isNull as isNull2, lt as lt2 } from "drizzle-orm";
|
|
5524
|
+
import { Err as Err11, Inject as Inject18, Repository as Repository6 } from "najm-core";
|
|
5525
|
+
import { DB as DB6 } from "najm-database";
|
|
5526
|
+
var __decorate33 = function(decorators, target, key, desc) {
|
|
5527
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5528
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5529
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5530
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5531
|
+
};
|
|
5532
|
+
var __metadata33 = function(k, v) {
|
|
5533
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
5534
|
+
};
|
|
5535
|
+
var CredentialSetupRepository = class CredentialSetupRepository2 {
|
|
5536
|
+
static {
|
|
5537
|
+
__name(this, "CredentialSetupRepository");
|
|
5538
|
+
}
|
|
5539
|
+
db;
|
|
5540
|
+
schema;
|
|
5541
|
+
get sessions() {
|
|
5542
|
+
const sessions = this.schema.credentialSetupSessions;
|
|
5543
|
+
if (!sessions) {
|
|
5544
|
+
Err11.invalidOperation("auth.schema.credentialSetupSessions is required to use CredentialSetupService");
|
|
5545
|
+
}
|
|
5546
|
+
return sessions;
|
|
5547
|
+
}
|
|
5548
|
+
async replaceActive(data) {
|
|
5549
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5550
|
+
await this.db.update(this.sessions).set({ revokedAt: now, updatedAt: now }).where(and5(eq8(this.sessions.userId, data.userId), eq8(this.sessions.purpose, data.purpose), isNull2(this.sessions.consumedAt), isNull2(this.sessions.revokedAt)));
|
|
5551
|
+
const [session] = await this.db.insert(this.sessions).values(data).returning({
|
|
5552
|
+
userId: this.sessions.userId,
|
|
5553
|
+
purpose: this.sessions.purpose,
|
|
5554
|
+
expiresAt: this.sessions.expiresAt
|
|
5555
|
+
});
|
|
5556
|
+
return session;
|
|
5557
|
+
}
|
|
5558
|
+
async findActive(tokenHash, purpose) {
|
|
5559
|
+
const [session] = await this.db.select({
|
|
5560
|
+
userId: this.sessions.userId,
|
|
5561
|
+
purpose: this.sessions.purpose,
|
|
5562
|
+
expiresAt: this.sessions.expiresAt
|
|
5563
|
+
}).from(this.sessions).where(and5(eq8(this.sessions.tokenHash, tokenHash), eq8(this.sessions.purpose, purpose), isNull2(this.sessions.consumedAt), isNull2(this.sessions.revokedAt), gt(this.sessions.expiresAt, (/* @__PURE__ */ new Date()).toISOString()))).limit(1);
|
|
5564
|
+
return session;
|
|
5565
|
+
}
|
|
5566
|
+
async consume(tokenHash, purpose) {
|
|
5567
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5568
|
+
const [session] = await this.db.update(this.sessions).set({ consumedAt: now, updatedAt: now }).where(and5(eq8(this.sessions.tokenHash, tokenHash), eq8(this.sessions.purpose, purpose), isNull2(this.sessions.consumedAt), isNull2(this.sessions.revokedAt), gt(this.sessions.expiresAt, now))).returning({
|
|
5569
|
+
userId: this.sessions.userId,
|
|
5570
|
+
purpose: this.sessions.purpose,
|
|
5571
|
+
expiresAt: this.sessions.expiresAt
|
|
5572
|
+
});
|
|
5573
|
+
return session;
|
|
5574
|
+
}
|
|
5575
|
+
async revoke(tokenHash, purpose) {
|
|
5576
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5577
|
+
const [session] = await this.db.update(this.sessions).set({ revokedAt: now, updatedAt: now }).where(and5(eq8(this.sessions.tokenHash, tokenHash), eq8(this.sessions.purpose, purpose), isNull2(this.sessions.consumedAt), isNull2(this.sessions.revokedAt))).returning({ userId: this.sessions.userId });
|
|
5578
|
+
return session;
|
|
5579
|
+
}
|
|
5580
|
+
async deleteExpired() {
|
|
5581
|
+
return this.db.delete(this.sessions).where(lt2(this.sessions.expiresAt, (/* @__PURE__ */ new Date()).toISOString())).returning({ userId: this.sessions.userId });
|
|
5582
|
+
}
|
|
5583
|
+
};
|
|
5584
|
+
__decorate33([
|
|
5585
|
+
DB6(),
|
|
5586
|
+
__metadata33("design:type", Object)
|
|
5587
|
+
], CredentialSetupRepository.prototype, "db", void 0);
|
|
5588
|
+
__decorate33([
|
|
5589
|
+
Inject18(AUTH_SCHEMA),
|
|
5590
|
+
__metadata33("design:type", Object)
|
|
5591
|
+
], CredentialSetupRepository.prototype, "schema", void 0);
|
|
5592
|
+
CredentialSetupRepository = __decorate33([
|
|
5593
|
+
Repository6()
|
|
5594
|
+
], CredentialSetupRepository);
|
|
5595
|
+
|
|
5596
|
+
// src/credentialSetup/CredentialSetupService.ts
|
|
5597
|
+
import { createHash as createHash5, randomBytes as randomBytes4 } from "crypto";
|
|
5598
|
+
import { CookieService as CookieService3 } from "najm-cookies";
|
|
5599
|
+
import { Err as Err12, Injectable as Injectable19 } from "najm-core";
|
|
5600
|
+
import { Transaction as Transaction3 } from "najm-database";
|
|
5601
|
+
var __decorate34 = function(decorators, target, key, desc) {
|
|
5602
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5603
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5604
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5605
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5606
|
+
};
|
|
5607
|
+
var __metadata34 = function(k, v) {
|
|
5608
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
5609
|
+
};
|
|
5610
|
+
var _a22;
|
|
5611
|
+
var _b12;
|
|
5612
|
+
var _c8;
|
|
5613
|
+
var _d5;
|
|
5614
|
+
var _e4;
|
|
5615
|
+
var _f4;
|
|
5616
|
+
var _g3;
|
|
5617
|
+
var DEFAULT_COOKIE_NAME = "najm.credential-setup";
|
|
5618
|
+
var DEFAULT_TTL_MS = 10 * 60 * 1e3;
|
|
5619
|
+
var MAX_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5620
|
+
var PURPOSE_PATTERN = /^[a-z0-9](?:[a-z0-9:_-]{0,62}[a-z0-9])?$/;
|
|
5621
|
+
var COOKIE_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
5622
|
+
var CredentialSetupService = class CredentialSetupService2 {
|
|
5623
|
+
static {
|
|
5624
|
+
__name(this, "CredentialSetupService");
|
|
5625
|
+
}
|
|
5626
|
+
repository;
|
|
5627
|
+
tokens;
|
|
5628
|
+
authCookies;
|
|
5629
|
+
cookies;
|
|
5630
|
+
constructor(repository, tokens, authCookies, cookies2) {
|
|
5631
|
+
this.repository = repository;
|
|
5632
|
+
this.tokens = tokens;
|
|
5633
|
+
this.authCookies = authCookies;
|
|
5634
|
+
this.cookies = cookies2;
|
|
5635
|
+
}
|
|
5636
|
+
/**
|
|
5637
|
+
* Revoke normal auth sessions and issue a single-purpose, short-lived,
|
|
5638
|
+
* browser-session cookie. No access or refresh token is minted.
|
|
5639
|
+
*/
|
|
5640
|
+
async begin(userId, options) {
|
|
5641
|
+
const resolved = this.resolveOptions(options);
|
|
5642
|
+
const token = randomBytes4(32).toString("base64url");
|
|
5643
|
+
const expiresAt = new Date(Date.now() + resolved.ttlMs).toISOString();
|
|
5644
|
+
await this.repository.deleteExpired();
|
|
5645
|
+
await this.repository.replaceActive({
|
|
5646
|
+
userId,
|
|
5647
|
+
purpose: resolved.purpose,
|
|
5648
|
+
tokenHash: this.hashToken(token),
|
|
5649
|
+
expiresAt
|
|
5650
|
+
});
|
|
5651
|
+
await this.tokens.invalidateUserAccessTokens(userId);
|
|
5652
|
+
await this.tokens.revokeAllForUser(userId);
|
|
5653
|
+
this.authCookies.clearRefreshToken();
|
|
5654
|
+
this.authCookies.clearSessionCookie();
|
|
5655
|
+
this.setCookie(token, resolved);
|
|
5656
|
+
return { purpose: resolved.purpose, expiresAt };
|
|
5657
|
+
}
|
|
5658
|
+
/** Validate and return the current active setup session without consuming it. */
|
|
5659
|
+
async require(options) {
|
|
5660
|
+
const resolved = this.resolveOptions(options);
|
|
5661
|
+
const token = this.cookies.get(resolved.cookieName);
|
|
5662
|
+
if (!token)
|
|
5663
|
+
Err12("Credential setup session is required", 401);
|
|
5664
|
+
const session = await this.repository.findActive(this.hashToken(token), resolved.purpose);
|
|
5665
|
+
if (!session) {
|
|
5666
|
+
this.clearCookie(resolved);
|
|
5667
|
+
Err12("Credential setup session is invalid or expired", 401);
|
|
5668
|
+
}
|
|
5669
|
+
return session;
|
|
5670
|
+
}
|
|
5671
|
+
/**
|
|
5672
|
+
* Atomically consume the setup session and execute the application-owned
|
|
5673
|
+
* credential mutation in the same database transaction. If the callback
|
|
5674
|
+
* fails, consumption rolls back and the browser may safely retry.
|
|
5675
|
+
*/
|
|
5676
|
+
async consume(options, complete) {
|
|
5677
|
+
const resolved = this.resolveOptions(options);
|
|
5678
|
+
const token = this.cookies.get(resolved.cookieName);
|
|
5679
|
+
if (!token)
|
|
5680
|
+
Err12("Credential setup session is required", 401);
|
|
5681
|
+
const session = await this.repository.consume(this.hashToken(token), resolved.purpose);
|
|
5682
|
+
if (!session) {
|
|
5683
|
+
this.clearCookie(resolved);
|
|
5684
|
+
Err12("Credential setup session is invalid or expired", 401);
|
|
5685
|
+
}
|
|
5686
|
+
const result = await complete(session);
|
|
5687
|
+
this.clearCookie(resolved);
|
|
5688
|
+
return result;
|
|
5689
|
+
}
|
|
5690
|
+
async cancel(options) {
|
|
5691
|
+
const resolved = this.resolveOptions(options);
|
|
5692
|
+
const token = this.cookies.get(resolved.cookieName);
|
|
5693
|
+
if (token) {
|
|
5694
|
+
await this.repository.revoke(this.hashToken(token), resolved.purpose);
|
|
5695
|
+
}
|
|
5696
|
+
this.clearCookie(resolved);
|
|
5697
|
+
return { cancelled: true };
|
|
5698
|
+
}
|
|
5699
|
+
async pruneExpired() {
|
|
5700
|
+
await this.repository.deleteExpired();
|
|
5701
|
+
}
|
|
5702
|
+
resolveOptions(options) {
|
|
5703
|
+
const purpose = options.purpose?.trim().toLowerCase();
|
|
5704
|
+
const cookieName = options.cookieName?.trim() || DEFAULT_COOKIE_NAME;
|
|
5705
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
5706
|
+
const cookiePath = options.cookiePath ?? "/";
|
|
5707
|
+
if (!purpose || !PURPOSE_PATTERN.test(purpose)) {
|
|
5708
|
+
Err12("Credential setup purpose must be 1-64 lowercase letters, numbers, colon, underscore, or hyphen", 500);
|
|
5709
|
+
}
|
|
5710
|
+
if (!COOKIE_NAME_PATTERN.test(cookieName) || cookieName.length > 128) {
|
|
5711
|
+
Err12("Credential setup cookie name is invalid", 500);
|
|
5712
|
+
}
|
|
5713
|
+
if (!Number.isInteger(ttlMs) || ttlMs < 1e3 || ttlMs > MAX_TTL_MS) {
|
|
5714
|
+
Err12("Credential setup ttlMs must be an integer between 1000 and 86400000", 500);
|
|
5715
|
+
}
|
|
5716
|
+
if (!cookiePath.startsWith("/") || cookiePath.includes(";") || cookiePath.includes("\\")) {
|
|
5717
|
+
Err12("Credential setup cookiePath must be a same-origin path", 500);
|
|
5718
|
+
}
|
|
5719
|
+
return { purpose, cookieName, ttlMs, cookiePath };
|
|
5720
|
+
}
|
|
5721
|
+
hashToken(token) {
|
|
5722
|
+
return createHash5("sha256").update(token).digest("hex");
|
|
5723
|
+
}
|
|
5724
|
+
setCookie(token, options) {
|
|
5725
|
+
this.cookies.setSession(options.cookieName, token, {
|
|
5726
|
+
httpOnly: true,
|
|
5727
|
+
path: options.cookiePath,
|
|
5728
|
+
sameSite: "Strict",
|
|
5729
|
+
secure: process.env.NODE_ENV === "production"
|
|
5730
|
+
});
|
|
5731
|
+
}
|
|
5732
|
+
clearCookie(options) {
|
|
5733
|
+
this.cookies.delete(options.cookieName, {
|
|
5734
|
+
path: options.cookiePath,
|
|
5735
|
+
secure: process.env.NODE_ENV === "production"
|
|
5736
|
+
});
|
|
5737
|
+
}
|
|
5738
|
+
};
|
|
5739
|
+
__decorate34([
|
|
5740
|
+
Transaction3({ retries: 2 }),
|
|
5741
|
+
__metadata34("design:type", Function),
|
|
5742
|
+
__metadata34("design:paramtypes", [String, Object]),
|
|
5743
|
+
__metadata34("design:returntype", typeof (_e4 = typeof Promise !== "undefined" && Promise) === "function" ? _e4 : Object)
|
|
5744
|
+
], CredentialSetupService.prototype, "begin", null);
|
|
5745
|
+
__decorate34([
|
|
5746
|
+
Transaction3({ retries: 2 }),
|
|
5747
|
+
__metadata34("design:type", Function),
|
|
5748
|
+
__metadata34("design:paramtypes", [Object, Function]),
|
|
5749
|
+
__metadata34("design:returntype", typeof (_f4 = typeof Promise !== "undefined" && Promise) === "function" ? _f4 : Object)
|
|
5750
|
+
], CredentialSetupService.prototype, "consume", null);
|
|
5751
|
+
__decorate34([
|
|
5752
|
+
Transaction3({ retries: 2 }),
|
|
5753
|
+
__metadata34("design:type", Function),
|
|
5754
|
+
__metadata34("design:paramtypes", [Object]),
|
|
5755
|
+
__metadata34("design:returntype", typeof (_g3 = typeof Promise !== "undefined" && Promise) === "function" ? _g3 : Object)
|
|
5756
|
+
], CredentialSetupService.prototype, "cancel", null);
|
|
5757
|
+
CredentialSetupService = __decorate34([
|
|
5758
|
+
Injectable19(),
|
|
5759
|
+
__metadata34("design:paramtypes", [typeof (_a22 = typeof CredentialSetupRepository !== "undefined" && CredentialSetupRepository) === "function" ? _a22 : Object, typeof (_b12 = typeof TokenService !== "undefined" && TokenService) === "function" ? _b12 : Object, typeof (_c8 = typeof CookieManager !== "undefined" && CookieManager) === "function" ? _c8 : Object, typeof (_d5 = typeof CookieService3 !== "undefined" && CookieService3) === "function" ? _d5 : Object])
|
|
5760
|
+
], CredentialSetupService);
|
|
5761
|
+
|
|
5762
|
+
// src/credentialSetup/index.ts
|
|
5763
|
+
var CREDENTIAL_SETUP_MODULE = [
|
|
5764
|
+
CredentialSetupRepository,
|
|
5765
|
+
CredentialSetupService
|
|
5766
|
+
];
|
|
5767
|
+
|
|
5459
5768
|
// src/AuthPlugin.ts
|
|
5460
5769
|
var DEFAULT_JWT = {
|
|
5461
5770
|
accessSecret: process.env.JWT_ACCESS_SECRET || "",
|
|
@@ -5477,9 +5786,9 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
|
|
|
5477
5786
|
const clientId = google.clientId ?? process.env.GOOGLE_CLIENT_ID ?? "";
|
|
5478
5787
|
const clientSecret = google.clientSecret ?? process.env.GOOGLE_CLIENT_SECRET ?? "";
|
|
5479
5788
|
if (!clientId)
|
|
5480
|
-
throw
|
|
5789
|
+
throw Err13.configRequired("auth.oauth.google", "GOOGLE_CLIENT_ID");
|
|
5481
5790
|
if (!clientSecret)
|
|
5482
|
-
throw
|
|
5791
|
+
throw Err13.configRequired("auth.oauth.google", "GOOGLE_CLIENT_SECRET");
|
|
5483
5792
|
const frontendUrl = config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000";
|
|
5484
5793
|
const callbackUrl = google.callbackUrl ?? process.env.GOOGLE_CALLBACK_URL ?? `${frontendUrl.replace(/\/$/, "")}/api/auth/oauth/google/callback`;
|
|
5485
5794
|
let callback;
|
|
@@ -5538,10 +5847,10 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
|
|
|
5538
5847
|
}
|
|
5539
5848
|
};
|
|
5540
5849
|
if (!finalConfig.jwt.accessSecret) {
|
|
5541
|
-
throw
|
|
5850
|
+
throw Err13.configRequired("auth", "JWT_ACCESS_SECRET");
|
|
5542
5851
|
}
|
|
5543
5852
|
if (!finalConfig.jwt.refreshSecret) {
|
|
5544
|
-
throw
|
|
5853
|
+
throw Err13.configRequired("auth", "JWT_REFRESH_SECRET");
|
|
5545
5854
|
}
|
|
5546
5855
|
return finalConfig;
|
|
5547
5856
|
}, "resolveAuthConfig");
|
|
@@ -5561,7 +5870,7 @@ var selectAuthSchema = /* @__PURE__ */ __name((config) => {
|
|
|
5561
5870
|
return authSchema;
|
|
5562
5871
|
}
|
|
5563
5872
|
}, "selectAuthSchema");
|
|
5564
|
-
var auth = /* @__PURE__ */ __name((config) => plugin("auth").version("1.0.0").depends(cache(), cookies(), i18n(), guards(), validation(config?.validation), rateLimit(config?.rateLimit), email(config?.email)).requires("database").contributes(I18N_CONTRIBUTIONS, AUTH_LOCALES).services(AUTH_MODULE, OAUTH_MODULE, users_exports, roles_exports, permissions_exports, tokens_exports, ScopeContext).config(AUTH_CONFIG, resolveAuthConfig(config)).set(AUTH_SCHEMA, selectAuthSchema(config)).set(AUTH_ENCRYPTION_KEY, config?.encryptionKey ?? null).build(), "auth");
|
|
5873
|
+
var auth = /* @__PURE__ */ __name((config) => plugin("auth").version("1.0.0").depends(cache(), cookies(), i18n(), guards(), validation(config?.validation), rateLimit(config?.rateLimit), email(config?.email)).requires("database").contributes(I18N_CONTRIBUTIONS, AUTH_LOCALES).services(AUTH_MODULE, OAUTH_MODULE, users_exports, roles_exports, permissions_exports, tokens_exports, CREDENTIAL_SETUP_MODULE, ScopeContext).config(AUTH_CONFIG, resolveAuthConfig(config)).set(AUTH_SCHEMA, selectAuthSchema(config)).set(AUTH_ENCRYPTION_KEY, config?.encryptionKey ?? null).build(), "auth");
|
|
5565
5874
|
|
|
5566
5875
|
// src/seed.ts
|
|
5567
5876
|
var toSeedId = /* @__PURE__ */ __name((prefix, value) => {
|
|
@@ -5708,6 +6017,7 @@ export {
|
|
|
5708
6017
|
AuthResolver,
|
|
5709
6018
|
AuthService,
|
|
5710
6019
|
AuthSessionService,
|
|
6020
|
+
CREDENTIAL_SETUP_MODULE,
|
|
5711
6021
|
Can,
|
|
5712
6022
|
CanCreate,
|
|
5713
6023
|
CanDelete,
|
|
@@ -5715,6 +6025,8 @@ export {
|
|
|
5715
6025
|
CanRead,
|
|
5716
6026
|
CanUpdate,
|
|
5717
6027
|
CookieManager,
|
|
6028
|
+
CredentialSetupRepository,
|
|
6029
|
+
CredentialSetupService,
|
|
5718
6030
|
EncryptionService,
|
|
5719
6031
|
Owned,
|
|
5720
6032
|
OwnershipToken,
|
|
@@ -5762,6 +6074,7 @@ export {
|
|
|
5762
6074
|
createRoleDto,
|
|
5763
6075
|
createTokenDto,
|
|
5764
6076
|
createUserDto,
|
|
6077
|
+
credentialSetupSessionsTable,
|
|
5765
6078
|
defineRoles,
|
|
5766
6079
|
emailParam,
|
|
5767
6080
|
formatDate,
|