@kurdel/auth-db 0.1.0-beta.2 → 0.1.0-beta.4

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.
Files changed (44) hide show
  1. package/README.md +42 -9
  2. package/lib/auth-database-module.d.ts +2 -0
  3. package/lib/auth-database-module.js +52 -11
  4. package/lib/auth-database-module.js.map +1 -1
  5. package/lib/auth-database-tables.d.ts +4 -0
  6. package/lib/auth-database-tables.js +4 -0
  7. package/lib/auth-database-tables.js.map +1 -1
  8. package/lib/database-api-key-repository.d.ts +2 -2
  9. package/lib/database-api-key-repository.js +3 -3
  10. package/lib/database-api-key-repository.js.map +1 -1
  11. package/lib/database-api-key-service.d.ts +3 -3
  12. package/lib/database-api-key-service.js +1 -1
  13. package/lib/database-api-key-service.js.map +1 -1
  14. package/lib/database-api-key-usage-recorder.d.ts +2 -2
  15. package/lib/database-api-key-usage-recorder.js +1 -1
  16. package/lib/database-api-key-usage-recorder.js.map +1 -1
  17. package/lib/database-auth-event-store.d.ts +14 -3
  18. package/lib/database-auth-event-store.js +41 -17
  19. package/lib/database-auth-event-store.js.map +1 -1
  20. package/lib/database-auth-user-repository.d.ts +2 -2
  21. package/lib/database-auth-user-repository.js +23 -6
  22. package/lib/database-auth-user-repository.js.map +1 -1
  23. package/lib/database-jwt-session-repository.d.ts +10 -0
  24. package/lib/database-jwt-session-repository.js +26 -0
  25. package/lib/database-jwt-session-repository.js.map +1 -0
  26. package/lib/database-jwt-session-service.d.ts +31 -0
  27. package/lib/database-jwt-session-service.js +74 -0
  28. package/lib/database-jwt-session-service.js.map +1 -0
  29. package/lib/database-password-credential-repository.d.ts +9 -0
  30. package/lib/database-password-credential-repository.js +19 -0
  31. package/lib/database-password-credential-repository.js.map +1 -0
  32. package/lib/database-password-service.d.ts +14 -0
  33. package/lib/database-password-service.js +34 -0
  34. package/lib/database-password-service.js.map +1 -0
  35. package/lib/database-user-service.d.ts +62 -2
  36. package/lib/database-user-service.js +292 -5
  37. package/lib/database-user-service.js.map +1 -1
  38. package/lib/index.d.ts +4 -0
  39. package/lib/index.js +4 -0
  40. package/lib/index.js.map +1 -1
  41. package/lib/tokens.d.ts +3 -0
  42. package/lib/tokens.js +3 -0
  43. package/lib/tokens.js.map +1 -1
  44. package/package.json +5 -5
