@venizia/ignis-docs 0.0.6-2 → 0.0.7-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 (120) hide show
  1. package/README.md +125 -388
  2. package/dist/mcp-server/common/config.d.ts +0 -21
  3. package/dist/mcp-server/common/config.d.ts.map +1 -1
  4. package/dist/mcp-server/common/config.js +1 -36
  5. package/dist/mcp-server/common/config.js.map +1 -1
  6. package/dist/mcp-server/helpers/docs.helper.d.ts +0 -24
  7. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  8. package/dist/mcp-server/helpers/docs.helper.js +0 -25
  9. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  10. package/dist/mcp-server/helpers/github.helper.d.ts +0 -13
  11. package/dist/mcp-server/helpers/github.helper.d.ts.map +1 -1
  12. package/dist/mcp-server/helpers/github.helper.js +3 -20
  13. package/dist/mcp-server/helpers/github.helper.js.map +1 -1
  14. package/dist/mcp-server/index.js +1 -20
  15. package/dist/mcp-server/index.js.map +1 -1
  16. package/dist/mcp-server/tools/base.tool.d.ts +4 -85
  17. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/base.tool.js +1 -38
  19. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  20. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +8 -2
  21. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  22. package/dist/mcp-server/tools/docs/get-document-content.tool.js +1 -10
  23. package/dist/mcp-server/tools/docs/get-document-content.tool.js.map +1 -1
  24. package/dist/mcp-server/tools/docs/get-document-metadata.tool.d.ts +13 -2
  25. package/dist/mcp-server/tools/docs/get-document-metadata.tool.d.ts.map +1 -1
  26. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +1 -10
  27. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js.map +1 -1
  28. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +16 -8
  29. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts.map +1 -1
  30. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +2 -25
  31. package/dist/mcp-server/tools/docs/get-package-overview.tool.js.map +1 -1
  32. package/dist/mcp-server/tools/docs/list-categories.tool.d.ts +5 -2
  33. package/dist/mcp-server/tools/docs/list-categories.tool.d.ts.map +1 -1
  34. package/dist/mcp-server/tools/docs/list-categories.tool.js +1 -10
  35. package/dist/mcp-server/tools/docs/list-categories.tool.js.map +1 -1
  36. package/dist/mcp-server/tools/docs/list-documents.tool.d.ts +11 -2
  37. package/dist/mcp-server/tools/docs/list-documents.tool.d.ts.map +1 -1
  38. package/dist/mcp-server/tools/docs/list-documents.tool.js +1 -10
  39. package/dist/mcp-server/tools/docs/list-documents.tool.js.map +1 -1
  40. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +13 -2
  41. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts.map +1 -1
  42. package/dist/mcp-server/tools/docs/search-documents.tool.js +1 -10
  43. package/dist/mcp-server/tools/docs/search-documents.tool.js.map +1 -1
  44. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +9 -2
  45. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  46. package/dist/mcp-server/tools/github/list-project-files.tool.js +1 -10
  47. package/dist/mcp-server/tools/github/list-project-files.tool.js.map +1 -1
  48. package/dist/mcp-server/tools/github/search-code.tool.d.ts +16 -2
  49. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  50. package/dist/mcp-server/tools/github/search-code.tool.js +2 -14
  51. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  52. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts +19 -6
  53. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  54. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +2 -19
  55. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  56. package/dist/mcp-server/tools/github/view-source-file.tool.d.ts +8 -2
  57. package/dist/mcp-server/tools/github/view-source-file.tool.d.ts.map +1 -1
  58. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -10
  59. package/dist/mcp-server/tools/github/view-source-file.tool.js.map +1 -1
  60. package/dist/mcp-server/tools/index.d.ts.map +1 -1
  61. package/dist/mcp-server/tools/index.js +0 -2
  62. package/dist/mcp-server/tools/index.js.map +1 -1
  63. package/package.json +68 -54
  64. package/wiki/best-practices/api-usage-examples.md +7 -5
  65. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  66. package/wiki/best-practices/code-style-standards/constants-configuration.md +1 -1
  67. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  68. package/wiki/best-practices/code-style-standards/function-patterns.md +1 -1
  69. package/wiki/best-practices/common-pitfalls.md +1 -1
  70. package/wiki/best-practices/data-modeling.md +33 -1
  71. package/wiki/best-practices/error-handling.md +7 -4
  72. package/wiki/best-practices/performance-optimization.md +1 -1
  73. package/wiki/best-practices/security-guidelines.md +5 -4
  74. package/wiki/guides/core-concepts/components-guide.md +1 -1
  75. package/wiki/guides/core-concepts/controllers.md +14 -8
  76. package/wiki/guides/core-concepts/persistent/models.md +32 -0
  77. package/wiki/guides/core-concepts/services.md +2 -1
  78. package/wiki/guides/get-started/5-minute-quickstart.md +1 -1
  79. package/wiki/guides/reference/mcp-docs-server.md +0 -134
  80. package/wiki/guides/tutorials/building-a-crud-api.md +2 -1
  81. package/wiki/guides/tutorials/complete-installation.md +2 -2
  82. package/wiki/guides/tutorials/ecommerce-api.md +3 -3
  83. package/wiki/guides/tutorials/realtime-chat.md +7 -6
  84. package/wiki/index.md +2 -1
  85. package/wiki/references/base/components.md +2 -1
  86. package/wiki/references/base/controllers.md +19 -12
  87. package/wiki/references/base/middlewares.md +2 -1
  88. package/wiki/references/base/models.md +11 -2
  89. package/wiki/references/base/services.md +2 -1
  90. package/wiki/references/components/authentication/api.md +525 -205
  91. package/wiki/references/components/authentication/errors.md +502 -105
  92. package/wiki/references/components/authentication/index.md +388 -75
  93. package/wiki/references/components/authentication/usage.md +428 -266
  94. package/wiki/references/components/authorization/api.md +1213 -0
  95. package/wiki/references/components/authorization/errors.md +387 -0
  96. package/wiki/references/components/authorization/index.md +712 -0
  97. package/wiki/references/components/authorization/usage.md +758 -0
  98. package/wiki/references/components/health-check.md +2 -1
  99. package/wiki/references/components/index.md +2 -0
  100. package/wiki/references/components/socket-io/index.md +9 -4
  101. package/wiki/references/components/socket-io/usage.md +1 -1
  102. package/wiki/references/components/static-asset/index.md +3 -5
  103. package/wiki/references/components/swagger.md +2 -1
  104. package/wiki/references/configuration/environment-variables.md +2 -1
  105. package/wiki/references/configuration/index.md +2 -1
  106. package/wiki/references/helpers/error/index.md +1 -1
  107. package/wiki/references/helpers/index.md +1 -0
  108. package/wiki/references/helpers/inversion/index.md +1 -1
  109. package/wiki/references/helpers/kafka/index.md +305 -0
  110. package/wiki/references/helpers/redis/index.md +2 -9
  111. package/wiki/references/quick-reference.md +3 -5
  112. package/wiki/references/utilities/crypto.md +2 -2
  113. package/wiki/references/utilities/date.md +5 -5
  114. package/wiki/references/utilities/index.md +3 -11
  115. package/wiki/references/utilities/jsx.md +4 -2
  116. package/wiki/references/utilities/module.md +1 -1
  117. package/wiki/references/utilities/parse.md +4 -4
  118. package/wiki/references/utilities/performance.md +2 -2
  119. package/wiki/references/utilities/promise.md +4 -4
  120. package/wiki/references/utilities/request.md +2 -2
