@venizia/ignis-docs 0.2.0 → 0.2.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) 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 +24 -13
  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 +27 -3
  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 +8 -4
  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 +247 -153
  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 +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,346 +1,111 @@
1
- # Authentication -- Setup & Configuration
2
-
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
-
5
- ## Quick Reference
6
-
7
- | Item | Value |
8
- |------|-------|
9
- | **Package** | `@venizia/ignis` |
10
- | **Class** | `AuthenticateComponent` |
11
- | **Runtimes** | Both |
12
-
13
- ### Key Components
14
-
15
- | Component | Purpose |
16
- |-----------|---------|
17
- | **AuthenticateComponent** | Main component registering auth services and controllers |
18
- | **AuthenticationStrategyRegistry** | Singleton managing available auth strategies |
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 |
22
- | **BasicAuthenticationStrategy** | Basic HTTP authentication using `BasicTokenService` |
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 |
27
- | **BasicTokenService** | Extract and verify Basic auth credentials |
28
- | **JWKSController** | Serves `/.well-known/jwks.json`-style endpoint at `/certs` |
29
- | **IAuthService** | Interface for custom auth implementation (sign-in, sign-up) |
30
- | **defineAuthController** | Factory function for creating custom auth controllers |
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 |
43
-
44
- ### Environment Variables
45
-
46
- | Variable | Purpose | Required |
47
- |----------|---------|----------|
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 |
57
-
58
- ### Auth Modes
59
-
60
- | Mode | Behavior |
61
- |------|----------|
62
- | `'any'` | First successful strategy wins (fallback mode) |
63
- | `'all'` | All strategies must pass (MFA mode) |
64
-
65
- ### Token Types
66
-
67
- | Constant | Value | Description |
68
- |----------|-------|-------------|
69
- | `AuthenticationTokenTypes.TYPE_AUTHORIZATION_CODE` | `'000_AUTHORIZATION_CODE'` | Authorization code grant type |
70
- | `AuthenticationTokenTypes.TYPE_ACCESS_TOKEN` | `'100_ACCESS_TOKEN'` | Access token type |
71
- | `AuthenticationTokenTypes.TYPE_REFRESH_TOKEN` | `'200_REFRESH_TOKEN'` | Refresh token type |
72
-
73
- ### Authentication Constants
74
-
75
- | Constant | Value | Description |
76
- |----------|-------|-------------|
77
- | `Authentication.AUTHENTICATION_STRATEGY` | `'authentication.strategy'` | Namespace prefix for strategy binding keys |
78
- | `Authentication.STRATEGY_JWT` | `'jwt'` | JWT strategy name |
79
- | `Authentication.STRATEGY_BASIC` | `'basic'` | Basic strategy name |
80
- | `Authentication.TYPE_BEARER` | `'Bearer'` | Bearer token type prefix |
81
- | `Authentication.TYPE_BASIC` | `'Basic'` | Basic token type prefix |
82
- | `Authentication.SKIP_AUTHENTICATION` | `'authentication.skip'` | Context key to dynamically skip auth |
83
- | `Authentication.CURRENT_USER` | `'auth.current.user'` | Context key for the authenticated user payload |
84
- | `Authentication.AUDIT_USER_ID` | `'audit.user.id'` | Context key for the authenticated user ID |
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
-
103
- #### Import Paths
1
+ ---
2
+ title: Authentication
3
+ description: JWT (JWS/JWKS) and Basic HTTP authentication component with multi-strategy support and a built-in auth controller
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Authentication
8
+
9
+ `AuthenticateComponent` wires up JWT and/or Basic HTTP authentication. It configures token services, route-level strategies, and an optional built-in `/auth` controller for sign-in, sign-up, and password change.
10
+
11
+ ## In one example
12
+
13
+ The smallest real setup: symmetric JWT (JWS), one strategy, one protected route.
104
14
 
105
15
  ```typescript
106
16
  import {
107
- // Component + Registry
108
- AuthenticateComponent,
109
17
  AuthenticateBindingKeys,
18
+ AuthenticateComponent,
110
19
  Authentication,
111
- AuthenticationFieldCodecs,
112
- AuthenticationModes,
113
- AuthenticationTokenTypes,
114
20
  AuthenticationStrategyRegistry,
115
-
116
- // JOSE Standards + Constants
21
+ BaseApplication,
117
22
  JOSEStandards,
118
- JWKSModes,
119
- JWKSKeyDrivers,
120
- JWKSKeyFormats,
121
-
122
- // Strategies
123
23
  JWSAuthenticationStrategy,
124
- JWKSIssuerAuthenticationStrategy,
125
- JWKSVerifierAuthenticationStrategy,
126
- BasicAuthenticationStrategy,
127
-
128
- // Services
129
- AbstractBearerTokenService,
130
- JWSTokenService,
131
- JWKSIssuerTokenService,
132
- JWKSVerifierTokenService,
133
- BasicTokenService,
134
-
135
- // Controllers
136
- defineAuthController,
137
- JWKSController,
138
- authenticate,
139
- } from '@venizia/ignis';
140
-
141
- import type {
142
- // Option types
143
- TAuthenticationRestOptions,
144
24
  TJWTTokenServiceOptions,
145
- IJWSTokenServiceOptions,
146
- IJWKSIssuerOptions,
147
- IJWKSVerifierOptions,
148
- TJWKSTokenServiceOptions,
149
- TBasicTokenServiceOptions,
150
- IAuthenticateOptions,
151
-
152
- // User + payload types
153
- IAuthUser,
154
- IJWTTokenPayload,
155
- IPayloadFieldCodec,
156
- IAuthService,
157
- IAuthenticationStrategy,
158
-
159
- // Controller types
160
- TDefineAuthControllerOpts,
161
-
162
- // Utility types
163
- TAuthStrategy,
164
- TAuthMode,
165
- TGetTokenExpiresFn,
166
- TJWKSAlgorithm,
167
- TJWKSKeyDriver,
168
- TJWKSKeyFormat,
169
- TJOSEStandard,
170
- TJWKSMode,
25
+ ValueOrPromise,
171
26
  } from '@venizia/ignis';
172
- ```
173
27
 
