najm-auth 4.2.4 → 6.0.0
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 +1372 -1372
- package/dist/client/index.js +17 -10
- package/dist/client/react/index.js +13 -6
- package/dist/client/server/index.js +20 -17
- package/dist/index.d.ts +120 -131
- package/dist/index.js +907 -664
- package/package.json +13 -12
package/dist/client/index.js
CHANGED
|
@@ -228,6 +228,20 @@ var TabSync = class {
|
|
|
228
228
|
}
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
+
// src/client/redirectPath.ts
|
|
232
|
+
function normalizeLocalRedirectPath(value) {
|
|
233
|
+
if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.includes("\\") || /[\u0000-\u001f\u007f]/.test(value)) return null;
|
|
234
|
+
try {
|
|
235
|
+
const base = new URL("https://najm.invalid");
|
|
236
|
+
const parsed = new URL(value, base);
|
|
237
|
+
if (parsed.origin !== base.origin || parsed.pathname.startsWith("//")) return null;
|
|
238
|
+
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
__name(normalizeLocalRedirectPath, "normalizeLocalRedirectPath");
|
|
244
|
+
|
|
231
245
|
// src/client/NajmAuthClient.ts
|
|
232
246
|
function isCredentialSetupPending(payload) {
|
|
233
247
|
return typeof payload === "object" && payload !== null && payload.nextStep === "credential_setup";
|
|
@@ -702,16 +716,9 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
702
716
|
}
|
|
703
717
|
}
|
|
704
718
|
validateReturnTo(value) {
|
|
705
|
-
const
|
|
706
|
-
if (!
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
const base = new URL("https://najm.invalid");
|
|
710
|
-
const parsed = new URL(candidate, base);
|
|
711
|
-
if (parsed.origin !== base.origin || parsed.username || parsed.password) {
|
|
712
|
-
throw new Error("returnTo must be a same-origin path");
|
|
713
|
-
}
|
|
714
|
-
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
719
|
+
const path = normalizeLocalRedirectPath(value.trim());
|
|
720
|
+
if (!path) throw new Error("returnTo must be a same-origin path");
|
|
721
|
+
return path;
|
|
715
722
|
}
|
|
716
723
|
};
|
|
717
724
|
function createAuthClient(config) {
|
|
@@ -771,18 +771,25 @@ __name(GitHubLoginButton, "GitHubLoginButton");
|
|
|
771
771
|
|
|
772
772
|
// src/client/react/OAuthCallback.tsx
|
|
773
773
|
import { useEffect as useEffect7 } from "react";
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (
|
|
774
|
+
|
|
775
|
+
// src/client/redirectPath.ts
|
|
776
|
+
function normalizeLocalRedirectPath(value) {
|
|
777
|
+
if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.includes("\\") || /[\u0000-\u001f\u007f]/.test(value)) return null;
|
|
778
778
|
try {
|
|
779
779
|
const base = new URL("https://najm.invalid");
|
|
780
780
|
const parsed = new URL(value, base);
|
|
781
|
-
if (parsed.origin !== base.origin) return
|
|
781
|
+
if (parsed.origin !== base.origin || parsed.pathname.startsWith("//")) return null;
|
|
782
782
|
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
783
783
|
} catch {
|
|
784
|
-
return
|
|
784
|
+
return null;
|
|
785
785
|
}
|
|
786
|
+
}
|
|
787
|
+
__name(normalizeLocalRedirectPath, "normalizeLocalRedirectPath");
|
|
788
|
+
|
|
789
|
+
// src/client/react/OAuthCallback.tsx
|
|
790
|
+
import { Fragment as Fragment14, jsx as jsx16 } from "react/jsx-runtime";
|
|
791
|
+
var safeReturnTo = /* @__PURE__ */ __name((value, fallback) => {
|
|
792
|
+
return normalizeLocalRedirectPath(value) ?? normalizeLocalRedirectPath(fallback) ?? "/";
|
|
786
793
|
}, "safeReturnTo");
|
|
787
794
|
function OAuthCallback({
|
|
788
795
|
fallback = null,
|
|
@@ -957,6 +957,20 @@ var TabSync = class {
|
|
|
957
957
|
}
|
|
958
958
|
};
|
|
959
959
|
|
|
960
|
+
// src/client/redirectPath.ts
|
|
961
|
+
function normalizeLocalRedirectPath(value) {
|
|
962
|
+
if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.includes("\\") || /[\u0000-\u001f\u007f]/.test(value)) return null;
|
|
963
|
+
try {
|
|
964
|
+
const base = new URL("https://najm.invalid");
|
|
965
|
+
const parsed = new URL(value, base);
|
|
966
|
+
if (parsed.origin !== base.origin || parsed.pathname.startsWith("//")) return null;
|
|
967
|
+
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
968
|
+
} catch {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
__name(normalizeLocalRedirectPath, "normalizeLocalRedirectPath");
|
|
973
|
+
|
|
960
974
|
// src/client/NajmAuthClient.ts
|
|
961
975
|
function isCredentialSetupPending(payload) {
|
|
962
976
|
return typeof payload === "object" && payload !== null && payload.nextStep === "credential_setup";
|
|
@@ -1431,16 +1445,9 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
1431
1445
|
}
|
|
1432
1446
|
}
|
|
1433
1447
|
validateReturnTo(value) {
|
|
1434
|
-
const
|
|
1435
|
-
if (!
|
|
1436
|
-
|
|
1437
|
-
}
|
|
1438
|
-
const base = new URL("https://najm.invalid");
|
|
1439
|
-
const parsed = new URL(candidate, base);
|
|
1440
|
-
if (parsed.origin !== base.origin || parsed.username || parsed.password) {
|
|
1441
|
-
throw new Error("returnTo must be a same-origin path");
|
|
1442
|
-
}
|
|
1443
|
-
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
1448
|
+
const path = normalizeLocalRedirectPath(value.trim());
|
|
1449
|
+
if (!path) throw new Error("returnTo must be a same-origin path");
|
|
1450
|
+
return path;
|
|
1444
1451
|
}
|
|
1445
1452
|
};
|
|
1446
1453
|
function createAuthClient(config) {
|
|
@@ -1800,13 +1807,9 @@ function getSafeRedirectPath(value, options = {}) {
|
|
|
1800
1807
|
fallback = "/dashboard",
|
|
1801
1808
|
blockedPrefixes = DEFAULT_BLOCKED_PREFIXES
|
|
1802
1809
|
} = typeof options === "string" ? { fallback: options } : options;
|
|
1803
|
-
const path = Array.isArray(value) ? value[0] : value;
|
|
1804
|
-
|
|
1805
|
-
path.startsWith(
|
|
1806
|
-
// `/\evil.test` is another way to spell the case above.
|
|
1807
|
-
path.startsWith("/\\") || blockedPrefixes.some((prefix) => path.startsWith(prefix)) || ASSET_EXTENSIONS.test(path.split("?")[0] ?? path)) {
|
|
1808
|
-
return fallback;
|
|
1809
|
-
}
|
|
1810
|
+
const path = normalizeLocalRedirectPath(Array.isArray(value) ? value[0] : value);
|
|
1811
|
+
const pathname = path?.split(/[?#]/)[0] ?? "";
|
|
1812
|
+
if (!path || blockedPrefixes.some((prefix) => pathname.startsWith(prefix)) || ASSET_EXTENSIONS.test(pathname)) return fallback;
|
|
1810
1813
|
return path;
|
|
1811
1814
|
}
|
|
1812
1815
|
__name(getSafeRedirectPath, "getSafeRedirectPath");
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { R as ResolvedIdentityConfig, I as IdentityConfig, T as TemporaryCredent
|
|
|
8
8
|
export { D as DEFAULT_IDENTITY_PRESET, E as EXACT_TEMPORARY_CREDENTIAL_KIND, a as IDENTITY_PRESETS, b as IdentityNormalizer, c as IdentityPreset, d as IdentityPresetName, M as MOROCCAN_CIN_TEMPORARY_CREDENTIAL_KIND, e as TemporaryCredential, f as TemporaryCredentialKind, g as compactPhone, i as isMoroccanCin, h as isTemporaryCredentialKind, m as moroccanCinTemporaryCredential, j as moroccoIdentityPreset, n as normalizeMoroccanCin, k as normalizeMoroccanPhone, l as normalizeTunisianPhone, r as resolveTemporaryCredentialKind, t as toTemporaryCredential, o as tunisiaIdentityPreset } from './ma-Cu6xUMez.js';
|
|
9
9
|
import { ZodType, z } from 'zod';
|
|
10
10
|
import { I18nService } from 'najm-i18n';
|
|
11
|
-
import { TDb, SeedEntry } from 'najm-database';
|
|
11
|
+
import { TDb, TransactionService, SeedEntry } from 'najm-database';
|
|
12
12
|
import { User, NewUser, RoleEntity, NewRoleEntity, Permission, NewPermission, RolePermission } from './schema/pg.js';
|
|
13
13
|
export { CredentialSetupRequirement, CredentialSetupSession, NewCredentialSetupRequirement, NewCredentialSetupSession, NewOAuthAccount, NewRolePermission, NewToken, OAuthAccount, Token, authSchema, baseFields, credentialSetupRequirementsTable, credentialSetupSessionsTable, oauthAccountsTable, permissionsTable, rolePermissionsTable, rolesTable, tokenStatusEnum, tokenTypeEnum, tokensTable, userStatusEnum, usersTable } from './schema/pg.js';
|
|
14
14
|
import { CookieService } from 'najm-cookies';
|
|
@@ -61,6 +61,7 @@ interface CredentialSetupPasswordOptions {
|
|
|
61
61
|
* Replacement-password schema. Default: 8–72 bytes with at least one letter
|
|
62
62
|
* and one digit — deliberately case-agnostic, because a first-login
|
|
63
63
|
* replacement is typed by someone who just proved they own the account.
|
|
64
|
+
* Every policy is still subject to the 1-72 UTF-8 byte limit for bcrypt.
|
|
64
65
|
*/
|
|
65
66
|
passwordSchema?: ZodType<string>;
|
|
66
67
|
/** Setup-session lifetime in milliseconds (default: 10 minutes). */
|
|
@@ -717,6 +718,8 @@ declare class CookieManager {
|
|
|
717
718
|
clearSessionCookie(): void;
|
|
718
719
|
}
|
|
719
720
|
|
|
721
|
+
type CredentialState = Pick<User, 'id' | 'password' | 'email' | 'status' | 'emailVerified'>;
|
|
722
|
+
|
|
720
723
|
interface UserWithPermissions extends Omit<User, 'password'> {
|
|
721
724
|
role?: string | null;
|
|
722
725
|
permissions: string[];
|
|
@@ -747,6 +750,8 @@ declare class UserRepository {
|
|
|
747
750
|
*/
|
|
748
751
|
getIdsByRole(roleId: string): Promise<string[]>;
|
|
749
752
|
update(id: string, data: Partial<NewUser>): Promise<User | undefined>;
|
|
753
|
+
/** The credential that authorized a delayed write must still own the account. */
|
|
754
|
+
updateWithCredential(id: string, data: Partial<NewUser>, expected: CredentialState): Promise<User | undefined>;
|
|
750
755
|
updateLastLogin(id: string): Promise<User>;
|
|
751
756
|
incrementFailedAttempts(id: string): Promise<User>;
|
|
752
757
|
resetFailedAttempts(id: string): Promise<User>;
|
|
@@ -777,6 +782,9 @@ declare class UserValidator {
|
|
|
777
782
|
* Check that a normalized phone number is not already taken.
|
|
778
783
|
*/
|
|
779
784
|
checkPhoneUnique(phone: string, excludeId?: string): Promise<void>;
|
|
785
|
+
/** The database remains authoritative when two validation reads both pass. */
|
|
786
|
+
writeUnique<T>(write: () => Promise<T>): Promise<T>;
|
|
787
|
+
checkCredentialUnchanged(updated: unknown): void;
|
|
780
788
|
/**
|
|
781
789
|
* Check if user exists by ID
|
|
782
790
|
*/
|
|
@@ -919,78 +927,12 @@ declare class RoleValidator {
|
|
|
919
927
|
isRoleNameExists(roleName: string): Promise<boolean>;
|
|
920
928
|
}
|
|
921
929
|
|
|
922
|
-
declare class RoleService {
|
|
923
|
-
private roleRepository;
|
|
924
|
-
private roleValidator;
|
|
925
|
-
private t;
|
|
926
|
-
constructor(roleRepository: RoleRepository, roleValidator: RoleValidator);
|
|
927
|
-
getAll(): Promise<{
|
|
928
|
-
id: string;
|
|
929
|
-
name: string;
|
|
930
|
-
description: string;
|
|
931
|
-
createdAt: string;
|
|
932
|
-
updatedAt: string;
|
|
933
|
-
}[]>;
|
|
934
|
-
getById(id: any): Promise<{
|
|
935
|
-
id: string;
|
|
936
|
-
name: string;
|
|
937
|
-
description: string;
|
|
938
|
-
createdAt: string;
|
|
939
|
-
updatedAt: string;
|
|
940
|
-
}>;
|
|
941
|
-
getByName(name: any): Promise<{
|
|
942
|
-
id: string;
|
|
943
|
-
name: string;
|
|
944
|
-
description: string;
|
|
945
|
-
createdAt: string;
|
|
946
|
-
updatedAt: string;
|
|
947
|
-
}>;
|
|
948
|
-
create(data: any): Promise<{
|
|
949
|
-
id: string;
|
|
950
|
-
name: string;
|
|
951
|
-
description: string;
|
|
952
|
-
createdAt: string;
|
|
953
|
-
updatedAt: string;
|
|
954
|
-
}>;
|
|
955
|
-
update(id: string, data: {
|
|
956
|
-
name?: string;
|
|
957
|
-
description?: string;
|
|
958
|
-
}): Promise<{
|
|
959
|
-
id: string;
|
|
960
|
-
name: string;
|
|
961
|
-
description: string;
|
|
962
|
-
createdAt: string;
|
|
963
|
-
updatedAt: string;
|
|
964
|
-
}>;
|
|
965
|
-
delete(id: string): Promise<{
|
|
966
|
-
id: string;
|
|
967
|
-
name: string;
|
|
968
|
-
description: string;
|
|
969
|
-
createdAt: string;
|
|
970
|
-
updatedAt: string;
|
|
971
|
-
}>;
|
|
972
|
-
seedDefaultRoles(defaultRoles: any): Promise<{
|
|
973
|
-
id: string;
|
|
974
|
-
name: string;
|
|
975
|
-
description: string;
|
|
976
|
-
createdAt: string;
|
|
977
|
-
updatedAt: string;
|
|
978
|
-
}[]>;
|
|
979
|
-
getRoleIdByName(name: any): Promise<string>;
|
|
980
|
-
/**
|
|
981
|
-
* checkNameUnique() reads before it writes, so two concurrent requests can
|
|
982
|
-
* both pass it and race to insert the same name. The database rejects the
|
|
983
|
-
* loser; answer it with the same 409 the validator would have given rather
|
|
984
|
-
* than leaking a raw driver error as a 500.
|
|
985
|
-
*/
|
|
986
|
-
private onDuplicateName;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
930
|
declare class TokenRepository {
|
|
990
931
|
db: TDb;
|
|
991
932
|
private schema;
|
|
992
933
|
private get tokens();
|
|
993
934
|
private get users();
|
|
935
|
+
getCredentialState(userId: string): Promise<any>;
|
|
994
936
|
/** Shared query helper, scoped to the current database/transaction identity. */
|
|
995
937
|
private queryHelper?;
|
|
996
938
|
private get q();
|
|
@@ -1073,8 +1015,9 @@ declare class TokenRepository {
|
|
|
1073
1015
|
declare class SessionInvalidationService {
|
|
1074
1016
|
private cache;
|
|
1075
1017
|
private tokens;
|
|
1018
|
+
private transactions?;
|
|
1076
1019
|
private config;
|
|
1077
|
-
constructor(cache: CacheService, tokens: TokenRepository);
|
|
1020
|
+
constructor(cache: CacheService, tokens: TokenRepository, transactions?: TransactionService);
|
|
1078
1021
|
/**
|
|
1079
1022
|
* Fields whose change ends existing sessions. Everything absent from this set
|
|
1080
1023
|
* — display name, avatar, language — is a profile edit and must leave the
|
|
@@ -1117,17 +1060,20 @@ declare class SessionInvalidationService {
|
|
|
1117
1060
|
* the same version and write the same successor back.
|
|
1118
1061
|
*/
|
|
1119
1062
|
invalidateAccessTokens(userId: string): Promise<number>;
|
|
1063
|
+
private invalidateAccessTokensNow;
|
|
1120
1064
|
dropUserCache(userId: string): Promise<void>;
|
|
1121
1065
|
/**
|
|
1122
1066
|
* End every session a user holds: access tokens by version, refresh sessions
|
|
1123
1067
|
* by row, and each family's liveness marker.
|
|
1124
1068
|
*
|
|
1125
|
-
*
|
|
1126
|
-
*
|
|
1127
|
-
*
|
|
1128
|
-
* after a rollback merely signs the user out again, which is safe.
|
|
1069
|
+
* Repeat invalidation when a caller-owned transaction completes. Readers can
|
|
1070
|
+
* recover from the previous committed state before commit, or from transient
|
|
1071
|
+
* state on a shared connection before rollback. Neither snapshot may survive.
|
|
1129
1072
|
*/
|
|
1130
1073
|
invalidateUser(userId: string): Promise<void>;
|
|
1074
|
+
revokeAllForUser(userId: string): Promise<any>;
|
|
1075
|
+
private afterTransaction;
|
|
1076
|
+
private revokeAllForUserNow;
|
|
1131
1077
|
/**
|
|
1132
1078
|
* Record that a family is live, and whose it is. Called wherever the family's
|
|
1133
1079
|
* refresh row is written.
|
|
@@ -1177,6 +1123,75 @@ declare class SessionInvalidationService {
|
|
|
1177
1123
|
isFamilyRevoked(tokenFamily: string): Promise<boolean>;
|
|
1178
1124
|
}
|
|
1179
1125
|
|
|
1126
|
+
declare class RoleService {
|
|
1127
|
+
private roleRepository;
|
|
1128
|
+
private roleValidator;
|
|
1129
|
+
private userRepository?;
|
|
1130
|
+
private sessionInvalidation?;
|
|
1131
|
+
private t;
|
|
1132
|
+
constructor(roleRepository: RoleRepository, roleValidator: RoleValidator, userRepository?: UserRepository, sessionInvalidation?: SessionInvalidationService);
|
|
1133
|
+
getAll(): Promise<{
|
|
1134
|
+
id: string;
|
|
1135
|
+
name: string;
|
|
1136
|
+
description: string;
|
|
1137
|
+
createdAt: string;
|
|
1138
|
+
updatedAt: string;
|
|
1139
|
+
}[]>;
|
|
1140
|
+
getById(id: any): Promise<{
|
|
1141
|
+
id: string;
|
|
1142
|
+
name: string;
|
|
1143
|
+
description: string;
|
|
1144
|
+
createdAt: string;
|
|
1145
|
+
updatedAt: string;
|
|
1146
|
+
}>;
|
|
1147
|
+
getByName(name: any): Promise<{
|
|
1148
|
+
id: string;
|
|
1149
|
+
name: string;
|
|
1150
|
+
description: string;
|
|
1151
|
+
createdAt: string;
|
|
1152
|
+
updatedAt: string;
|
|
1153
|
+
}>;
|
|
1154
|
+
create(data: any): Promise<{
|
|
1155
|
+
id: string;
|
|
1156
|
+
name: string;
|
|
1157
|
+
description: string;
|
|
1158
|
+
createdAt: string;
|
|
1159
|
+
updatedAt: string;
|
|
1160
|
+
}>;
|
|
1161
|
+
update(id: string, data: {
|
|
1162
|
+
name?: string;
|
|
1163
|
+
description?: string;
|
|
1164
|
+
}): Promise<{
|
|
1165
|
+
id: string;
|
|
1166
|
+
name: string;
|
|
1167
|
+
description: string;
|
|
1168
|
+
createdAt: string;
|
|
1169
|
+
updatedAt: string;
|
|
1170
|
+
}>;
|
|
1171
|
+
delete(id: string): Promise<{
|
|
1172
|
+
id: string;
|
|
1173
|
+
name: string;
|
|
1174
|
+
description: string;
|
|
1175
|
+
createdAt: string;
|
|
1176
|
+
updatedAt: string;
|
|
1177
|
+
}>;
|
|
1178
|
+
seedDefaultRoles(defaultRoles: any): Promise<{
|
|
1179
|
+
id: string;
|
|
1180
|
+
name: string;
|
|
1181
|
+
description: string;
|
|
1182
|
+
createdAt: string;
|
|
1183
|
+
updatedAt: string;
|
|
1184
|
+
}[]>;
|
|
1185
|
+
getRoleIdByName(name: any): Promise<string>;
|
|
1186
|
+
/**
|
|
1187
|
+
* checkNameUnique() reads before it writes, so two concurrent requests can
|
|
1188
|
+
* both pass it and race to insert the same name. The database rejects the
|
|
1189
|
+
* loser; answer it with the same 409 the validator would have given rather
|
|
1190
|
+
* than leaking a raw driver error as a 500.
|
|
1191
|
+
*/
|
|
1192
|
+
private onDuplicateName;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1180
1195
|
type SanitizedUser = Omit<User, 'password' | 'failedLoginAttempts' | 'lockoutUntil'> & {
|
|
1181
1196
|
role?: string | null;
|
|
1182
1197
|
permissions?: string[];
|
|
@@ -1235,6 +1250,7 @@ declare class UserService {
|
|
|
1235
1250
|
*/
|
|
1236
1251
|
update(id: string, data: Record<string, any>, options?: {
|
|
1237
1252
|
validatePasswordStrength?: boolean;
|
|
1253
|
+
expectedCredential?: CredentialState;
|
|
1238
1254
|
}): Promise<SanitizedUser>;
|
|
1239
1255
|
delete(id: string): Promise<SanitizedUser>;
|
|
1240
1256
|
deleteAll(): Promise<SanitizedUser[]>;
|
|
@@ -1270,6 +1286,7 @@ type SetPasswordTokenType = 'reset' | 'invite';
|
|
|
1270
1286
|
interface ConsumedSetPasswordToken {
|
|
1271
1287
|
userId: string;
|
|
1272
1288
|
type: SetPasswordTokenType;
|
|
1289
|
+
credentialState: string;
|
|
1273
1290
|
}
|
|
1274
1291
|
/**
|
|
1275
1292
|
* A freshly minted one-time set-password token. `jti` identifies this exact
|
|
@@ -1422,7 +1439,7 @@ declare class TokenService {
|
|
|
1422
1439
|
tokenFamily?: string;
|
|
1423
1440
|
}): string;
|
|
1424
1441
|
private createTokenPair;
|
|
1425
|
-
generateTokens(userId: string, tokenFamily?: string): Promise<{
|
|
1442
|
+
generateTokens(userId: string, tokenFamily?: string, capturedVersion?: number): Promise<{
|
|
1426
1443
|
userId: string;
|
|
1427
1444
|
tokenFamily: string;
|
|
1428
1445
|
roles: string[];
|
|
@@ -1433,6 +1450,7 @@ declare class TokenService {
|
|
|
1433
1450
|
accessTokenExpiresAt: number;
|
|
1434
1451
|
refreshTokenExpiresAt: number;
|
|
1435
1452
|
}>;
|
|
1453
|
+
private assertSessionVersion;
|
|
1436
1454
|
/**
|
|
1437
1455
|
* Blacklist an access token by its jti
|
|
1438
1456
|
* Token will be rejected until it naturally expires
|
|
@@ -1533,13 +1551,13 @@ declare class TokenService {
|
|
|
1533
1551
|
* Generate secure password reset token
|
|
1534
1552
|
* Returns both the plain token (to send via email) and userId for identification
|
|
1535
1553
|
*/
|
|
1536
|
-
generateResetToken(userId: string): Promise<SetPasswordToken>;
|
|
1554
|
+
generateResetToken(userId: string, expectedEmail?: string): Promise<SetPasswordToken>;
|
|
1537
1555
|
/**
|
|
1538
1556
|
* Generate secure account-invite token.
|
|
1539
1557
|
* Longer expiry (3d) than reset because an invited user may not check
|
|
1540
1558
|
* their email immediately. Consumed via the same reset-password endpoint.
|
|
1541
1559
|
*/
|
|
1542
|
-
generateInviteToken(userId: string): Promise<SetPasswordToken>;
|
|
1560
|
+
generateInviteToken(userId: string, expectedEmail?: string): Promise<SetPasswordToken>;
|
|
1543
1561
|
/**
|
|
1544
1562
|
* Discard a set-password token this process just minted, identified by the
|
|
1545
1563
|
* `jti` its generator returned. For the caller whose email send failed:
|
|
@@ -1728,8 +1746,10 @@ declare class CredentialSetupService {
|
|
|
1728
1746
|
private readonly tokens;
|
|
1729
1747
|
private readonly authCookies;
|
|
1730
1748
|
private readonly cookies;
|
|
1749
|
+
private readonly users;
|
|
1731
1750
|
private t?;
|
|
1732
|
-
|
|
1751
|
+
private config;
|
|
1752
|
+
constructor(repository: CredentialSetupRepository, tokens: TokenService, authCookies: CookieManager, cookies: CookieService, users: UserRepository);
|
|
1733
1753
|
/**
|
|
1734
1754
|
* Revoke normal auth sessions and issue a single-purpose, short-lived,
|
|
1735
1755
|
* browser-session cookie. No access or refresh token is minted.
|
|
@@ -1743,6 +1763,7 @@ declare class CredentialSetupService {
|
|
|
1743
1763
|
* fails, consumption rolls back and the browser may safely retry.
|
|
1744
1764
|
*/
|
|
1745
1765
|
consume<T>(options: CredentialSetupOptions, complete: (session: CredentialSetupSessionInfo) => Promise<T> | T): Promise<T>;
|
|
1766
|
+
private requireCurrentCredential;
|
|
1746
1767
|
cancel(options: CredentialSetupOptions): Promise<{
|
|
1747
1768
|
cancelled: true;
|
|
1748
1769
|
}>;
|
|
@@ -2272,6 +2293,22 @@ declare class PermissionRepository {
|
|
|
2272
2293
|
delete(id: string): Promise<Permission>;
|
|
2273
2294
|
getPermissionsByRole(roleId: string): Promise<Array<Pick<Permission, 'id' | 'name' | 'description' | 'resource' | 'action'>>>;
|
|
2274
2295
|
getRolesByPermission(permissionId: string): Promise<Array<Pick<RoleEntity, 'id' | 'name' | 'description'>>>;
|
|
2296
|
+
/**
|
|
2297
|
+
* Capture grants and mutate their parent in one database transaction. A
|
|
2298
|
+
* PostgreSQL parent-row lock conflicts with the FK check of a concurrent
|
|
2299
|
+
* grant. SQLite executes the whole callback synchronously under its write
|
|
2300
|
+
* lock; an async callback would commit before its awaited queries finish.
|
|
2301
|
+
*/
|
|
2302
|
+
private mutateWithRoles;
|
|
2303
|
+
updateWithRoles(id: string, data: Partial<NewPermission>): Promise<{
|
|
2304
|
+
permission: any;
|
|
2305
|
+
roleIds: any;
|
|
2306
|
+
}>;
|
|
2307
|
+
deleteWithRoles(id: string): Promise<{
|
|
2308
|
+
permission: any;
|
|
2309
|
+
roleIds: any;
|
|
2310
|
+
}>;
|
|
2311
|
+
deleteAllWithRoles(): Promise<any>;
|
|
2275
2312
|
assignPermissionToRole(roleId: string, permissionId: string): Promise<RolePermission>;
|
|
2276
2313
|
removePermissionFromRole(roleId: string, permissionId: string): Promise<RolePermission>;
|
|
2277
2314
|
checkRoleHasPermission(roleId: string, permissionId: string): Promise<boolean>;
|
|
@@ -2529,24 +2566,8 @@ declare class PermissionService {
|
|
|
2529
2566
|
resource: string;
|
|
2530
2567
|
action: string;
|
|
2531
2568
|
}>;
|
|
2532
|
-
update(id: string, data: any): Promise<
|
|
2533
|
-
|
|
2534
|
-
name: string;
|
|
2535
|
-
description: string;
|
|
2536
|
-
createdAt: string;
|
|
2537
|
-
updatedAt: string;
|
|
2538
|
-
resource: string;
|
|
2539
|
-
action: string;
|
|
2540
|
-
}>;
|
|
2541
|
-
delete(id: string): Promise<{
|
|
2542
|
-
id: string;
|
|
2543
|
-
name: string;
|
|
2544
|
-
description: string;
|
|
2545
|
-
createdAt: string;
|
|
2546
|
-
updatedAt: string;
|
|
2547
|
-
resource: string;
|
|
2548
|
-
action: string;
|
|
2549
|
-
}>;
|
|
2569
|
+
update(id: string, data: any): Promise<any>;
|
|
2570
|
+
delete(id: string): Promise<any>;
|
|
2550
2571
|
getPermissionsByRole(roleId: string): Promise<Pick<{
|
|
2551
2572
|
id: string;
|
|
2552
2573
|
name: string;
|
|
@@ -2581,15 +2602,7 @@ declare class PermissionService {
|
|
|
2581
2602
|
assigned: any[];
|
|
2582
2603
|
skipped: any[];
|
|
2583
2604
|
}>;
|
|
2584
|
-
deleteAll(): Promise<
|
|
2585
|
-
id: string;
|
|
2586
|
-
name: string;
|
|
2587
|
-
description: string;
|
|
2588
|
-
createdAt: string;
|
|
2589
|
-
updatedAt: string;
|
|
2590
|
-
resource: string;
|
|
2591
|
-
action: string;
|
|
2592
|
-
}[]>;
|
|
2605
|
+
deleteAll(): Promise<any>;
|
|
2593
2606
|
}
|
|
2594
2607
|
|
|
2595
2608
|
declare const createPermissionDto: z.ZodObject<{
|
|
@@ -2652,24 +2665,8 @@ declare class PermissionController {
|
|
|
2652
2665
|
resource: string;
|
|
2653
2666
|
action: string;
|
|
2654
2667
|
}>;
|
|
2655
|
-
update(params: PermissionIdParam, body: UpdatePermissionDto): Promise<
|
|
2656
|
-
|
|
2657
|
-
name: string;
|
|
2658
|
-
description: string;
|
|
2659
|
-
createdAt: string;
|
|
2660
|
-
updatedAt: string;
|
|
2661
|
-
resource: string;
|
|
2662
|
-
action: string;
|
|
2663
|
-
}>;
|
|
2664
|
-
delete(params: PermissionIdParam): Promise<{
|
|
2665
|
-
id: string;
|
|
2666
|
-
name: string;
|
|
2667
|
-
description: string;
|
|
2668
|
-
createdAt: string;
|
|
2669
|
-
updatedAt: string;
|
|
2670
|
-
resource: string;
|
|
2671
|
-
action: string;
|
|
2672
|
-
}>;
|
|
2668
|
+
update(params: PermissionIdParam, body: UpdatePermissionDto): Promise<any>;
|
|
2669
|
+
delete(params: PermissionIdParam): Promise<any>;
|
|
2673
2670
|
getByRole(params: RoleIdParam): Promise<Pick<{
|
|
2674
2671
|
id: string;
|
|
2675
2672
|
name: string;
|
|
@@ -2696,15 +2693,7 @@ declare class PermissionController {
|
|
|
2696
2693
|
roleId: string;
|
|
2697
2694
|
permissionId: string;
|
|
2698
2695
|
}>;
|
|
2699
|
-
deleteAll(): Promise<
|
|
2700
|
-
id: string;
|
|
2701
|
-
name: string;
|
|
2702
|
-
description: string;
|
|
2703
|
-
createdAt: string;
|
|
2704
|
-
updatedAt: string;
|
|
2705
|
-
resource: string;
|
|
2706
|
-
action: string;
|
|
2707
|
-
}[]>;
|
|
2696
|
+
deleteAll(): Promise<any>;
|
|
2708
2697
|
}
|
|
2709
2698
|
|
|
2710
2699
|
interface JoinStep {
|