@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.
- package/README.md +42 -9
- package/lib/auth-database-module.d.ts +2 -0
- package/lib/auth-database-module.js +52 -11
- package/lib/auth-database-module.js.map +1 -1
- package/lib/auth-database-tables.d.ts +4 -0
- package/lib/auth-database-tables.js +4 -0
- package/lib/auth-database-tables.js.map +1 -1
- package/lib/database-api-key-repository.d.ts +2 -2
- package/lib/database-api-key-repository.js +3 -3
- package/lib/database-api-key-repository.js.map +1 -1
- package/lib/database-api-key-service.d.ts +3 -3
- package/lib/database-api-key-service.js +1 -1
- package/lib/database-api-key-service.js.map +1 -1
- package/lib/database-api-key-usage-recorder.d.ts +2 -2
- package/lib/database-api-key-usage-recorder.js +1 -1
- package/lib/database-api-key-usage-recorder.js.map +1 -1
- package/lib/database-auth-event-store.d.ts +14 -3
- package/lib/database-auth-event-store.js +41 -17
- package/lib/database-auth-event-store.js.map +1 -1
- package/lib/database-auth-user-repository.d.ts +2 -2
- package/lib/database-auth-user-repository.js +23 -6
- package/lib/database-auth-user-repository.js.map +1 -1
- package/lib/database-jwt-session-repository.d.ts +10 -0
- package/lib/database-jwt-session-repository.js +26 -0
- package/lib/database-jwt-session-repository.js.map +1 -0
- package/lib/database-jwt-session-service.d.ts +31 -0
- package/lib/database-jwt-session-service.js +74 -0
- package/lib/database-jwt-session-service.js.map +1 -0
- package/lib/database-password-credential-repository.d.ts +9 -0
- package/lib/database-password-credential-repository.js +19 -0
- package/lib/database-password-credential-repository.js.map +1 -0
- package/lib/database-password-service.d.ts +14 -0
- package/lib/database-password-service.js +34 -0
- package/lib/database-password-service.js.map +1 -0
- package/lib/database-user-service.d.ts +62 -2
- package/lib/database-user-service.js +292 -5
- package/lib/database-user-service.js.map +1 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/tokens.d.ts +3 -0
- package/lib/tokens.js +3 -0
- package/lib/tokens.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Database-backed repository adapters for `@kurdel/auth`.
|
|
4
4
|
|
|
5
5
|
The package keeps authentication strategies storage-agnostic while providing
|
|
6
|
-
standard implementations over Kurdel's `
|
|
6
|
+
standard implementations over Kurdel's `Database` contract.
|
|
7
7
|
|
|
8
8
|
`DatabaseAuthUserRepository` resolves the current user and roles, while
|
|
9
9
|
`DatabaseApiKeyRepository` resolves safe credential metadata including its
|
|
@@ -17,6 +17,37 @@ role assignments, and issue, list, or revoke API keys. `AuthDatabaseModule`
|
|
|
17
17
|
registers both services as `AUTH_DB_TOKENS.UserService` and
|
|
18
18
|
`AUTH_DB_TOKENS.ApiKeyService`.
|
|
19
19
|
|
|
20
|
+
The module also exposes `DatabaseJwtSessionRepository` and
|
|
21
|
+
`DatabaseJwtSessionService`. Applications can create a server-side session,
|
|
22
|
+
place its ID in the JWT `jti` claim, configure that repository on `JwtStrategy`,
|
|
23
|
+
and revoke the session before the signed token expires. Session creation and
|
|
24
|
+
revocation are transactional and emit sanitized audit events when auditing is
|
|
25
|
+
enabled.
|
|
26
|
+
|
|
27
|
+
Password authentication uses a separate `password_credentials` table rather
|
|
28
|
+
than adding secrets to the user profile. `DatabasePasswordCredentialRepository`
|
|
29
|
+
resolves credentials by case-insensitive email, while
|
|
30
|
+
`DatabasePasswordService.set()` hashes and upserts a user's password.
|
|
31
|
+
`AuthDatabaseModule` wires both to `PasswordAuthenticationService` and uses
|
|
32
|
+
`ScryptPasswordHasher` by default. Applications can replace it with
|
|
33
|
+
`passwordHasher` in the module configuration.
|
|
34
|
+
|
|
35
|
+
User listings support status and text filters, stable sorting, and offset
|
|
36
|
+
pagination. `DatabaseUserService` also provides transactional bulk status,
|
|
37
|
+
role, and deletion operations; role lifecycle management with usage counts;
|
|
38
|
+
and dashboard statistics for users, credentials, and recent authentication
|
|
39
|
+
failures. `DatabaseAuthEventStore.listPage()` exposes global or per-user audit
|
|
40
|
+
history with type, date range, and offset filters while the existing `list()`
|
|
41
|
+
method remains available for simple queries.
|
|
42
|
+
|
|
43
|
+
Applications may also model authorization as roles containing permissions.
|
|
44
|
+
`DatabaseUserService.listPermissions()` exposes the application-owned catalog,
|
|
45
|
+
and `setRolePermissions()` replaces a role's assignments transactionally.
|
|
46
|
+
`DatabaseAuthUserRepository` resolves the union of permissions from every
|
|
47
|
+
current role into `AuthUser.permissions`. The application schema must provide
|
|
48
|
+
`permissions` and `role_permissions` tables; the runnable sample contains the
|
|
49
|
+
corresponding migration and seed data.
|
|
50
|
+
|
|
20
51
|
`DatabaseApiKeyUsageRecorder` updates `last_used_at` after successful
|
|
21
52
|
authentication. `AuthDatabaseModule` exposes it through
|
|
22
53
|
`AUTH_TOKENS.ApiKeyUsageRecorder`, ready to pass to `ApiKeyStrategy` as its
|
|
@@ -36,7 +67,7 @@ authorization events. The application must provide the configured
|
|
|
36
67
|
columns and indexes.
|
|
37
68
|
|
|
38
69
|
API-key issue or revocation and its database audit event run in the same
|
|
39
|
-
`
|
|
70
|
+
`Database.transaction` callback. If audit persistence fails, the credential
|
|
40
71
|
mutation is rolled back and the service returns the original error. User
|
|
41
72
|
creation, role replacement, profile updates, and deletion use the same
|
|
42
73
|
transaction API for their multi-statement operations.
|
|
@@ -51,12 +82,13 @@ const modules = [
|
|
|
51
82
|
strategies: [
|
|
52
83
|
{
|
|
53
84
|
name: 'api-key',
|
|
54
|
-
useFactory: ioc =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
useFactory: ioc =>
|
|
86
|
+
new ApiKeyStrategy({
|
|
87
|
+
header: 'x-api-key',
|
|
88
|
+
credentials: ioc.get(AUTH_TOKENS.ApiKeyRepository),
|
|
89
|
+
users: ioc.get(AUTH_TOKENS.UserRepository),
|
|
90
|
+
usage: ioc.get(AUTH_TOKENS.ApiKeyUsageRecorder),
|
|
91
|
+
}),
|
|
60
92
|
},
|
|
61
93
|
],
|
|
62
94
|
}),
|
|
@@ -76,7 +108,8 @@ new AuthDatabaseModule({
|
|
|
76
108
|
});
|
|
77
109
|
```
|
|
78
110
|
|
|
79
|
-
By default, the package expects `users`, `roles`, `user_roles`,
|
|
111
|
+
By default, the package expects `users`, `roles`, `user_roles`, `api_keys`, and
|
|
112
|
+
`password_credentials`
|
|
80
113
|
tables and uses SHA-256 for API-key lookup. Schema ownership remains with the
|
|
81
114
|
application; see `sample/auth-db` for migrations and a runnable example.
|
|
82
115
|
The management services expect the profile and credential metadata columns
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type PasswordHasher } from '@kurdel/auth';
|
|
1
2
|
import { ModulePriority, type AppModule, type ProviderConfig } from '@kurdel/core/app';
|
|
2
3
|
import { type ApiKeyHasher } from './api-key-hasher.js';
|
|
3
4
|
import type { AuthDatabaseTables } from './auth-database-tables.js';
|
|
4
5
|
export interface AuthDatabaseModuleConfig {
|
|
5
6
|
tables?: Partial<AuthDatabaseTables>;
|
|
6
7
|
apiKeyHasher?: ApiKeyHasher;
|
|
8
|
+
passwordHasher?: PasswordHasher;
|
|
7
9
|
/** Enables persistence of sanitized authentication audit events. */
|
|
8
10
|
audit?: boolean;
|
|
9
11
|
}
|
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
import { AUTH_TOKENS } from '@kurdel/auth';
|
|
1
|
+
import { AUTH_TOKENS, PasswordAuthenticationService, ScryptPasswordHasher, } from '@kurdel/auth';
|
|
2
2
|
import { ModulePriority } from '@kurdel/core/app';
|
|
3
|
-
import {
|
|
3
|
+
import { Database } from '@kurdel/db';
|
|
4
4
|
import { Sha256ApiKeyHasher } from './api-key-hasher.js';
|
|
5
5
|
import { DatabaseApiKeyRepository } from './database-api-key-repository.js';
|
|
6
6
|
import { DatabaseApiKeyService } from './database-api-key-service.js';
|
|
7
7
|
import { DatabaseApiKeyUsageRecorder } from './database-api-key-usage-recorder.js';
|
|
8
8
|
import { DatabaseAuthUserRepository } from './database-auth-user-repository.js';
|
|
9
9
|
import { DatabaseAuthEventStore } from './database-auth-event-store.js';
|
|
10
|
+
import { DatabaseJwtSessionRepository } from './database-jwt-session-repository.js';
|
|
11
|
+
import { DatabaseJwtSessionService } from './database-jwt-session-service.js';
|
|
12
|
+
import { DatabasePasswordCredentialRepository } from './database-password-credential-repository.js';
|
|
13
|
+
import { DatabasePasswordService } from './database-password-service.js';
|
|
10
14
|
import { DatabaseUserService } from './database-user-service.js';
|
|
11
15
|
import { AUTH_DB_TOKENS } from './tokens.js';
|
|
12
16
|
export class AuthDatabaseModule {
|
|
13
17
|
constructor(config = {}) {
|
|
14
18
|
this.priority = ModulePriority.User;
|
|
15
|
-
this.imports = { db:
|
|
19
|
+
this.imports = { db: Database };
|
|
16
20
|
const tables = config.tables ?? {};
|
|
17
21
|
const hasher = config.apiKeyHasher ?? new Sha256ApiKeyHasher();
|
|
22
|
+
const passwordHasher = config.passwordHasher ?? new ScryptPasswordHasher();
|
|
18
23
|
this.exports = {
|
|
19
24
|
userRepository: AUTH_TOKENS.UserRepository,
|
|
20
25
|
apiKeyRepository: AUTH_TOKENS.ApiKeyRepository,
|
|
21
26
|
apiKeyUsageRecorder: AUTH_TOKENS.ApiKeyUsageRecorder,
|
|
27
|
+
jwtSessionRepository: AUTH_TOKENS.JwtSessionRepository,
|
|
28
|
+
passwordCredentialRepository: AUTH_TOKENS.PasswordCredentialRepository,
|
|
29
|
+
passwordAuthenticationService: AUTH_TOKENS.PasswordAuthenticationService,
|
|
22
30
|
apiKeyHasher: AUTH_DB_TOKENS.ApiKeyHasher,
|
|
23
31
|
userService: AUTH_DB_TOKENS.UserService,
|
|
24
32
|
apiKeyService: AUTH_DB_TOKENS.ApiKeyService,
|
|
33
|
+
jwtSessionService: AUTH_DB_TOKENS.JwtSessionService,
|
|
34
|
+
passwordService: AUTH_DB_TOKENS.PasswordService,
|
|
25
35
|
...(config.audit ? { eventStore: AUTH_DB_TOKENS.EventStore } : {}),
|
|
26
36
|
};
|
|
27
37
|
this.providers = [
|
|
@@ -29,36 +39,67 @@ export class AuthDatabaseModule {
|
|
|
29
39
|
provide: AUTH_DB_TOKENS.ApiKeyHasher,
|
|
30
40
|
useInstance: hasher,
|
|
31
41
|
},
|
|
42
|
+
{
|
|
43
|
+
provide: AUTH_DB_TOKENS.PasswordHasher,
|
|
44
|
+
useInstance: passwordHasher,
|
|
45
|
+
},
|
|
32
46
|
{
|
|
33
47
|
provide: AUTH_TOKENS.UserRepository,
|
|
34
|
-
useFactory: ioc => new DatabaseAuthUserRepository(ioc.get(
|
|
48
|
+
useFactory: ioc => new DatabaseAuthUserRepository(ioc.get(Database), tables),
|
|
35
49
|
singleton: true,
|
|
36
50
|
},
|
|
37
51
|
{
|
|
38
52
|
provide: AUTH_TOKENS.ApiKeyRepository,
|
|
39
|
-
useFactory: ioc => new DatabaseApiKeyRepository(ioc.get(
|
|
53
|
+
useFactory: ioc => new DatabaseApiKeyRepository(ioc.get(Database), ioc.get(AUTH_DB_TOKENS.ApiKeyHasher), tables),
|
|
40
54
|
singleton: true,
|
|
41
55
|
},
|
|
42
56
|
{
|
|
43
57
|
provide: AUTH_TOKENS.ApiKeyUsageRecorder,
|
|
44
|
-
useFactory: ioc => new DatabaseApiKeyUsageRecorder(ioc.get(
|
|
58
|
+
useFactory: ioc => new DatabaseApiKeyUsageRecorder(ioc.get(Database), tables),
|
|
59
|
+
singleton: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
provide: AUTH_TOKENS.JwtSessionRepository,
|
|
63
|
+
useFactory: ioc => new DatabaseJwtSessionRepository(ioc.get(Database), tables),
|
|
64
|
+
singleton: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
provide: AUTH_TOKENS.PasswordCredentialRepository,
|
|
68
|
+
useFactory: ioc => new DatabasePasswordCredentialRepository(ioc.get(Database), tables),
|
|
69
|
+
singleton: true,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
provide: AUTH_TOKENS.PasswordAuthenticationService,
|
|
73
|
+
useFactory: ioc => new PasswordAuthenticationService(ioc.get(AUTH_TOKENS.PasswordCredentialRepository), ioc.get(AUTH_TOKENS.UserRepository), ioc.get(AUTH_DB_TOKENS.PasswordHasher)),
|
|
45
74
|
singleton: true,
|
|
46
75
|
},
|
|
47
76
|
{
|
|
48
77
|
provide: AUTH_DB_TOKENS.UserService,
|
|
49
|
-
useFactory: ioc => new DatabaseUserService(ioc.get(
|
|
78
|
+
useFactory: ioc => new DatabaseUserService(ioc.get(Database), tables),
|
|
50
79
|
singleton: true,
|
|
51
80
|
},
|
|
52
81
|
...(config.audit
|
|
53
|
-
? [
|
|
82
|
+
? [
|
|
83
|
+
{
|
|
54
84
|
provide: AUTH_DB_TOKENS.EventStore,
|
|
55
|
-
useFactory: (ioc) => new DatabaseAuthEventStore(ioc.get(
|
|
85
|
+
useFactory: (ioc) => new DatabaseAuthEventStore(ioc.get(Database), tables),
|
|
56
86
|
singleton: true,
|
|
57
|
-
}
|
|
87
|
+
},
|
|
88
|
+
]
|
|
58
89
|
: []),
|
|
59
90
|
{
|
|
60
91
|
provide: AUTH_DB_TOKENS.ApiKeyService,
|
|
61
|
-
useFactory: ioc => new DatabaseApiKeyService(ioc.get(
|
|
92
|
+
useFactory: ioc => new DatabaseApiKeyService(ioc.get(Database), ioc.get(AUTH_DB_TOKENS.ApiKeyHasher), tables, config.audit ? ioc.get(AUTH_DB_TOKENS.EventStore) : undefined),
|
|
93
|
+
singleton: true,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
provide: AUTH_DB_TOKENS.JwtSessionService,
|
|
97
|
+
useFactory: ioc => new DatabaseJwtSessionService(ioc.get(Database), tables, config.audit ? ioc.get(AUTH_DB_TOKENS.EventStore) : undefined),
|
|
98
|
+
singleton: true,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
provide: AUTH_DB_TOKENS.PasswordService,
|
|
102
|
+
useFactory: ioc => new DatabasePasswordService(ioc.get(Database), ioc.get(AUTH_DB_TOKENS.PasswordHasher), tables),
|
|
62
103
|
singleton: true,
|
|
63
104
|
},
|
|
64
105
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-database-module.js","sourceRoot":"","sources":["../src/auth-database-module.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth-database-module.js","sourceRoot":"","sources":["../src/auth-database-module.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,11 @@ export interface AuthDatabaseTables {
|
|
|
2
2
|
users: string;
|
|
3
3
|
roles: string;
|
|
4
4
|
userRoles: string;
|
|
5
|
+
permissions: string;
|
|
6
|
+
rolePermissions: string;
|
|
5
7
|
apiKeys: string;
|
|
8
|
+
jwtSessions: string;
|
|
9
|
+
passwordCredentials: string;
|
|
6
10
|
authEvents: string;
|
|
7
11
|
}
|
|
8
12
|
export declare const DEFAULT_AUTH_DATABASE_TABLES: Readonly<AuthDatabaseTables>;
|
|
@@ -2,7 +2,11 @@ export const DEFAULT_AUTH_DATABASE_TABLES = {
|
|
|
2
2
|
users: 'users',
|
|
3
3
|
roles: 'roles',
|
|
4
4
|
userRoles: 'user_roles',
|
|
5
|
+
permissions: 'permissions',
|
|
6
|
+
rolePermissions: 'role_permissions',
|
|
5
7
|
apiKeys: 'api_keys',
|
|
8
|
+
jwtSessions: 'jwt_sessions',
|
|
9
|
+
passwordCredentials: 'password_credentials',
|
|
6
10
|
authEvents: 'auth_events',
|
|
7
11
|
};
|
|
8
12
|
export function resolveAuthDatabaseTables(tables = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-database-tables.js","sourceRoot":"","sources":["../src/auth-database-tables.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-database-tables.js","sourceRoot":"","sources":["../src/auth-database-tables.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,4BAA4B,GAAiC;IACxE,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,SAAS,EAAE,YAAY;IACvB,WAAW,EAAE,aAAa;IAC1B,eAAe,EAAE,kBAAkB;IACnC,OAAO,EAAE,UAAU;IACnB,WAAW,EAAE,cAAc;IAC3B,mBAAmB,EAAE,sBAAsB;IAC3C,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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ApiKeyCredential, ApiKeyRepository } from '@kurdel/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Database } from '@kurdel/db';
|
|
3
3
|
import type { ApiKeyHasher } from './api-key-hasher.js';
|
|
4
4
|
import { type AuthDatabaseTables } from './auth-database-tables.js';
|
|
5
5
|
export declare class DatabaseApiKeyRepository implements ApiKeyRepository {
|
|
6
6
|
private readonly db;
|
|
7
7
|
private readonly hasher;
|
|
8
8
|
private readonly tables;
|
|
9
|
-
constructor(db:
|
|
9
|
+
constructor(db: Database, hasher: ApiKeyHasher, tables?: Partial<AuthDatabaseTables>);
|
|
10
10
|
findByKey(key: string): Promise<ApiKeyCredential | null>;
|
|
11
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveAuthDatabaseTables
|
|
1
|
+
import { resolveAuthDatabaseTables } from './auth-database-tables.js';
|
|
2
2
|
export class DatabaseApiKeyRepository {
|
|
3
3
|
constructor(db, hasher, tables = {}) {
|
|
4
4
|
this.db = db;
|
|
@@ -6,14 +6,14 @@ export class DatabaseApiKeyRepository {
|
|
|
6
6
|
this.tables = resolveAuthDatabaseTables(tables);
|
|
7
7
|
}
|
|
8
8
|
async findByKey(key) {
|
|
9
|
-
const record = await this.db.get({
|
|
9
|
+
const record = (await this.db.get({
|
|
10
10
|
sql: [
|
|
11
11
|
'SELECT id, user_id, status, expires_at',
|
|
12
12
|
`FROM ${this.tables.apiKeys}`,
|
|
13
13
|
'WHERE key_hash = ?;',
|
|
14
14
|
].join(' '),
|
|
15
15
|
params: [this.hasher.hash(key)],
|
|
16
|
-
});
|
|
16
|
+
}));
|
|
17
17
|
if (!record)
|
|
18
18
|
return null;
|
|
19
19
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-api-key-repository.js","sourceRoot":"","sources":["../src/database-api-key-repository.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"database-api-key-repository.js","sourceRoot":"","sources":["../src/database-api-key-repository.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAA2B,MAAM,2BAA2B,CAAC;AAS/F,MAAM,OAAO,wBAAwB;IAGnC,YACmB,EAAY,EACZ,MAAoB,EACrC,SAAsC,EAAE;QAFvB,OAAE,GAAF,EAAE,CAAU;QACZ,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,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAChC,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,CAAC,CAA6B,CAAC;QAChC,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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AuthEvent, AuthEventSink } from '@kurdel/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Database, DatabaseSession } from '@kurdel/db';
|
|
3
3
|
import type { ApiKeyHasher } from './api-key-hasher.js';
|
|
4
4
|
import { type AuthDatabaseTables } from './auth-database-tables.js';
|
|
5
5
|
type TransactionalAuthEventSink = AuthEventSink & {
|
|
6
|
-
report(event: AuthEvent, database?:
|
|
6
|
+
report(event: AuthEvent, database?: DatabaseSession): Promise<void> | void;
|
|
7
7
|
};
|
|
8
8
|
export interface CreateApiKeyInput {
|
|
9
9
|
userId: number;
|
|
@@ -43,7 +43,7 @@ export declare class DatabaseApiKeyService {
|
|
|
43
43
|
private readonly events?;
|
|
44
44
|
private readonly now;
|
|
45
45
|
private readonly tables;
|
|
46
|
-
constructor(db:
|
|
46
|
+
constructor(db: Database, hasher: ApiKeyHasher, tables?: Partial<AuthDatabaseTables>, events?: TransactionalAuthEventSink | undefined, now?: () => Date);
|
|
47
47
|
list(userId: number): Promise<ApiKeyMetadata[]>;
|
|
48
48
|
create(input: CreateApiKeyInput): Promise<CreatedApiKey>;
|
|
49
49
|
revoke(userId: number, apiKeyId: string): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import { resolveAuthDatabaseTables
|
|
2
|
+
import { resolveAuthDatabaseTables } from './auth-database-tables.js';
|
|
3
3
|
export class ActiveUserNotFoundError extends Error {
|
|
4
4
|
constructor(userId) {
|
|
5
5
|
super(`Active user '${userId}' was not found`);
|
|
@@ -1 +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,
|
|
1
|
+
{"version":3,"file":"database-api-key-service.js","sourceRoot":"","sources":["../src/database-api-key-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ApiKeyUsageRecorder } from '@kurdel/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Database } from '@kurdel/db';
|
|
3
3
|
import { type AuthDatabaseTables } from './auth-database-tables.js';
|
|
4
4
|
/** Records the most recent successful use of a database-backed API key. */
|
|
5
5
|
export declare class DatabaseApiKeyUsageRecorder implements ApiKeyUsageRecorder {
|
|
6
6
|
private readonly db;
|
|
7
7
|
private readonly tables;
|
|
8
|
-
constructor(db:
|
|
8
|
+
constructor(db: Database, tables?: Partial<AuthDatabaseTables>);
|
|
9
9
|
recordUsage(credentialId: string, usedAt: Date): Promise<void>;
|
|
10
10
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveAuthDatabaseTables
|
|
1
|
+
import { resolveAuthDatabaseTables } from './auth-database-tables.js';
|
|
2
2
|
/** Records the most recent successful use of a database-backed API key. */
|
|
3
3
|
export class DatabaseApiKeyUsageRecorder {
|
|
4
4
|
constructor(db, tables = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-api-key-usage-recorder.js","sourceRoot":"","sources":["../src/database-api-key-usage-recorder.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"database-api-key-usage-recorder.js","sourceRoot":"","sources":["../src/database-api-key-usage-recorder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAA2B,MAAM,2BAA2B,CAAC;AAE/F,2EAA2E;AAC3E,MAAM,OAAO,2BAA2B;IAGtC,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,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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AuthEvent, AuthEventSink } from '@kurdel/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Database, DatabaseSession } from '@kurdel/db';
|
|
3
3
|
import { type AuthDatabaseTables } from './auth-database-tables.js';
|
|
4
4
|
export type StoredAuthEvent = {
|
|
5
5
|
id: number;
|
|
@@ -15,13 +15,24 @@ export type StoredAuthEvent = {
|
|
|
15
15
|
export type ListAuthEventsInput = {
|
|
16
16
|
userId?: string | number;
|
|
17
17
|
type?: AuthEvent['type'];
|
|
18
|
+
from?: Date | string;
|
|
19
|
+
to?: Date | string;
|
|
18
20
|
limit?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
};
|
|
23
|
+
export type AuthEventList = {
|
|
24
|
+
events: StoredAuthEvent[];
|
|
25
|
+
total: number;
|
|
26
|
+
limit: number;
|
|
27
|
+
offset: number;
|
|
19
28
|
};
|
|
20
29
|
/** Persists and queries sanitized authentication audit events. */
|
|
21
30
|
export declare class DatabaseAuthEventStore implements AuthEventSink {
|
|
22
31
|
private readonly db;
|
|
23
32
|
private readonly tables;
|
|
24
|
-
constructor(db:
|
|
25
|
-
report(event: AuthEvent, database?:
|
|
33
|
+
constructor(db: Database, tables?: Partial<AuthDatabaseTables>);
|
|
34
|
+
report(event: AuthEvent, database?: DatabaseSession): Promise<void>;
|
|
26
35
|
list(input?: ListAuthEventsInput): Promise<StoredAuthEvent[]>;
|
|
36
|
+
listPage(input?: ListAuthEventsInput): Promise<AuthEventList>;
|
|
37
|
+
private serializeDate;
|
|
27
38
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveAuthDatabaseTables
|
|
1
|
+
import { resolveAuthDatabaseTables } from './auth-database-tables.js';
|
|
2
2
|
/** Persists and queries sanitized authentication audit events. */
|
|
3
3
|
export class DatabaseAuthEventStore {
|
|
4
4
|
constructor(db, tables = {}) {
|
|
@@ -15,16 +15,19 @@ export class DatabaseAuthEventStore {
|
|
|
15
15
|
params: [
|
|
16
16
|
event.type,
|
|
17
17
|
event.occurredAt.toISOString(),
|
|
18
|
-
'strategy' in event ? event.strategy ?? null : null,
|
|
18
|
+
'strategy' in event ? (event.strategy ?? null) : null,
|
|
19
19
|
event.userId === undefined ? null : String(event.userId),
|
|
20
20
|
event.credential?.type ?? null,
|
|
21
21
|
event.credential?.id ?? null,
|
|
22
22
|
'reason' in event ? event.reason : null,
|
|
23
|
-
'policy' in event ? event.policy ?? null : null,
|
|
23
|
+
'policy' in event ? (event.policy ?? null) : null,
|
|
24
24
|
],
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
async list(input = {}) {
|
|
28
|
+
return (await this.listPage(input)).events;
|
|
29
|
+
}
|
|
30
|
+
async listPage(input = {}) {
|
|
28
31
|
const conditions = [];
|
|
29
32
|
const params = [];
|
|
30
33
|
if (input.userId !== undefined) {
|
|
@@ -35,28 +38,49 @@ export class DatabaseAuthEventStore {
|
|
|
35
38
|
conditions.push('type = ?');
|
|
36
39
|
params.push(input.type);
|
|
37
40
|
}
|
|
41
|
+
if (input.from) {
|
|
42
|
+
conditions.push('occurred_at >= ?');
|
|
43
|
+
params.push(this.serializeDate(input.from));
|
|
44
|
+
}
|
|
45
|
+
if (input.to) {
|
|
46
|
+
conditions.push('occurred_at <= ?');
|
|
47
|
+
params.push(this.serializeDate(input.to));
|
|
48
|
+
}
|
|
38
49
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
39
50
|
const limit = Math.min(Math.max(input.limit ?? 50, 1), 100);
|
|
40
|
-
const
|
|
51
|
+
const offset = Math.max(input.offset ?? 0, 0);
|
|
52
|
+
const records = (await this.db.all({
|
|
41
53
|
sql: [
|
|
42
54
|
'SELECT id, type, occurred_at, strategy, user_id,',
|
|
43
55
|
'credential_type, credential_id, reason, policy',
|
|
44
56
|
`FROM ${this.tables.authEvents} ${where}`,
|
|
45
|
-
'ORDER BY occurred_at DESC, id DESC LIMIT ?;',
|
|
57
|
+
'ORDER BY occurred_at DESC, id DESC LIMIT ? OFFSET ?;',
|
|
46
58
|
].join(' '),
|
|
47
|
-
params: [...params, limit],
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
+
params: [...params, limit, offset],
|
|
60
|
+
}));
|
|
61
|
+
const count = (await this.db.get({
|
|
62
|
+
sql: `SELECT COUNT(*) AS count FROM ${this.tables.authEvents} ${where};`,
|
|
63
|
+
params,
|
|
59
64
|
}));
|
|
65
|
+
return {
|
|
66
|
+
events: records.map(record => ({
|
|
67
|
+
id: record.id,
|
|
68
|
+
type: record.type,
|
|
69
|
+
occurredAt: record.occurred_at,
|
|
70
|
+
strategy: record.strategy,
|
|
71
|
+
userId: record.user_id,
|
|
72
|
+
credentialType: record.credential_type,
|
|
73
|
+
credentialId: record.credential_id,
|
|
74
|
+
reason: record.reason,
|
|
75
|
+
policy: record.policy,
|
|
76
|
+
})),
|
|
77
|
+
total: count.count,
|
|
78
|
+
limit,
|
|
79
|
+
offset,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
serializeDate(value) {
|
|
83
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
60
84
|
}
|
|
61
85
|
}
|
|
62
86
|
//# sourceMappingURL=database-auth-event-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-auth-event-store.js","sourceRoot":"","sources":["../src/database-auth-event-store.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"database-auth-event-store.js","sourceRoot":"","sources":["../src/database-auth-event-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { AuthUserRepository } from '@kurdel/auth';
|
|
2
2
|
import type { AuthUser } from '@kurdel/common';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Database } from '@kurdel/db';
|
|
4
4
|
import { type AuthDatabaseTables } from './auth-database-tables.js';
|
|
5
5
|
export declare class DatabaseAuthUserRepository implements AuthUserRepository {
|
|
6
6
|
private readonly db;
|
|
7
7
|
private readonly tables;
|
|
8
|
-
constructor(db:
|
|
8
|
+
constructor(db: Database, tables?: Partial<AuthDatabaseTables>);
|
|
9
9
|
findById(id: string | number): Promise<AuthUser | null>;
|
|
10
10
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { resolveAuthDatabaseTables
|
|
1
|
+
import { resolveAuthDatabaseTables } from './auth-database-tables.js';
|
|
2
2
|
export class DatabaseAuthUserRepository {
|
|
3
3
|
constructor(db, tables = {}) {
|
|
4
4
|
this.db = db;
|
|
5
5
|
this.tables = resolveAuthDatabaseTables(tables);
|
|
6
6
|
}
|
|
7
7
|
async findById(id) {
|
|
8
|
-
const user = await this.db.get({
|
|
8
|
+
const user = (await this.db.get({
|
|
9
9
|
sql: `SELECT id, status FROM ${this.tables.users} WHERE id = ?;`,
|
|
10
10
|
params: [id],
|
|
11
|
-
});
|
|
11
|
+
}));
|
|
12
12
|
if (!user || user.status !== 'active')
|
|
13
13
|
return null;
|
|
14
|
-
const roles = await this.db.all({
|
|
14
|
+
const roles = (await this.db.all({
|
|
15
15
|
sql: [
|
|
16
16
|
`SELECT ${this.tables.roles}.name`,
|
|
17
17
|
`FROM ${this.tables.roles}`,
|
|
@@ -21,8 +21,25 @@ export class DatabaseAuthUserRepository {
|
|
|
21
21
|
`ORDER BY ${this.tables.roles}.name;`,
|
|
22
22
|
].join(' '),
|
|
23
23
|
params: [user.id],
|
|
24
|
-
});
|
|
25
|
-
|
|
24
|
+
}));
|
|
25
|
+
const permissions = (await this.db.all({
|
|
26
|
+
sql: [
|
|
27
|
+
`SELECT DISTINCT ${this.tables.permissions}.name`,
|
|
28
|
+
`FROM ${this.tables.permissions}`,
|
|
29
|
+
`INNER JOIN ${this.tables.rolePermissions}`,
|
|
30
|
+
`ON ${this.tables.rolePermissions}.permission_id = ${this.tables.permissions}.id`,
|
|
31
|
+
`INNER JOIN ${this.tables.userRoles}`,
|
|
32
|
+
`ON ${this.tables.userRoles}.role_id = ${this.tables.rolePermissions}.role_id`,
|
|
33
|
+
`WHERE ${this.tables.userRoles}.user_id = ?`,
|
|
34
|
+
`ORDER BY ${this.tables.permissions}.name;`,
|
|
35
|
+
].join(' '),
|
|
36
|
+
params: [user.id],
|
|
37
|
+
}));
|
|
38
|
+
return {
|
|
39
|
+
id: user.id,
|
|
40
|
+
roles: roles.map(role => role.name),
|
|
41
|
+
permissions: permissions.map(permission => permission.name),
|
|
42
|
+
};
|
|
26
43
|
}
|
|
27
44
|
}
|
|
28
45
|
//# sourceMappingURL=database-auth-user-repository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-auth-user-repository.js","sourceRoot":"","sources":["../src/database-auth-user-repository.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"database-auth-user-repository.js","sourceRoot":"","sources":["../src/database-auth-user-repository.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAA2B,MAAM,2BAA2B,CAAC;AAM/F,MAAM,OAAO,0BAA0B;IAGrC,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,EAAmB;QAChC,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,EAAE,CAAC;SACb,CAAC,CAA2B,CAAC;QAC9B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAEnD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAC/B,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,CAAC,CAAiB,CAAC;QAEpB,MAAM,WAAW,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YACrC,GAAG,EAAE;gBACH,mBAAmB,IAAI,CAAC,MAAM,CAAC,WAAW,OAAO;gBACjD,QAAQ,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE;gBACjC,cAAc,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE;gBAC3C,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,oBAAoB,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK;gBACjF,cAAc,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;gBACrC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,cAAc,IAAI,CAAC,MAAM,CAAC,eAAe,UAAU;gBAC9E,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,cAAc;gBAC5C,YAAY,IAAI,CAAC,MAAM,CAAC,WAAW,QAAQ;aAC5C,CAAC,IAAI,CAAC,GAAG,CAAC;YACX,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;SAClB,CAAC,CAAuB,CAAC;QAE1B,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YACnC,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;SAC5D,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JwtSession, JwtSessionRepository } from '@kurdel/auth';
|
|
2
|
+
import type { Database } from '@kurdel/db';
|
|
3
|
+
import { type AuthDatabaseTables } from './auth-database-tables.js';
|
|
4
|
+
/** Resolves persisted JWT sessions for token revocation checks. */
|
|
5
|
+
export declare class DatabaseJwtSessionRepository implements JwtSessionRepository {
|
|
6
|
+
private readonly db;
|
|
7
|
+
private readonly tables;
|
|
8
|
+
constructor(db: Database, tables?: Partial<AuthDatabaseTables>);
|
|
9
|
+
findById(id: string): Promise<JwtSession | null>;
|
|
10
|
+
}
|