174
- #### Entity Column Helper Imports
28
+ export class Application extends BaseApplication {
29
+ preConfigure(): ValueOrPromise<void> {
30
+ this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
31
+ standard: JOSEStandards.JWS,
32
+ options: {
33
+ jwtSecret: process.env.APP_ENV_JWT_SECRET!,
34
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
35
+ },
36
+ });
175
37
 
176
- ```typescript
177
- import {
178
- extraUserColumns,
179
- extraRoleColumns,
180
- extraPermissionColumns,
181
- extraPolicyDefinitionColumns,
182
- } from '@venizia/ignis';
38
+ this.component(AuthenticateComponent);
183
39
 
184
- import type {
185
- TPermissionOptions,
186
- TPermissionCommonColumns,
187
- TPolicyDefinitionOptions,
188
- TPolicyDefinitionCommonColumns,
189
- } from '@venizia/ignis';
40
+ // Strategies are NOT auto-registered - register them after the component
41
+ AuthenticationStrategyRegistry.getInstance().register({
42
+ container: this,
43
+ strategies: [{ name: Authentication.STRATEGY_JWT, strategy: JWSAuthenticationStrategy }],
44
+ });
45
+ }
46
+ }
190
47
  ```
191
48
 
192
- #### Status and Type Imports
193
-
194
49
  ```typescript
195
- import {
196
- UserStatuses,
197
- UserTypes,
198
- RoleStatuses,
199
- } from '@venizia/ignis';
50
+ const SECURE_ROUTE = {
51
+ path: '/data',
52
+ method: HTTP.Methods.GET,
53
+ authenticate: { strategies: [Authentication.STRATEGY_JWT] },
54
+ responses: jsonResponse({ description: 'Protected', schema: z.object({ data: z.any() }) }),
55
+ } as const;
200
56
  ```
201
57
 
202
- ## Component Binding Lifecycle
203
-
204
- ```mermaid
205
- flowchart TD
206
- A["preConfigure()"] --> B["Bind JWT_OPTIONS / BASIC_OPTIONS / REST_OPTIONS"]
207
- B --> C["this.component(AuthenticateComponent)"]
208
- C --> D["AuthenticateComponent.binding()"]
209
- D --> E{"jwtOptions.standard?"}
210
- E -->|"JWS"| F["defineJWSAuth()"]
211
- E -->|"JWKS"| G["defineJWKSAuth()"]
212
- E -->|"none"| H["Skip JWT"]
213
- F --> I["Register JWSTokenService"]
214
- G --> J{"mode?"}
215
- J -->|"issuer"| K["Register JWKSIssuerTokenService + JWKSController"]
216
- J -->|"verifier"| L["Register JWKSVerifierTokenService"]
217
- D --> M["defineBasicAuth()"]
218
- M --> N["Register BasicTokenService"]
219
- D --> O["defineControllers()"]
220
- O --> P["Register AuthController (factory-built)"]
221
- C --> Q["Manual strategy registration"]
222
- Q --> R["AuthenticationStrategyRegistry.register()"]
223
-
224
- style E fill:#fff3cd,stroke:#ffc107
225
- style J fill:#fff3cd,stroke:#ffc107
226
- ```
58
+ ## How it works
227
59
 
