@venizia/ignis-docs 0.2.0 → 0.2.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -1,346 +1,110 @@
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 - 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, via a discriminated `standard` field) and `BASIC_OPTIONS` from the DI container and registers whichever token services their presence implies. At least one of the two must be bound, or the component throws at startup.
61
+ - **Strategies are manual, on purpose.** The component registers *token services* (JWS/JWKS/Basic), not *strategies*. You register strategies yourself via `AuthenticationStrategyRegistry.getInstance().register(...)` after `this.component(AuthenticateComponent)` - this is what gives routes a `Authentication.STRATEGY_JWT` / `'basic'` name to reference.
62
+ - **The registry is a DI-backed singleton.** `AuthenticationStrategyRegistry` binds each registered strategy into the container as a singleton under `authentication.strategy.<name>` and resolves it by name when a route's `authenticate.strategies` list is checked.
63
+ - **`authenticate()` is the middleware entry point.** Route-level `authenticate: { strategies, mode }` config and the standalone `authenticate()` function both go through the same `AuthenticationProvider`, which tries strategies in `'any'` (first success wins) or `'all'` (every strategy must pass) mode and sets `Authentication.CURRENT_USER` on the Hono context.
64
+ - **The auth controller is optional and generated.** Setting `REST_OPTIONS.useAuthController: true` calls `defineAuthController()`, which builds a `BaseRestController` subclass at runtime with `/sign-in`, `/sign-up`, `/change-password`, `/token/refresh`, `/who-am-i`, and `/me` routes, backed by your own `IAuthService` implementation.
229
65
 
230
- ### Step 1: Bind Configuration
66
+ **JOSE standards**
231
67
 
232
- Bind JWT options using the discriminated union `TJWTTokenServiceOptions`, which requires a `standard` field to select the JOSE standard.
68
+ | Standard | Class | Keying | Use case |
69
+ |----------|-------|--------|----------|
70
+ | JWS | `JWSTokenService` | Shared secret (HS256) | Single service signs and verifies |
71
+ | JWKS Issuer | `JWKSIssuerTokenService` | Private + public key (ES256/RS256/EdDSA) | This service issues tokens and serves `/certs` |
72
+ | JWKS Verifier | `JWKSVerifierTokenService` | Remote JWKS URL | This service only verifies tokens from another issuer |
233
73
 
234
- ### JWS (Symmetric JWT) Setup
74
+ ## Common tasks
235
75
 
236
- ```typescript
237
- import {
238
- AuthenticateBindingKeys,
239
- JOSEStandards,
240
- TJWTTokenServiceOptions,
241
- } from '@venizia/ignis';
76
+ **Configure JWS (symmetric JWT).** One shared secret signs and verifies.
242
77
 
78
+ ```typescript
243
79
  this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
244
80
  standard: JOSEStandards.JWS,
245
81
  options: {
246
- jwtSecret: process.env.APP_ENV_JWT_SECRET,
247
- applicationSecret: process.env.APP_ENV_APPLICATION_SECRET, // Optional - enables AES payload encryption
82
+ jwtSecret: process.env.APP_ENV_JWT_SECRET!,
248
83
  getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
249
84
  },
250
85
  });
251
86
  ```
252
87
 
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
88
+ **Configure JWKS Issuer (asymmetric JWT, microservice-friendly).** Signs with a private key, serves the public key at `/certs`.
265
89
 
266
90
  ```typescript
267
- import {
268
- AuthenticateBindingKeys,
269
- JOSEStandards,
270
- JWKSModes,
271
- JWKSKeyDrivers,
272
- JWKSKeyFormats,
273
- TJWTTokenServiceOptions,
274
- } from '@venizia/ignis';
275
-
276
91
  this.bind<TJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
277
92
  standard: JOSEStandards.JWKS,
278
93
  options: {
279
94
  mode: JWKSModes.ISSUER,
280
95
  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
- },
96
+ keys: { driver: JWKSKeyDrivers.FILE, format: JWKSKeyFormats.PEM, private: './keys/private.pem', public: './keys/public.pem' },
287
97
  kid: 'my-key-id-1',
288
98
  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
99
  },
330
100
  });
331
101
  ```
332
102
 
333
- ### Basic Auth Only (Alternative Setup)
103
+ **Add Basic auth.** Provide a `verifyCredentials` callback; it becomes the `'basic'` strategy's source of truth.
334
104
 
335
105
  ```typescript
336
- import {
337
- AuthenticateBindingKeys,
338
- TBasicTokenServiceOptions,
339
- } from '@venizia/ignis';
340
-
341
106
  this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
