@kurdel/auth-db 0.1.0-beta.1

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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/lib/api-key-hasher.d.ts +6 -0
  4. package/lib/api-key-hasher.js +7 -0
  5. package/lib/api-key-hasher.js.map +1 -0
  6. package/lib/auth-database-module.d.ts +18 -0
  7. package/lib/auth-database-module.js +67 -0
  8. package/lib/auth-database-module.js.map +1 -0
  9. package/lib/auth-database-tables.d.ts +9 -0
  10. package/lib/auth-database-tables.js +18 -0
  11. package/lib/auth-database-tables.js.map +1 -0
  12. package/lib/database-api-key-repository.d.ts +11 -0
  13. package/lib/database-api-key-repository.js +27 -0
  14. package/lib/database-api-key-repository.js.map +1 -0
  15. package/lib/database-api-key-service.d.ts +52 -0
  16. package/lib/database-api-key-service.js +110 -0
  17. package/lib/database-api-key-service.js.map +1 -0
  18. package/lib/database-api-key-usage-recorder.d.ts +10 -0
  19. package/lib/database-api-key-usage-recorder.js +15 -0
  20. package/lib/database-api-key-usage-recorder.js.map +1 -0
  21. package/lib/database-auth-event-store.d.ts +27 -0
  22. package/lib/database-auth-event-store.js +62 -0
  23. package/lib/database-auth-event-store.js.map +1 -0
  24. package/lib/database-auth-user-repository.d.ts +10 -0
  25. package/lib/database-auth-user-repository.js +28 -0
  26. package/lib/database-auth-user-repository.js.map +1 -0
  27. package/lib/database-user-service.d.ts +64 -0
  28. package/lib/database-user-service.js +214 -0
  29. package/lib/database-user-service.js.map +1 -0
  30. package/lib/index.d.ts +10 -0
  31. package/lib/index.js +11 -0
  32. package/lib/index.js.map +1 -0
  33. package/lib/tokens.d.ts +6 -0
  34. package/lib/tokens.js +7 -0
  35. package/lib/tokens.js.map +1 -0
  36. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Andrii Sorokin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @kurdel/auth-db