228
- ## Setup
60
+ - **One component, three auth mechanisms.** `AuthenticateComponent.binding()` reads `JWT_OPTIONS` (JWS or JWKS) and `BASIC_OPTIONS` from the DI container, and registers the token services their presence implies.
61
+ - Bind at least one of the two - the component throws at startup if neither is set.
62
+ - **Strategies are manual, on purpose.** The component registers *token services* - JWS, JWKS, Basic - never *strategies*. Register strategies yourself after the component, via `AuthenticationStrategyRegistry.getInstance().register(...)`.
63
+ - **The registry is a DI-backed singleton.** `AuthenticationStrategyRegistry` binds each strategy into the container as a singleton, under the key `authentication.strategy.<name>`. A route's `authenticate.strategies` list references strategies by that same name.
64
+ - **`authenticate()` is the middleware entry point.** Route-level `authenticate: { strategies, mode }` config and the standalone `authenticate()` function both run through the same `AuthenticationProvider`, which sets `Authentication.CURRENT_USER` on success. See [Multi-strategy authentication](./usage#multi-strategy-authentication) for what `'any'` and `'all'` mode do.
65
+ - **The auth controller is optional and generated.** Set `REST_OPTIONS.useAuthController: true` to call `defineAuthController()`, which builds a `BaseRestController` subclass at runtime - routes for `/sign-in`, `/sign-up`, `/change-password`, `/token/refresh`, `/who-am-i`, and `/me`. You back it with your own `IAuthService` implementation.
229
66
 
230
- ### Step 1: Bind Configuration
67
+ **JOSE standards**
231
68
 
232
- Bind JWT options using the discriminated union `TJWTTokenServiceOptions`, which requires a `standard` field to select the JOSE standard.
69
+ | Standard | Class | Keying | Use case |
70
+ |----------|-------|--------|----------|
71
+ | JWS | `JWSTokenService` | Shared secret (HS256) | Single service signs and verifies |
72
+ | JWKS Issuer | `JWKSIssuerTokenService` | Private + public key (ES256/RS256/EdDSA) | Issues tokens, serves `/certs` |
73
+ | JWKS Verifier | `JWKSVerifierTokenService` | Remote JWKS URL | Verifies tokens from another issuer, only |
233
74
 
234
- ### JWS (Symmetric JWT) Setup
75
+ ## Common tasks
235
76
 
236
- ```typescript
237
- import {
238
- AuthenticateBindingKeys,
239
- JOSEStandards,
240
- TJWTTokenServiceOptions,
241
- } from '@venizia/ignis';
77
+ **Configure JWS (symmetric JWT).** One shared secret signs and verifies.
242
78
 
79
+ ```typescript
243
80
  this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
244
81
  standard: JOSEStandards.JWS,
245
82
  options: {
246
- jwtSecret: process.env.APP_ENV_JWT_SECRET,
247
- applicationSecret: process.env.APP_ENV_APPLICATION_SECRET, // Optional - enables AES payload encryption
83
+ jwtSecret: process.env.APP_ENV_JWT_SECRET!,
248
84
  getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
249
85
  },
250
86
  });
251
87
  ```
252
88
 
253
- **Example `.env` file (JWS):**
254
-
255
- ```
256
- APP_ENV_JWT_SECRET=your-strong-jwt-secret
257
- APP_ENV_APPLICATION_SECRET=your-strong-application-secret
258
- APP_ENV_JWT_EXPIRES_IN=86400
259
- ```
260
-
261
- > [!NOTE]
262
- > `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).
263
-
264
- ### JWKS Issuer (Asymmetric JWT) Setup
89
+ **Configure JWKS Issuer (asymmetric JWT, microservice-friendly).** Signs with a private key, serves the public key at `/certs`.
265
90
 
266
91
  ```typescript
267
- import {
268
- AuthenticateBindingKeys,
269
- JOSEStandards,
270
- JWKSModes,
271
- JWKSKeyDrivers,
272
- JWKSKeyFormats,
273
- TJWTTokenServiceOptions,
274
- } from '@venizia/ignis';
275
-
276
92
  this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
277
93
  standard: JOSEStandards.JWKS,
278
94
  options: {
279
95
  mode: JWKSModes.ISSUER,
280
96
  algorithm: 'ES256',
281
- keys: {
282
- driver: JWKSKeyDrivers.FILE, // or JWKSKeyDrivers.TEXT
283
- format: JWKSKeyFormats.PEM, // or JWKSKeyFormats.JWK
284
- private: './keys/private.pem',
285
- public: './keys/public.pem',
286
- },
97
+ keys: { driver: JWKSKeyDrivers.FILE, format: JWKSKeyFormats.PEM, private: './keys/private.pem', public: './keys/public.pem' },
287
98
  kid: 'my-key-id-1',
288
99
  getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
289
- // Optional AES payload encryption
290
- aesAlgorithm: 'aes-256-cbc',
291
- applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
292
- },
293
- });
294
- ```
295
-
296
- **Example `.env` file (JWKS Issuer):**
297
-
298
- ```
299
- APP_ENV_JWKS_ALGORITHM=ES256
300
- APP_ENV_JWKS_KEY_DRIVER=file
301
- APP_ENV_JWKS_KEY_FORMAT=pem
302
- APP_ENV_JWKS_PRIVATE_KEY=./keys/private.pem
303
- APP_ENV_JWKS_PUBLIC_KEY=./keys/public.pem
304
- APP_ENV_JWKS_KID=my-key-id-1
305
- APP_ENV_JWT_EXPIRES_IN=86400
306
- APP_ENV_APPLICATION_SECRET=your-strong-application-secret
307
- ```
308
-
309
- ### JWKS Verifier (Remote Verification) Setup
310
-
311
- ```typescript
312
- import {
313
- AuthenticateBindingKeys,
314
- JOSEStandards,
315
- JWKSModes,
316
- TJWTTokenServiceOptions,
317
- } from '@venizia/ignis';
318
-
319
- this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
320
- standard: JOSEStandards.JWKS,
321
- options: {
322
- mode: JWKSModes.VERIFIER,
323
- jwksUrl: 'https://auth-service.example.com/certs',
324
- cacheTtlMs: 43_200_000, // Cache JWKS for 12 hours (default)
325
- cooldownMs: 30_000, // Wait 30s between JWKS refreshes (default)
326
- // Optional AES payload decryption (must match issuer's applicationSecret)
327
- aesAlgorithm: 'aes-256-cbc',
328
- applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
329
100
  },
330
101
  });
331
102
  ```
332
103
 
333
- ### Basic Auth Only (Alternative Setup)
104
+ **Add Basic auth.** Provide a `verifyCredentials` callback. It becomes the `'basic'` strategy's source of truth.
334
105
 