@@ -1,6 +1,6 @@
1
1
  # Authentication -- Setup & Configuration
2
2
 
3
- > JWT and Basic HTTP authentication with AES-encrypted payloads, multi-strategy support, and built-in auth controller
3
+ > JWT authentication with JWS (symmetric) and JWKS (asymmetric) standards, optional AES-encrypted payloads, Basic HTTP authentication, multi-strategy support, and built-in auth controller
4
4
 
5
5
  ## Quick Reference
6
6
 
@@ -16,21 +16,44 @@
16
16
  |-----------|---------|
17
17
  | **AuthenticateComponent** | Main component registering auth services and controllers |
18
18
  | **AuthenticationStrategyRegistry** | Singleton managing available auth strategies |
19
- | **JWTAuthenticationStrategy** | JWT verification using `JWTTokenService` |
19
+ | **JWSAuthenticationStrategy** | JWT verification using symmetric `JWSTokenService` (HS256) |
20
+ | **JWKSIssuerAuthenticationStrategy** | JWT verification using asymmetric `JWKSIssuerTokenService` (ES256/RS256/EdDSA) |
21
+ | **JWKSVerifierAuthenticationStrategy** | JWT verification via remote JWKS URL |
20
22
  | **BasicAuthenticationStrategy** | Basic HTTP authentication using `BasicTokenService` |
21
- | **JWTTokenService** | Generate, verify, encrypt/decrypt JWT tokens |
23
+ | **AbstractBearerTokenService** | Base class for all Bearer token services (JWS, JWKS Issuer, JWKS Verifier) |
24
+ | **JWSTokenService** | Symmetric JWT — sign, verify, optional AES encrypt/decrypt |
25
+ | **JWKSIssuerTokenService** | Asymmetric JWT — sign with private key, verify with public key, serve JWKS endpoint |
26
+ | **JWKSVerifierTokenService** | Asymmetric JWT — verify-only via remote JWKS URL |
22
27
  | **BasicTokenService** | Extract and verify Basic auth credentials |
28
+ | **JWKSController** | Serves `/.well-known/jwks.json`-style endpoint at `/certs` |
23
29
  | **IAuthService** | Interface for custom auth implementation (sign-in, sign-up) |
24
30
  | **defineAuthController** | Factory function for creating custom auth controllers |
25
- | **authenticate** | Standalone function wrapping `AuthenticationStrategyRegistry.getInstance().authenticate()` |
31
+ | **AbstractJWKSTokenService** | Base class for JWKS services with lazy initialization and retry-on-failure |
32
+ | **authenticate** | Standalone middleware function using `AuthenticationProvider` to create auth middleware |
33
+
34
+ ### JOSE Standards
35
+
36
+ The authentication module supports two JOSE (JSON Object Signing and Encryption) standards:
37
+
38
+ | Standard | Class | Use Case | Signing | Key Type |
39
+ |----------|-------|----------|---------|----------|
40
+ | **JWS** | `JWSTokenService` | Single-service apps where the same service signs and verifies | HS256 (symmetric) | Shared secret (`jwtSecret`) |
41
+ | **JWKS (Issuer)** | `JWKSIssuerTokenService` | Multi-service / microservice architectures where one service issues tokens | ES256 / RS256 / EdDSA (asymmetric) | Private key (sign) + Public key (verify) |
42
+ | **JWKS (Verifier)** | `JWKSVerifierTokenService` | Services that only verify tokens issued by another service | N/A (verify-only) | Remote JWKS URL |
26
43
 
27
44
  ### Environment Variables
28
45
 
29
46
  | Variable | Purpose | Required |
30
47
  |----------|---------|----------|