2
+
3
+ Database-backed repository adapters for `@kurdel/auth`.
4
+
5
+ The package keeps authentication strategies storage-agnostic while providing
6
+ standard implementations over Kurdel's `IDatabase` contract.
7
+
8
+ `DatabaseAuthUserRepository` resolves the current user and roles, while
9
+ `DatabaseApiKeyRepository` resolves safe credential metadata including its
10
+ stable ID. After a strategy succeeds, this data is available through
11
+ `ctx.auth.user` and `ctx.auth.credential`; raw API keys are never exposed in the
12
+ authentication context.
13
+
14
+ The package also provides `DatabaseUserService` and `DatabaseApiKeyService` for
15
+ administrative workflows. They create, list, update, and delete users, manage
16
+ role assignments, and issue, list, or revoke API keys. `AuthDatabaseModule`
17
+ registers both services as `AUTH_DB_TOKENS.UserService` and
18
+ `AUTH_DB_TOKENS.ApiKeyService`.
19
+
20
+ `DatabaseApiKeyUsageRecorder` updates `last_used_at` after successful
21
+ authentication. `AuthDatabaseModule` exposes it through
22
+ `AUTH_TOKENS.ApiKeyUsageRecorder`, ready to pass to `ApiKeyStrategy` as its
23
+ `usage` option.
24
+
25
+ Database audit persistence is opt-in because applications own their schema:
26
+
27
+ ```ts
28
+ new AuthDatabaseModule({ audit: true });
29
+ ```
30
+
31
+ This registers `DatabaseAuthEventStore` as `AUTH_DB_TOKENS.EventStore` and
32
+ wires API-key issue and revoke events into the management service. Pass the
33
+ same store to `AuthModule.events` to persist runtime authentication and
34
+ authorization events. The application must provide the configured
35
+ `auth_events` table; the runnable sample includes a migration with the expected
36
+ columns and indexes.
37
+
38
+ API-key issue or revocation and its database audit event run in the same
39
+ `IDatabase.transaction` callback. If audit persistence fails, the credential
40
+ mutation is rolled back and the service returns the original error. User
41
+ creation, role replacement, profile updates, and deletion use the same
42
+ transaction API for their multi-statement operations.
43
+
44
+ ```ts
45
+ import { ApiKeyStrategy, AUTH_TOKENS, AuthModule } from '@kurdel/auth';
46
+ import { AuthDatabaseModule } from '@kurdel/auth-db';
47
+
48
+ const modules = [
49
+ new AuthDatabaseModule(),
50
+ new AuthModule({
51
+ strategies: [
52
+ {
53
+ name: 'api-key',
54
+ useFactory: ioc => new ApiKeyStrategy({
55
+ header: 'x-api-key',
56
+ credentials: ioc.get(AUTH_TOKENS.ApiKeyRepository),
57
+ users: ioc.get(AUTH_TOKENS.UserRepository),
58
+ usage: ioc.get(AUTH_TOKENS.ApiKeyUsageRecorder),
59
+ }),
60
+ },
61
+ ],
62
+ }),
63
+ ];
64
+ ```
65
+
66
+ Custom table names and hashing implementations can be supplied through the
67
+ module configuration:
68
+
69
+ ```ts
70
+ new AuthDatabaseModule({
71
+ tables: {
72
+ users: 'application_users',
73
+ apiKeys: 'application_api_keys',
74
+ },
75
+ apiKeyHasher: customHasher,
76
+ });
77
+ ```
78
+
79
+ By default, the package expects `users`, `roles`, `user_roles`, and `api_keys`
80
+ tables and uses SHA-256 for API-key lookup. Schema ownership remains with the
81
+ application; see `sample/auth-db` for migrations and a runnable example.
82
+ The management services expect the profile and credential metadata columns
83
+ shown in those migrations, including user name, email, status and timestamps,
84
+ plus API-key name, status, expiration and last-use timestamps.
85
+ When audit persistence is enabled, the default event table is `auth_events`;
86
+ it can be changed through `tables.authEvents`.
87
+
88
+ See the [`@kurdel/auth` documentation](../auth/README.md) for route protection,
89
+ authentication context, and custom strategy contracts.
90
+
91
+ ## License
92
+
93
+ MIT © Andrii Sorokin
@@ -0,0 +1,6 @@
1
+ export interface ApiKeyHasher {
2
+ hash(key: string): string;
3
+ }
4
+ export declare class Sha256ApiKeyHasher implements ApiKeyHasher {
5
+ hash(key: string): string;
6
+ }
@@ -0,0 +1,7 @@
1
+ import crypto from 'node:crypto';
2
+ export class Sha256ApiKeyHasher {
3
+ hash(key) {
4
+ return crypto.createHash('sha256').update(key).digest('hex');
5
+ }
6
+ }
7
+ //# sourceMappingURL=api-key-hasher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-hasher.js","sourceRoot":"","sources":["../src/api-key-hasher.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAMjC,MAAM,OAAO,kBAAkB;IAC7B,IAAI,CAAC,GAAW;QACd,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import { ModulePriority, type AppModule, type ProviderConfig } from '@kurdel/core/app';
2
+ import { type ApiKeyHasher } from './api-key-hasher.js';
3
+ import type { AuthDatabaseTables } from './auth-database-tables.js';
4
+ export interface AuthDatabaseModuleConfig {
5
+ tables?: Partial<AuthDatabaseTables>;
6
+ apiKeyHasher?: ApiKeyHasher;
7
+ /** Enables persistence of sanitized authentication audit events. */
8
+ audit?: boolean;
9
+ }
10
+ export declare class AuthDatabaseModule implements AppModule {
11
+ readonly priority = ModulePriority.User;
12
+ readonly imports: {
13
+ db: symbol;
14
+ };
15
+ readonly exports: Record<string, symbol>;
16
+ readonly providers: ProviderConfig[];
17
+ constructor(config?: AuthDatabaseModuleConfig);
18
+ }
@@ -0,0 +1,67 @@
1
+ import { AUTH_TOKENS } from '@kurdel/auth';
2
+ import { ModulePriority } from '@kurdel/core/app';
3
+ import { IDatabase } from '@kurdel/db';
4
+ import { Sha256ApiKeyHasher } from './api-key-hasher.js';
5
+ import { DatabaseApiKeyRepository } from './database-api-key-repository.js';
6
+ import { DatabaseApiKeyService } from './database-api-key-service.js';
7
+ import { DatabaseApiKeyUsageRecorder } from './database-api-key-usage-recorder.js';
8
+ import { DatabaseAuthUserRepository } from './database-auth-user-repository.js';
9
+ import { DatabaseAuthEventStore } from './database-auth-event-store.js';
10
+ import { DatabaseUserService } from './database-user-service.js';
11
+ import { AUTH_DB_TOKENS } from './tokens.js';
12
+ export class AuthDatabaseModule {
13
+ constructor(config = {}) {
14
+ this.priority = ModulePriority.User;
15
+ this.imports = { db: IDatabase };
16
+ const tables = config.tables ?? {};
17
+ const hasher = config.apiKeyHasher ?? new Sha256ApiKeyHasher();
18
+ this.exports = {
19
+ userRepository: AUTH_TOKENS.UserRepository,
20
+ apiKeyRepository: AUTH_TOKENS.ApiKeyRepository,
21
+ apiKeyUsageRecorder: AUTH_TOKENS.ApiKeyUsageRecorder,
22
+ apiKeyHasher: AUTH_DB_TOKENS.ApiKeyHasher,
23
+ userService: AUTH_DB_TOKENS.UserService,
24
+ apiKeyService: AUTH_DB_TOKENS.ApiKeyService,
25
+ ...(config.audit ? { eventStore: AUTH_DB_TOKENS.EventStore } : {}),
26
+ };
27
+ this.providers = [
28
+ {
29
+ provide: AUTH_DB_TOKENS.ApiKeyHasher,
30
+ useInstance: hasher,
31
+ },
32
+ {
33
+ provide: AUTH_TOKENS.UserRepository,
34
+ useFactory: ioc => new DatabaseAuthUserRepository(ioc.get(IDatabase), tables),
35
+ singleton: true,
36
+ },
37
+ {
38
+ provide: AUTH_TOKENS.ApiKeyRepository,
39
+ useFactory: ioc => new DatabaseApiKeyRepository(ioc.get(IDatabase), ioc.get(AUTH_DB_TOKENS.ApiKeyHasher), tables),
40
+ singleton: true,
41
+ },
42
+ {
43
+ provide: AUTH_TOKENS.ApiKeyUsageRecorder,
44
+ useFactory: ioc => new DatabaseApiKeyUsageRecorder(ioc.get(IDatabase), tables),
45
+ singleton: true,
46
+ },
47
+ {
48
+ provide: AUTH_DB_TOKENS.UserService,
49
+ useFactory: ioc => new DatabaseUserService(ioc.get(IDatabase), tables),
50
+ singleton: true,
51
+ },
52
+ ...(config.audit
53
+ ? [{
54
+ provide: AUTH_DB_TOKENS.EventStore,
55
+ useFactory: (ioc) => new DatabaseAuthEventStore(ioc.get(IDatabase), tables),
56
+ singleton: true,
57
+ }]
58
+ : []),
59
+ {
60
+ provide: AUTH_DB_TOKENS.ApiKeyService,
61
+ useFactory: ioc => new DatabaseApiKeyService(ioc.get(IDatabase), ioc.get(AUTH_DB_TOKENS.ApiKeyHasher), tables, config.audit ? ioc.get(AUTH_DB_TOKENS.EventStore) : undefined),
62
+ singleton: true,
63
+ },
64
+ ];
65
+ }
66
+ }
67
+ //# sourceMappingURL=auth-database-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-database-module.js","sourceRoot":"","sources":["../src/auth-database-module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface AuthDatabaseTables {
2
+ users: string;
3
+ roles: string;
4
+ userRoles: string;
5
+ apiKeys: string;
6
+ authEvents: string;
7
+ }
8
+ export declare const DEFAULT_AUTH_DATABASE_TABLES: Readonly<AuthDatabaseTables>;
9
+ export declare function resolveAuthDatabaseTables(tables?: Partial<AuthDatabaseTables>): AuthDatabaseTables;
@@ -0,0 +1,18 @@
1
+ export const DEFAULT_AUTH_DATABASE_TABLES = {
2
+ users: 'users',
3
+ roles: 'roles',
4
+ userRoles: 'user_roles',
5
+ apiKeys: 'api_keys',
6
+ authEvents: 'auth_events',
7
+ };
8
+ export function resolveAuthDatabaseTables(tables = {}) {
9
+ const resolved = { ...DEFAULT_AUTH_DATABASE_TABLES, ...tables };
10
+ Object.values(resolved).forEach(assertSqlIdentifier);
11
+ return resolved;
12
+ }
13
+ function assertSqlIdentifier(identifier) {
14
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
15
+ throw new Error(`Invalid auth database table name '${identifier}'`);
16
+ }
17
+ }
18
+ //# sourceMappingURL=auth-database-tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-database-tables.js","sourceRoot":"","sources":["../src/auth-database-tables.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,4BAA4B,GAAiC;IACxE,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,SAAS,EAAE,YAAY;IACvB,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,aAAa;CAC1B,CAAC;AAEF,MAAM,UAAU,yBAAyB,CACvC,SAAsC,EAAE;IAExC,MAAM,QAAQ,GAAG,EAAE,GAAG,4BAA4B,EAAE,GAAG,MAAM,EAAE,CAAC;IAChE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,mBAAmB,CAAC,UAAkB;IAC7C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,qCAAqC,UAAU,GAAG,CAAC,CAAC;IACtE,CAAC;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ApiKeyCredential, ApiKeyRepository } from '@kurdel/auth';
2
+ import type { IDatabase } from '@kurdel/db';
3
+ import type { ApiKeyHasher } from './api-key-hasher.js';
4
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
5
+ export declare class DatabaseApiKeyRepository implements ApiKeyRepository {
6
+ private readonly db;
7
+ private readonly hasher;
8
+ private readonly tables;
9
+ constructor(db: IDatabase, hasher: ApiKeyHasher, tables?: Partial<AuthDatabaseTables>);
10
+ findByKey(key: string): Promise<ApiKeyCredential | null>;
11
+ }
@@ -0,0 +1,27 @@
1
+ import { resolveAuthDatabaseTables, } from './auth-database-tables.js';
2
+ export class DatabaseApiKeyRepository {
3
+ constructor(db, hasher, tables = {}) {
4
+ this.db = db;
5
+ this.hasher = hasher;
6
+ this.tables = resolveAuthDatabaseTables(tables);
7
+ }
8
+ async findByKey(key) {
9
+ const record = await this.db.get({
10
+ sql: [
11
+ 'SELECT id, user_id, status, expires_at',
12
+ `FROM ${this.tables.apiKeys}`,
13
+ 'WHERE key_hash = ?;',
14
+ ].join(' '),
15
+ params: [this.hasher.hash(key)],
16
+ });
17
+ if (!record)
18
+ return null;
19
+ return {
20
+ id: record.id,
21
+ userId: record.user_id,
22
+ revoked: record.status !== 'active',
23
+ expiresAt: record.expires_at ? new Date(record.expires_at) : undefined,
24
+ };
25
+ }
26
+ }
27
+ //# sourceMappingURL=database-api-key-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-api-key-repository.js","sourceRoot":"","sources":["../src/database-api-key-repository.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,yBAAyB,GAE1B,MAAM,2BAA2B,CAAC;AASnC,MAAM,OAAO,wBAAwB;IAGnC,YACmB,EAAa,EACb,MAAoB,EACrC,SAAsC,EAAE;QAFvB,OAAE,GAAF,EAAE,CAAW;QACb,WAAM,GAAN,MAAM,CAAc;QAGrC,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW;QACzB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAC/B,GAAG,EAAE;gBACH,wCAAwC;gBACxC,QAAQ,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;gBAC7B,qBAAqB;aACtB,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SAChC,CAA6B,CAAC;QAC/B,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,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;SACvE,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,52 @@
1
+ import type { AuthEvent, AuthEventSink } from '@kurdel/auth';
2
+ import type { IDatabase, IDatabaseSession } from '@kurdel/db';
3
+ import type { ApiKeyHasher } from './api-key-hasher.js';
4
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
5
+ type TransactionalAuthEventSink = AuthEventSink & {
6
+ report(event: AuthEvent, database?: IDatabaseSession): Promise<void> | void;
7
+ };
8
+ export interface CreateApiKeyInput {
9
+ userId: number;
10
+ name: string;
11
+ expiresAt?: Date;
12
+ }
13
+ export interface CreatedApiKey {
14
+ id: string;
15
+ key: string;
16
+ name: string;
17
+ expiresAt: string | null;
18
+ }
19
+ export interface ApiKeyMetadata {
20
+ id: string;
21
+ name: string;
22
+ status: 'active' | 'revoked' | 'expired';
23
+ expiresAt: string | null;
24
+ lastUsedAt: string | null;
25
+ createdAt: string;
26
+ }
27
+ export declare class ActiveUserNotFoundError extends Error {
28
+ readonly userId: number;
29
+ constructor(userId: number);
30
+ }
31
+ export declare class ApiKeyUserNotFoundError extends Error {
32
+ readonly userId: number;
33
+ constructor(userId: number);
34
+ }
35
+ export declare class ApiKeyNotFoundError extends Error {
36
+ readonly userId: number;
37
+ readonly apiKeyId: string;
38
+ constructor(userId: number, apiKeyId: string);
39
+ }
40
+ export declare class DatabaseApiKeyService {
41
+ private readonly db;
42
+ private readonly hasher;
43
+ private readonly events?;
44
+ private readonly now;
45
+ private readonly tables;
46
+ constructor(db: IDatabase, hasher: ApiKeyHasher, tables?: Partial<AuthDatabaseTables>, events?: TransactionalAuthEventSink | undefined, now?: () => Date);
47
+ list(userId: number): Promise<ApiKeyMetadata[]>;
48
+ create(input: CreateApiKeyInput): Promise<CreatedApiKey>;
49
+ revoke(userId: number, apiKeyId: string): Promise<void>;
50
+ private effectiveStatus;
51
+ }
52
+ export {};
@@ -0,0 +1,110 @@
1
+ import crypto from 'node:crypto';
2
+ import { resolveAuthDatabaseTables, } from './auth-database-tables.js';
3
+ export class ActiveUserNotFoundError extends Error {
4
+ constructor(userId) {
5
+ super(`Active user '${userId}' was not found`);
6
+ this.userId = userId;
7
+ }
8
+ }
9
+ export class ApiKeyUserNotFoundError extends Error {
10
+ constructor(userId) {
11
+ super(`User '${userId}' was not found`);
12
+ this.userId = userId;
13
+ }
14
+ }
15
+ export class ApiKeyNotFoundError extends Error {
16
+ constructor(userId, apiKeyId) {
17
+ super(`API key '${apiKeyId}' was not found for user '${userId}'`);
18
+ this.userId = userId;
19
+ this.apiKeyId = apiKeyId;
20
+ }
21
+ }
22
+ export class DatabaseApiKeyService {
23
+ constructor(db, hasher, tables = {}, events, now = () => new Date()) {
24
+ this.db = db;
25
+ this.hasher = hasher;
26
+ this.events = events;
27
+ this.now = now;
28
+ this.tables = resolveAuthDatabaseTables(tables);
29
+ }
30
+ async list(userId) {
31
+ const user = (await this.db.get({
32
+ sql: `SELECT id, status FROM ${this.tables.users} WHERE id = ?;`,
33
+ params: [userId],
34
+ }));
35
+ if (!user)
36
+ throw new ApiKeyUserNotFoundError(userId);
37
+ const records = (await this.db.all({
38
+ sql: [
39
+ 'SELECT id, name, status, expires_at, last_used_at, created_at',
40
+ `FROM ${this.tables.apiKeys} WHERE user_id = ? ORDER BY created_at DESC, id DESC;`,
41
+ ].join(' '),
42
+ params: [userId],
43
+ }));
44
+ return records.map(record => ({
45
+ id: record.id,
46
+ name: record.name,
47
+ status: this.effectiveStatus(record),
48
+ expiresAt: record.expires_at,
49
+ lastUsedAt: record.last_used_at,
50
+ createdAt: record.created_at,
51
+ }));
52
+ }
53
+ async create(input) {
54
+ const id = crypto.randomUUID();
55
+ const key = `kdl_${crypto.randomBytes(32).toString('base64url')}`;
56
+ const expiresAt = input.expiresAt?.toISOString() ?? null;
57
+ await this.db.transaction(async (transaction) => {
58
+ const user = (await transaction.get({
59
+ sql: `SELECT id, status FROM ${this.tables.users} WHERE id = ?;`,
60
+ params: [input.userId],
61
+ }));
62
+ if (!user || user.status !== 'active') {
63
+ throw new ActiveUserNotFoundError(input.userId);
64
+ }
65
+ await transaction.run({
66
+ sql: [
67
+ `INSERT INTO ${this.tables.apiKeys}`,
68
+ '(id, user_id, key_hash, name, status, expires_at)',
69
+ 'VALUES (?, ?, ?, ?, ?, ?);',
70
+ ].join(' '),
71
+ params: [id, input.userId, this.hasher.hash(key), input.name, 'active', expiresAt],
72
+ });
73
+ await this.events?.report({
74
+ type: 'api-key.issued',
75
+ occurredAt: this.now(),
76
+ userId: input.userId,
77
+ credential: { type: 'api-key', id },
78
+ }, transaction);
79
+ });
80
+ return { id, key, name: input.name, expiresAt };
81
+ }
82
+ async revoke(userId, apiKeyId) {
83
+ await this.db.transaction(async (transaction) => {
84
+ const apiKey = (await transaction.get({
85
+ sql: `SELECT id FROM ${this.tables.apiKeys} WHERE id = ? AND user_id = ?;`,
86
+ params: [apiKeyId, userId],
87
+ }));
88
+ if (!apiKey)
89
+ throw new ApiKeyNotFoundError(userId, apiKeyId);
90
+ await transaction.run({
91
+ sql: `UPDATE ${this.tables.apiKeys} SET status = 'revoked' WHERE id = ? AND user_id = ?;`,
92
+ params: [apiKeyId, userId],
93
+ });
94
+ await this.events?.report({
95
+ type: 'api-key.revoked',
96
+ occurredAt: this.now(),
97
+ userId,
98
+ credential: { type: 'api-key', id: apiKeyId },
99
+ }, transaction);
100
+ });
101
+ }
102
+ effectiveStatus(record) {
103
+ if (record.status === 'revoked')
104
+ return 'revoked';
105
+ if (record.expires_at && new Date(record.expires_at).getTime() <= Date.now())
106
+ return 'expired';
107
+ return 'active';
108
+ }
109
+ }
110
+ //# sourceMappingURL=database-api-key-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-api-key-service.js","sourceRoot":"","sources":["../src/database-api-key-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAMjC,OAAO,EACL,yBAAyB,GAE1B,MAAM,2BAA2B,CAAC;AA0CnC,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAqB,MAAc;QACjC,KAAK,CAAC,gBAAgB,MAAM,iBAAiB,CAAC,CAAC;QAD5B,WAAM,GAAN,MAAM,CAAQ;IAEnC,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAqB,MAAc;QACjC,KAAK,CAAC,SAAS,MAAM,iBAAiB,CAAC,CAAC;QADrB,WAAM,GAAN,MAAM,CAAQ;IAEnC,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YACW,MAAc,EACd,QAAgB;QAEzB,KAAK,CAAC,YAAY,QAAQ,6BAA6B,MAAM,GAAG,CAAC,CAAC;QAHzD,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAQ;IAG3B,CAAC;CACF;AAED,MAAM,OAAO,qBAAqB;IAGhC,YACmB,EAAa,EACb,MAAoB,EACrC,SAAsC,EAAE,EACvB,MAAmC,EACnC,MAAkB,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;QAJlC,OAAE,GAAF,EAAE,CAAW;QACb,WAAM,GAAN,MAAM,CAAc;QAEpB,WAAM,GAAN,MAAM,CAA6B;QACnC,QAAG,GAAH,GAAG,CAA+B;QAEnD,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAC9B,GAAG,EAAE,0BAA0B,IAAI,CAAC,MAAM,CAAC,KAAK,gBAAgB;YAChE,MAAM,EAAE,CAAC,MAAM,CAAC;SACjB,CAAC,CAA2B,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAErD,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YACjC,GAAG,EAAE;gBACH,+DAA+D;gBAC/D,QAAQ,IAAI,CAAC,MAAM,CAAC,OAAO,uDAAuD;aACnF,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,EAAE,CAAC,MAAM,CAAC;SACjB,CAAC,CAAmB,CAAC;QACtB,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC5B,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;YACpC,SAAS,EAAE,MAAM,CAAC,UAAU;YAC5B,UAAU,EAAE,MAAM,CAAC,YAAY;YAC/B,SAAS,EAAE,MAAM,CAAC,UAAU;SAC7B,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAwB;QACnC,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAClE,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC;QAEzD,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAC,WAAW,EAAC,EAAE;YAC5C,MAAM,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC;gBAClC,GAAG,EAAE,0BAA0B,IAAI,CAAC,MAAM,CAAC,KAAK,gBAAgB;gBAChE,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC;aACvB,CAAC,CAA2B,CAAC;YAC9B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACtC,MAAM,IAAI,uBAAuB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClD,CAAC;YAED,MAAM,WAAW,CAAC,GAAG,CAAC;gBACpB,GAAG,EAAE;oBACH,eAAe,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;oBACpC,mDAAmD;oBACnD,4BAA4B;iBAC7B,CAAC,IAAI,CAAC,GAAG,CAAC;gBACX,MAAM,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC;aACnF,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;gBACxB,IAAI,EAAE,gBAAgB;gBACtB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;gBACtB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;aACpC,EAAE,WAAW,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,QAAgB;QAC3C,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAC,WAAW,EAAC,EAAE;YAC5C,MAAM,MAAM,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC;gBACpC,GAAG,EAAE,kBAAkB,IAAI,CAAC,MAAM,CAAC,OAAO,gCAAgC;gBAC1E,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;aAC3B,CAAC,CAA+B,CAAC;YAClC,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAE7D,MAAM,WAAW,CAAC,GAAG,CAAC;gBACpB,GAAG,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,OAAO,uDAAuD;gBACzF,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;aAC3B,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;gBACxB,IAAI,EAAE,iBAAiB;gBACvB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;gBACtB,MAAM;gBACN,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE;aAC9C,EAAE,WAAW,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,eAAe,CAAC,MAAoB;QAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAClD,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,SAAS,CAAC;QAC/F,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import type { ApiKeyUsageRecorder } from '@kurdel/auth';
2
+ import type { IDatabase } from '@kurdel/db';
3
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
4
+ /** Records the most recent successful use of a database-backed API key. */
5
+ export declare class DatabaseApiKeyUsageRecorder implements ApiKeyUsageRecorder {
6
+ private readonly db;
7
+ private readonly tables;
8
+ constructor(db: IDatabase, tables?: Partial<AuthDatabaseTables>);
9
+ recordUsage(credentialId: string, usedAt: Date): Promise<void>;
10
+ }
@@ -0,0 +1,15 @@
1
+ import { resolveAuthDatabaseTables, } from './auth-database-tables.js';
2
+ /** Records the most recent successful use of a database-backed API key. */
3
+ export class DatabaseApiKeyUsageRecorder {
4
+ constructor(db, tables = {}) {
5
+ this.db = db;
6
+ this.tables = resolveAuthDatabaseTables(tables);
7
+ }
8
+ async recordUsage(credentialId, usedAt) {
9
+ await this.db.run({
10
+ sql: `UPDATE ${this.tables.apiKeys} SET last_used_at = ? WHERE id = ?;`,
11
+ params: [usedAt.toISOString(), credentialId],
12
+ });
13
+ }
14
+ }
15
+ //# sourceMappingURL=database-api-key-usage-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-api-key-usage-recorder.js","sourceRoot":"","sources":["../src/database-api-key-usage-recorder.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,yBAAyB,GAE1B,MAAM,2BAA2B,CAAC;AAEnC,2EAA2E;AAC3E,MAAM,OAAO,2BAA2B;IAGtC,YACmB,EAAa,EAC9B,SAAsC,EAAE;QADvB,OAAE,GAAF,EAAE,CAAW;QAG9B,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,YAAoB,EAAE,MAAY;QAClD,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAChB,GAAG,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,OAAO,qCAAqC;YACvE,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,YAAY,CAAC;SAC7C,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import type { AuthEvent, AuthEventSink } from '@kurdel/auth';
2
+ import type { IDatabase, IDatabaseSession } from '@kurdel/db';
3
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
4
+ export type StoredAuthEvent = {
5
+ id: number;
6
+ type: AuthEvent['type'];
7
+ occurredAt: string;
8
+ strategy: string | null;
9
+ userId: string | null;
10
+ credentialType: string | null;
11
+ credentialId: string | null;
12
+ reason: string | null;
13
+ policy: string | null;
14
+ };
15
+ export type ListAuthEventsInput = {
16
+ userId?: string | number;
17
+ type?: AuthEvent['type'];
18
+ limit?: number;
19
+ };
20
+ /** Persists and queries sanitized authentication audit events. */
21
+ export declare class DatabaseAuthEventStore implements AuthEventSink {
22
+ private readonly db;
23
+ private readonly tables;
24
+ constructor(db: IDatabase, tables?: Partial<AuthDatabaseTables>);
25
+ report(event: AuthEvent, database?: IDatabaseSession): Promise<void>;
26
+ list(input?: ListAuthEventsInput): Promise<StoredAuthEvent[]>;
27
+ }
@@ -0,0 +1,62 @@
1
+ import { resolveAuthDatabaseTables, } from './auth-database-tables.js';
2
+ /** Persists and queries sanitized authentication audit events. */
3
+ export class DatabaseAuthEventStore {
4
+ constructor(db, tables = {}) {
5
+ this.db = db;
6
+ this.tables = resolveAuthDatabaseTables(tables);
7
+ }
8
+ async report(event, database = this.db) {
9
+ await database.run({
10
+ sql: [
11
+ `INSERT INTO ${this.tables.authEvents}`,
12
+ '(type, occurred_at, strategy, user_id, credential_type, credential_id, reason, policy)',
13
+ 'VALUES (?, ?, ?, ?, ?, ?, ?, ?);',
14
+ ].join(' '),
15
+ params: [
16
+ event.type,
17
+ event.occurredAt.toISOString(),
18
+ 'strategy' in event ? event.strategy ?? null : null,
19
+ event.userId === undefined ? null : String(event.userId),
20
+ event.credential?.type ?? null,
21
+ event.credential?.id ?? null,
22
+ 'reason' in event ? event.reason : null,
23
+ 'policy' in event ? event.policy ?? null : null,
24
+ ],
25
+ });
26
+ }
27
+ async list(input = {}) {
28
+ const conditions = [];
29
+ const params = [];
30
+ if (input.userId !== undefined) {
31
+ conditions.push('user_id = ?');
32
+ params.push(String(input.userId));
33
+ }
34
+ if (input.type) {
35
+ conditions.push('type = ?');
36
+ params.push(input.type);
37
+ }
38
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
39
+ const limit = Math.min(Math.max(input.limit ?? 50, 1), 100);
40
+ const records = await this.db.all({
41
+ sql: [
42
+ 'SELECT id, type, occurred_at, strategy, user_id,',
43
+ 'credential_type, credential_id, reason, policy',
44
+ `FROM ${this.tables.authEvents} ${where}`,
45
+ 'ORDER BY occurred_at DESC, id DESC LIMIT ?;',
46
+ ].join(' '),
47
+ params: [...params, limit],
48
+ });
49
+ return records.map(record => ({
50
+ id: record.id,
51
+ type: record.type,
52
+ occurredAt: record.occurred_at,
53
+ strategy: record.strategy,
54
+ userId: record.user_id,
55
+ credentialType: record.credential_type,
56
+ credentialId: record.credential_id,
57
+ reason: record.reason,
58
+ policy: record.policy,
59
+ }));
60
+ }
61
+ }
62
+ //# sourceMappingURL=database-auth-event-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-auth-event-store.js","sourceRoot":"","sources":["../src/database-auth-event-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AuthUserRepository } from '@kurdel/auth';
2
+ import type { AuthUser } from '@kurdel/common';
3
+ import type { IDatabase } from '@kurdel/db';
4
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
5
+ export declare class DatabaseAuthUserRepository implements AuthUserRepository {
6
+ private readonly db;
7
+ private readonly tables;
8
+ constructor(db: IDatabase, tables?: Partial<AuthDatabaseTables>);
9
+ findById(id: string | number): Promise<AuthUser | null>;
10
+ }
@@ -0,0 +1,28 @@
1
+ import { resolveAuthDatabaseTables, } from './auth-database-tables.js';
2
+ export class DatabaseAuthUserRepository {
3
+ constructor(db, tables = {}) {
4
+ this.db = db;
5
+ this.tables = resolveAuthDatabaseTables(tables);
6
+ }
7
+ async findById(id) {
8
+ const user = await this.db.get({
9
+ sql: `SELECT id, status FROM ${this.tables.users} WHERE id = ?;`,
10
+ params: [id],
11
+ });
12
+ if (!user || user.status !== 'active')
13
+ return null;
14
+ const roles = await this.db.all({
15
+ sql: [
16
+ `SELECT ${this.tables.roles}.name`,
17
+ `FROM ${this.tables.roles}`,
18
+ `INNER JOIN ${this.tables.userRoles}`,
19
+ `ON ${this.tables.userRoles}.role_id = ${this.tables.roles}.id`,
20
+ `WHERE ${this.tables.userRoles}.user_id = ?`,
21
+ `ORDER BY ${this.tables.roles}.name;`,
22
+ ].join(' '),
23
+ params: [user.id],
24
+ });
25
+ return { id: user.id, roles: roles.map(role => role.name) };
26
+ }
27
+ }
28
+ //# sourceMappingURL=database-auth-user-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-auth-user-repository.js","sourceRoot":"","sources":["../src/database-auth-user-repository.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,yBAAyB,GAE1B,MAAM,2BAA2B,CAAC;AAKnC,MAAM,OAAO,0BAA0B;IAGrC,YACmB,EAAa,EAC9B,SAAsC,EAAE;QADvB,OAAE,GAAF,EAAE,CAAW;QAG9B,IAAI,CAAC,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAmB;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAC7B,GAAG,EAAE,0BAA0B,IAAI,CAAC,MAAM,CAAC,KAAK,gBAAgB;YAChE,MAAM,EAAE,CAAC,EAAE,CAAC;SACb,CAA2B,CAAC;QAC7B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAEnD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAC9B,GAAG,EAAE;gBACH,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,OAAO;gBAClC,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;gBAC3B,cAAc,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;gBACrC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,cAAc,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK;gBAC/D,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,cAAc;gBAC5C,YAAY,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ;aACtC,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;SAClB,CAAiB,CAAC;QAEnB,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,64 @@
1
+ import type { IDatabase } from '@kurdel/db';
2
+ import { type AuthDatabaseTables } from './auth-database-tables.js';
3
+ export type UserStatus = 'active' | 'disabled';
4
+ export interface CreateUserInput {
5
+ name: string;
6
+ email: string;
7
+ roles: string[];
8
+ }
9
+ export interface UpdateUserInput {
10
+ name?: string;
11
+ email?: string;
12
+ status?: UserStatus;
13
+ roles?: string[];
14
+ }
15
+ export interface ListUsersInput {
16
+ limit: number;
17
+ offset: number;
18
+ status?: UserStatus;
19
+ }
20
+ export interface ManagedUser {
21
+ id: number;
22
+ name: string;
23
+ email: string;
24
+ status: UserStatus;
25
+ roles: string[];
26
+ createdAt: string;
27
+ updatedAt: string;
28
+ }
29
+ export interface UserList {
30
+ users: ManagedUser[];
31
+ total: number;
32
+ limit: number;
33
+ offset: number;
34
+ }
35
+ export declare class UnknownRolesError extends Error {
36
+ readonly roles: string[];
37
+ constructor(roles: string[]);
38
+ }
39
+ export declare class UserNotFoundError extends Error {
40
+ readonly userId: number;
41
+ constructor(userId: number);
42
+ }
43
+ export declare class DuplicateUserEmailError extends Error {
44
+ readonly email: string;
45
+ constructor(email: string);
46
+ }
47
+ export declare class DatabaseUserService {
48
+ private readonly db;
49
+ private readonly tables;
50
+ constructor(db: IDatabase, tables?: Partial<AuthDatabaseTables>);
51
+ listRoles(): Promise<string[]>;
52
+ create(input: CreateUserInput): Promise<ManagedUser>;
53
+ list(input: ListUsersInput): Promise<UserList>;
54
+ findById(userId: number): Promise<ManagedUser>;
55
+ update(userId: number, input: UpdateUserInput): Promise<ManagedUser>;
56
+ delete(userId: number): Promise<void>;
57
+ private findRecord;
58
+ private resolveRoles;
59
+ private replaceRoles;
60
+ private loadRoles;
61
+ private mapUser;
62
+ private normalizeEmail;
63
+ private rethrowEmailConflict;
64
+ }
@@ -0,0 +1,214 @@
1
+ import { resolveAuthDatabaseTables, } from './auth-database-tables.js';
2
+ export class UnknownRolesError extends Error {
3
+ constructor(roles) {
4
+ super(`Unknown roles: ${roles.join(', ')}`);
5
+ this.roles = roles;
6
+ }
7
+ }
8
+ export class UserNotFoundError extends Error {
9
+ constructor(userId) {
10
+ super(`User ${userId} not found`);
11
+ this.userId = userId;
12
+ }
13
+ }
14
+ export class DuplicateUserEmailError extends Error {
15
+ constructor(email) {
16
+ super(`A user with email ${email} already exists`);
17
+ this.email = email;
18
+ }
19
+ }
20
+ export class DatabaseUserService {
21
+ constructor(db, tables = {}) {
22
+ this.db = db;
23
+ this.tables = resolveAuthDatabaseTables(tables);
24
+ }
25
+ async listRoles() {
26
+ const roles = (await this.db.all({
27
+ sql: `SELECT name FROM ${this.tables.roles} ORDER BY name;`,
28
+ params: [],
29
+ }));
30
+ return roles.map(role => role.name);
31
+ }
32
+ async create(input) {
33
+ const email = this.normalizeEmail(input.email);
34
+ const roles = await this.resolveRoles(input.roles);
35
+ try {
36
+ return await this.db.transaction(async (transaction) => {
37
+ const user = (await transaction.get({
38
+ sql: [
39
+ `INSERT INTO ${this.tables.users} (name, email, status)`,
40
+ "VALUES (?, ?, 'active')",
41
+ 'RETURNING id, name, email, status, created_at, updated_at;',
42
+ ].join(' '),
43
+ params: [input.name, email],
44
+ }));
45
+ await this.replaceRoles(transaction, user.id, roles);
46
+ return this.mapUser(user, roles.map(role => role.name));
47
+ });
48
+ }
49
+ catch (error) {
50
+ this.rethrowEmailConflict(error, email);
51
+ }
52
+ }
53
+ async list(input) {
54
+ const where = input.status ? 'WHERE status = ?' : '';
55
+ const params = input.status ? [input.status] : [];
56
+ const records = (await this.db.all({
57
+ sql: [
58
+ 'SELECT id, name, email, status, created_at, updated_at',
59
+ `FROM ${this.tables.users} ${where}`,
60
+ 'ORDER BY id DESC LIMIT ? OFFSET ?;',
61
+ ].join(' '),
62
+ params: [...params, input.limit, input.offset],
63
+ }));
64
+ const count = (await this.db.get({
65
+ sql: `SELECT COUNT(*) AS count FROM ${this.tables.users} ${where};`,
66
+ params,
67
+ }));
68
+ const roles = await this.loadRoles(records.map(user => user.id));
69
+ return {
70
+ users: records.map(user => this.mapUser(user, roles.get(user.id) ?? [])),
71
+ total: count.count,
72
+ limit: input.limit,
73
+ offset: input.offset,
74
+ };
75
+ }
76
+ async findById(userId) {
77
+ const user = await this.findRecord(userId);
78
+ if (!user)
79
+ throw new UserNotFoundError(userId);
80
+ const roles = await this.loadRoles([userId]);
81
+ return this.mapUser(user, roles.get(userId) ?? []);
82
+ }
83
+ async update(userId, input) {
84
+ const existing = await this.findRecord(userId);
85
+ if (!existing)
86
+ throw new UserNotFoundError(userId);
87
+ const roles = input.roles ? await this.resolveRoles(input.roles) : undefined;
88
+ const email = input.email ? this.normalizeEmail(input.email) : undefined;
89
+ try {
90
+ await this.db.transaction(async (transaction) => {
91
+ const updates = [];
92
+ const params = [];
93
+ for (const [column, value] of [
94
+ ['name', input.name],
95
+ ['email', email],
96
+ ['status', input.status],
97
+ ]) {
98
+ if (value !== undefined) {
99
+ updates.push(`${column} = ?`);
100
+ params.push(value);
101
+ }
102
+ }
103
+ if (updates.length > 0 || roles) {
104
+ updates.push('updated_at = CURRENT_TIMESTAMP');
105
+ await transaction.run({
106
+ sql: `UPDATE ${this.tables.users} SET ${updates.join(', ')} WHERE id = ?;`,
107
+ params: [...params, userId],
108
+ });
109
+ }
110
+ if (roles)
111
+ await this.replaceRoles(transaction, userId, roles);
112
+ });
113
+ }
114
+ catch (error) {
115
+ this.rethrowEmailConflict(error, email);
116
+ }
117
+ return this.findById(userId);
118
+ }
119
+ async delete(userId) {
120
+ const existing = await this.findRecord(userId);
121
+ if (!existing)
122
+ throw new UserNotFoundError(userId);
123
+ await this.db.transaction(async (transaction) => {
124
+ await transaction.run({
125
+ sql: `DELETE FROM ${this.tables.apiKeys} WHERE user_id = ?;`,
126
+ params: [userId],
127
+ });
128
+ await transaction.run({
129
+ sql: `DELETE FROM ${this.tables.userRoles} WHERE user_id = ?;`,
130
+ params: [userId],
131
+ });
132
+ await transaction.run({
133
+ sql: `DELETE FROM ${this.tables.users} WHERE id = ?;`,
134
+ params: [userId],
135
+ });
136
+ });
137
+ }
138
+ async findRecord(userId) {
139
+ return (await this.db.get({
140
+ sql: [
141
+ 'SELECT id, name, email, status, created_at, updated_at',
142
+ `FROM ${this.tables.users} WHERE id = ?;`,
143
+ ].join(' '),
144
+ params: [userId],
145
+ }));
146
+ }
147
+ async resolveRoles(names) {
148
+ const uniqueNames = [...new Set(names)];
149
+ const placeholders = uniqueNames.map(() => '?').join(', ');
150
+ const records = (await this.db.all({
151
+ sql: `SELECT id, name FROM ${this.tables.roles} WHERE name IN (${placeholders});`,
152
+ params: uniqueNames,
153
+ }));
154
+ const found = new Set(records.map(role => role.name));
155
+ const unknown = uniqueNames.filter(role => !found.has(role));
156
+ if (unknown.length > 0)
157
+ throw new UnknownRolesError(unknown);
158
+ const byName = new Map(records.map(role => [role.name, role]));
159
+ return uniqueNames.map(name => byName.get(name));
160
+ }
161
+ async replaceRoles(database, userId, roles) {
162
+ await database.run({
163
+ sql: `DELETE FROM ${this.tables.userRoles} WHERE user_id = ?;`,
164
+ params: [userId],
165
+ });
166
+ for (const role of roles) {
167
+ await database.run({
168
+ sql: `INSERT INTO ${this.tables.userRoles} (user_id, role_id) VALUES (?, ?);`,
169
+ params: [userId, role.id],
170
+ });
171
+ }
172
+ }
173
+ async loadRoles(userIds) {
174
+ const result = new Map();
175
+ if (userIds.length === 0)
176
+ return result;
177
+ const records = (await this.db.all({
178
+ sql: [
179
+ `SELECT ${this.tables.userRoles}.user_id, ${this.tables.roles}.name`,
180
+ `FROM ${this.tables.userRoles}`,
181
+ `INNER JOIN ${this.tables.roles}`,
182
+ `ON ${this.tables.roles}.id = ${this.tables.userRoles}.role_id`,
183
+ `WHERE ${this.tables.userRoles}.user_id IN (${userIds.map(() => '?').join(', ')})`,
184
+ `ORDER BY ${this.tables.roles}.name;`,
185
+ ].join(' '),
186
+ params: userIds,
187
+ }));
188
+ for (const role of records) {
189
+ result.set(role.user_id, [...(result.get(role.user_id) ?? []), role.name]);
190
+ }
191
+ return result;
192
+ }
193
+ mapUser(user, roles) {
194
+ return {
195
+ id: user.id,
196
+ name: user.name,
197
+ email: user.email,
198
+ status: user.status,
199
+ roles,
200
+ createdAt: user.created_at,
201
+ updatedAt: user.updated_at,
202
+ };
203
+ }
204
+ normalizeEmail(email) {
205
+ return email.toLowerCase();
206
+ }
207
+ rethrowEmailConflict(error, email) {
208
+ if (email && error instanceof Error && error.message.includes(`${this.tables.users}.email`)) {
209
+ throw new DuplicateUserEmailError(email);
210
+ }
211
+ throw error;
212
+ }
213
+ }
214
+ //# sourceMappingURL=database-user-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-user-service.js","sourceRoot":"","sources":["../src/database-user-service.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from './api-key-hasher.js';
2
+ export * from './auth-database-module.js';
3
+ export * from './auth-database-tables.js';
4
+ export * from './database-api-key-repository.js';
5
+ export * from './database-api-key-service.js';
6
+ export * from './database-api-key-usage-recorder.js';
7
+ export * from './database-auth-user-repository.js';
8
+ export * from './database-auth-event-store.js';
9
+ export * from './database-user-service.js';
10
+ export * from './tokens.js';
package/lib/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export * from './api-key-hasher.js';
2
+ export * from './auth-database-module.js';
3
+ export * from './auth-database-tables.js';
4
+ export * from './database-api-key-repository.js';
5
+ export * from './database-api-key-service.js';
6
+ export * from './database-api-key-usage-recorder.js';
7
+ export * from './database-auth-user-repository.js';
8
+ export * from './database-auth-event-store.js';
9
+ export * from './database-user-service.js';
10
+ export * from './tokens.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const AUTH_DB_TOKENS: {
2
+ ApiKeyHasher: symbol;
3
+ UserService: symbol;
4
+ ApiKeyService: symbol;
5
+ EventStore: symbol;
6
+ };
package/lib/tokens.js ADDED
@@ -0,0 +1,7 @@
1
+ export const AUTH_DB_TOKENS = {
2
+ ApiKeyHasher: Symbol('AuthDbApiKeyHasher'),
3
+ UserService: Symbol('AuthDbUserService'),
4
+ ApiKeyService: Symbol('AuthDbApiKeyService'),
5
+ EventStore: Symbol('AuthDbEventStore'),
6
+ };
7
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAC,oBAAoB,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC,mBAAmB,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC,qBAAqB,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC,kBAAkB,CAAC;CACvC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@kurdel/auth-db",
3
+ "version": "0.1.0-beta.1",
4
+ "description": "Database-backed authentication adapters for @kurdel/auth",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./lib/index.js"
8
+ },
9
+ "main": "./lib/index.js",
10
+ "types": "./lib/index.d.ts",
11
+ "files": ["lib"],
12
+ "scripts": {
13
+ "prepack": "npm run build",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "clean": "rimraf lib ../../.cache/tsconfig.auth-db.build.tsbuildinfo",
17
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
18
+ "build:force": "npm run clean && tsc -p tsconfig.build.json --force && tsc-alias -p tsconfig.build.json"
19
+ },
20
+ "keywords": ["kurdel", "authentication", "database", "api-key", "framework"],
21
+ "author": "Andrii Sorokin",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/ignorantic/kurdel.git",
29
+ "directory": "packages/auth-db"
30
+ },
31
+ "homepage": "https://github.com/ignorantic/kurdel/tree/main/packages/auth-db#readme",
32
+ "bugs": {
33
+ "url": "https://github.com/ignorantic/kurdel/issues"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "tag": "beta"
38
+ },
39
+ "dependencies": {
40
+ "@kurdel/auth": "0.1.0-beta.1",
41
+ "@kurdel/common": "0.1.0-beta.1",
42
+ "@kurdel/core": "0.1.0-beta.1",
43
+ "@kurdel/db": "0.1.0-beta.1"
44
+ },
45
+ "devDependencies": {
46
+ "rimraf": "^6.0.1",
47
+ "tsc-alias": "^1.8.16"
48
+ }
49
+ }