@@ -0,0 +1,26 @@
1
+ import { resolveAuthDatabaseTables } from './auth-database-tables.js';
2
+ /** Resolves persisted JWT sessions for token revocation checks. */
3
+ export class DatabaseJwtSessionRepository {
4
+ constructor(db, tables = {}) {
5
+ this.db = db;
6
+ this.tables = resolveAuthDatabaseTables(tables);
7
+ }
8
+ async findById(id) {
9
+ const record = await this.db.get({
10
+ sql: [
11
+ 'SELECT id, user_id, status, expires_at',
12
+ `FROM ${this.tables.jwtSessions} WHERE id = ?;`,
13
+ ].join(' '),
14
+ params: [id],
15
+ });
16
+ if (!record)
17
+ return null;
18
+ return {
19
+ id: record.id,
20
+ userId: record.user_id,
21
+ revoked: record.status !== 'active',
22
+ ...(record.expires_at ? { expiresAt: new Date(record.expires_at) } : {}),
23
+ };
24
+ }
25
+ }
26
+ //# sourceMappingURL=database-jwt-session-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-jwt-session-repository.js","sourceRoot":"","sources":["../src/database-jwt-session-repository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAA2B,MAAM,2BAA2B,CAAC;AAS/F,mEAAmE;AACnE,MAAM,OAAO,4BAA4B;IAGvC,YACmB,EAAY,EAC7B,SAAsC,EAAE;QADvB,OAAE,GAAF,EAAE,CAAU;QAG7B,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAC/B,GAAG,EAAE;gBACH,wCAAwC;gBACxC,QAAQ,IAAI,CAAC,MAAM,CAAC,WAAW,gBAAgB;aAChD,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,EAAE,CAAC,EAAE,CAAC;SACb,CAAiC,CAAC;QACnC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,MAAM,EAAE,MAAM,CAAC,OAAO;YACtB,OAAO,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ;YACnC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzE,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ import type { AuthEvent, AuthEventSink } from '@kurdel/auth';
2
+ import type { Database, DatabaseSession } from '@kurdel/db';
3
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
4
+ type TransactionalAuthEventSink = AuthEventSink & {
5
+ report(event: AuthEvent, database?: DatabaseSession): Promise<void> | void;
6
+ };
7
+ export type CreatedJwtSession = {
8
+ id: string;
9
+ userId: number;
10
+ expiresAt: string;
11
+ };
12
+ export declare class JwtSessionNotFoundError extends Error {
13
+ readonly userId: number;
14
+ readonly sessionId: string;
15
+ constructor(userId: number, sessionId: string);
16
+ }
17
+ export declare class ActiveJwtSessionUserNotFoundError extends Error {
18
+ readonly userId: number;
19
+ constructor(userId: number);
20
+ }
21
+ /** Creates and revokes the server-side state referenced by JWT `jti` claims. */
22
+ export declare class DatabaseJwtSessionService {
23
+ private readonly db;
24
+ private readonly events?;
25
+ private readonly now;
26
+ private readonly tables;
27
+ constructor(db: Database, tables?: Partial<AuthDatabaseTables>, events?: TransactionalAuthEventSink | undefined, now?: () => Date);
28
+ create(userId: number, expiresAt: Date): Promise<CreatedJwtSession>;
29
+ revoke(userId: number, sessionId: string): Promise<void>;
30
+ }
31
+ export {};
@@ -0,0 +1,74 @@
1
+ import crypto from 'node:crypto';
2
+ import { resolveAuthDatabaseTables } from './auth-database-tables.js';
3
+ export class JwtSessionNotFoundError extends Error {
4
+ constructor(userId, sessionId) {
5
+ super(`JWT session '${sessionId}' was not found for user '${userId}'`);
6
+ this.userId = userId;
7
+ this.sessionId = sessionId;
8
+ }
9
+ }
10
+ export class ActiveJwtSessionUserNotFoundError extends Error {
11
+ constructor(userId) {
12
+ super(`Active user '${userId}' was not found`);
13
+ this.userId = userId;
14
+ }
15
+ }
16
+ /** Creates and revokes the server-side state referenced by JWT `jti` claims. */
17
+ export class DatabaseJwtSessionService {
18
+ constructor(db, tables = {}, events, now = () => new Date()) {
19
+ this.db = db;
20
+ this.events = events;
21
+ this.now = now;
22
+ this.tables = resolveAuthDatabaseTables(tables);
23
+ }
24
+ async create(userId, expiresAt) {
25
+ const expirationTime = expiresAt.getTime();
26
+ if (!Number.isFinite(expirationTime) || expirationTime <= this.now().getTime()) {
27
+ throw new RangeError('JWT session expiration must be in the future');
28
+ }
29
+ const id = crypto.randomUUID();
30
+ await this.db.transaction(async (transaction) => {
31
+ const user = await transaction.get({
32
+ sql: `SELECT id FROM ${this.tables.users} WHERE id = ? AND status = 'active';`,
33
+ params: [userId],
34
+ });
35
+ if (!user)
36
+ throw new ActiveJwtSessionUserNotFoundError(userId);
37
+ await transaction.run({
38
+ sql: [
39
+ `INSERT INTO ${this.tables.jwtSessions}`,
40
+ '(id, user_id, status, expires_at) VALUES (?, ?, ?, ?);',
41
+ ].join(' '),
42
+ params: [id, userId, 'active', expiresAt.toISOString()],
43
+ });
44
+ await this.events?.report({
45
+ type: 'jwt-session.created',
46
+ occurredAt: this.now(),
47
+ userId,
48
+ credential: { type: 'jwt', id },
49
+ }, transaction);
50
+ });
51
+ return { id, userId, expiresAt: expiresAt.toISOString() };
52
+ }
53
+ async revoke(userId, sessionId) {
54
+ await this.db.transaction(async (transaction) => {
55
+ const session = await transaction.get({
56
+ sql: `SELECT id FROM ${this.tables.jwtSessions} WHERE id = ? AND user_id = ?;`,
57
+ params: [sessionId, userId],
58
+ });
59
+ if (!session)
60
+ throw new JwtSessionNotFoundError(userId, sessionId);
61
+ await transaction.run({
62
+ sql: `UPDATE ${this.tables.jwtSessions} SET status = 'revoked' WHERE id = ?;`,
63
+ params: [sessionId],
64
+ });
65
+ await this.events?.report({
66
+ type: 'jwt-session.revoked',
67
+ occurredAt: this.now(),
68
+ userId,
69
+ credential: { type: 'jwt', id: sessionId },
70
+ }, transaction);
71
+ });
72
+ }
73
+ }
74
+ //# sourceMappingURL=database-jwt-session-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-jwt-session-service.js","sourceRoot":"","sources":["../src/database-jwt-session-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { PasswordCredential, PasswordCredentialRepository } from '@kurdel/auth';
2
+ import type { Database } from '@kurdel/db';
3
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
4
+ export declare class DatabasePasswordCredentialRepository implements PasswordCredentialRepository {
5
+ private readonly db;
6
+ private readonly tables;
7
+ constructor(db: Database, tables?: Partial<AuthDatabaseTables>);
8
+ findByLogin(login: string): Promise<PasswordCredential | null>;
9
+ }
@@ -0,0 +1,19 @@
1
+ import { resolveAuthDatabaseTables } from './auth-database-tables.js';
2
+ export class DatabasePasswordCredentialRepository {
3
+ constructor(db, tables = {}) {
4
+ this.db = db;
5
+ this.tables = resolveAuthDatabaseTables(tables);
6
+ }
7
+ async findByLogin(login) {
8
+ const record = (await this.db.get({
9
+ sql: [
10
+ `SELECT credentials.user_id, credentials.password_hash FROM ${this.tables.passwordCredentials} credentials`,
11
+ `INNER JOIN ${this.tables.users} users ON users.id = credentials.user_id`,
12
+ 'WHERE LOWER(users.email) = LOWER(?);',
13
+ ].join(' '),
14
+ params: [login.trim()],
15
+ }));
16
+ return record ? { userId: record.user_id, passwordHash: record.password_hash } : null;
17
+ }
18
+ }
19
+ //# sourceMappingURL=database-password-credential-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-password-credential-repository.js","sourceRoot":"","sources":["../src/database-password-credential-repository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAA2B,MAAM,2BAA2B,CAAC;AAI/F,MAAM,OAAO,oCAAoC;IAG/C,YACmB,EAAY,EAC7B,SAAsC,EAAE;QADvB,OAAE,GAAF,EAAE,CAAU;QAG7B,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa;QAC7B,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAChC,GAAG,EAAE;gBACH,8DAA8D,IAAI,CAAC,MAAM,CAAC,mBAAmB,cAAc;gBAC3G,cAAc,IAAI,CAAC,MAAM,CAAC,KAAK,0CAA0C;gBACzE,sCAAsC;aACvC,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SACvB,CAAC,CAAiC,CAAC;QACpC,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACxF,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import type { PasswordHasher } from '@kurdel/auth';
2
+ import type { Database } from '@kurdel/db';
3
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
4
+ export declare class PasswordUserNotFoundError extends Error {
5
+ readonly userId: number;
6
+ constructor(userId: number);
7
+ }
8
+ export declare class DatabasePasswordService {
9
+ private readonly db;
10
+ private readonly hasher;
11
+ private readonly tables;
12
+ constructor(db: Database, hasher: PasswordHasher, tables?: Partial<AuthDatabaseTables>);
13
+ set(userId: number, password: string): Promise<void>;
14
+ }
@@ -0,0 +1,34 @@
1
+ import { resolveAuthDatabaseTables } from './auth-database-tables.js';
2
+ export class PasswordUserNotFoundError extends Error {
3
+ constructor(userId) {
4
+ super(`User '${userId}' was not found`);
5
+ this.userId = userId;
6
+ }
7
+ }
8
+ export class DatabasePasswordService {
9
+ constructor(db, hasher, tables = {}) {
10
+ this.db = db;
11
+ this.hasher = hasher;
12
+ this.tables = resolveAuthDatabaseTables(tables);
13
+ }
14
+ async set(userId, password) {
15
+ const passwordHash = await this.hasher.hash(password);
16
+ await this.db.transaction(async (transaction) => {
17
+ const user = await transaction.get({
18
+ sql: `SELECT id FROM ${this.tables.users} WHERE id = ?;`,
19
+ params: [userId],
20
+ });
21
+ if (!user)
22
+ throw new PasswordUserNotFoundError(userId);
23
+ await transaction.run({
24
+ sql: [
25
+ `INSERT INTO ${this.tables.passwordCredentials} (user_id, password_hash, updated_at)`,
26
+ 'VALUES (?, ?, CURRENT_TIMESTAMP)',
27
+ 'ON CONFLICT(user_id) DO UPDATE SET password_hash = excluded.password_hash, updated_at = CURRENT_TIMESTAMP;',
28
+ ].join(' '),
29
+ params: [userId, passwordHash],
30
+ });
31
+ });
32
+ }
33
+ }
34
+ //# sourceMappingURL=database-password-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-password-service.js","sourceRoot":"","sources":["../src/database-password-service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAA2B,MAAM,2BAA2B,CAAC;AAE/F,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,YAAqB,MAAc;QACjC,KAAK,CAAC,SAAS,MAAM,iBAAiB,CAAC,CAAC;QADrB,WAAM,GAAN,MAAM,CAAQ;IAEnC,CAAC;CACF;AAED,MAAM,OAAO,uBAAuB;IAGlC,YACmB,EAAY,EACZ,MAAsB,EACvC,SAAsC,EAAE;QAFvB,OAAE,GAAF,EAAE,CAAU;QACZ,WAAM,GAAN,MAAM,CAAgB;QAGvC,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,QAAgB;QACxC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAC,WAAW,EAAC,EAAE;YAC5C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC;gBACjC,GAAG,EAAE,kBAAkB,IAAI,CAAC,MAAM,CAAC,KAAK,gBAAgB;gBACxD,MAAM,EAAE,CAAC,MAAM,CAAC;aACjB,CAAC,CAAC;YACH,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACvD,MAAM,WAAW,CAAC,GAAG,CAAC;gBACpB,GAAG,EAAE;oBACH,eAAe,IAAI,CAAC,MAAM,CAAC,mBAAmB,uCAAuC;oBACrF,kCAAkC;oBAClC,4GAA4G;iBAC7G,CAAC,IAAI,CAAC,GAAG,CAAC;gBACX,MAAM,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC;aAC/B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -1,4 +1,4 @@
1
- import type { IDatabase } from '@kurdel/db';
1
+ import type { Database } from '@kurdel/db';
2
2
  import { type AuthDatabaseTables } from './auth-database-tables.js';