31
- | `APP_ENV_APPLICATION_SECRET` | Encrypt JWT payload | Required for JWT |
32
- | `APP_ENV_JWT_SECRET` | Sign and verify JWT signature | Required for JWT |
33
- | `APP_ENV_JWT_EXPIRES_IN` | Token expiration (seconds) | Optional |
48
+ | `APP_ENV_JWT_SECRET` | Sign and verify JWT signature (JWS only) | Required for JWS |
49
+ | `APP_ENV_APPLICATION_SECRET` | AES-encrypt JWT payload fields | Optional |
50
+ | `APP_ENV_JWT_EXPIRES_IN` | Token expiration (seconds) | Required |
51
+ | `APP_ENV_JWKS_ALGORITHM` | JWKS signing algorithm (e.g., `ES256`) | Required for JWKS |
52
+ | `APP_ENV_JWKS_KEY_DRIVER` | Key source: `text` or `file` | Required for JWKS |
53
+ | `APP_ENV_JWKS_KEY_FORMAT` | Key format: `pem` or `jwk` | Required for JWKS |
54
+ | `APP_ENV_JWKS_PRIVATE_KEY` | Private key content or file path | Required for JWKS Issuer |
55
+ | `APP_ENV_JWKS_PUBLIC_KEY` | Public key content or file path | Required for JWKS Issuer |
56
+ | `APP_ENV_JWKS_KID` | Key ID for JWKS endpoint | Required for JWKS Issuer |
34
57
 
35
58
  ### Auth Modes
36
59
 
@@ -51,10 +74,6 @@
51
74
 
52
75
  | Constant | Value | Description |
53
76
  |----------|-------|-------------|
54
- | `Authentication.ACCESS_TOKEN_SECRET` | `'token.secret'` | Default access token secret key |
55
- | `Authentication.ACCESS_TOKEN_EXPIRES_IN` | `86400` | Default access token expiration (seconds, 24h) |
56
- | `Authentication.REFRESH_TOKEN_SECRET` | `'refresh.secret'` | Default refresh token secret key |
57
- | `Authentication.REFRESH_TOKEN_EXPIRES_IN` | `86400` | Default refresh token expiration (seconds, 24h) |
58
77
  | `Authentication.AUTHENTICATION_STRATEGY` | `'authentication.strategy'` | Namespace prefix for strategy binding keys |
59
78
  | `Authentication.STRATEGY_JWT` | `'jwt'` | JWT strategy name |
60
79
  | `Authentication.STRATEGY_BASIC` | `'basic'` | Basic strategy name |
@@ -64,36 +83,91 @@
64
83
  | `Authentication.CURRENT_USER` | `'auth.current.user'` | Context key for the authenticated user payload |
65
84
  | `Authentication.AUDIT_USER_ID` | `'audit.user.id'` | Context key for the authenticated user ID |
66
85
 
86
+ ### JWKS Constants
87
+
88
+ | Class | Constant | Value | Description |
89
+ |-------|----------|-------|-------------|
90
+ | `JOSEStandards` | `JWS` | `'JWS'` | Symmetric JWT standard |
91
+ | `JOSEStandards` | `JWKS` | `'JWKS'` | Asymmetric JWT standard |
92
+ | `JWKSModes` | `ISSUER` | `'issuer'` | Issuer mode (sign + verify + serve JWKS) |
93
+ | `JWKSModes` | `VERIFIER` | `'verifier'` | Verifier mode (verify-only via remote JWKS) |
94
+ | `JWKSKeyDrivers` | `TEXT` | `'text'` | Key provided as inline text |
95
+ | `JWKSKeyDrivers` | `FILE` | `'file'` | Key loaded from file path |
96
+ | `JWKSKeyFormats` | `PEM` | `'pem'` | PEM-encoded key format |
97
+ | `JWKSKeyFormats` | `JWK` | `'jwk'` | JSON Web Key format |
98
+
99
+ Each constants class also provides:
100
+ - `SCHEME_SET: Set<string>` — set of all valid values
101
+ - `isValid(input: string): boolean` — check if a value is recognized
102
+
67
103
  #### Import Paths
104
+
68
105
  ```typescript
69
106
  import {
107
+ // Component + Registry
70
108
  AuthenticateComponent,
71
109
  AuthenticateBindingKeys,
72
110
  Authentication,
111
+ AuthenticationFieldCodecs,
73
112
  AuthenticationModes,
74
113
  AuthenticationTokenTypes,
75
114
  AuthenticationStrategyRegistry,
76
- JWTAuthenticationStrategy,
115
+
116
+ // JOSE Standards + Constants
117
+ JOSEStandards,
118
+ JWKSModes,
119
+ JWKSKeyDrivers,
120
+ JWKSKeyFormats,
121
+
122
+ // Strategies
123
+ JWSAuthenticationStrategy,
124
+ JWKSIssuerAuthenticationStrategy,
125
+ JWKSVerifierAuthenticationStrategy,
77
126
  BasicAuthenticationStrategy,
78
- JWTTokenService,
127
+
128
+ // Services
129
+ AbstractBearerTokenService,
130
+ JWSTokenService,
131
+ JWKSIssuerTokenService,
132
+ JWKSVerifierTokenService,
79
133
  BasicTokenService,
134
+
135
+ // Controllers
80
136
  defineAuthController,
137
+ JWKSController,
81
138
  authenticate,
82
139
  } from '@venizia/ignis';
83
140
 
84
141
  import type {
142
+ // Option types
85
143
  TAuthenticationRestOptions,
86
- IJWTTokenServiceOptions,
87
- IBasicTokenServiceOptions,
144
+ TJWTTokenServiceOptions,
145
+ IJWSTokenServiceOptions,
146
+ IJWKSIssuerOptions,
147
+ IJWKSVerifierOptions,
148
+ TJWKSTokenServiceOptions,
149
+ TBasicTokenServiceOptions,
88
150
  IAuthenticateOptions,
151
+
152
+ // User + payload types
89
153
  IAuthUser,
90
154
  IJWTTokenPayload,
155
+ IPayloadFieldCodec,
91
156
  IAuthService,
92
157
  IAuthenticationStrategy,
158
+
159
+ // Controller types
93
160
  TDefineAuthControllerOpts,
161
+
162
+ // Utility types
94
163
  TAuthStrategy,
95
164
  TAuthMode,
96
165
  TGetTokenExpiresFn,
166
+ TJWKSAlgorithm,
167
+ TJWKSKeyDriver,
168
+ TJWKSKeyFormat,
169
+ TJOSEStandard,
170
+ TJWKSMode,
97
171
  } from '@venizia/ignis';
98
172
  ```