342
- verifyCredentials: async (opts) => {
343
- const { credentials, context } = opts;
107
+ verifyCredentials: async ({ credentials, context }) => {
344
108
  const user = await userRepo.findByUsername(credentials.username);
345
109
  if (user && await bcrypt.compare(credentials.password, user.passwordHash)) {
346
110
  return { userId: user.id, roles: user.roles };
@@ -350,534 +114,57 @@ this.bind<TBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTION
350
114
  });
351
115
  ```
352
116
 
353
- ### Combined JWKS + Basic with Auth Controller (Full Setup)
117
+ **Enable the built-in `/auth` controller.** Requires `jwtOptions` and a bound `IAuthService`.
354
118
 
355
119
  ```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';
120
+ this.service(AuthenticationService);
368
121
 
369
- // Bind REST options (enables auth controller)
370
122
  this.bind<TAuthenticationRestOptions>({ key: AuthenticateBindingKeys.REST_OPTIONS }).toValue({
371
123
  useAuthController: true,
372
124
  controllerOpts: {
373
125
  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
- });
126
+ serviceKey: BindingKeys.build({ namespace: BindingNamespaces.SERVICE, key: AuthenticationService.name }),
425
127
  },
426
128
  });
427
129
  ```
428
130
 
429
- ### Step 2: Register Component and Strategies
131
+ **Secure a route with multiple strategies.** `mode: 'any'` (default) falls back through strategies; `mode: 'all'` requires every one to pass.
430
132
 
431
133
  ```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',
134
+ const FALLBACK_AUTH_CONFIG = {
135
+ path: '/api/data',
698
136
  method: HTTP.Methods.GET,
699
- authenticate: { strategies: [Authentication.STRATEGY_JWT] },
700
- responses: jsonResponse({ description: 'Protected', schema: z.object({ data: z.any() }) }),
137
+ authenticate: { strategies: [Authentication.STRATEGY_JWT, Authentication.STRATEGY_BASIC], mode: AuthenticationModes.ANY },
138
+ responses: jsonResponse({ description: 'Data', schema: z.object({ data: z.any() }) }),
701
139
  } as const;
702
140
  ```
703
141
 
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) |
142
+ **Add auth entity columns to a Drizzle table.** Spread helper functions into `pgTable()` for User/Role/Permission/PolicyDefinition columns.
738
143
 
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
144
  ```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.
145
+ import { extraUserColumns } from '@venizia/ignis';
774
146
 
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>;
147
+ export const users = pgTable('users', {
148
+ ...withSerialId(),
149
+ ...withTimestamps(),
150
+ ...extraUserColumns(),
151
+ username: text('username').unique().notNull(),
152
+ });
853
153
  ```
854
154
 
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
155
+ ## See also
875
156
 
876
- - **References:**
877
- - [Middlewares](/references/base/middlewares) -- Custom authentication middleware
157
+ - [Usage & Examples](./usage) - securing routes, auth flows, JWKS microservice patterns, entity column helpers
158
+ - [API Reference](./api) - architecture, service class hierarchy, strategy registry, controller factory
159
+ - [Error Reference](./errors) - every error message and how to fix it
160
+ - [Components Overview](/guides/core-concepts/components) - component system basics
161
+ - [REST Controllers](/guides/core-concepts/rest-controllers) - protecting routes with `authenticate`
162
+ - [Crypto Helper](/extensions/helpers/crypto/) - password hashing utilities for `verifyCredentials`
878
163
 
879
- - **Best Practices:**
880
- - [Security Guidelines](/best-practices/security-guidelines) -- Authentication best practices
164
+ **Files:**
881
165
 
882
- - **Tutorials:**
883
- - [Building a CRUD API](/guides/tutorials/building-a-crud-api) -- Adding authentication
166
+ - [`packages/core/src/components/auth/authenticate/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/component.ts) - `AuthenticateComponent`
167
+ - [`packages/core/src/components/auth/authenticate/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/common/types.ts) - all option interfaces
168
+ - [`packages/core/src/components/auth/authenticate/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/common/keys.ts) - `AuthenticateBindingKeys`
169
+ - [`packages/core/src/components/auth/authenticate/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/authenticate/common/constants.ts) - `Authentication`, `JOSEStandards`, `JWKSModes`, `JWKSKeyDrivers`, `JWKSKeyFormats`
170
+ - [`packages/core/src/components/auth/models/entities`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/auth/models/entities) - `extraUserColumns`, `extraRoleColumns`, `extraPermissionColumns`, `extraPolicyDefinitionColumns`