335
106
  ```typescript
336
- import {
337
- AuthenticateBindingKeys,
338
- TBasicTokenServiceOptions,
339
- } from '@venizia/ignis';
340
-
341
107
  this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
342
- verifyCredentials: async (opts) => {
343
- const { credentials, context } = opts;
108
+ verifyCredentials: async ({ credentials, context }) => {
344
109
  const user = await userRepo.findByUsername(credentials.username);
345
110
  if (user && await bcrypt.compare(credentials.password, user.passwordHash)) {
346
111
  return { userId: user.id, roles: user.roles };
@@ -350,534 +115,57 @@ this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTION
350
115
  });
351
116
  ```
352
117
 
353
- ### Combined JWKS + Basic with Auth Controller (Full Setup)
118
+ **Enable the built-in `/auth` controller.** Requires `jwtOptions` and a bound `IAuthService`.
354
119
 
355
120
  ```typescript
356
- import {
357
- AuthenticateBindingKeys,
358
- JOSEStandards,
359
- JWKSModes,
360
- JWKSKeyDrivers,
361
- JWKSKeyFormats,
362
- TJWTTokenServiceOptions,
363
- TBasicTokenServiceOptions,
364
- TAuthenticationRestOptions,
365
- BindingKeys,
366
- BindingNamespaces,
367
- } from '@venizia/ignis';
121
+ this.service(AuthenticationService);
368
122
 
369
- // Bind REST options (enables auth controller)
370
123
  this.bind<TAuthenticationRestOptions>({ key: AuthenticateBindingKeys.REST_OPTIONS }).toValue({
371
124
  useAuthController: true,
372
125
  controllerOpts: {
373
126
  restPath: '/auth',
374
- serviceKey: BindingKeys.build({
375
- namespace: BindingNamespaces.SERVICE,
376
- key: AuthenticationService.name,
377
- }),
378
- payload: {
379
- signIn: {
380
- request: { schema: SignInRequestSchema },
381
- response: { schema: SignInResponseSchema },
382
- },
383
- signUp: {
384
- request: { schema: SignUpRequestSchema },
385
- response: { schema: SignUpResponseSchema },
386
- },
387
- changePassword: {
388
- request: { schema: ChangePasswordRequestSchema },
389
- response: { schema: ChangePasswordResponseSchema },
390
- },
391
- },
392
- },
393
- });
394
-
395
- // Bind JWT options (JWKS issuer mode)
396
- this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
397
- standard: JOSEStandards.JWKS,
398
- options: {
399
- mode: JWKSModes.ISSUER,
400
- algorithm: 'ES256',
401
- keys: {
402
- driver: JWKSKeyDrivers.FILE,
403
- format: JWKSKeyFormats.PEM,
404
- private: './keys/private.pem',
405
- public: './keys/public.pem',
406
- },
407
- kid: 'my-key-id-1',
408
- getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
409
- },
410
- });
411
-
412
- // Bind Basic auth options
413
- this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
414
- verifyCredentials: async (opts) => {
415
- const authenticateService = this.get<AuthenticationService>({
416
- key: BindingKeys.build({
417
- namespace: BindingNamespaces.SERVICE,
418
- key: AuthenticationService.name,
419
- }),
420
- });
421
- return authenticateService.signIn(opts.context, {
422
- identifier: { scheme: 'username', value: opts.credentials.username },
423
- credential: { scheme: 'basic', value: opts.credentials.password },
424
- });
127
+ serviceKey: BindingKeys.build({ namespace: BindingNamespaces.SERVICE, key: AuthenticationService.name }),
425
128
  },
426
129
  });
427
130
  ```
428
131
 
429
- ### Step 2: Register Component and Strategies
132
+ **Secure a route with multiple strategies.** `mode: 'any'` (default) falls back through strategies. `mode: 'all'` requires every one to pass.
430
133
 