99
173
 
@@ -128,46 +202,146 @@ import {
128
202
  } from '@venizia/ignis';
129
203
  ```
130
204
 
205
+ ## Component Binding Lifecycle
206
+
207
+ ```mermaid
208
+ flowchart TD
209
+ A["preConfigure()"] --> B["Bind JWT_OPTIONS / BASIC_OPTIONS / REST_OPTIONS"]
210
+ B --> C["this.component(AuthenticateComponent)"]
211
+ C --> D["AuthenticateComponent.binding()"]
212
+ D --> E{"jwtOptions.standard?"}
213
+ E -->|"JWS"| F["defineJWSAuth()"]
214
+ E -->|"JWKS"| G["defineJWKSAuth()"]
215
+ E -->|"none"| H["Skip JWT"]
216
+ F --> I["Register JWSTokenService"]
217
+ G --> J{"mode?"}
218
+ J -->|"issuer"| K["Register JWKSIssuerTokenService + JWKSController"]
219
+ J -->|"verifier"| L["Register JWKSVerifierTokenService"]
220
+ D --> M["defineBasicAuth()"]
221
+ M --> N["Register BasicTokenService"]
222
+ D --> O["defineControllers()"]
223
+ O --> P["Register AuthController (factory-built)"]
224
+ C --> Q["Manual strategy registration"]
225
+ Q --> R["AuthenticationStrategyRegistry.register()"]
226
+
227
+ style E fill:#fff3cd,stroke:#ffc107
228
+ style J fill:#fff3cd,stroke:#ffc107
229
+ ```
230
+
131
231
  ## Setup
132
232
 
133
233
  ### Step 1: Bind Configuration
134
234
 
135
- Bind at least one of `JWT_OPTIONS` or `BASIC_OPTIONS` in your application's `preConfigure()`.
235
+ Bind JWT options using the discriminated union `TJWTTokenServiceOptions`, which requires a `standard` field to select the JOSE standard.
136
236
 
137
- ### JWT Only (Primary Setup)
237
+ ### JWS (Symmetric JWT) Setup
138
238
 
139
239
  ```typescript
140
240
  import {
141
241
  AuthenticateBindingKeys,
142
- IJWTTokenServiceOptions,
242
+ JOSEStandards,
243
+ TJWTTokenServiceOptions,
143
244
  } from '@venizia/ignis';
144
245
 
145
- // Bind JWT options
146
- this.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
147
- applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
148
- jwtSecret: process.env.APP_ENV_JWT_SECRET,
149
- getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
246
+ this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
247
+ standard: JOSEStandards.JWS,
248
+ options: {
249
+ jwtSecret: process.env.APP_ENV_JWT_SECRET,
250
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET, // Optional — enables AES payload encryption
251
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
252
+ },
150
253
  });
151
254
  ```
152
255
 
153
- **Example `.env` file:**
256
+ **Example `.env` file (JWS):**
154
257
 
155
258
  ```
156
- APP_ENV_APPLICATION_SECRET=your-strong-application-secret
157
259
  APP_ENV_JWT_SECRET=your-strong-jwt-secret
260
+ APP_ENV_APPLICATION_SECRET=your-strong-application-secret
158
261
  APP_ENV_JWT_EXPIRES_IN=86400
