najm-auth 2.0.10 → 2.0.12
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 +41 -2
- package/dist/index.d.ts +85 -4
- package/dist/index.js +313 -16
- 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
|
|
@@ -573,10 +599,14 @@ var UserRepository = class UserRepository2 {
|
|
|
573
599
|
get roles() {
|
|
574
600
|
return this.schema.roles;
|
|
575
601
|
}
|
|
576
|
-
/** Shared query helper */
|
|
602
|
+
/** Shared query helper, scoped to the current database/transaction identity. */
|
|
577
603
|
queryHelper;
|
|
578
604
|
get q() {
|
|
579
|
-
|
|
605
|
+
const db = this.db;
|
|
606
|
+
if (this.queryHelper?.db !== db) {
|
|
607
|
+
this.queryHelper = { db, queries: new AuthQueries(db, this.schema) };
|
|
608
|
+
}
|
|
609
|
+
return this.queryHelper.queries;
|
|
580
610
|
}
|
|
581
611
|
async getAll(limit = 50, offset = 0) {
|
|
582
612
|
const allUsers = await this.db.select(this.q.userSelection()).from(this.users).leftJoin(this.roles, eq2(this.users.roleId, this.roles.id)).limit(limit).offset(offset);
|
|
@@ -1423,10 +1453,14 @@ var TokenRepository = class TokenRepository2 {
|
|
|
1423
1453
|
get users() {
|
|
1424
1454
|
return this.schema.users;
|
|
1425
1455
|
}
|
|
1426
|
-
/** Shared query helper */
|
|
1456
|
+
/** Shared query helper, scoped to the current database/transaction identity. */
|
|
1427
1457
|
queryHelper;
|
|
1428
1458
|
get q() {
|
|
1429
|
-
|
|
1459
|
+
const db = this.db;
|
|
1460
|
+
if (this.queryHelper?.db !== db) {
|
|
1461
|
+
this.queryHelper = { db, queries: new AuthQueries(db, this.schema) };
|
|
1462
|
+
}
|
|
1463
|
+
return this.queryHelper.queries;
|
|
1430
1464
|
}
|
|
1431
1465
|
/**
|
|
1432
1466
|
* Upsert the refresh-token row for a session, keyed on `tokenFamily` (the
|
|
@@ -2280,6 +2314,15 @@ var AuthService = class AuthService2 {
|
|
|
2280
2314
|
return this.inviteUser(body);
|
|
2281
2315
|
}
|
|
2282
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) {
|
|
2283
2326
|
const password = body.password;
|
|
2284
2327
|
const rawIdentifier = "identifier" in body ? body.identifier : body.email;
|
|
2285
2328
|
const identifier = normalizeAuthIdentifier(rawIdentifier);
|
|
@@ -2320,8 +2363,12 @@ var AuthService = class AuthService2 {
|
|
|
2320
2363
|
await this.userService.resetFailedAttempts(user.id);
|
|
2321
2364
|
}
|
|
2322
2365
|
const { password: _, failedLoginAttempts: __, lockoutUntil: ___, ...sanitized } = user;
|
|
2366
|
+
return sanitized;
|
|
2367
|
+
}
|
|
2368
|
+
/** Establish a complete normal auth session for an already verified user. */
|
|
2369
|
+
async establishSession(user) {
|
|
2323
2370
|
this.authSessionService ??= new AuthSessionService(this.tokenService, this.userService, this.cookieManager);
|
|
2324
|
-
return this.authSessionService.establish(
|
|
2371
|
+
return this.authSessionService.establish(user);
|
|
2325
2372
|
}
|
|
2326
2373
|
async refreshTokens() {
|
|
2327
2374
|
const generated = await this.tokenService.refreshTokens();
|
|
@@ -4207,7 +4254,7 @@ function toSingular(plural) {
|
|
|
4207
4254
|
}
|
|
4208
4255
|
__name(toSingular, "toSingular");
|
|
4209
4256
|
function createResourceGuards(ownershipClass, resourceType, resource, options) {
|
|
4210
|
-
var
|
|
4257
|
+
var _a23, _b13;
|
|
4211
4258
|
const writeGuard = options?.adminGuard ?? isAdmin;
|
|
4212
4259
|
let AccessGuard = class AccessGuard {
|
|
4213
4260
|
static {
|
|
@@ -4228,7 +4275,7 @@ function createResourceGuards(ownershipClass, resourceType, resource, options) {
|
|
|
4228
4275
|
__param10(1, Params4("id")),
|
|
4229
4276
|
__metadata24("design:type", Function),
|
|
4230
4277
|
__metadata24("design:paramtypes", [Object, String]),
|
|
4231
|
-
__metadata24("design:returntype", typeof (
|
|
4278
|
+
__metadata24("design:returntype", typeof (_a23 = typeof Promise !== "undefined" && Promise) === "function" ? _a23 : Object)
|
|
4232
4279
|
], AccessGuard.prototype, "canActivate", null);
|
|
4233
4280
|
AccessGuard = __decorate24([
|
|
4234
4281
|
Injectable12()
|
|
@@ -4251,7 +4298,7 @@ function createResourceGuards(ownershipClass, resourceType, resource, options) {
|
|
|
4251
4298
|
__param10(0, User4()),
|
|
4252
4299
|
__metadata24("design:type", Function),
|
|
4253
4300
|
__metadata24("design:paramtypes", [Object]),
|
|
4254
|
-
__metadata24("design:returntype", typeof (
|
|
4301
|
+
__metadata24("design:returntype", typeof (_b13 = typeof Promise !== "undefined" && Promise) === "function" ? _b13 : Object)
|
|
4255
4302
|
], ListGuard.prototype, "canActivate", null);
|
|
4256
4303
|
ListGuard = __decorate24([
|
|
4257
4304
|
Injectable12()
|
|
@@ -4389,7 +4436,7 @@ function configureOwnership(config) {
|
|
|
4389
4436
|
Injectable12()
|
|
4390
4437
|
], GeneratedOwnershipService);
|
|
4391
4438
|
function bodyGuard(resourceType, bodyField, optional = false) {
|
|
4392
|
-
var
|
|
4439
|
+
var _a23;
|
|
4393
4440
|
let BodyAccessGuard = class BodyAccessGuard {
|
|
4394
4441
|
static {
|
|
4395
4442
|
__name(this, "BodyAccessGuard");
|
|
@@ -4411,7 +4458,7 @@ function configureOwnership(config) {
|
|
|
4411
4458
|
__param10(1, Body5()),
|
|
4412
4459
|
__metadata24("design:type", Function),
|
|
4413
4460
|
__metadata24("design:paramtypes", [Object, Object]),
|
|
4414
|
-
__metadata24("design:returntype", typeof (
|
|
4461
|
+
__metadata24("design:returntype", typeof (_a23 = typeof Promise !== "undefined" && Promise) === "function" ? _a23 : Object)
|
|
4415
4462
|
], BodyAccessGuard.prototype, "canActivate", null);
|
|
4416
4463
|
BodyAccessGuard = __decorate24([
|
|
4417
4464
|
Injectable12()
|
|
@@ -5448,6 +5495,252 @@ var OAUTH_MODULE = [
|
|
|
5448
5495
|
OAuthController
|
|
5449
5496
|
];
|
|
5450
5497
|
|
|
5498
|
+
// src/credentialSetup/CredentialSetupRepository.ts
|
|
5499
|
+
import { and as and5, eq as eq8, gt, isNull as isNull2, lt as lt2 } from "drizzle-orm";
|
|
5500
|
+
import { Err as Err11, Inject as Inject18, Repository as Repository6 } from "najm-core";
|
|
5501
|
+
import { DB as DB6 } from "najm-database";
|
|
5502
|
+
var __decorate33 = function(decorators, target, key, desc) {
|
|
5503
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5504
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5505
|
+
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;
|
|
5506
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5507
|
+
};
|
|
5508
|
+
var __metadata33 = function(k, v) {
|
|
5509
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
5510
|
+
};
|
|
5511
|
+
var CredentialSetupRepository = class CredentialSetupRepository2 {
|
|
5512
|
+
static {
|
|
5513
|
+
__name(this, "CredentialSetupRepository");
|
|
5514
|
+
}
|
|
5515
|
+
db;
|
|
5516
|
+
schema;
|
|
5517
|
+
get sessions() {
|
|
5518
|
+
const sessions = this.schema.credentialSetupSessions;
|
|
5519
|
+
if (!sessions) {
|
|
5520
|
+
Err11.invalidOperation("auth.schema.credentialSetupSessions is required to use CredentialSetupService");
|
|
5521
|
+
}
|
|
5522
|
+
return sessions;
|
|
5523
|
+
}
|
|
5524
|
+
async replaceActive(data) {
|
|
5525
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5526
|
+
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)));
|
|
5527
|
+
const [session] = await this.db.insert(this.sessions).values(data).returning({
|
|
5528
|
+
userId: this.sessions.userId,
|
|
5529
|
+
purpose: this.sessions.purpose,
|
|
5530
|
+
expiresAt: this.sessions.expiresAt
|
|
5531
|
+
});
|
|
5532
|
+
return session;
|
|
5533
|
+
}
|
|
5534
|
+
async findActive(tokenHash, purpose) {
|
|
5535
|
+
const [session] = await this.db.select({
|
|
5536
|
+
userId: this.sessions.userId,
|
|
5537
|
+
purpose: this.sessions.purpose,
|
|
5538
|
+
expiresAt: this.sessions.expiresAt
|
|
5539
|
+
}).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);
|
|
5540
|
+
return session;
|
|
5541
|
+
}
|
|
5542
|
+
async consume(tokenHash, purpose) {
|
|
5543
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5544
|
+
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({
|
|
5545
|
+
userId: this.sessions.userId,
|
|
5546
|
+
purpose: this.sessions.purpose,
|
|
5547
|
+
expiresAt: this.sessions.expiresAt
|
|
5548
|
+
});
|
|
5549
|
+
return session;
|
|
5550
|
+
}
|
|
5551
|
+
async revoke(tokenHash, purpose) {
|
|
5552
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5553
|
+
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 });
|
|
5554
|
+
return session;
|
|
5555
|
+
}
|
|
5556
|
+
async deleteExpired() {
|
|
5557
|
+
return this.db.delete(this.sessions).where(lt2(this.sessions.expiresAt, (/* @__PURE__ */ new Date()).toISOString())).returning({ userId: this.sessions.userId });
|
|
5558
|
+
}
|
|
5559
|
+
};
|
|
5560
|
+
__decorate33([
|
|
5561
|
+
DB6(),
|
|
5562
|
+
__metadata33("design:type", Object)
|
|
5563
|
+
], CredentialSetupRepository.prototype, "db", void 0);
|
|
5564
|
+
__decorate33([
|
|
5565
|
+
Inject18(AUTH_SCHEMA),
|
|
5566
|
+
__metadata33("design:type", Object)
|
|
5567
|
+
], CredentialSetupRepository.prototype, "schema", void 0);
|
|
5568
|
+
CredentialSetupRepository = __decorate33([
|
|
5569
|
+
Repository6()
|
|
5570
|
+
], CredentialSetupRepository);
|
|
5571
|
+
|
|
5572
|
+
// src/credentialSetup/CredentialSetupService.ts
|
|
5573
|
+
import { createHash as createHash5, randomBytes as randomBytes4 } from "crypto";
|
|
5574
|
+
import { CookieService as CookieService3 } from "najm-cookies";
|
|
5575
|
+
import { Err as Err12, Injectable as Injectable19 } from "najm-core";
|
|
5576
|
+
import { Transaction as Transaction3 } from "najm-database";
|
|
5577
|
+
var __decorate34 = function(decorators, target, key, desc) {
|
|
5578
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5579
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5580
|
+
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;
|
|
5581
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5582
|
+
};
|
|
5583
|
+
var __metadata34 = function(k, v) {
|
|
5584
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
5585
|
+
};
|
|
5586
|
+
var _a22;
|
|
5587
|
+
var _b12;
|
|
5588
|
+
var _c8;
|
|
5589
|
+
var _d5;
|
|
5590
|
+
var _e4;
|
|
5591
|
+
var _f4;
|
|
5592
|
+
var _g3;
|
|
5593
|
+
var DEFAULT_COOKIE_NAME = "najm.credential-setup";
|
|
5594
|
+
var DEFAULT_TTL_MS = 10 * 60 * 1e3;
|
|
5595
|
+
var MAX_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5596
|
+
var PURPOSE_PATTERN = /^[a-z0-9](?:[a-z0-9:_-]{0,62}[a-z0-9])?$/;
|
|
5597
|
+
var COOKIE_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
5598
|
+
var CredentialSetupService = class CredentialSetupService2 {
|
|
5599
|
+
static {
|
|
5600
|
+
__name(this, "CredentialSetupService");
|
|
5601
|
+
}
|
|
5602
|
+
repository;
|
|
5603
|
+
tokens;
|
|
5604
|
+
authCookies;
|
|
5605
|
+
cookies;
|
|
5606
|
+
constructor(repository, tokens, authCookies, cookies2) {
|
|
5607
|
+
this.repository = repository;
|
|
5608
|
+
this.tokens = tokens;
|
|
5609
|
+
this.authCookies = authCookies;
|
|
5610
|
+
this.cookies = cookies2;
|
|
5611
|
+
}
|
|
5612
|
+
/**
|
|
5613
|
+
* Revoke normal auth sessions and issue a single-purpose, short-lived,
|
|
5614
|
+
* browser-session cookie. No access or refresh token is minted.
|
|
5615
|
+
*/
|
|
5616
|
+
async begin(userId, options) {
|
|
5617
|
+
const resolved = this.resolveOptions(options);
|
|
5618
|
+
const token = randomBytes4(32).toString("base64url");
|
|
5619
|
+
const expiresAt = new Date(Date.now() + resolved.ttlMs).toISOString();
|
|
5620
|
+
await this.repository.deleteExpired();
|
|
5621
|
+
await this.repository.replaceActive({
|
|
5622
|
+
userId,
|
|
5623
|
+
purpose: resolved.purpose,
|
|
5624
|
+
tokenHash: this.hashToken(token),
|
|
5625
|
+
expiresAt
|
|
5626
|
+
});
|
|
5627
|
+
await this.tokens.invalidateUserAccessTokens(userId);
|
|
5628
|
+
await this.tokens.revokeAllForUser(userId);
|
|
5629
|
+
this.authCookies.clearRefreshToken();
|
|
5630
|
+
this.authCookies.clearSessionCookie();
|
|
5631
|
+
this.setCookie(token, resolved);
|
|
5632
|
+
return { purpose: resolved.purpose, expiresAt };
|
|
5633
|
+
}
|
|
5634
|
+
/** Validate and return the current active setup session without consuming it. */
|
|
5635
|
+
async require(options) {
|
|
5636
|
+
const resolved = this.resolveOptions(options);
|
|
5637
|
+
const token = this.cookies.get(resolved.cookieName);
|
|
5638
|
+
if (!token)
|
|
5639
|
+
Err12("Credential setup session is required", 401);
|
|
5640
|
+
const session = await this.repository.findActive(this.hashToken(token), resolved.purpose);
|
|
5641
|
+
if (!session) {
|
|
5642
|
+
this.clearCookie(resolved);
|
|
5643
|
+
Err12("Credential setup session is invalid or expired", 401);
|
|
5644
|
+
}
|
|
5645
|
+
return session;
|
|
5646
|
+
}
|
|
5647
|
+
/**
|
|
5648
|
+
* Atomically consume the setup session and execute the application-owned
|
|
5649
|
+
* credential mutation in the same database transaction. If the callback
|
|
5650
|
+
* fails, consumption rolls back and the browser may safely retry.
|
|
5651
|
+
*/
|
|
5652
|
+
async consume(options, complete) {
|
|
5653
|
+
const resolved = this.resolveOptions(options);
|
|
5654
|
+
const token = this.cookies.get(resolved.cookieName);
|
|
5655
|
+
if (!token)
|
|
5656
|
+
Err12("Credential setup session is required", 401);
|
|
5657
|
+
const session = await this.repository.consume(this.hashToken(token), resolved.purpose);
|
|
5658
|
+
if (!session) {
|
|
5659
|
+
this.clearCookie(resolved);
|
|
5660
|
+
Err12("Credential setup session is invalid or expired", 401);
|
|
5661
|
+
}
|
|
5662
|
+
const result = await complete(session);
|
|
5663
|
+
this.clearCookie(resolved);
|
|
5664
|
+
return result;
|
|
5665
|
+
}
|
|
5666
|
+
async cancel(options) {
|
|
5667
|
+
const resolved = this.resolveOptions(options);
|
|
5668
|
+
const token = this.cookies.get(resolved.cookieName);
|
|
5669
|
+
if (token) {
|
|
5670
|
+
await this.repository.revoke(this.hashToken(token), resolved.purpose);
|
|
5671
|
+
}
|
|
5672
|
+
this.clearCookie(resolved);
|
|
5673
|
+
return { cancelled: true };
|
|
5674
|
+
}
|
|
5675
|
+
async pruneExpired() {
|
|
5676
|
+
await this.repository.deleteExpired();
|
|
5677
|
+
}
|
|
5678
|
+
resolveOptions(options) {
|
|
5679
|
+
const purpose = options.purpose?.trim().toLowerCase();
|
|
5680
|
+
const cookieName = options.cookieName?.trim() || DEFAULT_COOKIE_NAME;
|
|
5681
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
5682
|
+
const cookiePath = options.cookiePath ?? "/";
|
|
5683
|
+
if (!purpose || !PURPOSE_PATTERN.test(purpose)) {
|
|
5684
|
+
Err12("Credential setup purpose must be 1-64 lowercase letters, numbers, colon, underscore, or hyphen", 500);
|
|
5685
|
+
}
|
|
5686
|
+
if (!COOKIE_NAME_PATTERN.test(cookieName) || cookieName.length > 128) {
|
|
5687
|
+
Err12("Credential setup cookie name is invalid", 500);
|
|
5688
|
+
}
|
|
5689
|
+
if (!Number.isInteger(ttlMs) || ttlMs < 1e3 || ttlMs > MAX_TTL_MS) {
|
|
5690
|
+
Err12("Credential setup ttlMs must be an integer between 1000 and 86400000", 500);
|
|
5691
|
+
}
|
|
5692
|
+
if (!cookiePath.startsWith("/") || cookiePath.includes(";") || cookiePath.includes("\\")) {
|
|
5693
|
+
Err12("Credential setup cookiePath must be a same-origin path", 500);
|
|
5694
|
+
}
|
|
5695
|
+
return { purpose, cookieName, ttlMs, cookiePath };
|
|
5696
|
+
}
|
|
5697
|
+
hashToken(token) {
|
|
5698
|
+
return createHash5("sha256").update(token).digest("hex");
|
|
5699
|
+
}
|
|
5700
|
+
setCookie(token, options) {
|
|
5701
|
+
this.cookies.setSession(options.cookieName, token, {
|
|
5702
|
+
httpOnly: true,
|
|
5703
|
+
path: options.cookiePath,
|
|
5704
|
+
sameSite: "Strict",
|
|
5705
|
+
secure: process.env.NODE_ENV === "production"
|
|
5706
|
+
});
|
|
5707
|
+
}
|
|
5708
|
+
clearCookie(options) {
|
|
5709
|
+
this.cookies.delete(options.cookieName, {
|
|
5710
|
+
path: options.cookiePath,
|
|
5711
|
+
secure: process.env.NODE_ENV === "production"
|
|
5712
|
+
});
|
|
5713
|
+
}
|
|
5714
|
+
};
|
|
5715
|
+
__decorate34([
|
|
5716
|
+
Transaction3({ retries: 2 }),
|
|
5717
|
+
__metadata34("design:type", Function),
|
|
5718
|
+
__metadata34("design:paramtypes", [String, Object]),
|
|
5719
|
+
__metadata34("design:returntype", typeof (_e4 = typeof Promise !== "undefined" && Promise) === "function" ? _e4 : Object)
|
|
5720
|
+
], CredentialSetupService.prototype, "begin", null);
|
|
5721
|
+
__decorate34([
|
|
5722
|
+
Transaction3({ retries: 2 }),
|
|
5723
|
+
__metadata34("design:type", Function),
|
|
5724
|
+
__metadata34("design:paramtypes", [Object, Function]),
|
|
5725
|
+
__metadata34("design:returntype", typeof (_f4 = typeof Promise !== "undefined" && Promise) === "function" ? _f4 : Object)
|
|
5726
|
+
], CredentialSetupService.prototype, "consume", null);
|
|
5727
|
+
__decorate34([
|
|
5728
|
+
Transaction3({ retries: 2 }),
|
|
5729
|
+
__metadata34("design:type", Function),
|
|
5730
|
+
__metadata34("design:paramtypes", [Object]),
|
|
5731
|
+
__metadata34("design:returntype", typeof (_g3 = typeof Promise !== "undefined" && Promise) === "function" ? _g3 : Object)
|
|
5732
|
+
], CredentialSetupService.prototype, "cancel", null);
|
|
5733
|
+
CredentialSetupService = __decorate34([
|
|
5734
|
+
Injectable19(),
|
|
5735
|
+
__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])
|
|
5736
|
+
], CredentialSetupService);
|
|
5737
|
+
|
|
5738
|
+
// src/credentialSetup/index.ts
|
|
5739
|
+
var CREDENTIAL_SETUP_MODULE = [
|
|
5740
|
+
CredentialSetupRepository,
|
|
5741
|
+
CredentialSetupService
|
|
5742
|
+
];
|
|
5743
|
+
|
|
5451
5744
|
// src/AuthPlugin.ts
|
|
5452
5745
|
var DEFAULT_JWT = {
|
|
5453
5746
|
accessSecret: process.env.JWT_ACCESS_SECRET || "",
|
|
@@ -5469,9 +5762,9 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
|
|
|
5469
5762
|
const clientId = google.clientId ?? process.env.GOOGLE_CLIENT_ID ?? "";
|
|
5470
5763
|
const clientSecret = google.clientSecret ?? process.env.GOOGLE_CLIENT_SECRET ?? "";
|
|
5471
5764
|
if (!clientId)
|
|
5472
|
-
throw
|
|
5765
|
+
throw Err13.configRequired("auth.oauth.google", "GOOGLE_CLIENT_ID");
|
|
5473
5766
|
if (!clientSecret)
|
|
5474
|
-
throw
|
|
5767
|
+
throw Err13.configRequired("auth.oauth.google", "GOOGLE_CLIENT_SECRET");
|
|
5475
5768
|
const frontendUrl = config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000";
|
|
5476
5769
|
const callbackUrl = google.callbackUrl ?? process.env.GOOGLE_CALLBACK_URL ?? `${frontendUrl.replace(/\/$/, "")}/api/auth/oauth/google/callback`;
|
|
5477
5770
|
let callback;
|
|
@@ -5530,10 +5823,10 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
|
|
|
5530
5823
|
}
|
|
5531
5824
|
};
|
|
5532
5825
|
if (!finalConfig.jwt.accessSecret) {
|
|
5533
|
-
throw
|
|
5826
|
+
throw Err13.configRequired("auth", "JWT_ACCESS_SECRET");
|
|
5534
5827
|
}
|
|
5535
5828
|
if (!finalConfig.jwt.refreshSecret) {
|
|
5536
|
-
throw
|
|
5829
|
+
throw Err13.configRequired("auth", "JWT_REFRESH_SECRET");
|
|
5537
5830
|
}
|
|
5538
5831
|
return finalConfig;
|
|
5539
5832
|
}, "resolveAuthConfig");
|
|
@@ -5553,7 +5846,7 @@ var selectAuthSchema = /* @__PURE__ */ __name((config) => {
|
|
|
5553
5846
|
return authSchema;
|
|
5554
5847
|
}
|
|
5555
5848
|
}, "selectAuthSchema");
|
|
5556
|
-
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");
|
|
5849
|
+
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");
|
|
5557
5850
|
|
|
5558
5851
|
// src/seed.ts
|
|
5559
5852
|
var toSeedId = /* @__PURE__ */ __name((prefix, value) => {
|
|
@@ -5700,6 +5993,7 @@ export {
|
|
|
5700
5993
|
AuthResolver,
|
|
5701
5994
|
AuthService,
|
|
5702
5995
|
AuthSessionService,
|
|
5996
|
+
CREDENTIAL_SETUP_MODULE,
|
|
5703
5997
|
Can,
|
|
5704
5998
|
CanCreate,
|
|
5705
5999
|
CanDelete,
|
|
@@ -5707,6 +6001,8 @@ export {
|
|
|
5707
6001
|
CanRead,
|
|
5708
6002
|
CanUpdate,
|
|
5709
6003
|
CookieManager,
|
|
6004
|
+
CredentialSetupRepository,
|
|
6005
|
+
CredentialSetupService,
|
|
5710
6006
|
EncryptionService,
|
|
5711
6007
|
Owned,
|
|
5712
6008
|
OwnershipToken,
|
|
@@ -5754,6 +6050,7 @@ export {
|
|
|
5754
6050
|
createRoleDto,
|
|
5755
6051
|
createTokenDto,
|
|
5756
6052
|
createUserDto,
|
|
6053
|
+
credentialSetupSessionsTable,
|
|
5757
6054
|
defineRoles,
|
|
5758
6055
|
emailParam,
|
|
5759
6056
|
formatDate,
|