431
134
  ```typescript
432
- import {
433
- AuthenticateComponent,
434
- Authentication,
435
- AuthenticationStrategyRegistry,
436
- JWKSIssuerAuthenticationStrategy,
437
- BasicAuthenticationStrategy,
438
- BaseApplication,
439
- ValueOrPromise,
440
- } from '@venizia/ignis';
441
-
442
- export class Application extends BaseApplication {
443
- preConfigure(): ValueOrPromise<void> {
444
- // Register your auth service (if using auth controller)
445
- this.service(AuthenticationService);
446
-
447
- // Step 1 bindings here...
448
-
449
- // Register component
450
- this.component(AuthenticateComponent);
451
-
452
- // Register strategies manually AFTER the component
453
- AuthenticationStrategyRegistry.getInstance().register({
454
- container: this,
455
- strategies: [
456
- { name: Authentication.STRATEGY_JWT, strategy: JWKSIssuerAuthenticationStrategy },
457
- { name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
458
- ],
459
- });
460
- }
461
- }
462
- ```
463
-
464
- > [!IMPORTANT]
465
- > 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.
466
-
467
- > [!NOTE]
468
- > Choose the strategy class matching your JOSE standard:
469
- > - JWS: `JWSAuthenticationStrategy`
470
- > - JWKS Issuer: `JWKSIssuerAuthenticationStrategy`
471
- > - JWKS Verifier: `JWKSVerifierAuthenticationStrategy`
472
-
473
- ## Configuration
474
-
475
- ### TJWTTokenServiceOptions (Discriminated Union)
476
-
477
- ```mermaid
478
- flowchart LR
479
- T["TJWTTokenServiceOptions"] --> S{"standard"}
480
- S -->|"'JWS'"| JWS["IJWSTokenServiceOptions"]
481
- S -->|"'JWKS'"| JWKS["TJWKSTokenServiceOptions"]
482
- JWKS --> M{"mode"}
483
- M -->|"'issuer'"| ISS["IJWKSIssuerOptions"]
484
- M -->|"'verifier'"| VER["IJWKSVerifierOptions"]
485
-
486
- style S fill:#e8f4fd,stroke:#0d6efd
487
- style M fill:#e8f4fd,stroke:#0d6efd
488
- ```
489
-
490
- The top-level JWT options use a discriminated union on the `standard` field:
491
-
492
- ```typescript
493
- type TJWTTokenServiceOptions =
494
- | { standard: typeof JOSEStandards.JWS; options: IJWSTokenServiceOptions }
495
- | { standard: typeof JOSEStandards.JWKS; options: TJWKSTokenServiceOptions };
496
- ```
497
-
498
- 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`.
499
-
500
- ### JWS Options (IJWSTokenServiceOptions)
501
-
502
- | Option | Type | Default | Required | Description |
503
- |--------|------|---------|----------|-------------|
504
- | `jwtSecret` | `string` | -- | Yes | Secret for signing and verifying JWT signature |
505
- | `getTokenExpiresFn` | `TGetTokenExpiresFn` | -- | Yes | Function returning token expiration in seconds |
506
- | `applicationSecret` | `string` | -- | No | Secret for AES-encrypting JWT payload fields |
507
- | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload encryption |
508
- | `headerAlgorithm` | `string` | `'HS256'` | No | JWT signing algorithm |
509
- | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom field codecs for payload serialization |
510
-
511
- ```typescript
512
- interface IJWSTokenServiceOptions {
513
- headerAlgorithm?: string;
514
- jwtSecret: string;
515
- getTokenExpiresFn: TGetTokenExpiresFn;
516
- aesAlgorithm?: AESAlgorithmType;
517
- applicationSecret?: string;
518
- fieldCodecs?: IPayloadFieldCodec[];
519
- }
520
- ```
521
-
522
- > [!WARNING]
523
- > `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.
524
-
525
- > [!NOTE]
526
- > `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.
527
-
528
- ### JWKS Issuer Options (IJWKSIssuerOptions)
529
-
530
- | Option | Type | Default | Required | Description |
531
- |--------|------|---------|----------|-------------|
532
- | `mode` | `typeof JWKSModes.ISSUER` | -- | Yes | Must be `'issuer'` |
533
- | `algorithm` | `TJWKSAlgorithm` | -- | Yes | Signing algorithm: `'ES256'`, `'RS256'`, or `'EdDSA'` |
534
- | `keys.driver` | `TJWKSKeyDriver` | -- | Yes | Key source: `'text'` (inline) or `'file'` (file path) |
535
- | `keys.format` | `TJWKSKeyFormat` | -- | Yes | Key format: `'pem'` or `'jwk'` |
536
- | `keys.private` | `string` | -- | Yes | Private key content (text) or file path (file) |
537
- | `keys.public` | `string` | -- | Yes | Public key content (text) or file path (file) |
538
- | `kid` | `string` | -- | Yes | Key ID exposed in the JWKS endpoint |
539
- | `getTokenExpiresFn` | `TGetTokenExpiresFn` | -- | Yes | Function returning token expiration in seconds |
540
- | `rest` | `{ path: string }` | `{ path: '/certs' }` | No | Custom path for the JWKS endpoint |
541
- | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload encryption |
542
- | `applicationSecret` | `string` | -- | No | Secret for AES-encrypting JWT payload fields |
543
- | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom field codecs for payload serialization |
544
-
545
- ```typescript
546
- interface IJWKSIssuerOptions {
547
- mode: typeof JWKSModes.ISSUER;
548
- algorithm: TJWKSAlgorithm;
549
- rest?: { path: string };
550
- keys: {
551
- driver: TJWKSKeyDriver;
552
- format: TJWKSKeyFormat;
553
- private: string;
554
- public: string;
555
- };
556
- kid: string;
557
- getTokenExpiresFn: TGetTokenExpiresFn;
558
- aesAlgorithm?: AESAlgorithmType;
559
- applicationSecret?: string;
560
- fieldCodecs?: IPayloadFieldCodec[];
561
- }
562
- ```
563
-
564
- ### JWKS Verifier Options (IJWKSVerifierOptions)
565
-
566
- | Option | Type | Default | Required | Description |
567
- |--------|------|---------|----------|-------------|
568
- | `mode` | `typeof JWKSModes.VERIFIER` | -- | Yes | Must be `'verifier'` |
569
- | `jwksUrl` | `string` | -- | Yes | URL of the remote JWKS endpoint |
570
- | `cacheTtlMs` | `number` | `43_200_000` (12h) | No | How long to cache the JWKS response |
571
- | `cooldownMs` | `number` | `30_000` (30s) | No | Minimum time between JWKS refreshes |
572
- | `aesAlgorithm` | `AESAlgorithmType` | `'aes-256-cbc'` | No | AES algorithm for payload decryption |
573
- | `applicationSecret` | `string` | -- | No | Secret for AES-decrypting JWT payload fields |
574
- | `fieldCodecs` | `IPayloadFieldCodec[]` | `[]` | No | Custom field codecs for payload deserialization |
575
-
576
- ```typescript
577
- interface IJWKSVerifierOptions {
578
- mode: typeof JWKSModes.VERIFIER;
579
- jwksUrl: string;
580
- cacheTtlMs?: number;
581
- cooldownMs?: number;
582
- aesAlgorithm?: AESAlgorithmType;
583
- applicationSecret?: string;
584
- fieldCodecs?: IPayloadFieldCodec[];
585
- }
586
- ```
587
-
588
- > [!IMPORTANT]
589
- > 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.
590
-
591
- ### JWKS Token Service Options (Union)
592
-
593
- ```typescript
594
- type TJWKSTokenServiceOptions = IJWKSIssuerOptions | IJWKSVerifierOptions;
595
- ```
596
-
597
- This union is discriminated on the `mode` field (`'issuer'` vs `'verifier'`).
598
-
599
- ### Basic Auth Options
600
-
601
- | Option | Type | Default | Description |
602
- |--------|------|---------|-------------|
603
- | `verifyCredentials` | `(opts: { credentials, context }) => Promise<IAuthUser \| null>` | -- | Callback to verify Basic auth credentials |
604
-
605
- The `verifyCredentials` function receives an options object:
606
-
607
- ```typescript
608
- type TBasicAuthVerifyFn<E extends Env = Env> = (opts: {
609
- credentials: { username: string; password: string };
610
- context: TContext<E, string>;
611
- }) => Promise<IAuthUser | null>;
612
- ```
613
-
614
- #### TBasicTokenServiceOptions -- Full Interface
615
- ```typescript
616
- type TBasicTokenServiceOptions<E extends Env = Env> = {
617
- verifyCredentials: (opts: {
618
- credentials: { username: string; password: string };
619
- context: TContext<E, string>;
620
- }) => Promise<IAuthUser | null>;
621
- };
622
- ```
623
-
624
- ### REST Options
625
-
626
- | Option | Type | Default | Description |
627
- |--------|------|---------|-------------|
628
- | `useAuthController` | `boolean` | `false` | Enable/disable built-in auth controller |
629
- | `controllerOpts` | `TDefineAuthControllerOpts` | -- | Configuration for built-in auth controller (required when `useAuthController` is `true`) |
630
-
631
- `TAuthenticationRestOptions` is a discriminated union type:
632
-
633
- ```typescript
634
- type TAuthenticationRestOptions = {} & (
635
- | { useAuthController?: false | undefined }
636
- | {
637
- useAuthController: true;
638
- controllerOpts: TDefineAuthControllerOpts;
639
- }
640
- );
641
- ```
642
-
643
- > [!IMPORTANT]
644
- > When `useAuthController` is `true`, the `controllerOpts` field becomes required. The discriminated union enforces this at the type level -- you cannot set `useAuthController: true` without providing `controllerOpts`.
645
-
646
- ### Controller Options
647
-
648
- | Option | Type | Default | Description |
649
- |--------|------|---------|-------------|
650
- | `restPath` | `string` | `'/auth'` | Base path for auth endpoints |
651
- | `serviceKey` | `string` | -- | DI key for the auth service (required) |
652
- | `requireAuthenticatedSignUp` | `boolean` | `false` | Whether sign-up requires JWT authentication |
653
- | `payload` | `object` | `{}` | Custom Zod schemas for request/response payloads |
654
-
655
- #### TDefineAuthControllerOpts -- Full Interface
656
- ```typescript
657
- type TDefineAuthControllerOpts = {
658
- restPath?: string;
659
- serviceKey: string;
660
- requireAuthenticatedSignUp?: boolean;
661
- payload?: {
662
- signIn?: {
663
- request: { schema: TAnyObjectSchema };
664
- response: { schema: TAnyObjectSchema };
665
- };
666
- signUp?: {
667
- request: { schema: TAnyObjectSchema };
668
- response: { schema: TAnyObjectSchema };
669
- };
670
- changePassword?: {
671
- request: { schema?: TAnyObjectSchema };
672
- response: { schema: TAnyObjectSchema };
673
- };
674
- };
675
- };
676
- ```
677
-
678
- ### Route Configuration Options
679
-
680
- Per-route authentication is configured via the `authenticate` field on route configs, using `TRouteAuthenticateConfig`:
681
-
682
- ```typescript
683
- type TRouteAuthenticateConfig =
684
- | { skip: true }
685
- | { skip?: false; strategies?: TAuthStrategy[]; mode?: TAuthMode };
686
- ```
687
-
688
- | Option | Type | Default | Description |
689
- |--------|------|---------|-------------|
690
- | `authenticate.strategies` | `TAuthStrategy[]` | -- | Array of strategy names (e.g., `['jwt']`, `['jwt', 'basic']`) |
691
- | `authenticate.mode` | `'any' \| 'all'` | `'any'` | How to handle multiple strategies |
692
- | `authenticate.skip` | `true` | -- | Skip authentication for this route |
693
-
694
- Example route config:
695
- ```typescript
696
- const SECURE_ROUTE = {
697
- path: '/data',
135
+ const FALLBACK_AUTH_CONFIG = {
136
+ path: '/api/data',
698
137
  method: HTTP.Methods.GET,
699
- authenticate: { strategies: [Authentication.STRATEGY_JWT] },
700
- responses: jsonResponse({ description: 'Protected', schema: z.object({ data: z.any() }) }),
138
+ authenticate: { strategies: [Authentication.STRATEGY_JWT, Authentication.STRATEGY_BASIC], mode: AuthenticationModes.ANY },
139
+ responses: jsonResponse({ description: 'Data', schema: z.object({ data: z.any() }) }),
701
140
  } as const;
702
141
  ```
703
142
 
704
- ### IAuthUser Interface
705
-
706
- The base authenticated user type returned by strategies and available on the context:
707
-
708
- ```typescript
709
- interface IAuthUser {
710
- userId: IdType;
711
- [extra: string | symbol]: any;
712
- }
713
- ```
714
-
715
- > [!TIP]
716
- > `IAuthUser` is intentionally minimal. Your `IAuthService` implementation can extend the user payload with additional fields (roles, email, provider, etc.) -- these extra fields will be preserved through JWT token generation and available after authentication via `Authentication.CURRENT_USER`.
717
-
718
- ### SignInRequestSchema Field Constraints
719
-
720
- The built-in `SignInRequestSchema` enforces the following validation constraints on sign-in request payloads:
721
-
722
- | Field | Type | Constraints |
723
- |-------|------|-------------|
724
- | `identifier.scheme` | `string` | Non-empty, min 4 chars (required) |
725
- | `identifier.value` | `string` | Non-empty, min 8 chars (required) |
726
- | `credential.scheme` | `string` | Non-empty (required) |
727
- | `credential.value` | `string` | Non-empty, min 8 chars (required) |
728
- | `clientId` | `string` | Optional |
729
-
730
- ### SignUpRequestSchema Field Constraints
731
-
732
- The built-in `SignUpRequestSchema` uses a **flat structure** (not nested like `SignInRequestSchema`):
733
-
734
- | Field | Type | Constraints |
735
- |-------|------|-------------|
736
- | `username` | `string` | Non-empty, min 8 chars (required) |
737
- | `credential` | `string` | Non-empty, min 8 chars (required) |
143
+ **Add auth entity columns to a Drizzle table.** Spread helper functions into `pgTable()` for User/Role/Permission/PolicyDefinition columns.
738
144
 
739
- ### ChangePasswordRequestSchema Field Constraints
740
-
741
- The built-in `ChangePasswordRequestSchema` uses scheme-based credential naming:
742
-
743
- | Field | Type | Constraints |
744
- |-------|------|-------------|
745
- | `scheme` | `string` | Required |
746
- | `oldCredential` | `string` | Non-empty, min 8 chars (required) |
747
- | `newCredential` | `string` | Non-empty, min 8 chars (required) |
748
- | `userId` | `string \| number` | Required |
749
-
750
- #### IAuthService -- Full Interface
751
145
  ```typescript
752
- interface IAuthService<
753
- E extends Env = Env,
754
- SIRQ extends TSignInRequest = TSignInRequest,
755
- SIRS = AnyObject,
756
- SURQ extends TSignUpRequest = TSignUpRequest,
757
- SURS = AnyObject,
758
- CPRQ extends TChangePasswordRequest = TChangePasswordRequest,
759
- CPRS = AnyObject,
760
- UIRQ = AnyObject,
761
- UIRS = AnyObject,
762
- RTRS = AnyObject,
763
- > {
764
- signIn(context: TContext<E>, opts: SIRQ): Promise<SIRS>;
765
- signUp(context: TContext<E>, opts: SURQ): Promise<SURS>;
766
- changePassword(context: TContext<E>, opts: CPRQ): Promise<CPRS>;
767
- getUserInformation?(context: TContext<E>, opts: UIRQ): Promise<UIRS>;
768
- refreshToken?(context: TContext<E>): Promise<RTRS>;
769
- }
770
- ```
771
-
772
- > [!NOTE]
773
- > `IAuthService` is generic on the Hono `Env` type as well as all request/response types. The `getUserInformation` and `refreshToken` methods are both optional -- the auth controller factory returns a 501 (`core.not_supported`) when an endpoint that depends on one is called but the method is not implemented.
146
+ import { extraUserColumns, generateIdColumnDefs, generateTzColumnDefs } from '@venizia/ignis';
774
147
 
775
- #### IJWTTokenPayload -- Full Interface
776
- ```typescript
777
- interface IJWTTokenPayload extends JWTPayload, IAuthUser {
778
- userId: IdType;
779
- roles: { id: IdType; identifier: string; priority: number }[];
780
- clientId?: string;
781
- provider?: string;
782
- email?: string;
783
- name?: string;
784
- [extra: string | symbol]: any;
785
- }
786
- ```
787
-
788
- ## Binding Keys
789
-
790
- | Key | Constant | Type | Required | Default |
791
- |-----|----------|------|----------|---------|
792
- | `@app/authenticate/rest-options` | `AuthenticateBindingKeys.REST_OPTIONS` | `TAuthenticationRestOptions` | No | <code v-pre>{ useAuthController: false }</code> |
793
- | `@app/authenticate/jwt-options` | `AuthenticateBindingKeys.JWT_OPTIONS` | `TJWTTokenServiceOptions` | Conditional | -- |
794
- | `@app/authenticate/jwks-options` | `AuthenticateBindingKeys.JWKS_OPTIONS` | `IJWKSIssuerOptions \| IJWKSVerifierOptions` | Internal | Bound by the component |
795
- | `@app/authenticate/basic-options` | `AuthenticateBindingKeys.BASIC_OPTIONS` | `TBasicTokenServiceOptions` | Conditional | -- |
796
-
797
- > [!IMPORTANT]
798
- > At least one of `JWT_OPTIONS` or `BASIC_OPTIONS` must be bound. If neither is configured, the component will throw an error during `binding()`.
799
-
800
- > [!NOTE]
801
- > `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`.
802
-
803
- ### Context Variables
804
-
805
- These values are set on the Hono `Context` during authentication and can be accessed via `context.get()`:
806
-
807
- | Key | Constant | Type | Description |
808
- |-----|----------|------|-------------|
809
- | `auth.current.user` | `Authentication.CURRENT_USER` | `IAuthUser` | Authenticated user payload |
810
- | `audit.user.id` | `Authentication.AUDIT_USER_ID` | `IdType` | Authenticated user's ID |
811
- | `authentication.skip` | `Authentication.SKIP_AUTHENTICATION` | `boolean` | Dynamically skip auth |
812
-
813
- ### Strategy Constants
814
-
815
- | Constant | Value | Description |
816
- |----------|-------|-------------|
817
- | `Authentication.STRATEGY_JWT` | `'jwt'` | JWT strategy name |
818
- | `Authentication.STRATEGY_BASIC` | `'basic'` | Basic strategy name |
819
- | `Authentication.TYPE_BEARER` | `'Bearer'` | Bearer token type |
820
- | `Authentication.TYPE_BASIC` | `'Basic'` | Basic token type |
821
-
822
- ### AuthenticateStrategy Class
823
-
824
- Utility class for validating strategy names:
825
-
826
- ```typescript
827
- class AuthenticateStrategy {
828
- static readonly BASIC = 'basic';
829
- static readonly JWT = 'jwt';
830
- static readonly SCHEME_SET: Set<string>;
831
- static isValid(input: string): boolean;
832
- }
833
- type TAuthStrategy = TConstValue<typeof AuthenticateStrategy>;
834
- ```
835
-
836
- | Member | Type | Description |
837
- |--------|------|-------------|
838
- | `BASIC` | `string` | Constant for basic strategy name |
839
- | `JWT` | `string` | Constant for JWT strategy name |
840
- | `SCHEME_SET` | `Set<string>` | Set containing all valid strategy names |
841
- | `isValid(input)` | `(input: string) => boolean` | Returns `true` if the input is a recognized strategy name |
842
-
843
- ### AuthenticationModes Class
844
-
845
- Utility class for validating authentication modes:
846
-
847
- ```typescript
848
- class AuthenticationModes {
849
- static readonly ANY = 'any';
850
- static readonly ALL = 'all';
851
- }
852
- type TAuthMode = TConstValue<typeof AuthenticationModes>;
148
+ export const users = pgTable('users', {
149
+ ...generateIdColumnDefs(),
150
+ ...generateTzColumnDefs(),
151
+ ...extraUserColumns(),
152
+ username: text('username').unique().notNull(),
153
+ });
853
154
  ```
854
155
 
855
- | Member | Type | Description |
856
- |--------|------|-------------|
857
- | `ANY` | `string` | First successful strategy wins (fallback) |
858
- | `ALL` | `string` | All strategies must pass (MFA) |
859
-
860
- ## See Also
861
-
862
- - [Usage & Examples](./usage) -- Securing routes, auth flows, and API endpoints
863
- - [API Reference](./api) -- Architecture, service internals, and strategy registry
864
- - [Error Reference](./errors) -- Error messages and troubleshooting
865
-
866
- - **Guides:**
867
- - [Components Overview](/guides/core-concepts/components) -- Component system basics
868
- - [REST Controllers](/guides/core-concepts/rest-controllers) | [gRPC Controllers](/guides/core-concepts/grpc-controllers) -- Protecting routes with auth
869
-
870
- - **Components:**
871
- - [All Components](../index) -- Built-in components list
872
-
873
- - **Helpers:**
874
- - [Crypto Helper](/extensions/helpers/crypto/) -- Password hashing utilities
156
+ ## See also
875
157
 
876
- - **References:**
877
- - [Middlewares](/references/base/middlewares) -- Custom authentication middleware
158
+ - [Usage & Examples](./usage) - securing routes, auth flows, JWKS microservice patterns, entity column helpers
159
+ - [API Reference](./api) - architecture, service class hierarchy, strategy registry, controller factory
160
+ - [Error Reference](./errors) - every error message and how to fix it
161
+ - [Components Overview](/guides/core-concepts/components) - component system basics
162
+ - [REST Controllers](/guides/core-concepts/rest-controllers) - protecting routes with `authenticate`
163
+ - [Crypto Helper](/extensions/helpers/crypto/) - password hashing utilities for `verifyCredentials`
878
164
 
879
- - **Best Practices:**
880
- - [Security Guidelines](/best-practices/security-guidelines) -- Authentication best practices
165
+ **Files:**
881
166
 
882
- - **Tutorials:**
883
- - [Building a CRUD API](/guides/tutorials/building-a-crud-api) -- Adding authentication
167
+ - [`packages/core-server/src/components/auth/authenticate/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/auth/authenticate/component.ts) - `AuthenticateComponent`
168
+ - [`packages/core-server/src/components/auth/authenticate/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/auth/authenticate/common/types.ts) - all option interfaces
169
+ - [`packages/core-server/src/components/auth/authenticate/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/auth/authenticate/common/keys.ts) - `AuthenticateBindingKeys`
170
+ - [`packages/core-server/src/components/auth/authenticate/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/auth/authenticate/common/constants.ts) - `Authentication`, `JOSEStandards`, `JWKSModes`, `JWKSKeyDrivers`, `JWKSKeyFormats`
171
+ - [`packages/core-server/src/components/auth/models/entities`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/auth/models/entities) - `extraUserColumns`, `extraRoleColumns`, `extraPermissionColumns`, `extraPolicyDefinitionColumns`