159
262
  ```
160
263
 
264
+ > [!NOTE]
265
+ > `applicationSecret` is optional. When provided, all custom JWT claim keys and values are AES-encrypted. When omitted, payloads are stored in plaintext (standard JWT behavior).
266
+
267
+ ### JWKS Issuer (Asymmetric JWT) Setup
268
+
269
+ ```typescript
270
+ import {
271
+ AuthenticateBindingKeys,
272
+ JOSEStandards,
273
+ JWKSModes,
274
+ JWKSKeyDrivers,
275
+ JWKSKeyFormats,
276
+ TJWTTokenServiceOptions,
277
+ } from '@venizia/ignis';
278
+
279
+ this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
280
+ standard: JOSEStandards.JWKS,
281
+ options: {
282
+ mode: JWKSModes.ISSUER,
283
+ algorithm: 'ES256',
284
+ keys: {
285
+ driver: JWKSKeyDrivers.FILE, // or JWKSKeyDrivers.TEXT
286
+ format: JWKSKeyFormats.PEM, // or JWKSKeyFormats.JWK
287
+ private: './keys/private.pem',
288
+ public: './keys/public.pem',
289
+ },
290
+ kid: 'my-key-id-1',
291
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
292
+ // Optional AES payload encryption
293
+ aesAlgorithm: 'aes-256-cbc',
294
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
295
+ },
296
+ });
297
+ ```
298
+
299
+ **Example `.env` file (JWKS Issuer):**
300
+
301
+ ```
302
+ APP_ENV_JWKS_ALGORITHM=ES256
303
+ APP_ENV_JWKS_KEY_DRIVER=file
304
+ APP_ENV_JWKS_KEY_FORMAT=pem
305
+ APP_ENV_JWKS_PRIVATE_KEY=./keys/private.pem
306
+ APP_ENV_JWKS_PUBLIC_KEY=./keys/public.pem
307
+ APP_ENV_JWKS_KID=my-key-id-1
308
+ APP_ENV_JWT_EXPIRES_IN=86400
309
+ APP_ENV_APPLICATION_SECRET=your-strong-application-secret
310
+ ```
311
+
312
+ ### JWKS Verifier (Remote Verification) Setup
313
+
314
+ ```typescript
315
+ import {
316
+ AuthenticateBindingKeys,
317
+ JOSEStandards,
318
+ JWKSModes,
319
+ TJWTTokenServiceOptions,
320
+ } from '@venizia/ignis';
321
+
322
+ this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
323
+ standard: JOSEStandards.JWKS,
324
+ options: {
325
+ mode: JWKSModes.VERIFIER,
326
+ jwksUrl: 'https://auth-service.example.com/certs',
327
+ cacheTtlMs: 43_200_000, // Cache JWKS for 12 hours (default)
328
+ cooldownMs: 30_000, // Wait 30s between JWKS refreshes (default)
329
+ // Optional AES payload decryption (must match issuer's applicationSecret)
330
+ aesAlgorithm: 'aes-256-cbc',
331
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
332
+ },
333
+ });
334
+ ```
335
+
161
336
  ### Basic Auth Only (Alternative Setup)
162
337
 
163
338
  ```typescript
164
339
  import {
165
340
  AuthenticateBindingKeys,
166
- IBasicTokenServiceOptions,
341
+ TBasicTokenServiceOptions,
167
342
  } from '@venizia/ignis';
168
343
 
169
- // Bind Basic auth options
170
- this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
344
+ this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
171
345
  verifyCredentials: async (opts) => {
172
346
  const { credentials, context } = opts;
173
347
  const user = await userRepo.findByUsername(credentials.username);
@@ -179,14 +353,20 @@ this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTION
179
353
  });
180
354
  ```
181
355
 
182
- ### Combined JWT + Basic with Auth Controller (Full Setup)
356
+ ### Combined JWKS + Basic with Auth Controller (Full Setup)
183
357
 
184
358
  ```typescript
185
359
  import {
186
360
  AuthenticateBindingKeys,
187
- IJWTTokenServiceOptions,
188
- IBasicTokenServiceOptions,
361
+ JOSEStandards,
362
+ JWKSModes,
363
+ JWKSKeyDrivers,
364
+ JWKSKeyFormats,
365
+ TJWTTokenServiceOptions,
366
+ TBasicTokenServiceOptions,
189
367
  TAuthenticationRestOptions,
368
+ BindingKeys,
369
+ BindingNamespaces,
190
370
  } from '@venizia/ignis';
191
371
 
192
372
  // Bind REST options (enables auth controller)
@@ -194,6 +374,10 @@ this.bind<TAuthenticationRestOptions>({ key: AuthenticateBindingKeys.REST_OPTION
194
374
  useAuthController: true,
195
375
  controllerOpts: {
196
376
  restPath: '/auth',
377
+ serviceKey: BindingKeys.build({
378
+ namespace: BindingNamespaces.SERVICE,
379
+ key: AuthenticationService.name,
380
+ }),
197
381
  payload: {
198
382
  signIn: {
199
383
  request: { schema: SignInRequestSchema },
@@ -203,19 +387,33 @@ this.bind<TAuthenticationRestOptions>({ key: AuthenticateBindingKeys.REST_OPTION
203
387
  request: { schema: SignUpRequestSchema },
204
388
  response: { schema: SignUpResponseSchema },
205
389
  },
390
+ changePassword: {
391
+ request: { schema: ChangePasswordRequestSchema },
392
+ response: { schema: ChangePasswordResponseSchema },
393
+ },
206
394
  },
207
395
  },
208
396
  });
209
397
 
210
- // Bind JWT options
211
- this.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
212
- applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
213
- jwtSecret: process.env.APP_ENV_JWT_SECRET,
214
- getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
398
+ // Bind JWT options (JWKS issuer mode)
399
+ this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
400
+ standard: JOSEStandards.JWKS,
401
+ options: {
402
+ mode: JWKSModes.ISSUER,
403
+ algorithm: 'ES256',
404
+ keys: {
405
+ driver: JWKSKeyDrivers.FILE,
406
+ format: JWKSKeyFormats.PEM,
407
+ private: './keys/private.pem',
408
+ public: './keys/public.pem',
409
+ },
410
+ kid: 'my-key-id-1',
411
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
412
+ },
215
413
  });
216
414
 
217
415
  // Bind Basic auth options
