@shipfox/api-auth 10.1.0 → 11.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +30 -0
- package/README.md +42 -11
- package/dist/core/administration.d.ts +17 -1
- package/dist/core/administration.d.ts.map +1 -1
- package/dist/core/administration.js +107 -14
- package/dist/core/administration.js.map +1 -1
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +27 -10
- package/dist/core/auth.js.map +1 -1
- package/dist/core/jwt.d.ts +10 -0
- package/dist/core/jwt.d.ts.map +1 -1
- package/dist/core/jwt.js +3 -2
- package/dist/core/jwt.js.map +1 -1
- package/dist/db/admin-command.d.ts +18 -0
- package/dist/db/admin-command.d.ts.map +1 -0
- package/dist/db/admin-command.js +37 -0
- package/dist/db/admin-command.js.map +1 -0
- package/dist/db/admin-grants.d.ts +1 -1
- package/dist/db/admin-grants.d.ts.map +1 -1
- package/dist/db/admin-grants.js +12 -30
- package/dist/db/admin-grants.js.map +1 -1
- package/dist/db/admin-user-moderation.d.ts +29 -0
- package/dist/db/admin-user-moderation.d.ts.map +1 -0
- package/dist/db/admin-user-moderation.js +156 -0
- package/dist/db/admin-user-moderation.js.map +1 -0
- package/dist/db/admin-user-summary.d.ts +14 -0
- package/dist/db/admin-user-summary.d.ts.map +1 -0
- package/dist/db/admin-user-summary.js +31 -0
- package/dist/db/admin-user-summary.js.map +1 -0
- package/dist/db/admin-users.d.ts +2 -11
- package/dist/db/admin-users.d.ts.map +1 -1
- package/dist/db/admin-users.js +2 -27
- package/dist/db/admin-users.js.map +1 -1
- package/dist/db/refresh-tokens.d.ts +6 -4
- package/dist/db/refresh-tokens.d.ts.map +1 -1
- package/dist/db/refresh-tokens.js +31 -6
- package/dist/db/refresh-tokens.js.map +1 -1
- package/dist/db/schema/admin-command-results.d.ts +19 -2
- package/dist/db/schema/admin-command-results.d.ts.map +1 -1
- package/dist/db/schema/admin-command-results.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/auth/bearer-token-auth.d.ts +1 -0
- package/dist/presentation/auth/bearer-token-auth.d.ts.map +1 -1
- package/dist/presentation/auth/bearer-token-auth.js +4 -3
- package/dist/presentation/auth/bearer-token-auth.js.map +1 -1
- package/dist/presentation/auth/jwt-auth.d.ts +1 -4
- package/dist/presentation/auth/jwt-auth.d.ts.map +1 -1
- package/dist/presentation/auth/jwt-auth.js +23 -19
- package/dist/presentation/auth/jwt-auth.js.map +1 -1
- package/dist/presentation/routes/administration.d.ts.map +1 -1
- package/dist/presentation/routes/administration.js +94 -3
- package/dist/presentation/routes/administration.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/core/administration.ts +137 -15
- package/src/core/auth.test.ts +29 -10
- package/src/core/auth.ts +36 -14
- package/src/core/jwt.test.ts +20 -4
- package/src/core/jwt.ts +2 -1
- package/src/db/admin-command.ts +85 -0
- package/src/db/admin-grants.ts +19 -57
- package/src/db/admin-user-moderation.ts +230 -0
- package/src/db/admin-user-summary.ts +51 -0
- package/src/db/admin-users.ts +4 -49
- package/src/db/refresh-tokens.test.ts +0 -19
- package/src/db/refresh-tokens.ts +54 -23
- package/src/db/schema/admin-command-results.ts +23 -2
- package/src/index.ts +3 -0
- package/src/presentation/auth/bearer-token-auth.test.ts +17 -0
- package/src/presentation/auth/bearer-token-auth.ts +9 -2
- package/src/presentation/auth/jwt-auth.test.ts +34 -9
- package/src/presentation/auth/jwt-auth.ts +19 -13
- package/src/presentation/routes/administration.test.ts +253 -2
- package/src/presentation/routes/administration.ts +88 -1
- package/src/presentation/routes/password/change-password.test.ts +6 -0
- package/src/presentation/routes/password/password-reset-confirm.test.ts +14 -0
- package/src/presentation/routes/registration/signup.test.ts +2 -2
- package/src/presentation/routes/session/logout.test.ts +6 -0
- package/src/presentation/routes/session/refresh.test.ts +11 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { and, eq, gt, isNull, sql } from 'drizzle-orm';
|
|
2
|
+
import { hasMinimumAdminRole, highestAdminRole } from '#core/admin-role-model.js';
|
|
3
|
+
import { AdminRoleRequiredError, LastAdminOwnerError, UserNotFoundError } from '#core/errors.js';
|
|
4
|
+
import { findAdminCommandResult, lockAdminCommand, lockAdminOwnerGrants, storeAdminCommandResult, writeAdminAction } from './admin-command.js';
|
|
5
|
+
import { findAdministratorUserSummary } from './admin-user-summary.js';
|
|
6
|
+
import { db } from './db.js';
|
|
7
|
+
import { lockUserSessionMutations } from './refresh-tokens.js';
|
|
8
|
+
import { adminGrants } from './schema/admin-grants.js';
|
|
9
|
+
import { refreshTokens } from './schema/refresh-tokens.js';
|
|
10
|
+
import { users } from './schema/users.js';
|
|
11
|
+
function toStoredUserSummary(user) {
|
|
12
|
+
return {
|
|
13
|
+
id: user.id,
|
|
14
|
+
email: user.email,
|
|
15
|
+
name: user.name,
|
|
16
|
+
emailVerifiedAt: user.emailVerifiedAt?.toISOString() ?? null,
|
|
17
|
+
status: user.status,
|
|
18
|
+
createdAt: user.createdAt.toISOString(),
|
|
19
|
+
adminRole: user.adminRole
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function findUserSummary(tx, userId) {
|
|
23
|
+
const user = await findAdministratorUserSummary(tx, {
|
|
24
|
+
id: userId
|
|
25
|
+
});
|
|
26
|
+
if (!user) throw new Error('User summary query returned no rows');
|
|
27
|
+
return toStoredUserSummary(user);
|
|
28
|
+
}
|
|
29
|
+
function fromStoredResult(result) {
|
|
30
|
+
return {
|
|
31
|
+
user: {
|
|
32
|
+
id: result.user.id,
|
|
33
|
+
email: result.user.email,
|
|
34
|
+
name: result.user.name,
|
|
35
|
+
emailVerifiedAt: result.user.emailVerifiedAt ? new Date(result.user.emailVerifiedAt) : null,
|
|
36
|
+
status: result.user.status,
|
|
37
|
+
createdAt: new Date(result.user.createdAt),
|
|
38
|
+
adminRole: result.user.adminRole
|
|
39
|
+
},
|
|
40
|
+
correlationId: result.correlationId,
|
|
41
|
+
sessionsRevoked: result.sessionsRevoked
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function findCommandResult(tx, params) {
|
|
45
|
+
const result = await findAdminCommandResult(tx, params);
|
|
46
|
+
if (!result) return undefined;
|
|
47
|
+
if (!('userModeration' in result.result)) {
|
|
48
|
+
throw new Error('Administrator command result has an unexpected shape');
|
|
49
|
+
}
|
|
50
|
+
return fromStoredResult(result.result.userModeration);
|
|
51
|
+
}
|
|
52
|
+
async function storeCommandResult(tx, params, result) {
|
|
53
|
+
await storeAdminCommandResult(tx, params, {
|
|
54
|
+
userModeration: result
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function readTargetUserForUpdate(tx, userId) {
|
|
58
|
+
const rows = await tx.select({
|
|
59
|
+
id: users.id,
|
|
60
|
+
status: users.status
|
|
61
|
+
}).from(users).where(eq(users.id, userId)).limit(1).for('update');
|
|
62
|
+
const user = rows[0];
|
|
63
|
+
if (!user || user.status === 'deleted') throw new UserNotFoundError(userId);
|
|
64
|
+
return user;
|
|
65
|
+
}
|
|
66
|
+
async function requireActiveAdminOperator(tx, actorId) {
|
|
67
|
+
const actorRows = await tx.select({
|
|
68
|
+
status: users.status
|
|
69
|
+
}).from(users).where(eq(users.id, actorId)).limit(1);
|
|
70
|
+
const actor = actorRows[0];
|
|
71
|
+
const grants = await tx.select({
|
|
72
|
+
role: adminGrants.role
|
|
73
|
+
}).from(adminGrants).where(and(eq(adminGrants.userId, actorId), isNull(adminGrants.revokedAt)));
|
|
74
|
+
const role = highestAdminRole(grants.map(({ role })=>role));
|
|
75
|
+
if (actor?.status !== 'active' || !role || !hasMinimumAdminRole(role, 'admin-operator')) {
|
|
76
|
+
throw new AdminRoleRequiredError('admin-operator');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function revokeActiveSessions(tx, userId) {
|
|
80
|
+
const revoked = await tx.update(refreshTokens).set({
|
|
81
|
+
revokedAt: sql`now()`,
|
|
82
|
+
updatedAt: sql`now()`
|
|
83
|
+
}).where(and(eq(refreshTokens.userId, userId), isNull(refreshTokens.revokedAt), gt(refreshTokens.expiresAt, sql`now()`))).returning({
|
|
84
|
+
sessionId: refreshTokens.sessionId
|
|
85
|
+
});
|
|
86
|
+
return new Set(revoked.map(({ sessionId })=>sessionId)).size;
|
|
87
|
+
}
|
|
88
|
+
async function executeUserModerationCommand(params) {
|
|
89
|
+
return await db().transaction(async (tx)=>{
|
|
90
|
+
await lockAdminCommand(tx, params);
|
|
91
|
+
if (params.operation === 'suspend') await lockAdminOwnerGrants(tx);
|
|
92
|
+
const existing = await findCommandResult(tx, {
|
|
93
|
+
...params,
|
|
94
|
+
command: params.event.command
|
|
95
|
+
});
|
|
96
|
+
if (existing) return existing;
|
|
97
|
+
await lockUserSessionMutations(tx, params.userId);
|
|
98
|
+
const user = await readTargetUserForUpdate(tx, params.userId);
|
|
99
|
+
await requireActiveAdminOperator(tx, params.actorId);
|
|
100
|
+
let sessionsRevoked = 0;
|
|
101
|
+
if (params.operation === 'suspend') {
|
|
102
|
+
if (user.status === 'active') {
|
|
103
|
+
const activeOwners = await tx.select({
|
|
104
|
+
id: adminGrants.id
|
|
105
|
+
}).from(adminGrants).innerJoin(users, eq(adminGrants.userId, users.id)).where(and(eq(adminGrants.role, 'admin-owner'), isNull(adminGrants.revokedAt), eq(users.status, 'active'))).limit(2);
|
|
106
|
+
const targetIsActiveOwner = await tx.select({
|
|
107
|
+
id: adminGrants.id
|
|
108
|
+
}).from(adminGrants).where(and(eq(adminGrants.userId, user.id), eq(adminGrants.role, 'admin-owner'), isNull(adminGrants.revokedAt))).limit(1);
|
|
109
|
+
if (targetIsActiveOwner.length > 0 && activeOwners.length <= 1) {
|
|
110
|
+
throw new LastAdminOwnerError();
|
|
111
|
+
}
|
|
112
|
+
await tx.update(users).set({
|
|
113
|
+
status: 'suspended',
|
|
114
|
+
updatedAt: sql`now()`
|
|
115
|
+
}).where(eq(users.id, user.id));
|
|
116
|
+
}
|
|
117
|
+
sessionsRevoked = await revokeActiveSessions(tx, user.id);
|
|
118
|
+
} else if (params.operation === 'reactivate' && user.status === 'suspended') {
|
|
119
|
+
await tx.update(users).set({
|
|
120
|
+
status: 'active',
|
|
121
|
+
updatedAt: sql`now()`
|
|
122
|
+
}).where(eq(users.id, user.id));
|
|
123
|
+
} else if (params.operation === 'revoke-sessions') {
|
|
124
|
+
sessionsRevoked = await revokeActiveSessions(tx, user.id);
|
|
125
|
+
}
|
|
126
|
+
const summary = await findUserSummary(tx, user.id);
|
|
127
|
+
const result = {
|
|
128
|
+
user: summary,
|
|
129
|
+
correlationId: params.event.correlationId,
|
|
130
|
+
sessionsRevoked
|
|
131
|
+
};
|
|
132
|
+
await writeAdminAction(tx, params.event);
|
|
133
|
+
await storeCommandResult(tx, params, result);
|
|
134
|
+
return fromStoredResult(result);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
export async function suspendUserWithAudit(params) {
|
|
138
|
+
return await executeUserModerationCommand({
|
|
139
|
+
...params,
|
|
140
|
+
operation: 'suspend'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export async function reactivateUserWithAudit(params) {
|
|
144
|
+
return await executeUserModerationCommand({
|
|
145
|
+
...params,
|
|
146
|
+
operation: 'reactivate'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
export async function revokeUserSessionsWithAudit(params) {
|
|
150
|
+
return await executeUserModerationCommand({
|
|
151
|
+
...params,
|
|
152
|
+
operation: 'revoke-sessions'
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=admin-user-moderation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/db/admin-user-moderation.ts"],"sourcesContent":["import type {AdministrationActionEvent} from '@shipfox/api-common-dto';\nimport {and, eq, gt, isNull, sql} from 'drizzle-orm';\nimport {hasMinimumAdminRole, highestAdminRole} from '#core/admin-role-model.js';\nimport type {AdministratorUserSummary} from '#core/entities/administrator-read-model.js';\nimport {AdminRoleRequiredError, LastAdminOwnerError, UserNotFoundError} from '#core/errors.js';\nimport {\n findAdminCommandResult,\n lockAdminCommand,\n lockAdminOwnerGrants,\n storeAdminCommandResult,\n type Tx,\n writeAdminAction,\n} from './admin-command.js';\nimport {findAdministratorUserSummary} from './admin-user-summary.js';\nimport {db} from './db.js';\nimport {lockUserSessionMutations} from './refresh-tokens.js';\nimport type {\n StoredAdministratorUserSummary,\n StoredAdminUserModerationResult,\n} from './schema/admin-command-results.js';\nimport {adminGrants} from './schema/admin-grants.js';\nimport {refreshTokens} from './schema/refresh-tokens.js';\nimport {users} from './schema/users.js';\n\ninterface UserModerationCommandParams {\n actorId: string;\n userId: string;\n idempotencyKeyFingerprint: string;\n requestFingerprint: string;\n event: AdministrationActionEvent;\n}\n\nfunction toStoredUserSummary(user: AdministratorUserSummary): StoredAdministratorUserSummary {\n return {\n id: user.id,\n email: user.email,\n name: user.name,\n emailVerifiedAt: user.emailVerifiedAt?.toISOString() ?? null,\n status: user.status,\n createdAt: user.createdAt.toISOString(),\n adminRole: user.adminRole,\n };\n}\n\nasync function findUserSummary(tx: Tx, userId: string): Promise<StoredAdministratorUserSummary> {\n const user = await findAdministratorUserSummary(tx, {id: userId});\n if (!user) throw new Error('User summary query returned no rows');\n return toStoredUserSummary(user);\n}\n\nfunction fromStoredResult(result: StoredAdminUserModerationResult) {\n return {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n emailVerifiedAt: result.user.emailVerifiedAt ? new Date(result.user.emailVerifiedAt) : null,\n status: result.user.status,\n createdAt: new Date(result.user.createdAt),\n adminRole: result.user.adminRole,\n },\n correlationId: result.correlationId,\n sessionsRevoked: result.sessionsRevoked,\n };\n}\n\nasync function findCommandResult(\n tx: Tx,\n params: Pick<\n UserModerationCommandParams,\n 'actorId' | 'idempotencyKeyFingerprint' | 'requestFingerprint'\n > & {command: string},\n) {\n const result = await findAdminCommandResult(tx, params);\n if (!result) return undefined;\n if (!('userModeration' in result.result)) {\n throw new Error('Administrator command result has an unexpected shape');\n }\n return fromStoredResult(result.result.userModeration);\n}\n\nasync function storeCommandResult(\n tx: Tx,\n params: UserModerationCommandParams,\n result: StoredAdminUserModerationResult,\n): Promise<void> {\n await storeAdminCommandResult(tx, params, {userModeration: result});\n}\n\nasync function readTargetUserForUpdate(tx: Tx, userId: string) {\n const rows = await tx\n .select({id: users.id, status: users.status})\n .from(users)\n .where(eq(users.id, userId))\n .limit(1)\n .for('update');\n const user = rows[0];\n if (!user || user.status === 'deleted') throw new UserNotFoundError(userId);\n return user;\n}\n\nasync function requireActiveAdminOperator(tx: Tx, actorId: string): Promise<void> {\n const actorRows = await tx\n .select({status: users.status})\n .from(users)\n .where(eq(users.id, actorId))\n .limit(1);\n const actor = actorRows[0];\n const grants = await tx\n .select({role: adminGrants.role})\n .from(adminGrants)\n .where(and(eq(adminGrants.userId, actorId), isNull(adminGrants.revokedAt)));\n const role = highestAdminRole(grants.map(({role}) => role));\n\n if (actor?.status !== 'active' || !role || !hasMinimumAdminRole(role, 'admin-operator')) {\n throw new AdminRoleRequiredError('admin-operator');\n }\n}\n\nasync function revokeActiveSessions(tx: Tx, userId: string): Promise<number> {\n const revoked = await tx\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.userId, userId),\n isNull(refreshTokens.revokedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .returning({sessionId: refreshTokens.sessionId});\n\n return new Set(revoked.map(({sessionId}) => sessionId)).size;\n}\n\nasync function executeUserModerationCommand(\n params: UserModerationCommandParams & {\n operation: 'suspend' | 'reactivate' | 'revoke-sessions';\n },\n) {\n return await db().transaction(async (tx) => {\n await lockAdminCommand(tx, params);\n if (params.operation === 'suspend') await lockAdminOwnerGrants(tx);\n\n const existing = await findCommandResult(tx, {\n ...params,\n command: params.event.command,\n });\n if (existing) return existing;\n\n await lockUserSessionMutations(tx, params.userId);\n const user = await readTargetUserForUpdate(tx, params.userId);\n await requireActiveAdminOperator(tx, params.actorId);\n let sessionsRevoked = 0;\n\n if (params.operation === 'suspend') {\n if (user.status === 'active') {\n const activeOwners = await tx\n .select({id: adminGrants.id})\n .from(adminGrants)\n .innerJoin(users, eq(adminGrants.userId, users.id))\n .where(\n and(\n eq(adminGrants.role, 'admin-owner'),\n isNull(adminGrants.revokedAt),\n eq(users.status, 'active'),\n ),\n )\n .limit(2);\n const targetIsActiveOwner = await tx\n .select({id: adminGrants.id})\n .from(adminGrants)\n .where(\n and(\n eq(adminGrants.userId, user.id),\n eq(adminGrants.role, 'admin-owner'),\n isNull(adminGrants.revokedAt),\n ),\n )\n .limit(1);\n if (targetIsActiveOwner.length > 0 && activeOwners.length <= 1) {\n throw new LastAdminOwnerError();\n }\n\n await tx\n .update(users)\n .set({status: 'suspended', updatedAt: sql`now()`})\n .where(eq(users.id, user.id));\n }\n sessionsRevoked = await revokeActiveSessions(tx, user.id);\n } else if (params.operation === 'reactivate' && user.status === 'suspended') {\n await tx\n .update(users)\n .set({status: 'active', updatedAt: sql`now()`})\n .where(eq(users.id, user.id));\n } else if (params.operation === 'revoke-sessions') {\n sessionsRevoked = await revokeActiveSessions(tx, user.id);\n }\n\n const summary = await findUserSummary(tx, user.id);\n const result: StoredAdminUserModerationResult = {\n user: summary,\n correlationId: params.event.correlationId,\n sessionsRevoked,\n };\n await writeAdminAction(tx, params.event);\n await storeCommandResult(tx, params, result);\n return fromStoredResult(result);\n });\n}\n\nexport type UserModerationResult = Awaited<ReturnType<typeof executeUserModerationCommand>>;\n\nexport async function suspendUserWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'suspend'});\n}\n\nexport async function reactivateUserWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'reactivate'});\n}\n\nexport async function revokeUserSessionsWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'revoke-sessions'});\n}\n"],"names":["and","eq","gt","isNull","sql","hasMinimumAdminRole","highestAdminRole","AdminRoleRequiredError","LastAdminOwnerError","UserNotFoundError","findAdminCommandResult","lockAdminCommand","lockAdminOwnerGrants","storeAdminCommandResult","writeAdminAction","findAdministratorUserSummary","db","lockUserSessionMutations","adminGrants","refreshTokens","users","toStoredUserSummary","user","id","email","name","emailVerifiedAt","toISOString","status","createdAt","adminRole","findUserSummary","tx","userId","Error","fromStoredResult","result","Date","correlationId","sessionsRevoked","findCommandResult","params","undefined","userModeration","storeCommandResult","readTargetUserForUpdate","rows","select","from","where","limit","for","requireActiveAdminOperator","actorId","actorRows","actor","grants","role","revokedAt","map","revokeActiveSessions","revoked","update","set","updatedAt","expiresAt","returning","sessionId","Set","size","executeUserModerationCommand","transaction","operation","existing","command","event","activeOwners","innerJoin","targetIsActiveOwner","length","summary","suspendUserWithAudit","reactivateUserWithAudit","revokeUserSessionsWithAudit"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AdministratorUserSummary } from '#core/entities/administrator-read-model.js';
|
|
2
|
+
import type { db } from './db.js';
|
|
3
|
+
type Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];
|
|
4
|
+
export type AdministratorUserSummaryExecutor = ReturnType<typeof db> | Tx;
|
|
5
|
+
type AdministratorUserLookup = {
|
|
6
|
+
id: string;
|
|
7
|
+
email?: never;
|
|
8
|
+
} | {
|
|
9
|
+
email: string;
|
|
10
|
+
id?: never;
|
|
11
|
+
};
|
|
12
|
+
export declare function findAdministratorUserSummary(executor: AdministratorUserSummaryExecutor, params: AdministratorUserLookup): Promise<AdministratorUserSummary | undefined>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=admin-user-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-user-summary.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-summary.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,4CAA4C,CAAC;AACzF,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,SAAS,CAAC;AAIhC,KAAK,EAAE,GAAG,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7E,MAAM,MAAM,gCAAgC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC;AAE1E,KAAK,uBAAuB,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAC,GAAG;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAC,CAAC;AAEzF,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,gCAAgC,EAC1C,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC,CAmC/C"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { and, eq, isNull } from 'drizzle-orm';
|
|
2
|
+
import { highestAdminRole } from '#core/admin-role-model.js';
|
|
3
|
+
import { adminGrants } from './schema/admin-grants.js';
|
|
4
|
+
import { users } from './schema/users.js';
|
|
5
|
+
export async function findAdministratorUserSummary(executor, params) {
|
|
6
|
+
const identifier = 'id' in params ? eq(users.id, params.id) : eq(users.email, params.email);
|
|
7
|
+
const rows = await executor.select({
|
|
8
|
+
id: users.id,
|
|
9
|
+
email: users.email,
|
|
10
|
+
name: users.name,
|
|
11
|
+
emailVerifiedAt: users.emailVerifiedAt,
|
|
12
|
+
status: users.status,
|
|
13
|
+
createdAt: users.createdAt,
|
|
14
|
+
adminRole: adminGrants.role
|
|
15
|
+
}).from(users).leftJoin(adminGrants, and(eq(adminGrants.userId, users.id), isNull(adminGrants.revokedAt), eq(users.status, 'active'))).where(identifier);
|
|
16
|
+
const first = rows[0];
|
|
17
|
+
if (!first) return undefined;
|
|
18
|
+
return {
|
|
19
|
+
id: first.id,
|
|
20
|
+
email: first.email,
|
|
21
|
+
name: first.name,
|
|
22
|
+
emailVerifiedAt: first.emailVerifiedAt,
|
|
23
|
+
status: first.status,
|
|
24
|
+
createdAt: first.createdAt,
|
|
25
|
+
adminRole: highestAdminRole(rows.flatMap(({ adminRole })=>adminRole ? [
|
|
26
|
+
adminRole
|
|
27
|
+
] : []))
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=admin-user-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/db/admin-user-summary.ts"],"sourcesContent":["import {and, eq, isNull} from 'drizzle-orm';\nimport {highestAdminRole} from '#core/admin-role-model.js';\nimport type {AdministratorUserSummary} from '#core/entities/administrator-read-model.js';\nimport type {db} from './db.js';\nimport {adminGrants} from './schema/admin-grants.js';\nimport {users} from './schema/users.js';\n\ntype Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];\nexport type AdministratorUserSummaryExecutor = ReturnType<typeof db> | Tx;\n\ntype AdministratorUserLookup = {id: string; email?: never} | {email: string; id?: never};\n\nexport async function findAdministratorUserSummary(\n executor: AdministratorUserSummaryExecutor,\n params: AdministratorUserLookup,\n): Promise<AdministratorUserSummary | undefined> {\n const identifier = 'id' in params ? eq(users.id, params.id) : eq(users.email, params.email);\n const rows = await executor\n .select({\n id: users.id,\n email: users.email,\n name: users.name,\n emailVerifiedAt: users.emailVerifiedAt,\n status: users.status,\n createdAt: users.createdAt,\n adminRole: adminGrants.role,\n })\n .from(users)\n .leftJoin(\n adminGrants,\n and(\n eq(adminGrants.userId, users.id),\n isNull(adminGrants.revokedAt),\n eq(users.status, 'active'),\n ),\n )\n .where(identifier);\n\n const first = rows[0];\n if (!first) return undefined;\n\n return {\n id: first.id,\n email: first.email,\n name: first.name,\n emailVerifiedAt: first.emailVerifiedAt,\n status: first.status,\n createdAt: first.createdAt,\n adminRole: highestAdminRole(rows.flatMap(({adminRole}) => (adminRole ? [adminRole] : []))),\n } satisfies AdministratorUserSummary;\n}\n"],"names":["and","eq","isNull","highestAdminRole","adminGrants","users","findAdministratorUserSummary","executor","params","identifier","id","email","rows","select","name","emailVerifiedAt","status","createdAt","adminRole","role","from","leftJoin","userId","revokedAt","where","first","undefined","flatMap"],"mappings":"AAAA,SAAQA,GAAG,EAAEC,EAAE,EAAEC,MAAM,QAAO,cAAc;AAC5C,SAAQC,gBAAgB,QAAO,4BAA4B;AAG3D,SAAQC,WAAW,QAAO,2BAA2B;AACrD,SAAQC,KAAK,QAAO,oBAAoB;AAOxC,OAAO,eAAeC,6BACpBC,QAA0C,EAC1CC,MAA+B;IAE/B,MAAMC,aAAa,QAAQD,SAASP,GAAGI,MAAMK,EAAE,EAAEF,OAAOE,EAAE,IAAIT,GAAGI,MAAMM,KAAK,EAAEH,OAAOG,KAAK;IAC1F,MAAMC,OAAO,MAAML,SAChBM,MAAM,CAAC;QACNH,IAAIL,MAAMK,EAAE;QACZC,OAAON,MAAMM,KAAK;QAClBG,MAAMT,MAAMS,IAAI;QAChBC,iBAAiBV,MAAMU,eAAe;QACtCC,QAAQX,MAAMW,MAAM;QACpBC,WAAWZ,MAAMY,SAAS;QAC1BC,WAAWd,YAAYe,IAAI;IAC7B,GACCC,IAAI,CAACf,OACLgB,QAAQ,CACPjB,aACAJ,IACEC,GAAGG,YAAYkB,MAAM,EAAEjB,MAAMK,EAAE,GAC/BR,OAAOE,YAAYmB,SAAS,GAC5BtB,GAAGI,MAAMW,MAAM,EAAE,YAGpBQ,KAAK,CAACf;IAET,MAAMgB,QAAQb,IAAI,CAAC,EAAE;IACrB,IAAI,CAACa,OAAO,OAAOC;IAEnB,OAAO;QACLhB,IAAIe,MAAMf,EAAE;QACZC,OAAOc,MAAMd,KAAK;QAClBG,MAAMW,MAAMX,IAAI;QAChBC,iBAAiBU,MAAMV,eAAe;QACtCC,QAAQS,MAAMT,MAAM;QACpBC,WAAWQ,MAAMR,SAAS;QAC1BC,WAAWf,iBAAiBS,KAAKe,OAAO,CAAC,CAAC,EAACT,SAAS,EAAC,GAAMA,YAAY;gBAACA;aAAU,GAAG,EAAE;IACzF;AACF"}
|
package/dist/db/admin-users.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export interface AdministratorUserRecord {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string | null;
|
|
7
|
-
emailVerifiedAt: Date | null;
|
|
8
|
-
status: UserStatus;
|
|
9
|
-
createdAt: Date;
|
|
10
|
-
adminRole: AdminRole | null;
|
|
11
|
-
}
|
|
1
|
+
import type { AdministratorUserSummary } from '#core/entities/administrator-read-model.js';
|
|
2
|
+
export type AdministratorUserRecord = AdministratorUserSummary;
|
|
12
3
|
type AdministratorUserLookup = {
|
|
13
4
|
id: string;
|
|
14
5
|
email?: never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-users.d.ts","sourceRoot":"","sources":["../../src/db/admin-users.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"admin-users.d.ts","sourceRoot":"","sources":["../../src/db/admin-users.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,4CAA4C,CAAC;AAIzF,MAAM,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;AAE/D,KAAK,uBAAuB,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAC,GAAG;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAC,CAAC;AAEzF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CAE9C"}
|
package/dist/db/admin-users.js
CHANGED
|
@@ -1,32 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { highestAdminRole } from '#core/admin-role-model.js';
|
|
1
|
+
import { findAdministratorUserSummary } from './admin-user-summary.js';
|
|
3
2
|
import { db } from './db.js';
|
|
4
|
-
import { adminGrants } from './schema/admin-grants.js';
|
|
5
|
-
import { users } from './schema/users.js';
|
|
6
3
|
export async function findAdministratorUser(params) {
|
|
7
|
-
|
|
8
|
-
const rows = await db().select({
|
|
9
|
-
id: users.id,
|
|
10
|
-
email: users.email,
|
|
11
|
-
name: users.name,
|
|
12
|
-
emailVerifiedAt: users.emailVerifiedAt,
|
|
13
|
-
status: users.status,
|
|
14
|
-
createdAt: users.createdAt,
|
|
15
|
-
adminRole: adminGrants.role
|
|
16
|
-
}).from(users).leftJoin(adminGrants, and(eq(adminGrants.userId, users.id), isNull(adminGrants.revokedAt), eq(users.status, 'active'))).where(identifier);
|
|
17
|
-
const first = rows[0];
|
|
18
|
-
if (!first) return undefined;
|
|
19
|
-
return {
|
|
20
|
-
id: first.id,
|
|
21
|
-
email: first.email,
|
|
22
|
-
name: first.name,
|
|
23
|
-
emailVerifiedAt: first.emailVerifiedAt,
|
|
24
|
-
status: first.status,
|
|
25
|
-
createdAt: first.createdAt,
|
|
26
|
-
adminRole: highestAdminRole(rows.flatMap(({ adminRole })=>adminRole ? [
|
|
27
|
-
adminRole
|
|
28
|
-
] : []))
|
|
29
|
-
};
|
|
4
|
+
return await findAdministratorUserSummary(db(), params);
|
|
30
5
|
}
|
|
31
6
|
|
|
32
7
|
//# sourceMappingURL=admin-users.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/admin-users.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["../../src/db/admin-users.ts"],"sourcesContent":["import type {AdministratorUserSummary} from '#core/entities/administrator-read-model.js';\nimport {findAdministratorUserSummary} from './admin-user-summary.js';\nimport {db} from './db.js';\n\nexport type AdministratorUserRecord = AdministratorUserSummary;\n\ntype AdministratorUserLookup = {id: string; email?: never} | {email: string; id?: never};\n\nexport async function findAdministratorUser(\n params: AdministratorUserLookup,\n): Promise<AdministratorUserRecord | undefined> {\n return await findAdministratorUserSummary(db(), params);\n}\n"],"names":["findAdministratorUserSummary","db","findAdministratorUser","params"],"mappings":"AACA,SAAQA,4BAA4B,QAAO,0BAA0B;AACrE,SAAQC,EAAE,QAAO,UAAU;AAM3B,OAAO,eAAeC,sBACpBC,MAA+B;IAE/B,OAAO,MAAMH,6BAA6BC,MAAME;AAClD"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { RefreshToken } from '#core/entities/refresh-token.js';
|
|
2
|
+
import { db } from './db.js';
|
|
3
|
+
type Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];
|
|
4
|
+
export declare function lockUserSessionMutations(tx: Tx, userId: string): Promise<void>;
|
|
2
5
|
export interface CreateRefreshTokenParams {
|
|
3
6
|
sessionId?: string | undefined;
|
|
4
7
|
userId: string;
|
|
@@ -6,6 +9,7 @@ export interface CreateRefreshTokenParams {
|
|
|
6
9
|
expiresAt: Date;
|
|
7
10
|
}
|
|
8
11
|
export declare function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken>;
|
|
12
|
+
export declare function createRefreshTokenForActiveUser(params: CreateRefreshTokenParams): Promise<RefreshToken | undefined>;
|
|
9
13
|
/**
|
|
10
14
|
* Looks up the live session token by hash — one that can still authenticate as
|
|
11
15
|
* the current session. Returns `undefined` for revoked, expired, or already
|
|
@@ -15,10 +19,6 @@ export declare function createRefreshToken(params: CreateRefreshTokenParams): Pr
|
|
|
15
19
|
export declare function findActiveRefreshTokenByHash(params: {
|
|
16
20
|
hashedToken: string;
|
|
17
21
|
}): Promise<RefreshToken | undefined>;
|
|
18
|
-
export declare function findActiveRefreshSession(params: {
|
|
19
|
-
sessionId: string;
|
|
20
|
-
userId: string;
|
|
21
|
-
}): Promise<RefreshToken | undefined>;
|
|
22
22
|
/**
|
|
23
23
|
* Looks up a non-revoked, non-expired token by hash, including ones already
|
|
24
24
|
* rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
|
|
@@ -34,6 +34,7 @@ export declare function findRefreshTokenByHash(params: {
|
|
|
34
34
|
* If the successor insert fails, the predecessor rotation rolls back.
|
|
35
35
|
*/
|
|
36
36
|
export declare function rotateRefreshToken(params: {
|
|
37
|
+
userId?: string | undefined;
|
|
37
38
|
id: string;
|
|
38
39
|
currentHashedToken: string;
|
|
39
40
|
nextHashedToken: string;
|
|
@@ -50,4 +51,5 @@ export declare function revokeRefreshTokensForUser(params: {
|
|
|
50
51
|
userId: string;
|
|
51
52
|
exceptRefreshTokenId?: string | undefined;
|
|
52
53
|
}): Promise<void>;
|
|
54
|
+
export {};
|
|
53
55
|
//# sourceMappingURL=refresh-tokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-tokens.d.ts","sourceRoot":"","sources":["../../src/db/refresh-tokens.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"refresh-tokens.d.ts","sourceRoot":"","sources":["../../src/db/refresh-tokens.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAC,EAAE,EAAC,MAAM,SAAS,CAAC;AAI3B,KAAK,EAAE,GAAG,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7E,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpF;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC,CAchG;AAED,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAuBnC;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAiBpC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAgBpC;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAwDpC;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3F;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { and, eq, gt, isNull, ne, sql } from 'drizzle-orm';
|
|
2
2
|
import { db } from './db.js';
|
|
3
3
|
import { refreshTokens, toRefreshToken } from './schema/refresh-tokens.js';
|
|
4
|
+
import { users } from './schema/users.js';
|
|
5
|
+
export async function lockUserSessionMutations(tx, userId) {
|
|
6
|
+
await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${`auth_user_sessions:${userId}`}))`);
|
|
7
|
+
}
|
|
4
8
|
export async function createRefreshToken(params) {
|
|
5
9
|
const rows = await db().insert(refreshTokens).values({
|
|
6
10
|
sessionId: params.sessionId,
|
|
@@ -12,6 +16,24 @@ export async function createRefreshToken(params) {
|
|
|
12
16
|
if (!row) throw new Error('Insert returned no rows');
|
|
13
17
|
return toRefreshToken(row);
|
|
14
18
|
}
|
|
19
|
+
export async function createRefreshTokenForActiveUser(params) {
|
|
20
|
+
return await db().transaction(async (tx)=>{
|
|
21
|
+
await lockUserSessionMutations(tx, params.userId);
|
|
22
|
+
const userRows = await tx.select({
|
|
23
|
+
status: users.status
|
|
24
|
+
}).from(users).where(eq(users.id, params.userId)).limit(1);
|
|
25
|
+
if (userRows[0]?.status !== 'active') return undefined;
|
|
26
|
+
const rows = await tx.insert(refreshTokens).values({
|
|
27
|
+
sessionId: params.sessionId,
|
|
28
|
+
userId: params.userId,
|
|
29
|
+
hashedToken: params.hashedToken,
|
|
30
|
+
expiresAt: params.expiresAt
|
|
31
|
+
}).returning();
|
|
32
|
+
const row = rows[0];
|
|
33
|
+
if (!row) throw new Error('Insert returned no rows');
|
|
34
|
+
return toRefreshToken(row);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
15
37
|
/**
|
|
16
38
|
* Looks up the live session token by hash — one that can still authenticate as
|
|
17
39
|
* the current session. Returns `undefined` for revoked, expired, or already
|
|
@@ -23,12 +45,6 @@ export async function createRefreshToken(params) {
|
|
|
23
45
|
if (!row) return undefined;
|
|
24
46
|
return toRefreshToken(row);
|
|
25
47
|
}
|
|
26
|
-
export async function findActiveRefreshSession(params) {
|
|
27
|
-
const rows = await db().select().from(refreshTokens).where(and(eq(refreshTokens.sessionId, params.sessionId), eq(refreshTokens.userId, params.userId), isNull(refreshTokens.revokedAt), isNull(refreshTokens.rotatedAt), gt(refreshTokens.expiresAt, sql`now()`))).limit(1);
|
|
28
|
-
const row = rows[0];
|
|
29
|
-
if (!row) return undefined;
|
|
30
|
-
return toRefreshToken(row);
|
|
31
|
-
}
|
|
32
48
|
/**
|
|
33
49
|
* Looks up a non-revoked, non-expired token by hash, including ones already
|
|
34
50
|
* rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
|
|
@@ -46,6 +62,15 @@ export async function findActiveRefreshSession(params) {
|
|
|
46
62
|
* If the successor insert fails, the predecessor rotation rolls back.
|
|
47
63
|
*/ export async function rotateRefreshToken(params) {
|
|
48
64
|
return await db().transaction(async (tx)=>{
|
|
65
|
+
const userId = params.userId ?? (await tx.select({
|
|
66
|
+
userId: refreshTokens.userId
|
|
67
|
+
}).from(refreshTokens).where(eq(refreshTokens.id, params.id)).limit(1))[0]?.userId;
|
|
68
|
+
if (!userId) return undefined;
|
|
69
|
+
await lockUserSessionMutations(tx, userId);
|
|
70
|
+
const userRows = await tx.select({
|
|
71
|
+
status: users.status
|
|
72
|
+
}).from(users).where(eq(users.id, userId)).limit(1);
|
|
73
|
+
if (userRows[0]?.status !== 'active') return undefined;
|
|
49
74
|
const rotatedRows = await tx.update(refreshTokens).set({
|
|
50
75
|
rotatedAt: sql`now()`,
|
|
51
76
|
lastUsedAt: sql`now()`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/refresh-tokens.ts"],"sourcesContent":["import {and, eq, gt, isNull, ne, sql} from 'drizzle-orm';\nimport type {RefreshToken} from '#core/entities/refresh-token.js';\nimport {db} from './db.js';\nimport {refreshTokens, toRefreshToken} from './schema/refresh-tokens.js';\n\nexport interface CreateRefreshTokenParams {\n sessionId?: string | undefined;\n userId: string;\n hashedToken: string;\n expiresAt: Date;\n}\n\nexport async function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken> {\n const rows = await db()\n .insert(refreshTokens)\n .values({\n sessionId: params.sessionId,\n userId: params.userId,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n return toRefreshToken(row);\n}\n\n/**\n * Looks up the live session token by hash — one that can still authenticate as\n * the current session. Returns `undefined` for revoked, expired, or already\n * rotated tokens; rotated rows survive in the table only for the grace window\n * and are not \"active\".\n */\nexport async function findActiveRefreshTokenByHash(params: {\n hashedToken: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.hashedToken, params.hashedToken),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\nexport async function findActiveRefreshSession(params: {\n sessionId: string;\n userId: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.sessionId, params.sessionId),\n eq(refreshTokens.userId, params.userId),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\n/**\n * Looks up a non-revoked, non-expired token by hash, including ones already\n * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to\n * rotate, honour the grace window, or reject reuse, so — unlike\n * {@link findActiveRefreshTokenByHash} — it must still see rotated rows.\n */\nexport async function findRefreshTokenByHash(params: {\n hashedToken: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.hashedToken, params.hashedToken),\n isNull(refreshTokens.revokedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\n/**\n * Atomically claims rotation and inserts the successor token. The predecessor is\n * marked rather than deleted so later reuse of the retired token can be detected.\n * If the successor insert fails, the predecessor rotation rolls back.\n */\nexport async function rotateRefreshToken(params: {\n id: string;\n currentHashedToken: string;\n nextHashedToken: string;\n expiresAt: Date;\n}): Promise<RefreshToken | undefined> {\n return await db().transaction(async (tx) => {\n const rotatedRows = await tx\n .update(refreshTokens)\n .set({\n rotatedAt: sql`now()`,\n lastUsedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(refreshTokens.id, params.id),\n eq(refreshTokens.hashedToken, params.currentHashedToken),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n\n const rotated = rotatedRows[0];\n if (!rotated) return undefined;\n\n const successorRows = await tx\n .insert(refreshTokens)\n .values({\n sessionId: rotated.sessionId,\n userId: rotated.userId,\n hashedToken: params.nextHashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n if (!successorRows[0]) throw new Error('Insert returned no rows');\n\n return toRefreshToken(rotated);\n });\n}\n\nexport async function revokeRefreshTokenByHash(params: {hashedToken: string}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt)));\n}\n\nexport async function revokeRefreshSession(params: {\n sessionId: string;\n userId: string;\n}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.sessionId, params.sessionId),\n eq(refreshTokens.userId, params.userId),\n isNull(refreshTokens.revokedAt),\n ),\n );\n}\n\nexport async function revokeRefreshTokensForUser(params: {\n userId: string;\n exceptRefreshTokenId?: string | undefined;\n}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.userId, params.userId),\n isNull(refreshTokens.revokedAt),\n params.exceptRefreshTokenId ? ne(refreshTokens.id, params.exceptRefreshTokenId) : undefined,\n ),\n );\n}\n"],"names":["and","eq","gt","isNull","ne","sql","db","refreshTokens","toRefreshToken","createRefreshToken","params","rows","insert","values","sessionId","userId","hashedToken","expiresAt","returning","row","Error","findActiveRefreshTokenByHash","select","from","where","revokedAt","rotatedAt","limit","undefined","findActiveRefreshSession","findRefreshTokenByHash","rotateRefreshToken","transaction","tx","rotatedRows","update","set","lastUsedAt","updatedAt","id","currentHashedToken","rotated","successorRows","nextHashedToken","revokeRefreshTokenByHash","revokeRefreshSession","revokeRefreshTokensForUser","exceptRefreshTokenId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/db/refresh-tokens.ts"],"sourcesContent":["import {and, eq, gt, isNull, ne, sql} from 'drizzle-orm';\nimport type {RefreshToken} from '#core/entities/refresh-token.js';\nimport {db} from './db.js';\nimport {refreshTokens, toRefreshToken} from './schema/refresh-tokens.js';\nimport {users} from './schema/users.js';\n\ntype Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];\n\nexport async function lockUserSessionMutations(tx: Tx, userId: string): Promise<void> {\n await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${`auth_user_sessions:${userId}`}))`);\n}\n\nexport interface CreateRefreshTokenParams {\n sessionId?: string | undefined;\n userId: string;\n hashedToken: string;\n expiresAt: Date;\n}\n\nexport async function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken> {\n const rows = await db()\n .insert(refreshTokens)\n .values({\n sessionId: params.sessionId,\n userId: params.userId,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n return toRefreshToken(row);\n}\n\nexport async function createRefreshTokenForActiveUser(\n params: CreateRefreshTokenParams,\n): Promise<RefreshToken | undefined> {\n return await db().transaction(async (tx) => {\n await lockUserSessionMutations(tx, params.userId);\n const userRows = await tx\n .select({status: users.status})\n .from(users)\n .where(eq(users.id, params.userId))\n .limit(1);\n if (userRows[0]?.status !== 'active') return undefined;\n\n const rows = await tx\n .insert(refreshTokens)\n .values({\n sessionId: params.sessionId,\n userId: params.userId,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n return toRefreshToken(row);\n });\n}\n\n/**\n * Looks up the live session token by hash — one that can still authenticate as\n * the current session. Returns `undefined` for revoked, expired, or already\n * rotated tokens; rotated rows survive in the table only for the grace window\n * and are not \"active\".\n */\nexport async function findActiveRefreshTokenByHash(params: {\n hashedToken: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.hashedToken, params.hashedToken),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\n/**\n * Looks up a non-revoked, non-expired token by hash, including ones already\n * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to\n * rotate, honour the grace window, or reject reuse, so — unlike\n * {@link findActiveRefreshTokenByHash} — it must still see rotated rows.\n */\nexport async function findRefreshTokenByHash(params: {\n hashedToken: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.hashedToken, params.hashedToken),\n isNull(refreshTokens.revokedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\n/**\n * Atomically claims rotation and inserts the successor token. The predecessor is\n * marked rather than deleted so later reuse of the retired token can be detected.\n * If the successor insert fails, the predecessor rotation rolls back.\n */\nexport async function rotateRefreshToken(params: {\n userId?: string | undefined;\n id: string;\n currentHashedToken: string;\n nextHashedToken: string;\n expiresAt: Date;\n}): Promise<RefreshToken | undefined> {\n return await db().transaction(async (tx) => {\n const userId =\n params.userId ??\n (\n await tx\n .select({userId: refreshTokens.userId})\n .from(refreshTokens)\n .where(eq(refreshTokens.id, params.id))\n .limit(1)\n )[0]?.userId;\n if (!userId) return undefined;\n\n await lockUserSessionMutations(tx, userId);\n const userRows = await tx\n .select({status: users.status})\n .from(users)\n .where(eq(users.id, userId))\n .limit(1);\n if (userRows[0]?.status !== 'active') return undefined;\n\n const rotatedRows = await tx\n .update(refreshTokens)\n .set({\n rotatedAt: sql`now()`,\n lastUsedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(refreshTokens.id, params.id),\n eq(refreshTokens.hashedToken, params.currentHashedToken),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n\n const rotated = rotatedRows[0];\n if (!rotated) return undefined;\n\n const successorRows = await tx\n .insert(refreshTokens)\n .values({\n sessionId: rotated.sessionId,\n userId: rotated.userId,\n hashedToken: params.nextHashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n if (!successorRows[0]) throw new Error('Insert returned no rows');\n\n return toRefreshToken(rotated);\n });\n}\n\nexport async function revokeRefreshTokenByHash(params: {hashedToken: string}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt)));\n}\n\nexport async function revokeRefreshSession(params: {\n sessionId: string;\n userId: string;\n}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.sessionId, params.sessionId),\n eq(refreshTokens.userId, params.userId),\n isNull(refreshTokens.revokedAt),\n ),\n );\n}\n\nexport async function revokeRefreshTokensForUser(params: {\n userId: string;\n exceptRefreshTokenId?: string | undefined;\n}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.userId, params.userId),\n isNull(refreshTokens.revokedAt),\n params.exceptRefreshTokenId ? ne(refreshTokens.id, params.exceptRefreshTokenId) : undefined,\n ),\n );\n}\n"],"names":["and","eq","gt","isNull","ne","sql","db","refreshTokens","toRefreshToken","users","lockUserSessionMutations","tx","userId","execute","createRefreshToken","params","rows","insert","values","sessionId","hashedToken","expiresAt","returning","row","Error","createRefreshTokenForActiveUser","transaction","userRows","select","status","from","where","id","limit","undefined","findActiveRefreshTokenByHash","revokedAt","rotatedAt","findRefreshTokenByHash","rotateRefreshToken","rotatedRows","update","set","lastUsedAt","updatedAt","currentHashedToken","rotated","successorRows","nextHashedToken","revokeRefreshTokenByHash","revokeRefreshSession","revokeRefreshTokensForUser","exceptRefreshTokenId"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AdminRole } from '@shipfox/api-auth-dto';
|
|
2
|
+
import type { UserStatus } from '#core/entities/user.js';
|
|
2
3
|
export interface StoredAdminGrant {
|
|
3
4
|
id: string;
|
|
4
5
|
userId: string;
|
|
@@ -7,9 +8,25 @@ export interface StoredAdminGrant {
|
|
|
7
8
|
createdAt: string;
|
|
8
9
|
updatedAt: string;
|
|
9
10
|
}
|
|
10
|
-
export interface
|
|
11
|
-
|
|
11
|
+
export interface StoredAdministratorUserSummary {
|
|
12
|
+
id: string;
|
|
13
|
+
email: string;
|
|
14
|
+
name: string | null;
|
|
15
|
+
emailVerifiedAt: string | null;
|
|
16
|
+
status: UserStatus;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
adminRole: AdminRole | null;
|
|
12
19
|
}
|
|
20
|
+
export interface StoredAdminUserModerationResult {
|
|
21
|
+
user: StoredAdministratorUserSummary;
|
|
22
|
+
correlationId: string;
|
|
23
|
+
sessionsRevoked: number;
|
|
24
|
+
}
|
|
25
|
+
export type StoredAdminCommandResult = {
|
|
26
|
+
grant: StoredAdminGrant;
|
|
27
|
+
} | {
|
|
28
|
+
userModeration: StoredAdminUserModerationResult;
|
|
29
|
+
};
|
|
13
30
|
export declare const adminCommandResults: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
14
31
|
name: "admin_command_results";
|
|
15
32
|
schema: undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-command-results.d.ts","sourceRoot":"","sources":["../../../src/db/schema/admin-command-results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"admin-command-results.d.ts","sourceRoot":"","sources":["../../../src/db/schema/admin-command-results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAGrD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAIvD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,8BAA8B;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,8BAA8B,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,wBAAwB,GAChC;IACE,KAAK,EAAE,gBAAgB,CAAC;CACzB,GACD;IACE,cAAc,EAAE,+BAA+B,CAAC;CACjD,CAAC;AAEN,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmB/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,OAAO,mBAAmB,CAAC,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/db/schema/admin-command-results.ts"],"sourcesContent":["import type {AdminRole} from '@shipfox/api-auth-dto';\nimport {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport {pgTable} from './common.js';\nimport {users} from './users.js';\n\nexport interface StoredAdminGrant {\n id: string;\n userId: string;\n role: AdminRole;\n revokedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface StoredAdminCommandResult {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/db/schema/admin-command-results.ts"],"sourcesContent":["import type {AdminRole} from '@shipfox/api-auth-dto';\nimport {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {UserStatus} from '#core/entities/user.js';\nimport {pgTable} from './common.js';\nimport {users} from './users.js';\n\nexport interface StoredAdminGrant {\n id: string;\n userId: string;\n role: AdminRole;\n revokedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface StoredAdministratorUserSummary {\n id: string;\n email: string;\n name: string | null;\n emailVerifiedAt: string | null;\n status: UserStatus;\n createdAt: string;\n adminRole: AdminRole | null;\n}\n\nexport interface StoredAdminUserModerationResult {\n user: StoredAdministratorUserSummary;\n correlationId: string;\n sessionsRevoked: number;\n}\n\nexport type StoredAdminCommandResult =\n | {\n grant: StoredAdminGrant;\n }\n | {\n userModeration: StoredAdminUserModerationResult;\n };\n\nexport const adminCommandResults = pgTable(\n 'admin_command_results',\n {\n id: uuidv7PrimaryKey(),\n actorId: uuid('actor_id')\n .notNull()\n .references(() => users.id, {onDelete: 'cascade'}),\n idempotencyKeyFingerprint: text('idempotency_key_fingerprint').notNull(),\n command: text('command').notNull(),\n requestFingerprint: text('request_fingerprint').notNull(),\n result: jsonb('result').$type<StoredAdminCommandResult>().notNull(),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('auth_admin_command_results_actor_key_unique').on(\n table.actorId,\n table.idempotencyKeyFingerprint,\n ),\n ],\n);\n\nexport type AdminCommandResultDb = typeof adminCommandResults.$inferSelect;\n"],"names":["uuidv7PrimaryKey","jsonb","text","timestamp","uniqueIndex","uuid","pgTable","users","adminCommandResults","id","actorId","notNull","references","onDelete","idempotencyKeyFingerprint","command","requestFingerprint","result","$type","createdAt","withTimezone","defaultNow","table","on"],"mappings":"AACA,SAAQA,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,IAAI,QAAO,sBAAsB;AAE9E,SAAQC,OAAO,QAAO,cAAc;AACpC,SAAQC,KAAK,QAAO,aAAa;AAmCjC,OAAO,MAAMC,sBAAsBF,QACjC,yBACA;IACEG,IAAIT;IACJU,SAASL,KAAK,YACXM,OAAO,GACPC,UAAU,CAAC,IAAML,MAAME,EAAE,EAAE;QAACI,UAAU;IAAS;IAClDC,2BAA2BZ,KAAK,+BAA+BS,OAAO;IACtEI,SAASb,KAAK,WAAWS,OAAO;IAChCK,oBAAoBd,KAAK,uBAAuBS,OAAO;IACvDM,QAAQhB,MAAM,UAAUiB,KAAK,GAA6BP,OAAO;IACjEQ,WAAWhB,UAAU,cAAc;QAACiB,cAAc;IAAI,GAAGT,OAAO,GAAGU,UAAU;AAC/E,GACA,CAACC,QAAU;QACTlB,YAAY,+CAA+CmB,EAAE,CAC3DD,MAAMZ,OAAO,EACbY,MAAMR,yBAAyB;KAElC,EACD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ShipfoxModule } from '@shipfox/node-module';
|
|
|
2
2
|
import type { SignupPolicy } from '#core/ports.js';
|
|
3
3
|
export type { AdminRole, JobLeaseTokenClaims, RunnerSessionTokenClaims } from '@shipfox/api-auth-dto';
|
|
4
4
|
export { ADMIN_ROLES, getCurrentAdminRole, hasMinimumAdminRole, highestAdminRole, requireAdminRole, revokeAdminGrant, } from '#core/admin-role.js';
|
|
5
|
-
export { bootstrapFirstAdminOwner, grantAdministratorRole, revokeAdministratorGrant, } from '#core/administration.js';
|
|
5
|
+
export { bootstrapFirstAdminOwner, grantAdministratorRole, reactivateAdministratorUser, revokeAdministratorGrant, revokeAdministratorUserSessions, suspendAdministratorUser, } from '#core/administration.js';
|
|
6
6
|
export type { CreateSessionForUserError, CreateSessionForUserParams, CreateSessionForUserResult, ProvisionUserParams, } from '#core/auth.js';
|
|
7
7
|
export { createSessionForUser, provisionUser } from '#core/auth.js';
|
|
8
8
|
export type { EmailOwner, FindUserByEmailParams } from '#core/email-owner.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAqBjD,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAE,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAClE,YAAY,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,EACrB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,2BAA2B,EAChC,mBAAmB,EACnB,8BAA8B,EAC9B,KAAK,gBAAgB,EACrB,KAAK,MAAM,GACZ,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAC,0BAA0B,EAAC,MAAM,wCAAwC,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAC,6BAA6B,EAAC,MAAM,2CAA2C,CAAC;AAIxF,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,OAAO,0CAA0C,EAAE,2BAA2B,CAAC;IAC3F,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,YAA8C,GAC/C,EAAE,uBAAuB,GAAG,aAAa,CAiBzC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAqBjD,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAE,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,wBAAwB,EACxB,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAClE,YAAY,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,EACrB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,2BAA2B,EAChC,mBAAmB,EACnB,8BAA8B,EAC9B,KAAK,gBAAgB,EACrB,KAAK,MAAM,GACZ,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAC,0BAA0B,EAAC,MAAM,wCAAwC,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAC,6BAA6B,EAAC,MAAM,2CAA2C,CAAC;AAIxF,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,OAAO,0CAA0C,EAAE,2BAA2B,CAAC;IAC3F,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,YAA8C,GAC/C,EAAE,uBAAuB,GAAG,aAAa,CAiBzC"}
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ const authPublisherEventSchemas = {
|
|
|
20
20
|
...administrationActionEventSchemas
|
|
21
21
|
};
|
|
22
22
|
export { ADMIN_ROLES, getCurrentAdminRole, hasMinimumAdminRole, highestAdminRole, requireAdminRole, revokeAdminGrant } from '#core/admin-role.js';
|
|
23
|
-
export { bootstrapFirstAdminOwner, grantAdministratorRole, revokeAdministratorGrant } from '#core/administration.js';
|
|
23
|
+
export { bootstrapFirstAdminOwner, grantAdministratorRole, reactivateAdministratorUser, revokeAdministratorGrant, revokeAdministratorUserSessions, suspendAdministratorUser } from '#core/administration.js';
|
|
24
24
|
export { createSessionForUser, provisionUser } from '#core/auth.js';
|
|
25
25
|
export { findUserByEmail } from '#core/email-owner.js';
|
|
26
26
|
export { AdminBootstrapClosedError, AdminGrantAlreadyExistsError, AdminGrantNotFoundError, AdminIdempotencyKeyReuseError, AdminRoleRequiredError, AuthDependencyUnavailableError, EmailNotVerifiedError, InvalidAdminBootstrapTokenError, InvalidCredentialsError, LastAdminOwnerError, SignupNotAllowedError, UserNotFoundError } from '#core/errors.js';
|