@kurdel/auth 0.1.0-beta.5 → 0.1.0-beta.6
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 +28 -24
- package/lib/domain/auth-context.d.ts +16 -1
- package/lib/domain/auth-event-sink-provider.d.ts +10 -0
- package/lib/domain/auth-event.d.ts +34 -3
- package/lib/domain/auth-event.js +8 -1
- package/lib/domain/auth-event.js.map +1 -1
- package/lib/domain/auth-strategy-provider.d.ts +17 -0
- package/lib/domain/auth-strategy.d.ts +17 -7
- package/lib/domain/authorization-policy-composition.d.ts +20 -3
- package/lib/domain/authorization-policy-composition.js +24 -3
- package/lib/domain/authorization-policy-composition.js.map +1 -1
- package/lib/domain/authorization-policy-provider.d.ts +17 -0
- package/lib/domain/authorization-policy.d.ts +39 -3
- package/lib/domain/authorization-policy.js +8 -2
- package/lib/domain/authorization-policy.js.map +1 -1
- package/lib/domain/permission.d.ts +12 -2
- package/lib/domain/permission.js +16 -3
- package/lib/domain/permission.js.map +1 -1
- package/lib/infra/in-memory/in-memory-api-key-repository.d.ts +11 -1
- package/lib/infra/in-memory/in-memory-api-key-repository.js +11 -1
- package/lib/infra/in-memory/in-memory-api-key-repository.js.map +1 -1
- package/lib/infra/in-memory/in-memory-auth-user-repository.d.ts +11 -1
- package/lib/infra/in-memory/in-memory-auth-user-repository.js +11 -1
- package/lib/infra/in-memory/in-memory-auth-user-repository.js.map +1 -1
- package/lib/infra/in-memory/in-memory-jwt-repository.d.ts +5 -1
- package/lib/infra/in-memory/in-memory-jwt-repository.js +5 -1
- package/lib/infra/in-memory/in-memory-jwt-repository.js.map +1 -1
- package/lib/password/password-authentication-service.d.ts +29 -0
- package/lib/password/password-authentication-service.js +31 -1
- package/lib/password/password-authentication-service.js.map +1 -1
- package/lib/password/password-hasher.d.ts +18 -0
- package/lib/password/scrypt-password-hasher.d.ts +46 -1
- package/lib/password/scrypt-password-hasher.js +33 -1
- package/lib/password/scrypt-password-hasher.js.map +1 -1
- package/lib/repositories/api-key/api-key-repository.d.ts +22 -12
- package/lib/repositories/api-key/api-key-usage-recorder.d.ts +12 -1
- package/lib/repositories/jwt/jwt-repository.d.ts +7 -1
- package/lib/repositories/jwt/jwt-session-repository.d.ts +27 -3
- package/lib/repositories/password/password-credential-repository.d.ts +25 -1
- package/lib/repositories/user/auth-user-repository.d.ts +16 -4
- package/lib/runtime/auth-module.d.ts +32 -28
- package/lib/runtime/auth-module.js +26 -12
- package/lib/runtime/auth-module.js.map +1 -1
- package/lib/runtime/auth-strategy-registry.d.ts +24 -7
- package/lib/runtime/auth-strategy-registry.js +24 -7
- package/lib/runtime/auth-strategy-registry.js.map +1 -1
- package/lib/runtime/authorization-policy-registry.d.ts +35 -1
- package/lib/runtime/authorization-policy-registry.js +35 -1
- package/lib/runtime/authorization-policy-registry.js.map +1 -1
- package/lib/runtime/create-auth-middleware.d.ts +32 -0
- package/lib/runtime/create-auth-middleware.js +41 -2
- package/lib/runtime/create-auth-middleware.js.map +1 -1
- package/lib/strategies/api-key/api-key-strategy-provider.d.ts +18 -0
- package/lib/strategies/api-key/api-key-strategy-provider.js +30 -0
- package/lib/strategies/api-key/api-key-strategy-provider.js.map +1 -0
- package/lib/strategies/api-key/api-key-strategy.d.ts +39 -1
- package/lib/strategies/api-key/api-key-strategy.js +34 -1
- package/lib/strategies/api-key/api-key-strategy.js.map +1 -1
- package/lib/strategies/api-key/index.d.ts +1 -0
- package/lib/strategies/api-key/index.js +1 -0
- package/lib/strategies/api-key/index.js.map +1 -1
- package/lib/strategies/jwt/index.d.ts +1 -0
- package/lib/strategies/jwt/index.js +1 -0
- package/lib/strategies/jwt/index.js.map +1 -1
- package/lib/strategies/jwt/jwt-service.d.ts +61 -10
- package/lib/strategies/jwt/jwt-service.js +38 -9
- package/lib/strategies/jwt/jwt-service.js.map +1 -1
- package/lib/strategies/jwt/jwt-strategy-provider.d.ts +18 -0
- package/lib/strategies/jwt/jwt-strategy-provider.js +27 -0
- package/lib/strategies/jwt/jwt-strategy-provider.js.map +1 -0
- package/lib/strategies/jwt/jwt-strategy.d.ts +43 -4
- package/lib/strategies/jwt/jwt-strategy.js +26 -2
- package/lib/strategies/jwt/jwt-strategy.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -15,30 +15,31 @@ npm install @kurdel/auth
|
|
|
15
15
|
|
|
16
16
|
## Configure authentication
|
|
17
17
|
|
|
18
|
-
Register one or more named strategies with `AuthModule`.
|
|
19
|
-
|
|
18
|
+
Register one or more named strategies with `AuthModule`. Built-in provider
|
|
19
|
+
helpers resolve their framework dependencies from the application container:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
-
import {
|
|
22
|
+
import { apiKeyStrategy, AuthModule, jwtStrategy } from '@kurdel/auth';
|
|
23
23
|
|
|
24
24
|
const auth = new AuthModule({
|
|
25
|
-
strategies: [
|
|
26
|
-
{
|
|
27
|
-
name: 'api-key',
|
|
28
|
-
useFactory: ioc =>
|
|
29
|
-
new ApiKeyStrategy({
|
|
30
|
-
header: 'x-api-key',
|
|
31
|
-
credentials: ioc.get(AUTH_TOKENS.ApiKeyRepository),
|
|
32
|
-
users: ioc.get(AUTH_TOKENS.UserRepository),
|
|
33
|
-
usage: ioc.get(AUTH_TOKENS.ApiKeyUsageRecorder),
|
|
34
|
-
}),
|
|
35
|
-
},
|
|
36
|
-
],
|
|
25
|
+
strategies: [apiKeyStrategy({ usage: true }), jwtStrategy({ sessions: true })],
|
|
37
26
|
});
|
|
38
27
|
```
|
|
39
28
|
|
|
29
|
+
This example assumes the application container already provides `JwtService`,
|
|
30
|
+
the user and credential repositories, `JwtSessionRepository`, and
|
|
31
|
+
`ApiKeyUsageRecorder`. `AuthDatabaseModule` supplies the repository-backed
|
|
32
|
+
dependencies; applications using `@kurdel/auth` alone must register their own
|
|
33
|
+
implementations. Omit `sessions` or `usage` when those optional services are
|
|
34
|
+
not needed.
|
|
35
|
+
|
|
40
36
|
`AuthModule` registers the authentication middleware automatically. Strategy
|
|
41
|
-
names are
|
|
37
|
+
names are referenced by route metadata. `apiKeyStrategy()` uses `x-api-key` by
|
|
38
|
+
default; `usage: true` resolves the registered usage recorder. `jwtStrategy()`
|
|
39
|
+
uses the standard Bearer header; `sessions: true` resolves the registered JWT
|
|
40
|
+
session repository. Both options also accept explicit repository instances for
|
|
41
|
+
custom integrations. Applications can still register custom names and manual
|
|
42
|
+
strategy instances or factories through `AuthStrategyProvider`.
|
|
42
43
|
|
|
43
44
|
## Protect routes
|
|
44
45
|
|
|
@@ -226,18 +227,21 @@ the repository credential ID when one exists. The JWT strategy exposes
|
|
|
226
227
|
`credential.type` as `jwt`, uses the `jti` claim as its optional ID, and places
|
|
227
228
|
the verified payload in `claims`.
|
|
228
229
|
|
|
229
|
-
To make JWTs revocable before their cryptographic expiration,
|
|
230
|
-
`JwtSessionRepository
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
To make JWTs revocable before their cryptographic expiration, enable a
|
|
231
|
+
registered `JwtSessionRepository`. Session-backed JWTs must contain a `jti`;
|
|
232
|
+
authentication then verifies that the referenced session exists, belongs to
|
|
233
|
+
the token subject, has not been revoked, and has not expired:
|
|
233
234
|
|
|
234
235
|
```ts
|
|
235
|
-
|
|
236
|
-
sessions: jwtSessions,
|
|
237
|
-
});
|
|
236
|
+
jwtStrategy({ sessions: true });
|
|
238
237
|
```
|
|
239
238
|
|
|
240
|
-
|
|
239
|
+
An explicit repository instance may be supplied instead of `true`. Without
|
|
240
|
+
`sessions`, JWT verification remains stateless and backward compatible.
|
|
241
|
+
Database-backed applications can pair short-lived access JWTs with rotating
|
|
242
|
+
opaque refresh tokens through `DatabaseJwtSessionService` from
|
|
243
|
+
`@kurdel/auth-db`. Refresh tokens are not JWTs and should never be placed in
|
|
244
|
+
authorization headers or persisted in plaintext by an application.
|
|
241
245
|
|
|
242
246
|
## User and credential repositories
|
|
243
247
|
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import type { AuthCredential, AuthUser } from '@kurdel/common';
|
|
2
2
|
export type { AuthContext, AuthCredential } from '@kurdel/common';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* ## AuthenticationResult
|
|
5
|
+
*
|
|
6
|
+
* Result returned by an authentication strategy after successful
|
|
7
|
+
* credential validation.
|
|
8
|
+
*
|
|
9
|
+
* The authentication middleware enriches this result with the strategy
|
|
10
|
+
* name before exposing it through `ctx.auth`.
|
|
11
|
+
*/
|
|
4
12
|
export interface AuthenticationResult<TUser extends AuthUser = AuthUser> {
|
|
13
|
+
/** Authenticated application user. */
|
|
5
14
|
user: TUser;
|
|
15
|
+
/** Metadata describing the authenticated credential. */
|
|
6
16
|
credential?: AuthCredential;
|
|
17
|
+
/**
|
|
18
|
+
* Optional authentication claims produced by the strategy.
|
|
19
|
+
*
|
|
20
|
+
* For example, JWT strategies may expose verified token claims.
|
|
21
|
+
*/
|
|
7
22
|
claims?: Record<string, unknown>;
|
|
8
23
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { Container } from '@kurdel/ioc';
|
|
2
2
|
import type { AuthEventSink } from './auth-event.js';
|
|
3
|
+
/**
|
|
4
|
+
* ## AuthEventSinkProvider
|
|
5
|
+
*
|
|
6
|
+
* Describes how an authentication event sink is supplied to
|
|
7
|
+
* {@link AuthModule}.
|
|
8
|
+
*
|
|
9
|
+
* Applications may provide either:
|
|
10
|
+
* - an existing event sink instance
|
|
11
|
+
* - a factory that resolves the sink from the IoC container
|
|
12
|
+
*/
|
|
3
13
|
export type AuthEventSinkProvider = {
|
|
4
14
|
use: AuthEventSink;
|
|
5
15
|
} | {
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import type { AuthCredential, AuthUser } from '@kurdel/common';
|
|
2
|
+
/**
|
|
3
|
+
* Common properties shared by every authentication lifecycle event.
|
|
4
|
+
*/
|
|
2
5
|
type AuthEventBase = {
|
|
6
|
+
/** Time at which the event occurred. */
|
|
3
7
|
occurredAt: Date;
|
|
8
|
+
/** Authenticated user, when known. */
|
|
4
9
|
userId?: AuthUser['id'];
|
|
10
|
+
/** Credential involved in the event, when applicable. */
|
|
5
11
|
credential?: AuthCredential;
|
|
6
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* ## AuthEvent
|
|
15
|
+
*
|
|
16
|
+
* Represents a sanitized authentication or authorization lifecycle event.
|
|
17
|
+
*
|
|
18
|
+
* Events intentionally exclude secrets and raw credentials so they can be
|
|
19
|
+
* safely persisted, logged, or forwarded to external audit systems.
|
|
20
|
+
*/
|
|
7
21
|
export type AuthEvent = (AuthEventBase & {
|
|
8
22
|
type: 'authentication.succeeded';
|
|
9
23
|
strategy: string;
|
|
@@ -20,13 +34,30 @@ export type AuthEvent = (AuthEventBase & {
|
|
|
20
34
|
}) | (AuthEventBase & {
|
|
21
35
|
type: 'api-key.issued' | 'api-key.revoked';
|
|
22
36
|
}) | (AuthEventBase & {
|
|
23
|
-
type: 'jwt-session.created' | 'jwt-session.revoked';
|
|
37
|
+
type: 'jwt-session.created' | 'jwt-session.refreshed' | 'jwt-session.revoked';
|
|
24
38
|
});
|
|
25
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* ## AuthEventSink
|
|
41
|
+
*
|
|
42
|
+
* Receives sanitized authentication lifecycle events.
|
|
43
|
+
*
|
|
44
|
+
* Implementations may persist events, write them to logs, publish them to
|
|
45
|
+
* message brokers, or forward them to external audit systems.
|
|
46
|
+
*/
|
|
26
47
|
export interface AuthEventSink {
|
|
48
|
+
/**
|
|
49
|
+
* Reports an authentication lifecycle event.
|
|
50
|
+
*/
|
|
27
51
|
report(event: AuthEvent): Promise<void> | void;
|
|
28
52
|
}
|
|
29
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* ## NoopAuthEventSink
|
|
55
|
+
*
|
|
56
|
+
* Default {@link AuthEventSink} implementation that silently ignores all
|
|
57
|
+
* reported events.
|
|
58
|
+
*
|
|
59
|
+
* Used when an application does not configure audit reporting.
|
|
60
|
+
*/
|
|
30
61
|
export declare class NoopAuthEventSink implements AuthEventSink {
|
|
31
62
|
report(_event: AuthEvent): void;
|
|
32
63
|
}
|
package/lib/domain/auth-event.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* ## NoopAuthEventSink
|
|
3
|
+
*
|
|
4
|
+
* Default {@link AuthEventSink} implementation that silently ignores all
|
|
5
|
+
* reported events.
|
|
6
|
+
*
|
|
7
|
+
* Used when an application does not configure audit reporting.
|
|
8
|
+
*/
|
|
2
9
|
export class NoopAuthEventSink {
|
|
3
10
|
report(_event) { }
|
|
4
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-event.js","sourceRoot":"","sources":["../../src/domain/auth-event.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-event.js","sourceRoot":"","sources":["../../src/domain/auth-event.ts"],"names":[],"mappings":"AA+DA;;;;;;;GAOG;AACH,MAAM,OAAO,iBAAiB;IAC5B,MAAM,CAAC,MAAiB,IAAS,CAAC;CACnC"}
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import type { Container } from '@kurdel/ioc';
|
|
2
2
|
import type { AuthStrategy } from '../domain/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* ## AuthStrategyProvider
|
|
5
|
+
*
|
|
6
|
+
* Describes how an authentication strategy is supplied to
|
|
7
|
+
* {@link AuthModule}.
|
|
8
|
+
*
|
|
9
|
+
* Applications may provide either:
|
|
10
|
+
* - an existing strategy instance
|
|
11
|
+
* - a factory that resolves the strategy from the IoC container
|
|
12
|
+
*
|
|
13
|
+
* Each strategy is registered under the specified name and can later be
|
|
14
|
+
* referenced from route authentication metadata.
|
|
15
|
+
*/
|
|
3
16
|
export type AuthStrategyProvider = {
|
|
17
|
+
/** Strategy name used by route metadata. */
|
|
4
18
|
name: string;
|
|
19
|
+
/** Existing strategy instance. */
|
|
5
20
|
use: AuthStrategy;
|
|
6
21
|
} | {
|
|
22
|
+
/** Strategy name used by route metadata. */
|
|
7
23
|
name: string;
|
|
24
|
+
/** Factory that resolves the strategy from the IoC container. */
|
|
8
25
|
useFactory: (c: Container) => AuthStrategy;
|
|
9
26
|
};
|
|
@@ -3,15 +3,25 @@ import type { AuthenticationResult } from './auth-context.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* ## AuthStrategy
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* Each strategy decides how to extract and validate credentials
|
|
8
|
-
* from the incoming HTTP request.
|
|
6
|
+
* Contract implemented by every authentication strategy.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* A strategy is responsible for extracting credentials from an incoming
|
|
9
|
+
* HTTP request, validating them, and resolving the authenticated
|
|
10
|
+
* application identity.
|
|
11
|
+
*
|
|
12
|
+
* Guarantees:
|
|
13
|
+
* - returns an {@link AuthenticationResult} after successful authentication
|
|
14
|
+
* - returns `null` when authentication cannot be established
|
|
15
|
+
* - does not throw for invalid or missing credentials
|
|
16
|
+
*
|
|
17
|
+
* Strategies remain independent of storage. User identities and
|
|
18
|
+
* credential metadata are resolved through repository contracts.
|
|
14
19
|
*/
|
|
15
20
|
export interface AuthStrategy {
|
|
21
|
+
/**
|
|
22
|
+
* Attempts to authenticate the incoming request.
|
|
23
|
+
*
|
|
24
|
+
* Returns `null` when the request does not contain valid credentials.
|
|
25
|
+
*/
|
|
16
26
|
authenticate(req: HttpRequest): Promise<AuthenticationResult | null>;
|
|
17
27
|
}
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { type AuthorizationPolicy } from './authorization-policy.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Combines multiple authorization policies using logical AND.
|
|
4
|
+
*
|
|
5
|
+
* Access is granted only when every nested policy grants access.
|
|
6
|
+
* Evaluation stops at the first denial.
|
|
7
|
+
*/
|
|
3
8
|
export declare function allOf(...policies: AuthorizationPolicy[]): AuthorizationPolicy;
|
|
4
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Combines multiple authorization policies using logical OR.
|
|
11
|
+
*
|
|
12
|
+
* Access is granted when any nested policy grants access.
|
|
13
|
+
* When every policy denies access, the most informative denial
|
|
14
|
+
* reason is preserved.
|
|
15
|
+
*/
|
|
5
16
|
export declare function anyOf(...policies: AuthorizationPolicy[]): AuthorizationPolicy;
|
|
6
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Inverts the result of an authorization policy.
|
|
19
|
+
*
|
|
20
|
+
* When the wrapped policy grants access, the returned policy denies
|
|
21
|
+
* access. An optional denial reason may be supplied for the inverted
|
|
22
|
+
* decision.
|
|
23
|
+
*/
|
|
7
24
|
export declare function not(policy: AuthorizationPolicy, reason?: string): AuthorizationPolicy;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { authorizationDecision, } from './authorization-policy.js';
|
|
2
|
+
/**
|
|
3
|
+
* Evaluates a policy and normalizes its result into an
|
|
4
|
+
* {@link AuthorizationDecision}.
|
|
5
|
+
*/
|
|
2
6
|
async function evaluate(policy, auth, ctx) {
|
|
3
7
|
return authorizationDecision(await policy.authorize(auth, ctx));
|
|
4
8
|
}
|
|
5
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Combines multiple authorization policies using logical AND.
|
|
11
|
+
*
|
|
12
|
+
* Access is granted only when every nested policy grants access.
|
|
13
|
+
* Evaluation stops at the first denial.
|
|
14
|
+
*/
|
|
6
15
|
export function allOf(...policies) {
|
|
7
16
|
return {
|
|
8
17
|
async authorize(auth, ctx) {
|
|
@@ -15,7 +24,13 @@ export function allOf(...policies) {
|
|
|
15
24
|
},
|
|
16
25
|
};
|
|
17
26
|
}
|
|
18
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Combines multiple authorization policies using logical OR.
|
|
29
|
+
*
|
|
30
|
+
* Access is granted when any nested policy grants access.
|
|
31
|
+
* When every policy denies access, the most informative denial
|
|
32
|
+
* reason is preserved.
|
|
33
|
+
*/
|
|
19
34
|
export function anyOf(...policies) {
|
|
20
35
|
return {
|
|
21
36
|
async authorize(auth, ctx) {
|
|
@@ -31,7 +46,13 @@ export function anyOf(...policies) {
|
|
|
31
46
|
},
|
|
32
47
|
};
|
|
33
48
|
}
|
|
34
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Inverts the result of an authorization policy.
|
|
51
|
+
*
|
|
52
|
+
* When the wrapped policy grants access, the returned policy denies
|
|
53
|
+
* access. An optional denial reason may be supplied for the inverted
|
|
54
|
+
* decision.
|
|
55
|
+
*/
|
|
35
56
|
export function not(policy, reason) {
|
|
36
57
|
return {
|
|
37
58
|
async authorize(auth, ctx) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-policy-composition.js","sourceRoot":"","sources":["../../src/domain/authorization-policy-composition.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,qBAAqB,GAGtB,MAAM,2BAA2B,CAAC;AAEnC,KAAK,UAAU,QAAQ,CACrB,MAA2B,EAC3B,IAA2B,EAC3B,GAAgB;IAEhB,OAAO,qBAAqB,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAClE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"authorization-policy-composition.js","sourceRoot":"","sources":["../../src/domain/authorization-policy-composition.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,qBAAqB,GAGtB,MAAM,2BAA2B,CAAC;AAEnC;;;GAGG;AACH,KAAK,UAAU,QAAQ,CACrB,MAA2B,EAC3B,IAA2B,EAC3B,GAAgB;IAEhB,OAAO,qBAAqB,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,QAA+B;IACtD,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG;YACvB,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;gBACnD,IAAI,CAAC,QAAQ,CAAC,OAAO;oBAAE,OAAO,QAAQ,CAAC;YACzC,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,QAA+B;IACtD,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG;YACvB,IAAI,MAAM,GAA0B,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAEvD,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;gBAEnD,IAAI,QAAQ,CAAC,OAAO;oBAAE,OAAO,QAAQ,CAAC;gBACtC,IAAI,QAAQ,CAAC,MAAM;oBAAE,MAAM,GAAG,QAAQ,CAAC;YACzC,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CACjB,MAA2B,EAC3B,MAAe;IAEf,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG;YACvB,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YAEnD,OAAO,QAAQ,CAAC,OAAO;gBACrB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;gBACnD,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import type { Container } from '@kurdel/ioc';
|
|
2
2
|
import type { AuthorizationPolicy } from './authorization-policy.js';
|
|
3
|
+
/**
|
|
4
|
+
* ## AuthorizationPolicyProvider
|
|
5
|
+
*
|
|
6
|
+
* Describes how an authorization policy is supplied to
|
|
7
|
+
* {@link AuthModule}.
|
|
8
|
+
*
|
|
9
|
+
* Applications may provide either:
|
|
10
|
+
* - an existing policy instance
|
|
11
|
+
* - a factory that resolves the policy from the IoC container
|
|
12
|
+
*
|
|
13
|
+
* Each policy is registered under the specified name and can later be
|
|
14
|
+
* referenced from route authorization metadata.
|
|
15
|
+
*/
|
|
3
16
|
export type AuthorizationPolicyProvider = {
|
|
17
|
+
/** Policy name used by route metadata. */
|
|
4
18
|
name: string;
|
|
19
|
+
/** Existing policy instance. */
|
|
5
20
|
use: AuthorizationPolicy;
|
|
6
21
|
} | {
|
|
22
|
+
/** Policy name used by route metadata. */
|
|
7
23
|
name: string;
|
|
24
|
+
/** Factory that resolves the policy from the IoC container. */
|
|
8
25
|
useFactory: (container: Container) => AuthorizationPolicy;
|
|
9
26
|
};
|
|
@@ -1,14 +1,50 @@
|
|
|
1
1
|
import type { AuthContext } from '@kurdel/common';
|
|
2
2
|
import type { HttpContext } from '@kurdel/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* ## AuthorizationDecision
|
|
5
|
+
*
|
|
6
|
+
* Result of an authorization policy evaluation.
|
|
7
|
+
*/
|
|
3
8
|
export type AuthorizationDecision = {
|
|
9
|
+
/** Indicates whether access is granted. */
|
|
4
10
|
allowed: boolean;
|
|
5
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Safe, non-sensitive reason code suitable for audit events.
|
|
13
|
+
*
|
|
14
|
+
* This value is intended for diagnostics and should not expose
|
|
15
|
+
* confidential application details.
|
|
16
|
+
*/
|
|
6
17
|
reason?: string;
|
|
7
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Result returned by an authorization policy.
|
|
21
|
+
*
|
|
22
|
+
* Policies may return either a simple boolean or a richer
|
|
23
|
+
* {@link AuthorizationDecision}.
|
|
24
|
+
*/
|
|
8
25
|
export type AuthorizationPolicyResult = boolean | AuthorizationDecision;
|
|
9
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* ## AuthorizationPolicy
|
|
28
|
+
*
|
|
29
|
+
* Contract implemented by application authorization policies.
|
|
30
|
+
*
|
|
31
|
+
* Policies evaluate an authenticated request and decide whether it
|
|
32
|
+
* should be allowed to proceed.
|
|
33
|
+
*
|
|
34
|
+
* Policies should:
|
|
35
|
+
* - remain deterministic
|
|
36
|
+
* - avoid observable side effects
|
|
37
|
+
* - return diagnostic reasons only when they are safe to expose
|
|
38
|
+
*/
|
|
10
39
|
export interface AuthorizationPolicy {
|
|
40
|
+
/**
|
|
41
|
+
* Evaluates whether the authenticated request is authorized.
|
|
42
|
+
*/
|
|
11
43
|
authorize(auth: Readonly<AuthContext>, ctx: HttpContext): AuthorizationPolicyResult | Promise<AuthorizationPolicyResult>;
|
|
12
44
|
}
|
|
13
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Normalizes a policy result into an {@link AuthorizationDecision}.
|
|
47
|
+
*
|
|
48
|
+
* Boolean results are converted into the equivalent decision object.
|
|
49
|
+
*/
|
|
14
50
|
export declare function authorizationDecision(result: AuthorizationPolicyResult): AuthorizationDecision;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes a policy result into an {@link AuthorizationDecision}.
|
|
3
|
+
*
|
|
4
|
+
* Boolean results are converted into the equivalent decision object.
|
|
5
|
+
*/
|
|
2
6
|
export function authorizationDecision(result) {
|
|
3
|
-
return typeof result === 'boolean'
|
|
7
|
+
return typeof result === 'boolean'
|
|
8
|
+
? { allowed: result }
|
|
9
|
+
: result;
|
|
4
10
|
}
|
|
5
11
|
//# sourceMappingURL=authorization-policy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-policy.js","sourceRoot":"","sources":["../../src/domain/authorization-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authorization-policy.js","sourceRoot":"","sources":["../../src/domain/authorization-policy.ts"],"names":[],"mappings":"AAsDA;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAiC;IAEjC,OAAO,OAAO,MAAM,KAAK,SAAS;QAChC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE;QACrB,CAAC,CAAC,MAAM,CAAC;AACb,CAAC"}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { AuthUser } from '@kurdel/common';
|
|
2
2
|
import type { AuthorizationPolicy } from './authorization-policy.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Determines whether a user has a resolved permission.
|
|
5
|
+
*
|
|
6
|
+
* Permissions are expected to be precomputed during authentication and
|
|
7
|
+
* attached to the authenticated user.
|
|
8
|
+
*/
|
|
4
9
|
export declare function hasPermission(user: Readonly<AuthUser>, permission: string): boolean;
|
|
5
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Creates an authorization policy that requires a specific permission.
|
|
12
|
+
*
|
|
13
|
+
* Access is granted only when the authenticated user has the specified
|
|
14
|
+
* resolved permission.
|
|
15
|
+
*/
|
|
6
16
|
export declare function permissionPolicy(permission: string): AuthorizationPolicy;
|
package/lib/domain/permission.js
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Determines whether a user has a resolved permission.
|
|
3
|
+
*
|
|
4
|
+
* Permissions are expected to be precomputed during authentication and
|
|
5
|
+
* attached to the authenticated user.
|
|
6
|
+
*/
|
|
2
7
|
export function hasPermission(user, permission) {
|
|
3
8
|
return user.permissions?.includes(permission) ?? false;
|
|
4
9
|
}
|
|
5
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Creates an authorization policy that requires a specific permission.
|
|
12
|
+
*
|
|
13
|
+
* Access is granted only when the authenticated user has the specified
|
|
14
|
+
* resolved permission.
|
|
15
|
+
*/
|
|
6
16
|
export function permissionPolicy(permission) {
|
|
7
17
|
return {
|
|
8
18
|
authorize: auth => hasPermission(auth.user, permission)
|
|
9
19
|
? { allowed: true }
|
|
10
|
-
: {
|
|
20
|
+
: {
|
|
21
|
+
allowed: false,
|
|
22
|
+
reason: `missing-permission:${permission}`,
|
|
23
|
+
},
|
|
11
24
|
};
|
|
12
25
|
}
|
|
13
26
|
//# sourceMappingURL=permission.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/domain/permission.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/domain/permission.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAwB,EACxB,UAAkB;IAElB,OAAO,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAkB;IAElB,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,EAAE,CAChB,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;YAClC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;YACnB,CAAC,CAAC;gBACE,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,sBAAsB,UAAU,EAAE;aAC3C;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { ApiKeyCredential, ApiKeyRepository } from '../../repositories/index.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* ## InMemoryApiKeyRepository
|
|
4
|
+
*
|
|
5
|
+
* In-memory implementation of {@link ApiKeyRepository}.
|
|
6
|
+
*
|
|
7
|
+
* Intended for tests, examples, and small applications where
|
|
8
|
+
* credential data is managed directly in memory.
|
|
9
|
+
*/
|
|
3
10
|
export declare class InMemoryApiKeyRepository implements ApiKeyRepository {
|
|
4
11
|
private readonly keys;
|
|
5
12
|
constructor(keys: Record<string, ApiKeyCredential>);
|
|
13
|
+
/**
|
|
14
|
+
* Resolves the credential associated with an API key.
|
|
15
|
+
*/
|
|
6
16
|
findByKey(key: string): ApiKeyCredential | null;
|
|
7
17
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* ## InMemoryApiKeyRepository
|
|
3
|
+
*
|
|
4
|
+
* In-memory implementation of {@link ApiKeyRepository}.
|
|
5
|
+
*
|
|
6
|
+
* Intended for tests, examples, and small applications where
|
|
7
|
+
* credential data is managed directly in memory.
|
|
8
|
+
*/
|
|
2
9
|
export class InMemoryApiKeyRepository {
|
|
3
10
|
constructor(keys) {
|
|
4
11
|
this.keys = keys;
|
|
5
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves the credential associated with an API key.
|
|
15
|
+
*/
|
|
6
16
|
findByKey(key) {
|
|
7
17
|
return this.keys[key] ?? null;
|
|
8
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-api-key-repository.js","sourceRoot":"","sources":["../../../src/infra/in-memory/in-memory-api-key-repository.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"in-memory-api-key-repository.js","sourceRoot":"","sources":["../../../src/infra/in-memory/in-memory-api-key-repository.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,OAAO,wBAAwB;IACnC,YAA6B,IAAsC;QAAtC,SAAI,GAAJ,IAAI,CAAkC;IAAG,CAAC;IAEvE;;OAEG;IACH,SAAS,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import type { AuthUser } from '@kurdel/common';
|
|
2
2
|
import type { AuthUserRepository } from '../../repositories/index.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* ## InMemoryAuthUserRepository
|
|
5
|
+
*
|
|
6
|
+
* In-memory implementation of {@link AuthUserRepository}.
|
|
7
|
+
*
|
|
8
|
+
* Intended for tests, examples, and small applications where user
|
|
9
|
+
* identities are managed directly in memory.
|
|
10
|
+
*/
|
|
4
11
|
export declare class InMemoryAuthUserRepository implements AuthUserRepository {
|
|
5
12
|
private readonly users;
|
|
6
13
|
constructor(users: AuthUser[]);
|
|
14
|
+
/**
|
|
15
|
+
* Resolves the current user by identifier.
|
|
16
|
+
*/
|
|
7
17
|
findById(id: string | number): AuthUser | null;
|
|
8
18
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* ## InMemoryAuthUserRepository
|
|
3
|
+
*
|
|
4
|
+
* In-memory implementation of {@link AuthUserRepository}.
|
|
5
|
+
*
|
|
6
|
+
* Intended for tests, examples, and small applications where user
|
|
7
|
+
* identities are managed directly in memory.
|
|
8
|
+
*/
|
|
2
9
|
export class InMemoryAuthUserRepository {
|
|
3
10
|
constructor(users) {
|
|
4
11
|
this.users = users;
|
|
5
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves the current user by identifier.
|
|
15
|
+
*/
|
|
6
16
|
findById(id) {
|
|
7
17
|
return this.users.find(user => user.id === id) ?? null;
|
|
8
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-auth-user-repository.js","sourceRoot":"","sources":["../../../src/infra/in-memory/in-memory-auth-user-repository.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"in-memory-auth-user-repository.js","sourceRoot":"","sources":["../../../src/infra/in-memory/in-memory-auth-user-repository.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,MAAM,OAAO,0BAA0B;IACrC,YAA6B,KAAiB;QAAjB,UAAK,GAAL,KAAK,CAAY;IAAG,CAAC;IAElD;;OAEG;IACH,QAAQ,CAAC,EAAmB;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;IACzD,CAAC;CACF"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { AuthUser } from '@kurdel/common';
|
|
2
2
|
import type { JwtRepository } from '../../repositories/index.js';
|
|
3
3
|
import { InMemoryAuthUserRepository } from './in-memory-auth-user-repository.js';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated Use {@link InMemoryAuthUserRepository} instead.
|
|
6
|
+
*
|
|
7
|
+
* Compatibility alias for the previous repository name.
|
|
8
|
+
*/
|
|
5
9
|
export declare class InMemoryJwtRepository extends InMemoryAuthUserRepository implements JwtRepository {
|
|
6
10
|
constructor(users: AuthUser[]);
|
|
7
11
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { InMemoryAuthUserRepository } from './in-memory-auth-user-repository.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Use {@link InMemoryAuthUserRepository} instead.
|
|
4
|
+
*
|
|
5
|
+
* Compatibility alias for the previous repository name.
|
|
6
|
+
*/
|
|
3
7
|
export class InMemoryJwtRepository extends InMemoryAuthUserRepository {
|
|
4
8
|
constructor(users) {
|
|
5
9
|
super(users);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-jwt-repository.js","sourceRoot":"","sources":["../../../src/infra/in-memory/in-memory-jwt-repository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF
|
|
1
|
+
{"version":3,"file":"in-memory-jwt-repository.js","sourceRoot":"","sources":["../../../src/infra/in-memory/in-memory-jwt-repository.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF;;;;GAIG;AACH,MAAM,OAAO,qBACX,SAAQ,0BAA0B;IAElC,YAAY,KAAiB;QAC3B,KAAK,CAAC,KAAK,CAAC,CAAC;IACf,CAAC;CACF"}
|