218
- this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
416
+ this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
219
417
  verifyCredentials: async (opts) => {
220
418
  const authenticateService = this.get<AuthenticationService>({
221
419
  key: BindingKeys.build({
@@ -231,14 +429,14 @@ this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTION
231
429
  });
232
430
  ```
233
431
 
234
- ### Step 2: Register Component
432
+ ### Step 2: Register Component and Strategies
235
433
 
236
434
  ```typescript
237
435
  import {
238
436
  AuthenticateComponent,
239
437
  Authentication,
240
438
  AuthenticationStrategyRegistry,
241
- JWTAuthenticationStrategy,
439
+ JWKSIssuerAuthenticationStrategy,
242
440
  BasicAuthenticationStrategy,
243
441
  BaseApplication,
244
442
  ValueOrPromise,
@@ -254,11 +452,11 @@ export class Application extends BaseApplication {
254
452
  // Register component
255
453
  this.component(AuthenticateComponent);
256
454
 
257
- // Register strategies
455
+ // Register strategies manually AFTER the component
258
456
  AuthenticationStrategyRegistry.getInstance().register({
259
457
  container: this,
260
458
  strategies: [
261
- { name: Authentication.STRATEGY_JWT, strategy: JWTAuthenticationStrategy },
459
+ { name: Authentication.STRATEGY_JWT, strategy: JWKSIssuerAuthenticationStrategy },
262
460
  { name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
263
461
  ],
264
462
  });
@@ -266,46 +464,141 @@ export class Application extends BaseApplication {
266
464
  }
267
465
  ```
268
466
 
467
+ > [!IMPORTANT]
468
+ > Strategies are NOT auto-registered by `AuthenticateComponent`. You must manually register them after calling `this.component(AuthenticateComponent)`. This gives you full control over which strategies are available.
469
+
269
470
  > [!NOTE]
270
- > Only register the strategies you need. JWT-only setups can omit `BasicAuthenticationStrategy` and vice versa.
471
+ > Choose the strategy class matching your JOSE standard:
472
+ > - JWS: `JWSAuthenticationStrategy`
473
+ > - JWKS Issuer: `JWKSIssuerAuthenticationStrategy`
474
+ > - JWKS Verifier: `JWKSVerifierAuthenticationStrategy`
271
475
 
272
476
  ## Configuration
273
477
 
274
- ### JWT Options
478
+ ### TJWTTokenServiceOptions (Discriminated Union)
275
479
 
276
- | Option | Type | Default | Description |
277
- |--------|------|---------|-------------|
278
- | `jwtSecret` | `string` | -- | Secret for signing and verifying JWT signature |
279
- | `applicationSecret` | `string` | -- | Secret for AES-encrypting JWT payload fields |
280
- | `getTokenExpiresFn` | `() => ValueOrPromise<number>` | -- | Function returning token expiration in seconds |
281
- | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | AES algorithm for payload encryption |
282
- | `headerAlgorithm` | `string` | `'HS256'` | JWT signing algorithm |
480
+ ```mermaid
481
+ flowchart LR
482
+ T["TJWTTokenServiceOptions"] --> S{"standard"}
483
+ S -->|"'JWS'"| JWS["IJWSTokenServiceOptions"]
484
+ S -->|"'JWKS'"| JWKS["TJWKSTokenServiceOptions"]
485
+ JWKS --> M{"mode"}
486
+ M -->|"'issuer'"| ISS["IJWKSIssuerOptions"]
487
+ M -->|"'verifier'"| VER["IJWKSVerifierOptions"]
283
488
 
284
- > [!WARNING]
285
- > Both `applicationSecret` and `jwtSecret` are mandatory when using JWT authentication. They must be strong, unique secret values. The component will throw an error if either is missing or set to `'unknown_secret'`. Additionally, the error message from `defineJWTAuth` **includes the actual provided secret value** in the error output (e.g., <code v-pre>[defineJWTAuth] Invalid jwtSecret | Provided: {{jwtSecret}}</code>), so ensure these errors are never exposed to end users.
489
+ style S fill:#e8f4fd,stroke:#0d6efd
490
+ style M fill:#e8f4fd,stroke:#0d6efd
491
+ ```
286
492
 
287
- > [!NOTE]
288
- > The `getTokenExpiresFn` is called on every token generation, not just once. This allows dynamic expiration (e.g., shorter tokens for mobile, longer for admin).
493
+ The top-level JWT options use a discriminated union on the `standard` field:
494
+
495
+ ```typescript
496
+ type TJWTTokenServiceOptions =
497
+ | { standard: typeof JOSEStandards.JWS; options: IJWSTokenServiceOptions }
498
+ | { standard: typeof JOSEStandards.JWKS; options: TJWKSTokenServiceOptions };
499
+ ```
500
+
501
+ This enables clean TypeScript narrowing — once you set `standard: JOSEStandards.JWS`, the `options` field is typed as `IJWSTokenServiceOptions`; with `standard: JOSEStandards.JWKS`, it becomes `TJWKSTokenServiceOptions`.
502
+
503
+ ### JWS Options (IJWSTokenServiceOptions)
289
504
 
290
- **Example `.env` file:**
505
+ | Option | Type | Default | Required | Description |
506
+ |--------|------|---------|----------|-------------|
507
+ | `jwtSecret` | `string` | -- | Yes | Secret for signing and verifying JWT signature |
508
+ | `getTokenExpiresFn` | `TGetTokenExpiresFn` | -- | Yes | Function returning token expiration in seconds |
509
+ | `applicationSecret` | `string` | -- | No | Secret for AES-encrypting JWT payload fields |
510
+ | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload encryption |
511
+ | `headerAlgorithm` | `string` | `'HS256'` | No | JWT signing algorithm |
512
+ | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom field codecs for payload serialization |
291
513
 
514
+ ```typescript
515
+ interface IJWSTokenServiceOptions {
516
+ headerAlgorithm?: string;
517
+ jwtSecret: string;
518
+ getTokenExpiresFn: TGetTokenExpiresFn;
519
+ aesAlgorithm?: AESAlgorithmType;
520
+ applicationSecret?: string;
521
+ fieldCodecs?: IPayloadFieldCodec[];
522
+ }
292
523
  ```
293
- APP_ENV_APPLICATION_SECRET=your-strong-application-secret
294
- APP_ENV_JWT_SECRET=your-strong-jwt-secret
295
- APP_ENV_JWT_EXPIRES_IN=86400
524
+
525
+ > [!WARNING]
526
+ > `jwtSecret` is mandatory. The component will throw an error if it is missing or set to `'unknown_secret'`. The error message from `defineJWSAuth` **includes the actual provided secret value** in the error output, so ensure these errors are never exposed to end users.
527
+
528
+ > [!NOTE]
529
+ > `applicationSecret` is optional. When provided, custom JWT payload fields are AES-encrypted (keys and values). When omitted, the JWT payload is stored in standard plaintext. Standard JWT fields (`iss`, `sub`, `aud`, `jti`, `nbf`, `exp`, `iat`) are never encrypted.
530
+
531
+ ### JWKS Issuer Options (IJWKSIssuerOptions)
532
+
533
+ | Option | Type | Default | Required | Description |
534
+ |--------|------|---------|----------|-------------|
535
+ | `mode` | `typeof JWKSModes.ISSUER` | -- | Yes | Must be `'issuer'` |
536
+ | `algorithm` | `TJWKSAlgorithm` | -- | Yes | Signing algorithm: `'ES256'`, `'RS256'`, or `'EdDSA'` |
537
+ | `keys.driver` | `TJWKSKeyDriver` | -- | Yes | Key source: `'text'` (inline) or `'file'` (file path) |
538
+ | `keys.format` | `TJWKSKeyFormat` | -- | Yes | Key format: `'pem'` or `'jwk'` |
539
+ | `keys.private` | `string` | -- | Yes | Private key content (text) or file path (file) |
540
+ | `keys.public` | `string` | -- | Yes | Public key content (text) or file path (file) |
541
+ | `kid` | `string` | -- | Yes | Key ID exposed in the JWKS endpoint |
542
+ | `getTokenExpiresFn` | `TGetTokenExpiresFn` | -- | Yes | Function returning token expiration in seconds |
543
+ | `rest` | `{ path: string }` | `{ path: '/certs' }` | No | Custom path for the JWKS endpoint |
544
+ | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload encryption |
545
+ | `applicationSecret` | `string` | -- | No | Secret for AES-encrypting JWT payload fields |
546
+ | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom field codecs for payload serialization |
547
+
548
+ ```typescript
549
+ interface IJWKSIssuerOptions {
550
+ mode: typeof JWKSModes.ISSUER;
551
+ algorithm: TJWKSAlgorithm;
552
+ rest?: { path: string };
553
+ keys: {
554
+ driver: TJWKSKeyDriver;
555
+ format: TJWKSKeyFormat;
556
+ private: string;
557
+ public: string;
558
+ };
559
+ kid: string;
560
+ getTokenExpiresFn: TGetTokenExpiresFn;
561
+ aesAlgorithm?: AESAlgorithmType;
562
+ applicationSecret?: string;
563
+ fieldCodecs?: IPayloadFieldCodec[];
564
+ }
296
565
  ```
297
566
 
298
- #### IJWTTokenServiceOptions -- Full Interface
567
+ ### JWKS Verifier Options (IJWKSVerifierOptions)
568
+
569
+ | Option | Type | Default | Required | Description |
570
+ |--------|------|---------|----------|-------------|
571
+ | `mode` | `typeof JWKSModes.VERIFIER` | -- | Yes | Must be `'verifier'` |
572
+ | `jwksUrl` | `string` | -- | Yes | URL of the remote JWKS endpoint |
573
+ | `cacheTtlMs` | `number` | `43_200_000` (12h) | No | How long to cache the JWKS response |
574
+ | `cooldownMs` | `number` | `30_000` (30s) | No | Minimum time between JWKS refreshes |
575
+ | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload decryption |
576
+ | `applicationSecret` | `string` | -- | No | Secret for AES-decrypting JWT payload fields |
577
+ | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom field codecs for payload deserialization |
578
+
299
579
  ```typescript
300
- interface IJWTTokenServiceOptions {
301
- jwtSecret: string;
302
- applicationSecret: string;
303
- getTokenExpiresFn: () => ValueOrPromise<number>;
580
+ interface IJWKSVerifierOptions {
581
+ mode: typeof JWKSModes.VERIFIER;
582
+ jwksUrl: string;
583
+ cacheTtlMs?: number;
584
+ cooldownMs?: number;
304
585
  aesAlgorithm?: AESAlgorithmType;
305
- headerAlgorithm?: string;
586
+ applicationSecret?: string;
587
+ fieldCodecs?: IPayloadFieldCodec[];
306
588
  }
307
589
  ```
308
590
 
591
+ > [!IMPORTANT]
592
+ > In verifier mode, the `applicationSecret` must match the issuer's secret exactly. If the issuer encrypts payloads with AES, the verifier must use the same `applicationSecret` to decrypt them.
593
+
594
+ ### JWKS Token Service Options (Union)
595
+
596
+ ```typescript
597
+ type TJWKSTokenServiceOptions = IJWKSIssuerOptions | IJWKSVerifierOptions;
598
+ ```
599
+
600
+ This union is discriminated on the `mode` field (`'issuer'` vs `'verifier'`).
601
+
309
602
  ### Basic Auth Options
310
603
 
311
604
  | Option | Type | Default | Description |
@@ -321,14 +614,14 @@ type TBasicAuthVerifyFn<E extends Env = Env> = (opts: {
321
614
  }) => Promise<IAuthUser | null>;
322
615
  ```
323
616
 
324
- #### IBasicTokenServiceOptions -- Full Interface
617
+ #### TBasicTokenServiceOptions -- Full Interface
325
618
  ```typescript
326
- interface IBasicTokenServiceOptions<E extends Env = Env> {
619
+ type TBasicTokenServiceOptions<E extends Env = Env> = {
327
620
  verifyCredentials: (opts: {
328
621
  credentials: { username: string; password: string };
329
622
  context: TContext<E, string>;
330
623
  }) => Promise<IAuthUser | null>;
331
- }
624
+ };
332
625
  ```
333
626
 
334
627
  ### REST Options
@@ -358,7 +651,7 @@ type TAuthenticationRestOptions = {} & (
358
651
  | Option | Type | Default | Description |
359
652
  |--------|------|---------|-------------|
360
653
  | `restPath` | `string` | `'/auth'` | Base path for auth endpoints |
361
- | `serviceKey` | `string` | `'services.AuthenticationService'` | DI key for the auth service |
654
+ | `serviceKey` | `string` | -- | DI key for the auth service (required) |
362
655
  | `requireAuthenticatedSignUp` | `boolean` | `false` | Whether sign-up requires JWT authentication |
363
656
  | `payload` | `object` | `{}` | Custom Zod schemas for request/response payloads |
364
657
 
@@ -366,7 +659,7 @@ type TAuthenticationRestOptions = {} & (
366
659
  ```typescript
367
660
  type TDefineAuthControllerOpts = {
368
661
  restPath?: string;
369
- serviceKey?: string;
662
+ serviceKey: string;
370
663
  requireAuthenticatedSignUp?: boolean;
371
664
  payload?: {
372
665
  signIn?: {
@@ -387,13 +680,29 @@ type TDefineAuthControllerOpts = {
387
680
 
388
681
  ### Route Configuration Options
389
682
 
390
- These options are used in route configs to control per-route authentication:
683
+ Per-route authentication is configured via the `authenticate` field on route configs, using `TRouteAuthenticateConfig`:
684
+
685
+ ```typescript
686
+ type TRouteAuthenticateConfig =
687
+ | { skip: true }
688
+ | { skip?: false; strategies?: TAuthStrategy[]; mode?: TAuthMode };
689
+ ```
391
690
 
392
691
  | Option | Type | Default | Description |
393
692
  |--------|------|---------|-------------|
394
- | `authStrategies` | `TAuthStrategy[]` | -- | Array of strategy names (e.g., `['jwt']`, `['jwt', 'basic']`) |
395
- | `authMode` | `'any' \| 'all'` | `'any'` | How to handle multiple strategies |
396
- | `skipAuth` | `boolean` | `false` | Skip authentication for this route |
693
+ | `authenticate.strategies` | `TAuthStrategy[]` | -- | Array of strategy names (e.g., `['jwt']`, `['jwt', 'basic']`) |
694
+ | `authenticate.mode` | `'any' \| 'all'` | `'any'` | How to handle multiple strategies |
695
+ | `authenticate.skip` | `true` | -- | Skip authentication for this route |
696
+
697
+ Example route config:
698
+ ```typescript
699
+ const SECURE_ROUTE = {
700
+ path: '/data',
701
+ method: HTTP.Methods.GET,
702
+ authenticate: { strategies: [Authentication.STRATEGY_JWT] },
703
+ responses: jsonResponse({ description: 'Protected', schema: z.object({ data: z.any() }) }),
704
+ } as const;
705
+ ```
397
706
 
398
707
  ### IAuthUser Interface
399
708
 
@@ -482,12 +791,16 @@ interface IJWTTokenPayload extends JWTPayload, IAuthUser {
482
791
  | Key | Constant | Type | Required | Default |
483
792
  |-----|----------|------|----------|---------|
484
793
  | `@app/authenticate/rest-options` | `AuthenticateBindingKeys.REST_OPTIONS` | `TAuthenticationRestOptions` | No | <code v-pre>{ useAuthController: false }</code> |
485
- | `@app/authenticate/jwt-options` | `AuthenticateBindingKeys.JWT_OPTIONS` | `IJWTTokenServiceOptions` | Conditional | -- |
486
- | `@app/authenticate/basic-options` | `AuthenticateBindingKeys.BASIC_OPTIONS` | `IBasicTokenServiceOptions` | Conditional | -- |
794
+ | `@app/authenticate/jwt-options` | `AuthenticateBindingKeys.JWT_OPTIONS` | `TJWTTokenServiceOptions` | Conditional | -- |
795
+ | `@app/authenticate/jwks-options` | `AuthenticateBindingKeys.JWKS_OPTIONS` | `IJWKSIssuerOptions \| IJWKSVerifierOptions` | Internal | Bound by the component |
796
+ | `@app/authenticate/basic-options` | `AuthenticateBindingKeys.BASIC_OPTIONS` | `TBasicTokenServiceOptions` | Conditional | -- |
487
797
 
488
798
  > [!IMPORTANT]
489
799
  > At least one of `JWT_OPTIONS` or `BASIC_OPTIONS` must be bound. If neither is configured, the component will throw an error during `binding()`.
490
800
 
801
+ > [!NOTE]
802
+ > `JWKS_OPTIONS` is bound internally by the component when `standard: JOSEStandards.JWKS` is configured. You do not need to bind it manually. The component extracts the JWKS options from the discriminated union and re-binds them to `JWKS_OPTIONS` so that the JWKS services can resolve them via `@inject`.
803
+
491
804
  ### Context Variables
492
805
 
493
806
  These values are set on the Hono `Context` during authentication and can be accessed via `context.get()`: