@venizia/ignis-docs 0.2.0 → 0.2.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -1,552 +1,518 @@
1
- # Authentication -- API Reference
1
+ ---
2
+ title: Authentication Reference
3
+ description: Full option tables, binding keys, service class hierarchy, strategy registry, and controller factory for the Authentication component
4
+ difficulty: intermediate
5
+ ---
2
6
 
3
- > Architecture, service hierarchy, strategy registry, JWKS controller, and controller factory. See [Setup & Configuration](./) for initial setup.
7
+ # Authentication Reference
8
+
9
+ Every option, binding key, class, and method the Authentication component exposes. See the [Overview](./) for the guided introduction.
10
+
11
+ **Files:**
12
+
13
+ - [`packages/core/src/components/auth/authenticate/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate) - component, services, strategies, controllers
14
+ - [`packages/core/src/components/auth/models/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/models) - entity column helpers + request schemas
15
+ - [`packages/core/src/components/auth/base/abstract-auth-registry.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/base/abstract-auth-registry.ts) - `AbstractAuthRegistry`
16
+
17
+ ## Import paths
18
+
19
+ ```typescript
20
+ import {
21
+ // Component + registry
22
+ AuthenticateComponent,
23
+ AuthenticateBindingKeys,
24
+ Authentication,
25
+ AuthenticationFieldCodecs,
26
+ AuthenticationModes,
27
+ AuthenticationTokenTypes,
28
+ AuthenticationStrategyRegistry,
29
+
30
+ // JOSE standards + constants
31
+ JOSEStandards,
32
+ JWKSModes,
33
+ JWKSKeyDrivers,
34
+ JWKSKeyFormats,
35
+
36
+ // Strategies
37
+ JWSAuthenticationStrategy,
38
+ JWKSIssuerAuthenticationStrategy,
39
+ JWKSVerifierAuthenticationStrategy,
40
+ BasicAuthenticationStrategy,
41
+
42
+ // Services
43
+ AbstractBearerTokenService,
44
+ JWSTokenService,
45
+ JWKSIssuerTokenService,
46
+ JWKSVerifierTokenService,
47
+ BasicTokenService,
48
+
49
+ // Controllers
50
+ defineAuthController,
51
+ JWKSController,
52
+ authenticate,
53
+
54
+ // Entity column helpers
55
+ extraUserColumns,
56
+ extraRoleColumns,
57
+ extraPermissionColumns,
58
+ extraPolicyDefinitionColumns,
59
+ UserStatuses,
60
+ UserTypes,
61
+ RoleStatuses,
62
+ } from '@venizia/ignis';
63
+
64
+ import type {
65
+ TAuthenticationRestOptions,
66
+ TJWTTokenServiceOptions,
67
+ IJWSTokenServiceOptions,
68
+ IJWKSIssuerOptions,
69
+ IJWKSVerifierOptions,
70
+ TJWKSTokenServiceOptions,
71
+ TBasicTokenServiceOptions,
72
+ IAuthenticateOptions,
73
+ IAuthUser,
74
+ IJWTTokenPayload,
75
+ IPayloadFieldCodec,
76
+ IAuthService,
77
+ IAuthenticationStrategy,
78
+ TDefineAuthControllerOpts,
79
+ TAuthStrategy,
80
+ TAuthMode,
81
+ TGetTokenExpiresFn,
82
+ TJWKSAlgorithm,
83
+ TJWKSKeyDriver,
84
+ TJWKSKeyFormat,
85
+ TJOSEStandard,
86
+ TJWKSMode,
87
+ TPermissionOptions,
88
+ TPermissionCommonColumns,
89
+ TPolicyDefinitionOptions,
90
+ TPolicyDefinitionCommonColumns,
91
+ } from '@venizia/ignis';
92
+ ```
4
93
 
5
94
  ## Architecture
6
95
 
7
96
  ```
8
- ┌──────────────────────────────────────────────────────────────────┐
9
- │ Application │
10
- │ │
11
- │ preConfigure()
12
- │ ├── bind JWT_OPTIONS (TJWTTokenServiceOptions) │
13
- │ │ └── standard: JWS | JWKS │
14
- │ ├── bind BASIC_OPTIONS / REST_OPTIONS │
15
- ├── this.component(AuthenticateComponent) │
16
- └── AuthenticationStrategyRegistry.register() manual │
17
- └────────────────────────────┬─────────────────────────────────────┘
18
-
19
-
20
- ┌──────────────────────────────────────────────────────────────────┐
21
- │ AuthenticateComponent.binding()
22
- │ │
23
- │ 1. Read JWT_OPTIONS, BASIC_OPTIONS, REST_OPTIONS │
24
- │ 2. Switch on jwtOptions.standard:
25
- │ ├── JWS → defineJWSAuth() → JWSTokenService │
26
- │ └── JWKS defineJWKSAuth() → switch on mode: │
27
- │ ├── issuer → JWKSIssuerTokenService │
28
- │ │ + JWKSController (/certs)
29
- └── verifier JWKSVerifierTokenService │
30
- │ 3. defineBasicAuth() → BasicTokenService │
31
- │ 4. defineControllers() → AuthController (factory-built) │
32
- │ 5. defineOAuth2() → stub (not yet implemented) │
33
- └────────────────────────────┬─────────────────────────────────────┘
34
-
35
- ┌─────────────────────┼──────────────────────┐
36
- ▼ ▼ ▼
37
- ┌──────────────┐ ┌────────────────┐ ┌──────────────────┐
38
- │ Bearer Token │ │ BasicToken │ │ AuthController │
39
- │ Services │ │ Service │ │ (factory-built) │
40
- │ (see below) │ └───────┬────────┘ └────────┬─────────┘
41
- └──────┬───────┘ │ │
42
- │ ▼ ▼
43
- │ ┌──────────────┐ ┌──────────────────┐
44
- │ │ Basic │ │ /sign-in │
45
- │ │ Strategy │ │ /sign-up │
46
- │ └──────────────┘ │ /change-password │
47
- │ │ /who-am-i │
48
- ▼ └──────────────────┘
49
- ┌──────────────────────────────────────────────────────────┐
50
- │ Bearer Token Service Hierarchy │
51
- │ │
52
- │ AbstractBearerTokenService (base) │
53
- │ ├── extractCredentials() │
54
- │ ├── verify() → doVerify() │
55
- │ ├── generate() → getSigner() + getSigningKey() │
56
- │ ├── encryptPayload() / decryptPayload() (optional) │
57
- │ │ │
58
- │ ├── JWSTokenService (symmetric HS256) │
59
- │ │ ├── JWSAuthenticationStrategy │
60
- │ │ └── sign with shared secret │
61
- │ │ │
62
- │ └── AbstractJWKSTokenService (lazy-init + retry) │
63
- │ ├── ensureInitialized() / initialize() │
64
- │ │ │
65
- │ ├── JWKSIssuerTokenService (asymmetric) │
66
- │ │ ├── JWKSIssuerAuthenticationStrategy │
67
- │ │ ├── sign with private key │
68
- │ │ ├── verify with public key │
69
- │ │ └── getJWKS() / getJWKSAsync() │
70
- │ │ │
71
- │ └── JWKSVerifierTokenService (remote verify) │
72
- │ ├── JWKSVerifierAuthenticationStrategy │
73
- │ └── verify via createRemoteJWKSet() │
74
- └──────────────────────────────────────────────────────────┘
97
+ Application.preConfigure()
98
+ ├── bind JWT_OPTIONS (TJWTTokenServiceOptions, discriminated on `standard`)
99
+ ├── bind BASIC_OPTIONS / REST_OPTIONS
100
+ ├── this.component(AuthenticateComponent)
101
+ └── AuthenticationStrategyRegistry.register() -- manual, after the component
102
+
103
+ AuthenticateComponent.binding()
104
+ ├── switch on jwtOptions.standard
105
+ ├── JWS -> defineJWSAuth() -> registers JWSTokenService
106
+ │ └── JWKS -> defineJWKSAuth() -> switch on mode
107
+ ├── issuer -> JWKSIssuerTokenService + JWKSController (/certs)
108
+ │ └── verifier -> JWKSVerifierTokenService
109
+ ├── defineBasicAuth() -> registers BasicTokenService (if basicOptions bound)
110
+ ├── defineControllers() -> registers AuthController (if useAuthController: true)
111
+ └── defineOAuth2() -> stub, not implemented
112
+
113
+ Bearer token service hierarchy:
114
+ AbstractBearerTokenService (extractCredentials, verify, generate, encryptPayload/decryptPayload)
115
+ ├── JWSTokenService (symmetric HS256)
116
+ └── AbstractJWKSTokenService (lazy ensureInitialized() + retry-on-failure)
117
+ ├── JWKSIssuerTokenService (sign + verify + getJWKS/getJWKSAsync)
118
+ └── JWKSVerifierTokenService (verify only, via createRemoteJWKSet())
75
119
  ```
76
120
 
77
- ### Tech Stack
121
+ **Tech stack**
78
122
 
79
123
  | Technology | Purpose |
80
124
  |------------|---------|
81
- | **`jose`** | JWT signing (`SignJWT`), verification (`jwtVerify`), JWKS (`createRemoteJWKSet`, `exportJWK`, `importPKCS8`, `importSPKI`, `importJWK`), and type definitions |
82
- | **`@venizia/ignis-helpers`** | `AES` utility for payload encryption, `BaseHelper`/`BaseService` base classes, `getError` for error creation, `HTTP` result codes |
83
- | **Hono middleware** | Route-level authentication integration via `createMiddleware` from `hono/factory` |
84
- | **`node:fs/promises`** | Async file reading for JWKS key files |
85
- | **Drizzle ORM** | Database access for user lookup (in your implementation) |
125
+ | `jose` | JWT signing (`SignJWT`), verification (`jwtVerify`), JWKS (`createRemoteJWKSet`, `exportJWK`, `importPKCS8`, `importSPKI`, `importJWK`) |
126
+ | `@venizia/ignis-helpers` | `AES` payload encryption, `BaseHelper`/`BaseService`, `getError`, `HTTP` result codes |
127
+ | Hono middleware | Route-level integration via `createMiddleware` from `hono/factory` |
128
+ | `node:fs/promises` | Async key file reads for JWKS |
86
129
 
87
- ## Component Methods
130
+ ## Component methods
88
131
 
89
- The `AuthenticateComponent` uses five methods during its `binding()` lifecycle (four private, one public):
132
+ `AuthenticateComponent.binding()` runs four private configuration methods and one public stub:
90
133
 
91
134
  | Method | Purpose |
92
135
  |--------|---------|
93
- | `defineJWSAuth(opts)` | Validates JWS secrets (rejects falsy values and `'unknown_secret'`), validates `getTokenExpiresFn`, binds `IJWSTokenServiceOptions` to `JWT_OPTIONS`, registers `JWSTokenService`. |
94
- | `defineJWKSAuth(opts)` | Switches on `mode`: **Issuer** - validates keys, format, kid, getTokenExpiresFn; binds to `JWKS_OPTIONS`; registers `JWKSIssuerTokenService` + `JWKSController`. **Verifier** - validates jwksUrl; binds to `JWKS_OPTIONS`; registers `JWKSVerifierTokenService`. |
95
- | `defineBasicAuth(opts)` | Validates `verifyCredentials` callback presence, binds `BasicTokenService` as a service. Logs debug if skipped. |
96
- | `defineControllers(opts)` | Requires `jwtOptions` when `useAuthController: true`. Calls `defineAuthController()` factory and registers the generated controller. |
97
- | `defineOAuth2()` | **Public** stub method -- not yet implemented. Called during `binding()` but performs no action. |
136
+ | `defineJWSAuth(opts)` | Validates `jwtSecret` and `getTokenExpiresFn`, binds `IJWSTokenServiceOptions` to `JWT_OPTIONS`, registers `JWSTokenService` |
137
+ | `defineJWKSAuth(opts)` | Switches on `mode`. Issuer: validates keys/format/kid/getTokenExpiresFn, binds to `JWKS_OPTIONS`, registers `JWKSIssuerTokenService` + `JWKSController`. Verifier: validates `jwksUrl`, binds to `JWKS_OPTIONS`, registers `JWKSVerifierTokenService` |
138
+ | `defineBasicAuth(opts)` | Validates `verifyCredentials` presence, registers `BasicTokenService`. Skips (debug log) if `basicOptions` not bound |
139
+ | `defineControllers(opts)` | Requires `jwtOptions` when `useAuthController: true`. Calls `defineAuthController()` and registers the generated controller |
140
+ | `defineOAuth2()` | Public stub, called during `binding()`, performs no action - not yet implemented |
98
141
 
99
142
  > [!NOTE]
100
- > The component reads `TJWTTokenServiceOptions` (the discriminated union) from `AuthenticateBindingKeys.JWT_OPTIONS`, then re-binds just the inner options (`IJWSTokenServiceOptions` or `IJWKSIssuerOptions`/`IJWKSVerifierOptions`) to the appropriate binding key for the service to consume via `@inject`.
143
+ > The component reads the discriminated union from `JWT_OPTIONS`, then re-binds just the inner options object (`IJWSTokenServiceOptions` or `IJWKSIssuerOptions`/`IJWKSVerifierOptions`) to `JWKS_OPTIONS` or back to `JWT_OPTIONS`, so the token service can resolve it via a plain `@inject`.
101
144
 
102
- ## Strategy Registry
145
+ ## Binding keys
103
146
 
104
- `AuthenticationStrategyRegistry` is a **singleton** that manages all registered strategies. It extends `AbstractAuthRegistry<IAuthenticationStrategy>` (not generic on `Env`).
147
+ | Constant | Key string | Type | Required | Default |
148
+ |----------|-----------|------|----------|---------|
149
+ | `AuthenticateBindingKeys.REST_OPTIONS` | `@app/authenticate/rest-options` | `TAuthenticationRestOptions` | No | <code v-pre>{ useAuthController: false }</code> |
150
+ | `AuthenticateBindingKeys.JWT_OPTIONS` | `@app/authenticate/jwt-options` | `TJWTTokenServiceOptions` | Conditional | -- |
151
+ | `AuthenticateBindingKeys.JWKS_OPTIONS` | `@app/authenticate/jwks-options` | `IJWKSIssuerOptions \| IJWKSVerifierOptions` | Internal | Bound by the component from `JWT_OPTIONS` |
152
+ | `AuthenticateBindingKeys.BASIC_OPTIONS` | `@app/authenticate/basic-options` | `TBasicTokenServiceOptions` | Conditional | -- |
105
153
 
106
- ### API
154
+ > [!IMPORTANT]
155
+ > At least one of `JWT_OPTIONS` or `BASIC_OPTIONS` must be bound, or `AuthenticateComponent.binding()` throws.
107
156
 
108
- | Method | Signature | Returns | Description |
109
- |--------|-----------|---------|-------------|
110
- | `getInstance()` | `static` | `AuthenticationStrategyRegistry` | Returns the singleton instance (creates if not exists) |
111
- | `register` | <code v-pre>(opts: { container: Container; strategies: Array&lt;{ name: string; strategy: TClass&lt;IAuthenticationStrategy&gt; }&gt; }) =&gt; this</code> | `this` | Registers strategies as singletons in the container. Returns `this` for chaining. |
112
- | `resolveStrategy` | `(opts: { name: string }) => IAuthenticationStrategy` | `IAuthenticationStrategy` | Resolves a strategy instance from the container by name |
157
+ ## Option interfaces
113
158
 
114
- **Registration:**
115
- ```typescript
116
- AuthenticationStrategyRegistry.getInstance().register({
117
- container: this,
118
- strategies: [
119
- { name: Authentication.STRATEGY_JWT, strategy: JWKSIssuerAuthenticationStrategy },
120
- { name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
121
- ],
122
- });
123
- ```
159
+ ### TJWTTokenServiceOptions
124
160
 
125
- > [!NOTE]
126
- > `register()` returns `this`, enabling method chaining if needed.
127
-
128
- **How it works:**
129
- - Strategies are stored in an internal map and bound to the DI container as singletons
130
- - Binding keys follow the pattern `authentication.strategy.{name}` (e.g., `authentication.strategy.jwt`, `authentication.strategy.basic`)
131
- - The standalone `authenticate()` function creates middleware via `AuthenticationProvider`, which uses the registry to resolve strategies
132
-
133
- **Middleware creation:**
134
-
135
- The `authenticate()` function returns a Hono middleware that:
136
- 1. Checks if `Authentication.SKIP_AUTHENTICATION` is set on context - if true, skips entirely (logs debug)
137
- 2. Checks if `Authentication.CURRENT_USER` is already set on context - if true, skips (already authenticated)
138
- 3. Reads `strategies` and `mode` from the provided options
139
- 4. Executes strategies based on mode (`any` or `all`)
140
- 5. On success, sets `Authentication.CURRENT_USER` and `Authentication.AUDIT_USER_ID` on context
141
- 6. On failure, throws 401 with list of tried strategies
142
-
143
- ```mermaid
144
- sequenceDiagram
145
- participant C as Client
146
- participant MW as Auth Middleware
147
- participant P as AuthenticationProvider
148
- participant R as StrategyRegistry
149
- participant S as Strategy
150
- participant SVC as TokenService
151
-
152
- C->>MW: Request with Authorization header
153
- MW->>P: authenticateFn({ strategies, mode })
154
- P->>P: Check SKIP_AUTHENTICATION
155
- P->>P: Check CURRENT_USER already set
156
- P->>R: resolveStrategy({ name })
157
- R-->>P: strategy instance
158
- P->>S: authenticate(context)
159
- S->>SVC: extractCredentials(context)
160
- SVC-->>S: { type, token }
161
- S->>SVC: verify({ type, token })
162
- SVC-->>S: IJWTTokenPayload
163
- S-->>P: IAuthUser
164
- P->>MW: Set CURRENT_USER + AUDIT_USER_ID
165
- MW->>C: Continue to handler
166
- ```
167
-
168
- ### Standalone `authenticate()` Function
161
+ Discriminated union on `standard`:
169
162
 
170
163
  ```typescript
171
- const authenticationProvider = new AuthenticationProvider();
172
- const authenticateFn = authenticationProvider.value();
164
+ type TJWTTokenServiceOptions =
165
+ | { standard: typeof JOSEStandards.JWS; options: IJWSTokenServiceOptions }
166
+ | { standard: typeof JOSEStandards.JWKS; options: TJWKSTokenServiceOptions };
173
167
 
174
- export const authenticate = (opts: { strategies: string[]; mode?: TAuthMode }) => {
175
- return authenticateFn(opts);
176
- };
168
+ type TJWKSTokenServiceOptions = IJWKSIssuerOptions | IJWKSVerifierOptions; // discriminated on `mode`
177
169
  ```
178
170
 
179
- This is the primary export for creating auth middleware. It creates an `AuthenticationProvider` instance and calls `.value()` to get the middleware factory. The provider uses `AuthenticationStrategyRegistry.getInstance()` internally to resolve strategies.
171
+ ### IJWSTokenServiceOptions
172
+
173
+ | Option | Type | Default | Required | Description |
174
+ |--------|------|---------|----------|-------------|
175
+ | `jwtSecret` | `string` | -- | Yes | Secret for signing and verifying the JWT signature |
176
+ | `getTokenExpiresFn` | `TGetTokenExpiresFn` | -- | Yes | Returns token expiration in seconds |
177
+ | `applicationSecret` | `string` | -- | No | Enables AES payload field encryption when set |
178
+ | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload encryption |
179
+ | `headerAlgorithm` | `string` | `'HS256'` | No | JWT signing algorithm |
180
+ | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom serialize/deserialize per field name |
181
+
182
+ > [!WARNING]
183
+ > `jwtSecret` is mandatory - the component throws if it is missing or equals the placeholder `'unknown_secret'`. `applicationSecret` is optional: when omitted, the JWT payload is standard plaintext; standard fields (`iss`, `sub`, `aud`, `jti`, `nbf`, `exp`, `iat`) are never encrypted either way.
184
+
185
+ ### IJWKSIssuerOptions
186
+
187
+ | Option | Type | Default | Required | Description |
188
+ |--------|------|---------|----------|-------------|
189
+ | `mode` | `typeof JWKSModes.ISSUER` | -- | Yes | Must be `'issuer'` |
190
+ | `algorithm` | `TJWKSAlgorithm` | -- | Yes | `'ES256'`, `'RS256'`, or `'EdDSA'` |
191
+ | `keys.driver` | `TJWKSKeyDriver` | -- | Yes | `'text'` (inline) or `'file'` (path) |
192
+ | `keys.format` | `TJWKSKeyFormat` | -- | Yes | `'pem'` or `'jwk'` |
193
+ | `keys.private` | `string` | -- | Yes | Private key content or file path |
194
+ | `keys.public` | `string` | -- | Yes | Public key content or file path |
195
+ | `kid` | `string` | -- | Yes | Key ID exposed in the JWKS endpoint and JWT header |
196
+ | `getTokenExpiresFn` | `TGetTokenExpiresFn` | -- | Yes | Returns token expiration in seconds |
197
+ | `rest.path` | `string` | `'/certs'` | No | Path of the generated `JWKSController` |
198
+ | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload encryption |
199
+ | `applicationSecret` | `string` | -- | No | Enables AES payload field encryption when set |
200
+ | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom serialize/deserialize per field name |
201
+
202
+ ### IJWKSVerifierOptions
203
+
204
+ | Option | Type | Default | Required | Description |
205
+ |--------|------|---------|----------|-------------|
206
+ | `mode` | `typeof JWKSModes.VERIFIER` | -- | Yes | Must be `'verifier'` |
207
+ | `jwksUrl` | `string` | -- | Yes | URL of the issuer's JWKS endpoint |
208
+ | `cacheTtlMs` | `number` | `43_200_000` (12h) | No | `createRemoteJWKSet` `cacheMaxAge` |
209
+ | `cooldownMs` | `number` | `30_000` (30s) | No | `createRemoteJWKSet` `cooldownDuration` |
210
+ | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload decryption |
211
+ | `applicationSecret` | `string` | -- | No | Must match the issuer's secret to decrypt payloads |
212
+ | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Must match the issuer's codecs to decrypt custom fields |
213
+
214
+ > [!IMPORTANT]
215
+ > `JWKSVerifierTokenService` cannot sign tokens - `getSigner()`, `getSigningKey()`, and `getDefaultTokenExpiresFn()` all throw. Only `verify()` and `extractCredentials()` are functional.
216
+
217
+ ### TBasicTokenServiceOptions
218
+
219
+ | Option | Type | Description |
220
+ |--------|------|-------------|
221
+ | `verifyCredentials` | <code v-pre>(opts: { credentials: { username: string; password: string }; context: TContext }) =&gt; Promise&lt;IAuthUser \| null&gt;</code> | Callback that validates Basic credentials and returns the authenticated user, or `null` |
222
+
223
+ ### TAuthenticationRestOptions
224
+
225
+ Discriminated union on `useAuthController` - when `true`, `controllerOpts` becomes required:
180
226
 
181
- > [!NOTE]
182
- > In `all` mode, the **first** strategy's user payload is used as the identity source - all strategies must succeed but the first one wins for identity. If every strategy passes but the first user payload has no `userId`, the middleware throws a `401` with message `"Failed to identify authenticated user!"`. The `any` mode **discards errors** from each failing strategy (logs at debug level) and only throws after all strategies are exhausted.
183
-
184
- ## Service Class Hierarchy
185
-
186
- ```mermaid
187
- classDiagram
188
- class AbstractBearerTokenService {
189
- <<abstract>>
190
- #aes: AES | null
191
- #applicationSecret: string | null
192
- +extractCredentials(context) credentials
193
- +verify(opts) IJWTTokenPayload
194
- +generate(opts) string
195
- +encryptPayload(payload) Record
196
- +decryptPayload(opts) IJWTTokenPayload
197
- #doVerify(token)* IJWTTokenPayload
198
- +getSigner(opts)* SignJWT
199
- #getSigningKey()* Uint8Array | CryptoKey
200
- #getDefaultTokenExpiresFn()* TGetTokenExpiresFn
201
- }
202
-
203
- class JWSTokenService {
204
- #jwtSecret: Uint8Array
205
- #options: IJWSTokenServiceOptions
206
- }
207
-
208
- class AbstractJWKSTokenService {
209
- <<abstract>>
210
- #initialized: boolean
211
- #initPromise: Promise | null
212
- +ensureInitialized()
213
- #initialize()* void
214
- }
215
-
216
- class JWKSIssuerTokenService {
217
- #privateKey: CryptoKey | null
218
- #publicKey: CryptoKey | null
219
- #jwks: object | null
220
- +getJWKS() keys
221
- +getJWKSAsync() keys
222
- }
223
-
224
- class JWKSVerifierTokenService {
225
- #jwksVerifier: Function | null
226
- +getSigner() never
227
- +getSigningKey() never
228
- }
229
-
230
- AbstractBearerTokenService <|-- JWSTokenService
231
- AbstractBearerTokenService <|-- AbstractJWKSTokenService
232
- AbstractJWKSTokenService <|-- JWKSIssuerTokenService
233
- AbstractJWKSTokenService <|-- JWKSVerifierTokenService
227
+ ```typescript
228
+ type TAuthenticationRestOptions = {} & (
229
+ | { useAuthController?: false | undefined }
230
+ | { useAuthController: true; controllerOpts: TDefineAuthControllerOpts }
231
+ );
234
232
  ```
235
233
 
236
- ## AbstractBearerTokenService
237
-
238
- Base class for all Bearer token services. Extends `BaseService`. Generic on <code v-pre>&lt;E extends Env = Env&gt;</code>.
239
-
240
- **File:** `packages/core/src/components/auth/authenticate/services/bearer/abstract.service.ts`
234
+ ### TDefineAuthControllerOpts
241
235
 
242
- ### Static Fields
236
+ | Option | Type | Default | Description |
237
+ |--------|------|---------|-------------|
238
+ | `restPath` | `string` | `'/auth'` | Base path for the generated controller |
239
+ | `serviceKey` | `string` | -- | DI key for the `IAuthService` implementation (required) |
240
+ | `requireAuthenticatedSignUp` | `boolean` | `false` | Whether `POST /sign-up` requires a valid JWT |
241
+ | `payload.signIn` | `{ request: { schema }, response: { schema } }` | Built-in schemas | Custom Zod schemas for `/sign-in` |
242
+ | `payload.signUp` | `{ request: { schema }, response: { schema } }` | Built-in schemas | Custom Zod schemas for `/sign-up` |
243
+ | `payload.changePassword` | `{ request: { schema? }, response: { schema } }` | Built-in schemas | Custom Zod schemas for `/change-password` |
244
+ | `payload.refreshToken` | `{ response: { schema } }` | `AnyObjectSchema` | Custom response schema for `/token/refresh` |
245
+ | `payload.getUserInformation` | `{ response: { schema } }` | `AnyObjectSchema` | Custom response schema for `/me` and the `who-am-i` `userInformation` field |
243
246
 
244
- | Field | Type | Description |
245
- |-------|------|-------------|
246
- | `JWT_COMMON_FIELDS` | <code v-pre>Set&lt;'iss' \| 'sub' \| 'aud' \| 'jti' \| 'nbf' \| 'exp' \| 'iat'&gt;</code> | Standard JWT fields that are never encrypted |
247
+ ### Route authenticate config
247
248
 
248
- ### Protected Fields
249
+ ```typescript
250
+ type TRouteAuthenticateConfig =
251
+ | { skip: true }
252
+ | { skip?: false; strategies?: TAuthStrategy[]; mode?: TAuthMode };
253
+ ```
249
254
 
250
255
  | Field | Type | Default | Description |
251
256
  |-------|------|---------|-------------|
252
- | `aes` | `AES \| null` | `null` | AES utility instance, configured by `configurePayloadEncryption()` |
253
- | `applicationSecret` | `string \| null` | `null` | AES secret, configured by `configurePayloadEncryption()` |
254
- | `fieldCodecs` | <code v-pre>Map&lt;string, IPayloadFieldCodec&gt;</code> | `new Map()` | Field codec map keyed by field name, configured by `configurePayloadEncryption()` |
255
-
256
- ### Methods
257
+ | `authenticate.strategies` | `TAuthStrategy[]` | -- | Strategy names to try, e.g. `['jwt']`, `['jwt', 'basic']` |
258
+ | `authenticate.mode` | `'any' \| 'all'` | `'any'` | `'any'`: first success wins. `'all'`: every strategy must pass |
259
+ | `authenticate.skip` | `true` | -- | Skips authentication for this route entirely |
257
260
 
258
- | Method | Signature | Description |
259
- |--------|-----------|-------------|
260
- | `configurePayloadEncryption` | <code v-pre>(opts: { aesAlgorithm?: AESAlgorithmType; applicationSecret?: string; fieldCodecs?: IPayloadFieldCodec[] }) =&gt; void</code> | Configures optional AES encryption and field codecs. Codecs are converted to a Map keyed by `codec.key` for O(1) lookup. |
261
- | `extractCredentials` | <code v-pre>(context: TContext&lt;E, string&gt;) =&gt; { type: string; token: string }</code> | Extracts Bearer token from Authorization header |
262
- | `verify` | <code v-pre>(opts: { type: string; token: string }) =&gt; Promise&lt;IJWTTokenPayload&gt;</code> | Template method - calls `doVerify()` |
263
- | `generate` | <code v-pre>(opts: { payload: IJWTTokenPayload; getTokenExpiresFn?: TGetTokenExpiresFn }) =&gt; Promise&lt;string&gt;</code> | Template method - calls `getSigner()` + `getSigningKey()` |
264
- | `serializeField` | <code v-pre>(opts: { key: string; value: any }) =&gt; string</code> | Serializes a single field: codec → `JSON.stringify` fallback |
265
- | `deserializeField` | <code v-pre>(opts: { key: string; value: string }) =&gt; any</code> | Deserializes a single field: codec → `JSON.parse` fallback |
266
- | `encryptPayload` | <code v-pre>(payload: IJWTTokenPayload) =&gt; Record&lt;string, any&gt;</code> | AES-encrypts non-standard JWT fields using `serializeField`. Returns payload unchanged if AES not configured. |
267
- | `decryptPayload` | <code v-pre>(opts: { result: JWTVerifyResult&lt;IJWTTokenPayload&gt; }) =&gt; IJWTTokenPayload</code> | Decrypts AES-encrypted fields using `deserializeField`. Returns payload unchanged if AES not configured. |
261
+ ### IAuthUser / IJWTTokenPayload
268
262
 
269
- ### Abstract Methods (implemented by subclasses)
270
-
271
- | Method | Visibility | Signature | Description |
272
- |--------|------------|-----------|-------------|
273
- | `doVerify` | `protected` | `(token: string) => Promise<IJWTTokenPayload>` | Verify the raw JWT token and return the payload |
274
- | `getSigner` | **`public`** | <code v-pre>(opts: { payload: IJWTTokenPayload; getTokenExpiresFn: TGetTokenExpiresFn }) =&gt; Promise&lt;SignJWT&gt;</code> | Create a `jose.SignJWT` instance with the payload |
275
- | `getSigningKey` | `protected` | `() => ValueOrPromise<Uint8Array \| CryptoKey>` | Return the signing key |
276
- | `getDefaultTokenExpiresFn` | `protected` | `() => TGetTokenExpiresFn` | Return the default token expiry function |
277
-
278
- ## JWSTokenService
279
-
280
- Symmetric JWT (HS256) token service with optional AES-encrypted payloads. Extends `AbstractBearerTokenService`.
263
+ ```typescript
264
+ interface IAuthUser {
265
+ userId: IdType;
266
+ [extra: string | symbol]: any;
267
+ }
281
268
 
282
- **File:** `packages/core/src/components/auth/authenticate/services/bearer/jws.service.ts`
269
+ interface IJWTTokenPayload extends JWTPayload, IAuthUser {
270
+ userId: IdType;
271
+ roles: { id: IdType; identifier: string; priority: number }[];
272
+ clientId?: string;
273
+ provider?: string;
274
+ email?: string;
275
+ name?: string;
276
+ [extra: string | symbol]: any;
277
+ }
278
+ ```
283
279
 
284
- ### JWSAuthenticationStrategy
280
+ > [!TIP]
281
+ > `IAuthUser` is intentionally minimal. Your `IAuthService` can return extra fields (roles, email, provider) - they pass through JWT generation and are available on `Authentication.CURRENT_USER` after authentication.
285
282
 
286
- Extends `BaseHelper` and implements <code v-pre>IAuthenticationStrategy&lt;E&gt;</code>.
283
+ ### IAuthService
287
284
 
288
285
  ```typescript
289
- class JWSAuthenticationStrategy<E extends Env = Env>
290
- extends BaseHelper
291
- implements IAuthenticationStrategy<E>
292
- {
293
- name = Authentication.STRATEGY_JWT;
294
- standard = JOSEStandards.JWS;
295
-
296
- constructor(
297
- @inject({
298
- key: BindingKeys.build({
299
- namespace: BindingNamespaces.SERVICE,
300
- key: JWSTokenService.name,
301
- }),
302
- })
303
- private service: JWSTokenService<E>,
304
- ) { ... }
305
-
306
- authenticate(context: TContext<E, string>): Promise<IAuthUser> {
307
- const token = this.service.extractCredentials(context);
308
- return this.service.verify(token);
309
- }
286
+ interface IAuthService<
287
+ E extends Env = Env,
288
+ SIRQ extends TSignInRequest = TSignInRequest, SIRS = AnyObject,
289
+ SURQ extends TSignUpRequest = TSignUpRequest, SURS = AnyObject,
290
+ CPRQ extends TChangePasswordRequest = TChangePasswordRequest, CPRS = AnyObject,
291
+ UIRQ = AnyObject, UIRS = AnyObject,
292
+ RTRS = AnyObject,
293
+ > {
294
+ signIn(context: TContext<E>, opts: SIRQ): Promise<SIRS>;
295
+ signUp(context: TContext<E>, opts: SURQ): Promise<SURS>;
296
+ changePassword(context: TContext<E>, opts: CPRQ): Promise<CPRS>;
297
+ getUserInformation?(context: TContext<E>, opts: UIRQ): Promise<UIRS>;
298
+ refreshToken?(context: TContext<E>): Promise<RTRS>;
310
299
  }
311
300
  ```
312
301
 
313
- ### Protected Fields
302
+ > [!NOTE]
303
+ > `getUserInformation` and `refreshToken` are optional. The generated auth controller returns `501` for `/me`, `/token/refresh`, or `?withUserInformation=true` on `/who-am-i` if the bound service doesn't implement the corresponding method.
314
304
 
315
- | Field | Type | Description |
316
- |-------|------|-------------|
317
- | `jwtSecret` | `Uint8Array` | Encoded JWT secret for `jose` signing/verification |
318
- | `options` | `IJWSTokenServiceOptions` | Injected options |
305
+ ### Field codecs
319
306
 
320
- ### Constructor Behavior
307
+ ```typescript
308
+ interface IPayloadFieldCodec<T = unknown> {
309
+ key: string;
310
+ serialize(opts: { value: T }): string;
311
+ deserialize(opts: { raw: string }): T;
312
+ }
313
+ ```
321
314
 
322
- The constructor validates required options and throws immediately (status 500) if any are missing:
315
+ `AuthenticationFieldCodecs.ROLES_CODEC` is a ready-made codec for the `roles` field (pipe-separated `id|identifier|priority` strings). It is **not applied automatically** - pass it explicitly via `fieldCodecs: [AuthenticationFieldCodecs.ROLES_CODEC]` in the JWS/JWKS options. Without it, `roles` (and every other non-standard field) is serialized with plain `JSON.stringify` before AES encryption.
323
316
 
324
317
  ```typescript
325
- constructor(
326
- @inject({ key: AuthenticateBindingKeys.JWT_OPTIONS })
327
- protected options: IJWSTokenServiceOptions,
328
- ) {
329
- // Throws '[JWSTokenService] Invalid jwtSecret' if !jwtSecret
330
- // Throws '[JWSTokenService] Invalid getTokenExpiresFn' if !getTokenExpiresFn
331
- // Calls configurePayloadEncryption({ aesAlgorithm, applicationSecret })
332
- // Encodes jwtSecret to Uint8Array for jose
318
+ class AuthenticationFieldCodecs {
319
+ static readonly ROLES_CODEC: IPayloadFieldCodec<IJWTTokenPayload['roles']>;
320
+ static build<T>(opts: { key: string; serialize; deserialize }): IPayloadFieldCodec<T>;
333
321
  }
334
322
  ```
335
323
 
336
- > [!NOTE]
337
- > `applicationSecret` is no longer validated in the constructor. If not provided, AES encryption is simply not configured, and payloads pass through in plaintext.
324
+ ## Context variables
338
325
 
339
- ### Overridden Methods
326
+ Set on the Hono `Context` during authentication, readable via `context.get()`:
340
327
 
341
- | Method | Behavior |
342
- |--------|----------|
343
- | `doVerify(token)` | Calls `jwtVerify(token, this.jwtSecret)`, then `this.decryptPayload()` |
344
- | `getSigner(opts)` | Calls `this.encryptPayload()`, then creates `SignJWT` with HS256 header |
345
- | `getSigningKey()` | Returns `this.jwtSecret` |
346
- | `getDefaultTokenExpiresFn()` | Returns `this.options.getTokenExpiresFn` |
328
+ | Constant | Key string | Type | Description |
329
+ |----------|-----------|------|-------------|
330
+ | `Authentication.CURRENT_USER` | `auth.current.user` | `IAuthUser` | Authenticated user payload |
331
+ | `Authentication.AUDIT_USER_ID` | `audit.user.id` | `IdType` | Authenticated user's ID |
332
+ | `Authentication.SKIP_AUTHENTICATION` | `authentication.skip` | `boolean` | Set `true` in a preceding middleware to bypass auth |
347
333
 
348
- ## AbstractJWKSTokenService
334
+ ## Constants
349
335
 
350
- Base class for JWKS token services (Issuer + Verifier). Extends `AbstractBearerTokenService`.
336
+ **Authentication**
351
337
 
352
- **File:** `packages/core/src/components/auth/authenticate/services/bearer/jwks/abstract.service.ts`
338
+ | Constant | Value | Description |
339
+ |----------|-------|-------------|
340
+ | `Authentication.STRATEGY_JWT` | `'jwt'` | JWT strategy name |
341
+ | `Authentication.STRATEGY_BASIC` | `'basic'` | Basic strategy name |
342
+ | `Authentication.TYPE_BEARER` | `'Bearer'` | Bearer token type prefix |
343
+ | `Authentication.TYPE_BASIC` | `'Basic'` | Basic token type prefix |
344
+ | `Authentication.AUTHENTICATION_STRATEGY` | `'authentication.strategy'` | Binding key prefix for registered strategies |
353
345
 
354
- Consolidates the lazy-initialization pattern with retry-on-failure semantics. If `initialize()` rejects, `initPromise` is reset so the next call retries instead of caching the failure permanently.
346
+ **AuthenticationTokenTypes**
355
347
 
356
- ### Protected Fields
348
+ | Constant | Value |
349
+ |----------|-------|
350
+ | `TYPE_AUTHORIZATION_CODE` | `'000_AUTHORIZATION_CODE'` |
351
+ | `TYPE_ACCESS_TOKEN` | `'100_ACCESS_TOKEN'` |
352
+ | `TYPE_REFRESH_TOKEN` | `'200_REFRESH_TOKEN'` |
357
353
 
358
- | Field | Type | Default | Description |
359
- |-------|------|---------|-------------|
360
- | `initialized` | `boolean` | `false` | Whether the service has been initialized |
361
- | `initPromise` | `Promise<void> \| null` | `null` | Pending initialization promise (for concurrent callers) |
354
+ **JOSE / JWKS constants** - each class also exposes `SCHEME_SET: Set<string>` and `isValid(input): boolean`
362
355
 
363
- ### Methods
356
+ | Class | Members |
357
+ |-------|---------|
358
+ | `JOSEStandards` | `JWS` (`'JWS'`), `JWKS` (`'JWKS'`) |
359
+ | `JWKSModes` | `ISSUER` (`'issuer'`), `VERIFIER` (`'verifier'`) |
360
+ | `JWKSKeyDrivers` | `TEXT` (`'text'`), `FILE` (`'file'`) |
361
+ | `JWKSKeyFormats` | `PEM` (`'pem'`), `JWK` (`'jwk'`) |
362
+ | `AuthenticateStrategy` | `BASIC` (`'basic'`), `JWT` (`'jwt'`) - same values as `Authentication.STRATEGY_*` |
363
+ | `AuthenticationModes` | `ANY` (`'any'`), `ALL` (`'all'`) |
364
364
 
365
- | Method | Signature | Description |
366
- |--------|-----------|-------------|
367
- | `ensureInitialized` | `() => Promise<void>` | Lazily initializes the service on first call. Concurrent callers share the same promise. On failure, resets `initPromise` so the next call retries. |
365
+ ## Strategy registry
368
366
 
369
- ### Abstract Methods
367
+ `AuthenticationStrategyRegistry` is a singleton extending `AbstractAuthRegistry<IAuthenticationStrategy>`.
370
368
 
371
369
  | Method | Signature | Description |
372
- |--------|-----------|-------------|
373
- | `initialize` | `() => Promise<void>` | Perform one-time async initialization (load keys, create verifier, etc.) |
370
+ |--------|-----------|--------------|
371
+ | `getInstance()` | `static (): AuthenticationStrategyRegistry` | Returns (creating if needed) the singleton |
372
+ | `register` | <code v-pre>(opts: { container: Container; strategies: { name: string; strategy: TClass&lt;IAuthenticationStrategy&gt; }[] }) =&gt; this</code> | Binds each strategy into the container as a singleton under `authentication.strategy.<name>`. Returns `this` |
373
+ | `resolveStrategy` | `(opts: { name: string }) => IAuthenticationStrategy` | Resolves a registered strategy instance by name |
374
374
 
375
- ## JWKSIssuerTokenService
376
-
377
- Asymmetric JWT token service (issuer mode). Extends `AbstractJWKSTokenService`.
378
-
379
- **File:** `packages/core/src/components/auth/authenticate/services/bearer/jwks/issuer.service.ts`
375
+ ```typescript
376
+ AuthenticationStrategyRegistry.getInstance().register({
377
+ container: this,
378
+ strategies: [
379
+ { name: Authentication.STRATEGY_JWT, strategy: JWKSIssuerAuthenticationStrategy },
380
+ { name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
381
+ ],
382
+ });
383
+ ```
380
384
 
381
- ### JWKSIssuerAuthenticationStrategy
385
+ **Standalone `authenticate()` function** - the primary export for creating middleware outside the route-config `authenticate` field:
382
386
 
383
387
  ```typescript
384
- class JWKSIssuerAuthenticationStrategy<E extends Env = Env>
385
- extends BaseHelper
386
- implements IAuthenticationStrategy<E>
387
- {
388
- name = Authentication.STRATEGY_JWT;
389
- standard = JOSEStandards.JWKS;
390
-
391
- constructor(
392
- @inject({
393
- key: BindingKeys.build({
394
- namespace: BindingNamespaces.SERVICE,
395
- key: JWKSIssuerTokenService.name,
396
- }),
397
- })
398
- private service: JWKSIssuerTokenService<E>,
399
- ) { ... }
400
-
401
- authenticate(context: TContext<E, string>): Promise<IAuthUser> {
402
- const token = this.service.extractCredentials(context);
403
- return this.service.verify(token);
404
- }
405
- }
388
+ const authenticationProvider = new AuthenticationProvider();
389
+ const authenticateFn = authenticationProvider.value();
390
+
391
+ export const authenticate = (opts: { strategies: string[]; mode?: TAuthMode }) => authenticateFn(opts);
406
392
  ```
407
393
 
408
- ### Protected Fields
394
+ **`AuthenticationProvider` middleware behavior:**
409
395
 
410
- | Field | Type | Default | Description |
411
- |-------|------|---------|-------------|
412
- | `privateKey` | `CryptoKey \| Uint8Array \| null` | `null` | Private key for signing (loaded during `initialize`) |
413
- | `publicKey` | `CryptoKey \| Uint8Array \| null` | `null` | Public key for verification (loaded during `initialize`) |
414
- | `jwks` | `{ keys: JWK[] } \| null` | `null` | Cached JWKS for the `/certs` endpoint |
396
+ 1. If `Authentication.SKIP_AUTHENTICATION` is set on context, skips entirely (debug log)
397
+ 2. If `Authentication.CURRENT_USER` is already set, skips (already authenticated)
398
+ 3. Runs strategies per `mode` (`'any'` or `'all'`)
399
+ 4. On success, sets `Authentication.CURRENT_USER` and `Authentication.AUDIT_USER_ID`
400
+ 5. On failure, throws `401`
415
401
 
416
- ### Constructor Behavior
402
+ > [!NOTE]
403
+ > `'any'` mode discards each failing strategy's error (logs at debug) and only throws once every strategy is exhausted. `'all'` mode uses the **first** strategy's user payload as the identity source; if that payload has no `userId`, it throws `401` even though every strategy technically passed.
404
+
405
+ ## Service class hierarchy
417
406
 
418
- ```typescript
419
- constructor(
420
- @inject({ key: AuthenticateBindingKeys.JWKS_OPTIONS })
421
- protected options: IJWKSIssuerOptions,
422
- ) {
423
- // Calls configurePayloadEncryption({ aesAlgorithm, applicationSecret })
424
- // Keys are NOT loaded here - loaded lazily via ensureInitialized()
425
- }
407
+ ```
408
+ AbstractBearerTokenService<E> (extends BaseService)
409
+ #aes, #applicationSecret, #fieldCodecs
410
+ +extractCredentials(context) Extract Bearer token from Authorization header
411
+ +verify(opts) Template method -> doVerify()
412
+ +generate(opts) Template method -> getSigner() + getSigningKey()
413
+ +encryptPayload(payload) / decryptPayload(opts)
414
+ #doVerify(token)* +getSigner(opts)* #getSigningKey()* #getDefaultTokenExpiresFn()*
415
+
416
+ JWSTokenService Symmetric HS256, shared secret
417
+ used by JWSAuthenticationStrategy
418
+
419
+ AbstractJWKSTokenService Lazy ensureInitialized() + retry-on-failure
420
+ #initialized, #initPromise
421
+ +ensureInitialized() #initialize()*
422
+
423
+ JWKSIssuerTokenService Sign with private key, verify with public key
424
+ +getJWKS() (sync, throws if uninitialized) +getJWKSAsync()
425
+ used by JWKSIssuerAuthenticationStrategy
426
+
427
+ JWKSVerifierTokenService Verify only, via remote JWKS
428
+ used by JWKSVerifierAuthenticationStrategy
426
429
  ```
427
430
 
428
- ### Initialization Flow
431
+ ### AbstractBearerTokenService
429
432
 
430
- The `initialize()` method:
431
- 1. **Resolves key content** - reads from file (`readFile` from `node:fs/promises`) or uses inline text, based on `keys.driver`
432
- 2. **Parses key material** - imports keys using `importPKCS8`/`importSPKI` (PEM format) or `importJWK` (JWK format), based on `keys.format`
433
- 3. **Exports public JWK** - calls `exportJWK()` and adds `kid`, `alg`, `use: 'sig'` metadata
434
- 4. **Caches JWKS** - stores `{ keys: [publicJWK] }` for the `/certs` endpoint
435
- 5. **Sets `initialized = true`**
433
+ **File:** [`packages/core/src/components/auth/authenticate/services/bearer/abstract.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/services/bearer/abstract.service.ts)
436
434
 
437
- ### Overridden Methods
435
+ | Method | Signature | Description |
436
+ |--------|-----------|-------------|
437
+ | `configurePayloadEncryption` | <code v-pre>(opts: { aesAlgorithm?; applicationSecret?; fieldCodecs?: IPayloadFieldCodec[] }) =&gt; void</code> | Sets up AES + field codecs. AES only activates if `applicationSecret` is provided |
438
+ | `extractCredentials` | `(context) => { type: string; token: string }` | Parses `Authorization: Bearer <token>` |
439
+ | `verify` | `(opts: { type, token }) => Promise<IJWTTokenPayload>` | Calls `doVerify()`, wraps errors as sanitized `401` |
440
+ | `generate` | `(opts: { payload, getTokenExpiresFn? }) => Promise<string>` | Calls `getSigner()` then signs with `getSigningKey()` |
441
+ | `serializeField` / `deserializeField` | `(opts) => string` / `any` | Per-field codec lookup, `JSON.stringify`/`JSON.parse` fallback |
442
+ | `encryptPayload` | `(payload) => Record<string, any>` | AES-encrypts non-standard fields (keys and values). No-op if AES not configured |
443
+ | `decryptPayload` | `(opts: { result }) => IJWTTokenPayload` | Reverses `encryptPayload`. No-op if AES not configured |
438
444
 
439
- | Method | Behavior |
440
- |--------|----------|
441
- | `doVerify(token)` | Calls `ensureInitialized()`, then `jwtVerify(token, this.publicKey!)`, then `this.decryptPayload()` |
442
- | `getSigner(opts)` | Calls `ensureInitialized()`, then `this.encryptPayload()`, then creates `SignJWT` with algorithm + kid header |
443
- | `getSigningKey()` | Returns `this.privateKey` |
444
- | `getDefaultTokenExpiresFn()` | Returns `this.options.getTokenExpiresFn` |
445
+ Static: `JWT_COMMON_FIELDS: Set<'iss'|'sub'|'aud'|'jti'|'nbf'|'exp'|'iat'>` - never encrypted or touched by field codecs.
445
446
 
446
- ### JWKS Methods
447
+ ### JWSTokenService
447
448
 
448
- | Method | Signature | Description |
449
- |--------|-----------|-------------|
450
- | `getJWKS` | `() => { keys: JWK[] }` | Synchronous - returns cached JWKS. Throws if not yet initialized. |
451
- | `getJWKSAsync` | `() => Promise<{ keys: JWK[] }>` | Async - calls `ensureInitialized()` first, then returns JWKS. |
449
+ **File:** [`packages/core/src/components/auth/authenticate/services/bearer/jws.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/services/bearer/jws.service.ts)
452
450
 
453
- ### Internal Methods
451
+ Constructor validates `jwtSecret` and `getTokenExpiresFn` (throws `500` if missing), encodes the secret to `Uint8Array`, calls `configurePayloadEncryption()`. `doVerify` calls `jose.jwtVerify()` with the shared secret; `getSigner` signs with header `HS256` (or `headerAlgorithm` override).
454
452
 
455
- | Method | Signature | Description |
456
- |--------|-----------|-------------|
457
- | `resolveKeyContent` | `(opts: { keys }) => Promise<{ priv: string; pub: string }>` | Reads key content from file or returns inline text |
458
- | `parseKeyMaterial` | `(opts: { raw, algorithm, keys }) => Promise<{ priv, pub }>` | Imports keys using `jose` based on format (PEM or JWK) |
453
+ ### AbstractJWKSTokenService
459
454
 
460
- ## JWKSVerifierTokenService
455
+ **File:** [`packages/core/src/components/auth/authenticate/services/bearer/jwks/abstract.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/services/bearer/jwks/abstract.service.ts)
461
456
 
462
- Asymmetric JWT token service (verifier mode). Extends `AbstractJWKSTokenService`.
457
+ `ensureInitialized()` lazily runs `initialize()` on first call; concurrent callers share the pending promise. If `initialize()` rejects, the promise is reset so the next call retries instead of caching the failure.
463
458
 
464
- **File:** `packages/core/src/components/auth/authenticate/services/bearer/jwks/verifier.service.ts`
459
+ ### JWKSIssuerTokenService
465
460
 
466
- ### JWKSVerifierAuthenticationStrategy
461
+ **File:** [`packages/core/src/components/auth/authenticate/services/bearer/jwks/issuer.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/services/bearer/jwks/issuer.service.ts)
467
462
 
468
- ```typescript
469
- class JWKSVerifierAuthenticationStrategy<E extends Env = Env>
470
- extends BaseHelper
471
- implements IAuthenticationStrategy<E>
472
- {
473
- name = Authentication.STRATEGY_JWT;
474
- standard = JOSEStandards.JWKS;
475
-
476
- constructor(
477
- @inject({
478
- key: BindingKeys.build({
479
- namespace: BindingNamespaces.SERVICE,
480
- key: JWKSVerifierTokenService.name,
481
- }),
482
- })
483
- private service: JWKSVerifierTokenService<E>,
484
- ) { ... }
485
-
486
- authenticate(context: TContext<E, string>): Promise<IAuthUser> {
487
- const token = this.service.extractCredentials(context);
488
- return this.service.verify(token);
489
- }
490
- }
491
- ```
463
+ `initialize()`: reads key content (file via `readFile` or inline text, per `keys.driver`) → imports it (`importPKCS8`/`importSPKI` for PEM, `importJWK` for JWK) → exports the public JWK with `kid`/`alg`/`use: 'sig'` → caches `{ keys: [publicJWK] }`.
492
464
 
493
- ### Protected Fields
465
+ | Method | Signature | Description |
466
+ |--------|-----------|-------------|
467
+ | `getJWKS` | `() => { keys: JWK[] }` | Synchronous, returns the cached JWKS. Throws if called before `initialize()` completes |
468
+ | `getJWKSAsync` | `() => Promise<{ keys: JWK[] }>` | Calls `ensureInitialized()` first |
494
469
 
495
- | Field | Type | Default | Description |
496
- |-------|------|---------|-------------|
497
- | `jwksVerifier` | `ReturnType<typeof createRemoteJWKSet> \| null` | `null` | Remote JWKS verifier function |
470
+ ### JWKSVerifierTokenService
498
471
 
499
- ### Constructor Behavior
472
+ **File:** [`packages/core/src/components/auth/authenticate/services/bearer/jwks/verifier.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/services/bearer/jwks/verifier.service.ts)
500
473
 
501
- ```typescript
502
- constructor(
503
- @inject({ key: AuthenticateBindingKeys.JWKS_OPTIONS })
504
- protected options: IJWKSVerifierOptions,
505
- ) {
506
- // Calls configurePayloadEncryption({ aesAlgorithm, applicationSecret })
507
- // Remote JWKS is NOT fetched here - fetched lazily via ensureInitialized()
508
- }
509
- ```
474
+ `initialize()` calls `createRemoteJWKSet(jwksUrl, { cacheMaxAge: cacheTtlMs ?? 43_200_000, cooldownDuration: cooldownMs ?? 30_000 })`. `getSigner`/`getSigningKey`/`getDefaultTokenExpiresFn` all throw - this service is verify-only.
510
475
 
511
- ### Initialization Flow
476
+ ### BasicTokenService
512
477
 
513
- The `initialize()` method:
514
- 1. Creates a `createRemoteJWKSet()` from the configured `jwksUrl`
515
- 2. Configures `cacheMaxAge` (default 12h) and `cooldownDuration` (default 30s)
516
- 3. Sets `initialized = true`
478
+ **File:** [`packages/core/src/components/auth/authenticate/services/basic/service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/services/basic/service.ts)
517
479
 
518
- ### Overridden Methods
480
+ | Method | Signature | Description |
481
+ |--------|-----------|-------------|
482
+ | `extractCredentials` | `(context) => { username: string; password: string }` | Decodes `Authorization: Basic <base64>` |
483
+ | `verify` | `(opts: { credentials, context }) => Promise<IAuthUser>` | Calls the user-provided `verifyCredentials` |
484
+
485
+ Constructor throws `500` if `verifyCredentials` is missing from the injected options.
486
+
487
+ ## Strategy classes
488
+
489
+ All four strategies extend `BaseHelper`, implement `IAuthenticationStrategy<E>`, and follow the same shape: a `name` field, a `standard` field (Bearer strategies only), one injected token service, and an `authenticate(context)` method that calls `extractCredentials()` then `verify()`.
519
490
 
520
- | Method | Behavior |
521
- |--------|----------|
522
- | `doVerify(token)` | Calls `ensureInitialized()`, then `jwtVerify(token, this.jwksVerifier!)`, then `this.decryptPayload()` |
523
- | `getSigner(opts)` | Throws - verifier mode cannot sign tokens |
524
- | `getSigningKey()` | Throws - verifier mode cannot sign tokens |
525
- | `getDefaultTokenExpiresFn()` | Throws - verifier mode has no token expiry |
491
+ | Strategy | `name` | Injects | File |
492
+ |----------|--------|---------|------|
493
+ | `JWSAuthenticationStrategy` | `Authentication.STRATEGY_JWT` | `JWSTokenService` | [`strategies/jws.strategy.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/strategies/jws.strategy.ts) |
494
+ | `JWKSIssuerAuthenticationStrategy` | `Authentication.STRATEGY_JWT` | `JWKSIssuerTokenService` | [`strategies/jwks.strategy.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/strategies/jwks.strategy.ts) |
495
+ | `JWKSVerifierAuthenticationStrategy` | `Authentication.STRATEGY_JWT` | `JWKSVerifierTokenService` | same file |
496
+ | `BasicAuthenticationStrategy` | `Authentication.STRATEGY_BASIC` | `BasicTokenService` | [`strategies/basic.strategy.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/strategies/basic.strategy.ts) |
497
+
498
+ > [!NOTE]
499
+ > Choose the strategy class matching your JOSE standard - `JWKSIssuerAuthenticationStrategy` and `JWKSVerifierAuthenticationStrategy` both register under the same `'jwt'` name, so a JWKS issuer service uses only one of the two.
526
500
 
527
501
  ## JWKSController
528
502
 
529
- Serves the JWKS endpoint (default path `/certs`). This endpoint is **intentionally unauthenticated** - it serves the public keys needed by external verifiers.
503
+ Serves the JWKS endpoint (default path `/certs`, configurable via `rest.path`). Intentionally unauthenticated - it serves the public keys external verifiers need.
530
504
 
531
- **File:** `packages/core/src/components/auth/authenticate/controllers/jwks/controller.ts`
505
+ **File:** [`packages/core/src/components/auth/authenticate/controllers/jwks/controller.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/controllers/jwks/controller.ts)
532
506
 
533
507
  ```typescript
534
508
  class JWKSController extends BaseRestController {
535
- constructor(
536
- @inject({
537
- key: BindingKeys.build({
538
- namespace: BindingNamespaces.SERVICE,
539
- key: JWKSIssuerTokenService.name,
540
- }),
541
- })
542
- private jwksService: JWKSIssuerTokenService,
543
- ) {
509
+ constructor(@inject(...) private jwksService: JWKSIssuerTokenService) {
544
510
  super({ scope: JWKSController.name, path: '/certs', isStrict: true });
545
511
  }
546
512
 
547
- override binding(): ValueOrPromise<void> {
513
+ override binding() {
548
514
  this.defineRoute({
549
- configs: RouteConfigs.GET_JWKS_CERTS,
515
+ configs: RouteConfigs.GET_JWKS_CERTS, // GET '/'
550
516
  handler: async context => {
551
517
  const jwks = await this.jwksService.getJWKSAsync();
552
518
  context.header('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');
@@ -557,107 +523,40 @@ class JWKSController extends BaseRestController {
557
523
  }
558
524
  ```
559
525
 
560
- **Response format:**
561
- ```json
562
- {
563
- "keys": [
564
- {
565
- "kty": "EC",
566
- "kid": "my-key-id-1",
567
- "use": "sig",
568
- "alg": "ES256",
569
- "crv": "P-256",
570
- "x": "...",
571
- "y": "..."
572
- }
573
- ]
574
- }
575
- ```
576
-
577
- **Cache headers:** `Cache-Control: public, max-age=3600, stale-while-revalidate=86400`
578
-
579
- > [!NOTE]
580
- > The `/certs` path is configurable via `rest.path` in `IJWKSIssuerOptions`. The component applies the `@controller` decorator dynamically with the configured path.
581
-
582
- ## BasicTokenService
583
-
584
- All methods are instance methods on <code v-pre>BasicTokenService&lt;E extends Env = Env&gt;</code>, which extends `BaseService`.
526
+ The component applies `@controller({ path })` to `JWKSController` dynamically at binding time (via `Reflect.decorate`), since the path depends on a runtime option.
585
527
 
586
- **File:** `packages/core/src/components/auth/authenticate/services/basic/service.ts`
587
-
588
- ### BasicAuthenticationStrategy
589
-
590
- Extends `BaseHelper` and implements <code v-pre>IAuthenticationStrategy&lt;E&gt;</code>. Generic on <code v-pre>&lt;E extends Env = Env&gt;</code>.
591
-
592
- ```typescript
593
- class BasicAuthenticationStrategy<E extends Env = Env>
594
- extends BaseHelper
595
- implements IAuthenticationStrategy<E>
596
- {
597
- name = Authentication.STRATEGY_BASIC;
598
-
599
- constructor(
600
- @inject({
601
- key: BindingKeys.build({
602
- namespace: BindingNamespaces.SERVICE,
603
- key: BasicTokenService.name,
604
- }),
605
- })
606
- private service: BasicTokenService<E>,
607
- ) { ... }
608
-
609
- async authenticate(context: TContext<E, string>): Promise<IAuthUser> {
610
- const credentials = this.service.extractCredentials(context);
611
- return this.service.verify({ credentials, context });
612
- }
613
- }
614
- ```
528
+ ## Controller factory
615
529
 
616
- ### Methods
530
+ `defineAuthController(opts: TDefineAuthControllerOpts): typeof AuthController` builds a `BaseRestController` subclass at runtime.
617
531
 
618
- | Method | Signature | Description |
619
- |--------|-----------|-------------|
620
- | `extractCredentials` | <code v-pre>(context: TContext&lt;E, string&gt;) =&gt; { username: string; password: string }</code> | Decodes Base64 <code v-pre>Authorization: Basic &lt;base64&gt;</code> header |
621
- | `verify` | <code v-pre>(opts: { credentials: { username: string; password: string }; context: TContext&lt;E, string&gt; }) =&gt; Promise&lt;IAuthUser&gt;</code> | Calls user-provided `verifyCredentials` callback |
532
+ **File:** [`packages/core/src/components/auth/authenticate/controllers/factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/controllers/factory.ts)
622
533
 
623
- ### Constructor Behavior
534
+ **How it works:**
624
535
 
625
- ```typescript
626
- constructor(
627
- @inject({ key: AuthenticateBindingKeys.BASIC_OPTIONS })
628
- protected options: TBasicTokenServiceOptions<E>,
629
- ) {
630
- // Throws '[BasicTokenService] Invalid verifyCredentials function' if !options?.verifyCredentials
631
- }
632
- ```
536
+ 1. **Class creation.** `class AuthController extends BaseRestController {}` inside the factory closure, decorated with `@controller({ path: restPath })`, `isStrict: true`.
537
+ 2. **Service injection.** `inject({ key: serviceKey })(AuthController, undefined, 0)` is applied *after* class definition - the programmatic equivalent of decorating constructor parameter 0. The constructor throws `400` if the resolved service is falsy.
538
+ 3. **Routes.** Defined in `binding()` via `this.defineRoute()` - see the endpoint table in [Usage & Examples](./usage#api-endpoints).
539
+ 4. **Schemas.** Each endpoint uses `payload.<name>.{request,response}.schema` if provided, else a built-in default (or `AnyObjectSchema` for responses with no built-in schema).
633
540
 
634
- ## Entity Column Helper Types
541
+ Also exports `JWTTokenPayloadSchema`, the Zod schema backing `/who-am-i`'s response - extended at runtime with an optional `userInformation` field typed from `payload.getUserInformation.response.schema` (or `AnyObjectSchema`).
635
542
 
636
- The following types are exported for use when extending the auth entity column helpers:
543
+ ## Entity column helper types
637
544
 
638
- ### Permission Types
545
+ Exported for extending the auth entity column helpers - see [Usage & Examples](./usage#entity-column-helpers) for the columns themselves.
639
546
 
640
547
  ```typescript
641
- type TPermissionOptions = {
642
- idType?: 'string' | 'number';
643
- };
644
-
548
+ type TPermissionOptions = { idType?: 'string' | 'number' };
645
549
  type TPermissionCommonColumns = {
646
550
  code: NotNull<PgTextBuilderInitial<...>>;
647
551
  name: NotNull<PgTextBuilderInitial<...>>;
648
552
  subject: NotNull<PgTextBuilderInitial<...>>;
553
+ method: NotNull<PgTextBuilderInitial<...>>;
649
554
  action: NotNull<PgTextBuilderInitial<...>>;
650
555
  scope: NotNull<PgTextBuilderInitial<...>>;
651
- };
652
- ```
653
-
654
- ### Policy Definition Types
655
-
656
- ```typescript
657
- type TPolicyDefinitionOptions = {
658
- idType?: 'string' | 'number';
556
+ description: PgTextBuilderInitial<...>;
659
557
  };
660
558
 
559
+ type TPolicyDefinitionOptions = { idType?: 'string' | 'number' };
661
560
  type TPolicyDefinitionCommonColumns = {
662
561
  variant: ReturnType<typeof text>;
663
562
  subjectType: ReturnType<typeof text>;
@@ -668,89 +567,41 @@ type TPolicyDefinitionCommonColumns = {
668
567
  };
669
568
  ```
670
569
 
671
- ## Controller Factory
672
-
673
- The `defineAuthController()` function dynamically creates a controller class at runtime using decorator composition:
674
-
675
- **How it works:**
676
-
677
- 1. **Class creation:** A new class is created dynamically with `class AuthController extends BaseRestController {}` inside the factory closure
678
- 2. **Decorator application:** The `@controller({ path: restPath })` decorator is applied to set the base path. The controller is created with `isStrict: true`
679
- 3. **Service injection:** The auth service is injected via `inject({ key: serviceKey })(AuthController, undefined, 0)` after class definition -- this programmatically applies `@inject` to constructor parameter 0
680
- - Service key is provided via `controllerOpts.serviceKey` (required)
681
- - Service must implement `IAuthService` interface
682
- 4. **Route definition:** Routes are defined in the controller's `binding()` method using `this.defineRoute()`
683
- 5. **Schema customization:** Custom Zod schemas can be provided per endpoint via the `payload` option. Defaults to built-in schemas when not provided, with `AnyObjectSchema` as the response fallback.
684
-
685
- **Factory signature:**
686
-
687
- ```typescript
688
- function defineAuthController(opts: TDefineAuthControllerOpts): typeof AuthController;
689
- ```
690
-
691
- > [!NOTE]
692
- > The factory also exports `JWTTokenPayloadSchema`, a Zod schema used for the `/who-am-i` response validation. For `/who-am-i`, it is extended with an optional `userInformation` field that is populated when the request sets `?withUserInformation=true`. The same `getUserInformation` service method backs the dedicated `GET /me` route.
693
-
694
- **Service resolution:**
695
-
696
- The factory applies `@inject` programmatically to constructor parameter 0:
697
-
698
- ```typescript
699
- // Inside defineAuthController, after class definition:
700
- inject({ key: serviceKey })(AuthController, undefined, 0);
701
- ```
702
-
703
- This is equivalent to:
704
- ```typescript
705
- constructor(
706
- @inject({ key: serviceKey })
707
- authService: IAuthService,
708
- ) { ... }
709
- ```
710
-
711
- If the service is not bound, the component will throw: `"[AuthController] Failed to init auth controller | Invalid injectable authentication service!"`
712
-
713
- ## File Structure
570
+ ## File structure
714
571
 
715
572
  ```
716
- packages/core/src/components/auth/authenticate/
717
- ├── common/
718
- │ ├── codecs.ts # AuthenticationFieldCodecs (ROLES_CODEC, build() factory)
719
- │ ├── constants.ts # AuthenticateStrategy, JOSEStandards, JWKSModes, JWKSKeyDrivers, JWKSKeyFormats, Authentication, AuthenticationTokenTypes, AuthenticationModes
720
- │ ├── keys.ts # AuthenticateBindingKeys (REST_OPTIONS, JWT_OPTIONS, JWKS_OPTIONS, BASIC_OPTIONS)
721
- │ ├── types.ts # All option interfaces, discriminated unions, IAuthUser, IJWTTokenPayload, IPayloadFieldCodec, IAuthService
722
- └── index.ts # Barrel export
723
- ├── controllers/
724
- │ ├── factory.ts # defineAuthController() factory + JWTTokenPayloadSchema
725
- └── jwks/
726
- ├── controller.ts # JWKSController (serves /certs endpoint)
727
- └── definitions.ts # Route config for GET /certs
728
- ├── middlewares/
729
- └── authenticate.middleware.ts # Standalone authenticate() convenience function
730
- ├── providers/
731
- └── authentication.provider.ts # AuthenticationProvider (IProvider pattern, creates middleware)
732
- ├── services/
733
- ├── basic/
734
- │ │ └── service.ts # BasicTokenService
735
- │ ├── bearer/
736
- │ │ ├── abstract.service.ts # AbstractBearerTokenService (shared logic)
737
- ├── jws.service.ts # JWSTokenService (symmetric HS256)
738
- └── jwks/
739
- │ │ ├── abstract.service.ts # AbstractJWKSTokenService (lazy-init)
740
- │ ├── issuer.service.ts # JWKSIssuerTokenService
741
- │ │ └── verifier.service.ts # JWKSVerifierTokenService
742
- │ └── index.ts # Barrel export
743
- ├── strategies/
744
- │ ├── basic.strategy.ts # BasicAuthenticationStrategy
745
- │ ├── jws.strategy.ts # JWSAuthenticationStrategy
746
- │ ├── jwks.strategy.ts # JWKSIssuerAuthenticationStrategy + JWKSVerifierAuthenticationStrategy
747
- │ ├── strategy-registry.ts # AuthenticationStrategyRegistry singleton
748
- │ └── index.ts # Barrel export
749
- └── component.ts # AuthenticateComponent
573
+ packages/core/src/components/auth/
574
+ ├── authenticate/
575
+ │ ├── common/
576
+ ├── codecs.ts # AuthenticationFieldCodecs (ROLES_CODEC, build() factory)
577
+ ├── constants.ts # AuthenticateStrategy, JOSEStandards, JWKSModes, JWKSKeyDrivers, JWKSKeyFormats, Authentication, AuthenticationTokenTypes, AuthenticationModes
578
+ ├── keys.ts # AuthenticateBindingKeys
579
+ │ ├── types.ts # Option interfaces, discriminated unions, IAuthUser, IJWTTokenPayload, IAuthService
580
+ │ │ └── index.ts
581
+ │ ├── controllers/
582
+ │ ├── factory.ts # defineAuthController() + JWTTokenPayloadSchema
583
+ │ └── jwks/ # JWKSController + route config
584
+ ├── middlewares/
585
+ │ │ └── authenticate.middleware.ts # Standalone authenticate() function
586
+ ├── providers/
587
+ │ │ └── authentication.provider.ts # AuthenticationProvider
588
+ ├── services/
589
+ │ │ ├── basic/service.ts # BasicTokenService
590
+ │ └── bearer/
591
+ │ │ ├── abstract.service.ts # AbstractBearerTokenService
592
+ ├── jws.service.ts # JWSTokenService
593
+ │ │ └── jwks/ # AbstractJWKSTokenService, JWKSIssuerTokenService, JWKSVerifierTokenService
594
+ │ ├── strategies/ # JWSAuthenticationStrategy, JWKS*, BasicAuthenticationStrategy, AuthenticationStrategyRegistry
595
+ │ └── component.ts # AuthenticateComponent
596
+ ├── base/
597
+ └── abstract-auth-registry.ts # AbstractAuthRegistry (shared by authenticate + authorize)
598
+ └── models/
599
+ ├── entities/ # extraUserColumns, extraRoleColumns, extraPermissionColumns, extraPolicyDefinitionColumns
600
+ └── requests/ # SignInRequestSchema, SignUpRequestSchema, ChangePasswordRequestSchema
750
601
  ```
751
602
 
752
- ## See Also
603
+ ## See also
753
604
 
754
- - [Setup & Configuration](./) -- Binding keys, options interfaces, and initial setup
755
- - [Usage & Examples](./usage) -- Securing routes, auth flows, and API endpoints
756
- - [Error Reference](./errors) -- Error messages and troubleshooting
605
+ - [Overview](./) - guided introduction and common tasks
606
+ - [Usage & Examples](./usage) - securing routes, auth flows, JWKS microservice patterns, API endpoints
607
+ - [Error Reference](./errors) - every error message and how to fix it