3
3
  export type UserStatus = 'active' | 'disabled';
4
4
  export interface CreateUserInput {
@@ -16,6 +16,34 @@ export interface ListUsersInput {
16
16
  limit: number;
17
17
  offset: number;
18
18
  status?: UserStatus;
19
+ search?: string;
20
+ sortBy?: 'id' | 'name' | 'email' | 'status' | 'createdAt' | 'updatedAt';
21
+ sortDirection?: 'asc' | 'desc';
22
+ }
23
+ export interface BulkUpdateUsersInput {
24
+ userIds: number[];
25
+ status?: UserStatus;
26
+ addRoles?: string[];
27
+ removeRoles?: string[];
28
+ }
29
+ export interface RoleSummary {
30
+ id: number;
31
+ name: string;
32
+ userCount: number;
33
+ permissions: string[];
34
+ }
35
+ export interface AdminDashboardStats {
36
+ users: {
37
+ total: number;
38
+ active: number;
39
+ disabled: number;
40
+ };
41
+ apiKeys: {
42
+ active: number;
43
+ revoked: number;
44
+ expired: number;
45
+ };
46
+ failedAuthenticationsLast24Hours: number;
19
47
  }
20
48
  export interface ManagedUser {
21
49
  id: number;
@@ -36,6 +64,10 @@ export declare class UnknownRolesError extends Error {
36
64
  readonly roles: string[];
37
65
  constructor(roles: string[]);
38
66
  }
67
+ export declare class UnknownPermissionsError extends Error {
68
+ readonly permissions: string[];
69
+ constructor(permissions: string[]);
70
+ }
39
71
  export declare class UserNotFoundError extends Error {
40
72
  readonly userId: number;
41
73
  constructor(userId: number);
@@ -44,21 +76,49 @@ export declare class DuplicateUserEmailError extends Error {
44
76
  readonly email: string;
45
77
  constructor(email: string);
46
78
  }
79
+ export declare class RoleNotFoundError extends Error {
80
+ readonly roleId: number;
81
+ constructor(roleId: number);
82
+ }
83
+ export declare class DuplicateRoleNameError extends Error {
84
+ readonly name: string;
85
+ constructor(name: string);
86
+ }
87
+ export declare class RoleInUseError extends Error {
88
+ readonly roleId: number;
89
+ readonly userCount: number;
90
+ constructor(roleId: number, userCount: number);
91
+ }
47
92
  export declare class DatabaseUserService {
48
93
  private readonly db;
49
94
  private readonly tables;
50
- constructor(db: IDatabase, tables?: Partial<AuthDatabaseTables>);
95
+ constructor(db: Database, tables?: Partial<AuthDatabaseTables>);
51
96
  listRoles(): Promise<string[]>;
97
+ listRoleSummaries(): Promise<RoleSummary[]>;
98
+ listPermissions(): Promise<string[]>;
99
+ setRolePermissions(roleId: number, names: string[]): Promise<RoleSummary>;
100
+ createRole(name: string): Promise<RoleSummary>;
101
+ renameRole(roleId: number, name: string): Promise<RoleSummary>;
102
+ deleteRole(roleId: number): Promise<void>;
52
103
  create(input: CreateUserInput): Promise<ManagedUser>;
53
104
  list(input: ListUsersInput): Promise<UserList>;
105
+ bulkUpdate(input: BulkUpdateUsersInput): Promise<ManagedUser[]>;
106
+ bulkDelete(userIds: number[]): Promise<void>;
107
+ dashboardStats(): Promise<AdminDashboardStats>;
54
108
  findById(userId: number): Promise<ManagedUser>;
55
109
  update(userId: number, input: UpdateUserInput): Promise<ManagedUser>;
56
110
  delete(userId: number): Promise<void>;
57
111
  private findRecord;
112
+ private findRole;
113
+ private ensureUsersExist;
58
114
  private resolveRoles;
115
+ private resolvePermissions;
116
+ private loadRolePermissions;
59
117
  private replaceRoles;
60
118
  private loadRoles;
61
119
  private mapUser;
62
120
  private normalizeEmail;
63
121
  private rethrowEmailConflict;
122
+ private rethrowRoleConflict;
123
+ private isUniqueConstraintError;
64
124
  }