@venizia/ignis-docs 0.0.5 → 0.0.6-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 (98) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +0 -8
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +3 -18
  78. package/wiki/references/utilities/jsx.md +1 -8
  79. package/wiki/references/utilities/statuses.md +0 -7
  80. package/wiki/references/components/authentication.md +0 -476
  81. package/wiki/references/components/mail.md +0 -687
  82. package/wiki/references/components/socket-io.md +0 -562
  83. package/wiki/references/components/static-asset.md +0 -1277
  84. package/wiki/references/helpers/cron.md +0 -108
  85. package/wiki/references/helpers/crypto.md +0 -132
  86. package/wiki/references/helpers/env.md +0 -83
  87. package/wiki/references/helpers/error.md +0 -97
  88. package/wiki/references/helpers/inversion.md +0 -176
  89. package/wiki/references/helpers/logger.md +0 -296
  90. package/wiki/references/helpers/network.md +0 -396
  91. package/wiki/references/helpers/queue.md +0 -150
  92. package/wiki/references/helpers/redis.md +0 -142
  93. package/wiki/references/helpers/socket-io.md +0 -932
  94. package/wiki/references/helpers/storage.md +0 -665
  95. package/wiki/references/helpers/testing.md +0 -133
  96. package/wiki/references/helpers/types.md +0 -167
  97. package/wiki/references/helpers/uid.md +0 -167
  98. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -0,0 +1,444 @@
1
+ # Authentication -- API Reference
2
+
3
+ > Architecture, service internals, strategy registry, and controller factory. See [Setup & Configuration](./) for initial setup.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ ┌──────────────────────────────────────────────────────────┐
9
+ │ Application │
10
+ │ │
11
+ │ preConfigure() │
12
+ │ ├── bind JWT_OPTIONS / BASIC_OPTIONS / REST_OPTIONS │
13
+ │ ├── this.component(AuthenticateComponent) │
14
+ │ └── AuthenticationStrategyRegistry.register() │
15
+ └──────────────────────┬───────────────────────────────────┘
16
+
17
+
18
+ ┌──────────────────────────────────────────────────────────┐
19
+ │ AuthenticateComponent.binding() │
20
+ │ │
21
+ │ 1. validateOptions() — at least one auth required │
22
+ │ 2. defineJWTAuth() — bind JWTTokenService │
23
+ │ 3. defineBasicAuth() — bind BasicTokenService │
24
+ │ 4. defineControllers() — defineAuthController() │
25
+ │ 5. defineOAuth2() — stub (not yet implemented) │
26
+ └──────────────────────┬───────────────────────────────────┘
27
+
28
+ ┌─────────────┼─────────────┐
29
+ ▼ ▼ ▼
30
+ ┌────────────┐ ┌──────────────┐ ┌──────────────────┐
31
+ │ JWTToken │ │ BasicToken │ │ AuthController │
32
+ │ Service │ │ Service │ │ (factory-built) │
33
+ └──────┬─────┘ └──────┬───────┘ └────────┬─────────┘
34
+ │ │ │
35
+ ▼ ▼ ▼
36
+ ┌────────────┐ ┌──────────────┐ ┌──────────────────┐
37
+ │ JWT │ │ Basic │ │ /sign-in │
38
+ │ Strategy │ │ Strategy │ │ /sign-up │
39
+ │ │ │ │ │ /change-password │
40
+ └────────────┘ └──────────────┘ │ /who-am-i │
41
+ └──────────────────┘
42
+ ```
43
+
44
+ ### Tech Stack
45
+
46
+ | Technology | Purpose |
47
+ |------------|---------|
48
+ | **`jose`** | JWT signing (`SignJWT`), verification (`jwtVerify`), and type definitions (`JWTPayload`, `JWTVerifyResult`) |
49
+ | **`@venizia/ignis-helpers`** | `AES` utility for payload field encryption, `BaseHelper`/`BaseService` base classes, `getError` for error creation, `HTTP` result codes |
50
+ | **Hono middleware** | Route-level authentication integration via `createMiddleware` from `hono/factory` |
51
+ | **Drizzle ORM** | Database access for user lookup (in your implementation) |
52
+ | **lodash/isEmpty** | Used in strategy registry for name validation |
53
+
54
+ ## Component Private Methods
55
+
56
+ The `AuthenticateComponent` uses four private methods during its `binding()` lifecycle:
57
+
58
+ | Method | Purpose |
59
+ |--------|---------|
60
+ | `validateOptions(opts)` | Validates that at least one of `jwtOptions` or `basicOptions` is present. Throws if neither is provided. |
61
+ | `defineJWTAuth(opts)` | Validates JWT secrets (rejects falsy values and `'unknown_secret'`), validates `getTokenExpiresFn`, binds `JWTTokenService` as a service. Logs debug if skipped. |
62
+ | `defineBasicAuth(opts)` | Validates `verifyCredentials` callback presence, binds `BasicTokenService` as a service. Logs debug if skipped. |
63
+ | `defineControllers(opts)` | Requires `jwtOptions` when `useAuthController: true`. Calls `defineAuthController()` factory and registers the generated controller. |
64
+ | `defineOAuth2()` | Stub method -- not yet implemented. Called during `binding()` but performs no action. |
65
+
66
+ > [!WARNING]
67
+ > **Security concern:** The `defineJWTAuth()` method includes the actual secret value in its error message when validation fails (e.g., <code v-pre>[defineJWTAuth] Invalid jwtSecret | Provided: {{jwtSecret}}</code>). Ensure these startup errors are never exposed to end users or logged in production without sanitization.
68
+
69
+ ## Strategy Registry
70
+
71
+ <code v-pre>AuthenticationStrategyRegistry&lt;E extends Env = Env&gt;</code> is a **singleton** that manages all registered strategies. It extends `BaseHelper`.
72
+
73
+ ### API
74
+
75
+ | Method | Signature | Returns | Description |
76
+ |--------|-----------|---------|-------------|
77
+ | `getInstance()` | `static` | `AuthenticationStrategyRegistry` | Returns the singleton instance (creates if not exists) |
78
+ | `getStrategyKey` | `(opts: { name: string }) => string` | `string` | Returns the binding key for a strategy: <code v-pre>authentication.strategy.{{name}}</code> |
79
+ | `getStrategy` | `(opts: { container: Container; name: string }) => IAuthenticationStrategy` | `IAuthenticationStrategy` | Resolves a strategy instance from the container by name |
80
+ | `register` | <code v-pre>(opts: { container: Container; strategies: Array&lt;{ name: string; strategy: TClass&lt;IAuthenticationStrategy&lt;E&gt;&gt; }&gt; }) =&gt; this</code> | `this` | Registers strategies as singletons in the container. Returns `this` for chaining. |
81
+ | `authenticate` | `(opts: { strategies: string[]; mode?: TAuthMode }) => MiddlewareHandler` | `MiddlewareHandler` | Creates a Hono middleware that performs the auth check |
82
+
83
+ **Registration:**
84
+ ```typescript
85
+ AuthenticationStrategyRegistry.getInstance().register({
86
+ container: this,
87
+ strategies: [
88
+ { name: Authentication.STRATEGY_JWT, strategy: JWTAuthenticationStrategy },
89
+ { name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
90
+ ],
91
+ });
92
+ ```
93
+
94
+ > [!NOTE]
95
+ > `register()` returns `this`, enabling method chaining if needed.
96
+
97
+ **How it works:**
98
+ - Strategies are stored in an internal <code v-pre>Map&lt;string, { container, strategyClass }&gt;</code> and also bound to the DI container as singletons
99
+ - Binding keys follow the pattern `authentication.strategy.{name}` (e.g., `authentication.strategy.jwt`, `authentication.strategy.basic`)
100
+ - The `authenticate()` method returns a Hono `MiddlewareHandler` that performs the auth check
101
+ - The standalone `authenticate()` function is a convenience wrapper around the registry singleton
102
+
103
+ **Strategy binding:**
104
+ ```typescript
105
+ // Internally, the registry binds strategies like this:
106
+ container.bind({ key: 'authentication.strategy.jwt' })
107
+ .toClass(JWTAuthenticationStrategy)
108
+ .setScope(BindingScopes.SINGLETON);
109
+ ```
110
+
111
+ **Middleware creation:**
112
+
113
+ The `authenticate()` function returns a Hono middleware that:
114
+ 1. Checks if `Authentication.SKIP_AUTHENTICATION` is set on context -- if true, skips entirely (logs debug)
115
+ 2. Checks if `Authentication.CURRENT_USER` is already set on context -- if true, skips (already authenticated)
116
+ 3. Reads `strategies` and `mode` from the provided options
117
+ 4. Executes strategies based on mode (`any` or `all`)
118
+ 5. On success, sets `Authentication.CURRENT_USER` and `Authentication.AUDIT_USER_ID` on context
119
+ 6. On failure, throws 401 with list of tried strategies
120
+
121
+ ### Standalone `authenticate()` Function
122
+
123
+ ```typescript
124
+ export const authenticate = (opts: { strategies: string[]; mode?: TAuthMode }) => {
125
+ return AuthenticationStrategyRegistry.getInstance().authenticate(opts);
126
+ };
127
+ ```
128
+
129
+ This is the primary export for creating auth middleware. It delegates directly to the singleton registry.
130
+
131
+ > [!NOTE]
132
+ > In `all` mode, if every strategy passes but the final user payload has no `userId`, the middleware throws a `401` with message `"Failed to identify authenticated user!"`. The `any` mode collects errors from each failing strategy and only throws after all strategies are exhausted.
133
+
134
+ ## JWTTokenService
135
+
136
+ All methods are instance methods on <code v-pre>JWTTokenService&lt;E extends Env = Env&gt;</code>, which extends `BaseService`.
137
+
138
+ ### JWTAuthenticationStrategy
139
+
140
+ Extends `BaseHelper` and implements <code v-pre>IAuthenticationStrategy&lt;E&gt;</code>. Generic on <code v-pre>&lt;E extends Env = Env&gt;</code>.
141
+
142
+ ```typescript
143
+ class JWTAuthenticationStrategy<E extends Env = Env>
144
+ extends BaseHelper
145
+ implements IAuthenticationStrategy<E>
146
+ {
147
+ name = Authentication.STRATEGY_JWT; // 'jwt'
148
+
149
+ constructor(
150
+ @inject({ key: 'services.JWTTokenService' })
151
+ private service: JWTTokenService<E>,
152
+ ) { ... }
153
+
154
+ authenticate(context: TContext<E, string>): Promise<IAuthUser> {
155
+ const token = this.service.extractCredentials(context);
156
+ return this.service.verify(token);
157
+ }
158
+ }
159
+ ```
160
+
161
+ ### Methods
162
+
163
+ | Method | Signature | Description |
164
+ |--------|-----------|-------------|
165
+ | `extractCredentials` | <code v-pre>(context: TContext&lt;E, string&gt;) =&gt; { type: string; token: string }</code> | Extracts Bearer token from Authorization header |
166
+ | `verify` | <code v-pre>(opts: { type: string; token: string }) =&gt; Promise&lt;IJWTTokenPayload&gt;</code> | Verifies JWT signature via `jose.jwtVerify()` and decrypts payload |
167
+ | `generate` | <code v-pre>(opts: { payload: IJWTTokenPayload; getTokenExpiresFn?: TGetTokenExpiresFn }) =&gt; Promise&lt;string&gt;</code> | Encrypts payload, signs JWT with configurable expiration |
168
+ | `getSigner` | <code v-pre>(opts: { payload: IJWTTokenPayload; getTokenExpiresFn: TGetTokenExpiresFn }) =&gt; Promise&lt;SignJWT&gt;</code> | Creates a `jose.SignJWT` instance with encrypted payload, iat, exp, nbf |
169
+ | `encryptPayload` | <code v-pre>(payload: IJWTTokenPayload) =&gt; Record&lt;string, string&gt;</code> | AES-encrypts non-standard JWT fields (keys + values) |
170
+ | `decryptPayload` | <code v-pre>(opts: { result: JWTVerifyResult&lt;IJWTTokenPayload&gt; }) =&gt; IJWTTokenPayload</code> | Decrypts AES-encrypted fields back to IJWTTokenPayload |
171
+
172
+ ### Static Fields
173
+
174
+ - `JWT_COMMON_FIELDS`: <code v-pre>Set&lt;'iss' | 'sub' | 'aud' | 'jti' | 'nbf' | 'exp' | 'iat'&gt;</code> -- fields preserved as-is during encryption
175
+
176
+ ### Protected Fields
177
+
178
+ - `aes`: `AES` -- AES utility instance initialized with the configured algorithm
179
+ - `jwtSecret`: `Uint8Array` -- encoded JWT secret for `jose` signing/verification
180
+ - `options`: `IJWTTokenServiceOptions` -- injected options
181
+
182
+ ### Constructor Behavior
183
+
184
+ The constructor validates all three required options and throws immediately (status 500) if any are missing:
185
+
186
+ ```typescript
187
+ constructor(
188
+ @inject({ key: AuthenticateBindingKeys.JWT_OPTIONS })
189
+ protected options: IJWTTokenServiceOptions,
190
+ ) {
191
+ // Throws '[JWTTokenService] Invalid jwtSecret' if !jwtSecret
192
+ // Throws '[JWTTokenService] Invalid applicationSecret' if !applicationSecret
193
+ // Throws '[JWTTokenService] Invalid getTokenExpiresFn' if !getTokenExpiresFn
194
+ // Initializes AES with configured algorithm (default 'aes-256-cbc')
195
+ // Encodes jwtSecret to Uint8Array for jose
196
+ }
197
+ ```
198
+
199
+ ## BasicTokenService
200
+
201
+ All methods are instance methods on <code v-pre>BasicTokenService&lt;E extends Env = Env&gt;</code>, which extends `BaseService`.
202
+
203
+ ### BasicAuthenticationStrategy
204
+
205
+ Extends `BaseHelper` and implements <code v-pre>IAuthenticationStrategy&lt;E&gt;</code>. Generic on <code v-pre>&lt;E extends Env = Env&gt;</code>.
206
+
207
+ ```typescript
208
+ class BasicAuthenticationStrategy<E extends Env = Env>
209
+ extends BaseHelper
210
+ implements IAuthenticationStrategy<E>
211
+ {
212
+ name = Authentication.STRATEGY_BASIC; // 'basic'
213
+
214
+ constructor(
215
+ @inject({ key: 'services.BasicTokenService' })
216
+ private service: BasicTokenService<E>,
217
+ ) { ... }
218
+
219
+ async authenticate(context: TContext<E, string>): Promise<IAuthUser> {
220
+ const credentials = this.service.extractCredentials(context);
221
+ return this.service.verify({ credentials, context });
222
+ }
223
+ }
224
+ ```
225
+
226
+ ### Methods
227
+
228
+ | Method | Signature | Description |
229
+ |--------|-----------|-------------|
230
+ | `extractCredentials` | <code v-pre>(context: TContext&lt;E, string&gt;) =&gt; { username: string; password: string }</code> | Decodes Base64 <code v-pre>Authorization: Basic &lt;base64&gt;</code> header |
231
+ | `verify` | <code v-pre>(opts: { credentials: { username: string; password: string }; context: TContext&lt;E, string&gt; }) =&gt; Promise&lt;IAuthUser&gt;</code> | Calls user-provided `verifyCredentials` callback |
232
+
233
+ ### Private Fields
234
+
235
+ - `verifyCredentials` -- the callback function extracted from injected options
236
+
237
+ ### Constructor Behavior
238
+
239
+ ```typescript
240
+ constructor(
241
+ @inject({ key: AuthenticateBindingKeys.BASIC_OPTIONS })
242
+ protected options: IBasicTokenServiceOptions<E>,
243
+ ) {
244
+ // Throws '[BasicTokenService] Invalid verifyCredentials function' if !options?.verifyCredentials
245
+ }
246
+ ```
247
+
248
+ ## Entity Column Helper Types
249
+
250
+ The following types are exported for use when extending the auth entity column helpers:
251
+
252
+ ### Permission Types
253
+
254
+ ```typescript
255
+ type TPermissionOptions = {
256
+ idType?: 'string' | 'number';
257
+ };
258
+
259
+ type TPermissionCommonColumns = {
260
+ code: NotNull<PgTextBuilderInitial<...>>;
261
+ name: NotNull<PgTextBuilderInitial<...>>;
262
+ subject: NotNull<PgTextBuilderInitial<...>>;
263
+ pType: NotNull<PgTextBuilderInitial<...>>;
264
+ action: NotNull<PgTextBuilderInitial<...>>;
265
+ scope: NotNull<PgTextBuilderInitial<...>>;
266
+ };
267
+ ```
268
+
269
+ ### Permission Mapping Types
270
+
271
+ ```typescript
272
+ type TPermissionMappingOptions = {
273
+ idType?: 'string' | 'number';
274
+ };
275
+
276
+ type TPermissionMappingCommonColumns = {
277
+ effect: PgTextBuilderInitial<...>;
278
+ };
279
+ ```
280
+
281
+ ### User Role Types
282
+
283
+ ```typescript
284
+ type TUserRoleOptions = {
285
+ idType?: 'string' | 'number';
286
+ };
287
+
288
+ type TUserRoleCommonColumns = ReturnType<
289
+ typeof generatePrincipalColumnDefs<'principal', 'string' | 'number'>
290
+ >;
291
+ ```
292
+
293
+ ## Controller Factory
294
+
295
+ The `defineAuthController()` function dynamically creates a controller class at runtime using decorator composition:
296
+
297
+ **How it works:**
298
+
299
+ 1. **Class creation:** A new class is created dynamically with `class AuthController extends BaseController {}` inside the factory closure
300
+ 2. **Decorator application:** The `@controller({ path: restPath })` decorator is applied to set the base path. The controller is created with `isStrict: true`
301
+ 3. **Service injection:** The auth service is injected via `inject({ key: serviceKey })(AuthController, undefined, 0)` after class definition -- this programmatically applies `@inject` to constructor parameter 0
302
+ - Default service key: `'services.AuthenticationService'`
303
+ - Service must implement `IAuthService` interface
304
+ 4. **Route definition:** Routes are defined in the controller's `binding()` method using `this.defineRoute()`
305
+ 5. **Schema customization:** Custom Zod schemas can be provided per endpoint via the `payload` option. Defaults to built-in schemas when not provided, with `AnyObjectSchema` as the response fallback.
306
+
307
+ **Factory signature:**
308
+
309
+ ```typescript
310
+ function defineAuthController(opts: TDefineAuthControllerOpts): typeof AuthController;
311
+ ```
312
+
313
+ > [!NOTE]
314
+ > The factory also exports `JWTTokenPayloadSchema`, a Zod schema used for the `/who-am-i` response validation.
315
+
316
+ **Internal route binding:**
317
+
318
+ ```typescript
319
+ // Inside the factory-generated controller
320
+ binding(): void {
321
+ // /sign-in -- no auth, delegates to service.signIn()
322
+ this.defineRoute({
323
+ configs: {
324
+ path: '/sign-in',
325
+ method: 'post',
326
+ request: {
327
+ body: jsonContent({
328
+ description: 'Sign-in request body',
329
+ required: true,
330
+ schema: payload?.signIn?.request?.schema ?? SignInRequestSchema,
331
+ }),
332
+ },
333
+ responses: jsonResponse({
334
+ schema: payload?.signIn?.request?.schema ?? AnyObjectSchema,
335
+ description: 'Success Response',
336
+ }),
337
+ },
338
+ handler: async (context) => {
339
+ const body = await context.req.json();
340
+ const rs = await this.service.signIn(context, body);
341
+ return context.json(rs, HTTP.ResultCodes.RS_2.Ok);
342
+ },
343
+ });
344
+
345
+ // /sign-up -- conditionally requires JWT auth
346
+ this.defineRoute({
347
+ configs: {
348
+ path: '/sign-up',
349
+ method: 'post',
350
+ authenticate: {
351
+ strategies: !requireAuthenticatedSignUp ? [] : [Authentication.STRATEGY_JWT],
352
+ },
353
+ request: {
354
+ body: jsonContent({
355
+ description: 'Sign-up request body',
356
+ required: true,
357
+ schema: payload?.signUp?.request?.schema ?? SignUpRequestSchema,
358
+ }),
359
+ },
360
+ responses: jsonResponse({
361
+ schema: payload?.signUp?.response?.schema ?? AnyObjectSchema,
362
+ description: 'Success Response',
363
+ }),
364
+ },
365
+ handler: async (context) => {
366
+ const body = await context.req.json();
367
+ const rs = await this.service.signUp(context, body);
368
+ return context.json(rs, HTTP.ResultCodes.RS_2.Ok);
369
+ },
370
+ });
371
+
372
+ // /change-password -- always requires JWT auth
373
+ this.defineRoute({
374
+ configs: {
375
+ path: '/change-password',
376
+ method: 'post',
377
+ authenticate: { strategies: [Authentication.STRATEGY_JWT] },
378
+ request: {
379
+ body: jsonContent({
380
+ description: 'Change password request body',
381
+ required: true,
382
+ schema: payload?.changePassword?.request?.schema ?? ChangePasswordRequestSchema,
383
+ }),
384
+ },
385
+ responses: jsonResponse({
386
+ schema: payload?.changePassword?.response?.schema ?? AnyObjectSchema,
387
+ description: 'Success Response',
388
+ }),
389
+ },
390
+ handler: async (context) => {
391
+ const body = await context.req.json();
392
+ const rs = await this.service.changePassword(context, body);
393
+ return context.json(rs, HTTP.ResultCodes.RS_2.Ok);
394
+ },
395
+ });
396
+
397
+ // /who-am-i -- always requires JWT, returns current user from context
398
+ this.defineRoute({
399
+ configs: {
400
+ path: '/who-am-i',
401
+ method: 'get',
402
+ authenticate: { strategies: [Authentication.STRATEGY_JWT] },
403
+ responses: {
404
+ [HTTP.ResultCodes.RS_2.Ok]: jsonContent({
405
+ description: 'Success Response',
406
+ schema: JWTTokenPayloadSchema,
407
+ }),
408
+ },
409
+ },
410
+ handler: (context) => {
411
+ const currentUser = context.get(Authentication.CURRENT_USER as never) as IJWTTokenPayload;
412
+ return context.json(currentUser, HTTP.ResultCodes.RS_2.Ok);
413
+ },
414
+ });
415
+ }
416
+ ```
417
+
418
+ > [!TIP]
419
+ > If the default request/response schemas do not fit your needs, provide custom Zod schemas through the `payload` option in `controllerOpts`. This allows full control over validation while keeping the built-in routing.
420
+
421
+ **Service resolution:**
422
+
423
+ The factory applies `@inject` programmatically to constructor parameter 0:
424
+
425
+ ```typescript
426
+ // Inside defineAuthController, after class definition:
427
+ inject({ key: serviceKey })(AuthController, undefined, 0);
428
+ ```
429
+
430
+ This is equivalent to:
431
+ ```typescript
432
+ constructor(
433
+ @inject({ key: serviceKey })
434
+ authService: IAuthService,
435
+ ) { ... }
436
+ ```
437
+
438
+ If the service is not bound, the component will throw: `"[AuthController] Failed to init auth controller | Invalid injectable authentication service!"`
439
+
440
+ ## See Also
441
+
442
+ - [Setup & Configuration](./) -- Binding keys, options interfaces, and initial setup
443
+ - [Usage & Examples](./usage) -- Securing routes, auth flows, and API endpoints
444
+ - [Error Reference](./errors) -- Error messages and troubleshooting
@@ -0,0 +1,177 @@
1
+ # Authentication -- Error Reference
2
+
3
+ > Complete error messages and troubleshooting for the authentication module. See [Setup & Configuration](./) for initial setup.
4
+
5
+ ## Complete Error Reference
6
+
7
+ All error messages from the authentication module, organized by source:
8
+
9
+ ### Component Errors (AuthenticateComponent)
10
+
11
+ | Error Message | Status | Method |
12
+ |---------------|--------|--------|
13
+ | `[AuthenticateComponent] At least one of jwtOptions or basicOptions must be provided` | 500 | `validateOptions` |
14
+ | <code v-pre>[defineJWTAuth] Invalid jwtSecret &#124; Provided: {{jwtSecret}}</code> | 500 | `defineJWTAuth` |
15
+ | <code v-pre>[defineJWTAuth] Invalid applicationSecret &#124; Provided: {{applicationSecret}}</code> | 500 | `defineJWTAuth` |
16
+ | `[defineJWTAuth] getTokenExpiresFn is required` | 500 | `defineJWTAuth` |
17
+ | `[defineBasicAuth] verifyCredentials function is required` | 500 | `defineBasicAuth` |
18
+ | `[defineControllers] Auth controller requires jwtOptions to be configured` | 500 | `defineControllers` |
19
+
20
+ ### JWTTokenService Errors
21
+
22
+ | Error Message | Status | Method |
23
+ |---------------|--------|--------|
24
+ | `[JWTTokenService] Invalid jwtSecret` | 500 | `constructor` |
25
+ | `[JWTTokenService] Invalid applicationSecret` | 500 | `constructor` |
26
+ | `[JWTTokenService] Invalid getTokenExpiresFn` | 500 | `constructor` |
27
+ | `Unauthorized user! Missing authorization header` | 401 | `extractCredentials` |
28
+ | `Unauthorized user! Invalid schema of request token!` | 401 | `extractCredentials` |
29
+ | <code v-pre>Authorization header value is invalid format. It must follow the pattern: 'Bearer xx.yy.zz' where xx.yy.zz is a valid JWT token.</code> | 401 | `extractCredentials` |
30
+ | `[verify] Invalid request token!` | 401 | `verify` |
31
+ | <code v-pre>[verify] Failed to verify token &#124; Message: {{error.message}}</code> | 401 | `verify` |
32
+ | `[generate] Invalid token payload!` | 401 | `generate` |
33
+ | <code v-pre>[generate] Failed to generate token &#124; Error: {{error.message}}</code> | 500 | `generate` |
34
+
35
+ ### BasicTokenService Errors
36
+
37
+ | Error Message | Status | Method |
38
+ |---------------|--------|--------|
39
+ | `[BasicTokenService] Invalid verifyCredentials function` | 500 | `constructor` |
40
+ | `Unauthorized! Missing authorization header` | 401 | `extractCredentials` |
41
+ | `Unauthorized! Invalid authorization schema, expected Basic` | 401 | `extractCredentials` |
42
+ | `Unauthorized! Invalid authorization header format` | 401 | `extractCredentials` |
43
+ | `Unauthorized! Invalid base64 credentials format` | 401 | `extractCredentials` |
44
+ | `Unauthorized! Invalid username or password` | 401 | `verify` |
45
+
46
+ ### Strategy Registry Errors
47
+
48
+ | Error Message | Status | Method |
49
+ |---------------|--------|--------|
50
+ | <code v-pre>[getStrategyKey] Invalid strategy name &#124; name: {{name}}</code> | 500 | `getStrategyKey` |
51
+ | <code v-pre>[executeStrategy] Strategy not found: {{strategyName}}</code> | 500 | `executeStrategy` |
52
+ | <code v-pre>[executeStrategy] strategy: {{strategyName}} &#124; Authentication Strategy NOT FOUND</code> | 500 | `executeStrategy` |
53
+ | <code v-pre>Authentication failed. Tried strategies: {{strategies}}</code> | 401 | `authenticate` (any mode) |
54
+ | `Failed to identify authenticated user!` | 401 | `authenticate` (all mode) |
55
+ | <code v-pre>Invalid authentication mode &#124; mode: {{mode}}</code> | 500 | `authenticate` (default) |
56
+
57
+ ### Controller Factory Errors
58
+
59
+ | Error Message | Status | Method |
60
+ |---------------|--------|--------|
61
+ | `[AuthController] Failed to init auth controller | Invalid injectable authentication service!` | 500 | `constructor` |
62
+
63
+ ## Troubleshooting
64
+
65
+ ### "[AuthenticateComponent] At least one of jwtOptions or basicOptions must be provided"
66
+
67
+ **Cause:** The `AuthenticateComponent` requires at least one authentication method to be configured. Neither `JWT_OPTIONS` nor `BASIC_OPTIONS` was bound in the DI container.
68
+
69
+ **Fix:** Bind at least one set of options before registering the component:
70
+
71
+ ```typescript
72
+ this.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
73
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
74
+ jwtSecret: process.env.APP_ENV_JWT_SECRET,
75
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
76
+ });
77
+ ```
78
+
79
+ ### "[defineJWTAuth] Invalid jwtSecret" / "[defineJWTAuth] Invalid applicationSecret"
80
+
81
+ **Cause:** The JWT secret or application secret is missing, empty, or set to the default placeholder `'unknown_secret'`. The component's `defineJWTAuth()` method validates these values during binding. The error message includes the actual provided value (e.g., <code v-pre>[defineJWTAuth] Invalid jwtSecret | Provided: {{jwtSecret}}</code>).
82
+
83
+ **Fix:** Set strong, unique values for both secrets in your environment variables:
84
+
85
+ ```
86
+ APP_ENV_APPLICATION_SECRET=your-strong-application-secret
87
+ APP_ENV_JWT_SECRET=your-strong-jwt-secret
88
+ ```
89
+
90
+ ### "[defineJWTAuth] getTokenExpiresFn is required"
91
+
92
+ **Cause:** The `getTokenExpiresFn` function was not provided in the JWT options.
93
+
94
+ **Fix:** Include a `getTokenExpiresFn` in your JWT options binding:
95
+
96
+ ```typescript
97
+ this.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
98
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
99
+ jwtSecret: process.env.APP_ENV_JWT_SECRET,
100
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
101
+ });
102
+ ```
103
+
104
+ ### "[defineBasicAuth] verifyCredentials function is required"
105
+
106
+ **Cause:** `BASIC_OPTIONS` was bound but without a `verifyCredentials` callback function.
107
+
108
+ **Fix:** Provide a `verifyCredentials` function in the Basic options:
109
+
110
+ ```typescript
111
+ this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
112
+ verifyCredentials: async (opts) => {
113
+ // Your credential verification logic
114
+ return { userId: user.id };
115
+ },
116
+ });
117
+ ```
118
+
119
+ ### "[defineControllers] Auth controller requires jwtOptions to be configured"
120
+
121
+ **Cause:** The built-in auth controller (`useAuthController: true`) was enabled without binding JWT options. The auth controller requires JWT for token generation.
122
+
123
+ **Fix:** Always bind `JWT_OPTIONS` when using the auth controller:
124
+
125
+ ```typescript
126
+ this.bind<TAuthenticationRestOptions>({ key: AuthenticateBindingKeys.REST_OPTIONS }).toValue({
127
+ useAuthController: true,
128
+ controllerOpts: { restPath: '/auth' },
129
+ });
130
+
131
+ // This is required when useAuthController is true
132
+ this.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
133
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
134
+ jwtSecret: process.env.APP_ENV_JWT_SECRET,
135
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
136
+ });
137
+ ```
138
+
139
+ ### "Authentication failed. Tried strategies: jwt, basic"
140
+
141
+ **Cause:** All configured strategies failed to authenticate the request. In `'any'` mode, every strategy is tried in order; if none succeeds, this error is thrown with a `401 Unauthorized` status.
142
+
143
+ **Fix:** Verify the client is sending the correct `Authorization` header:
144
+ - For JWT: `Authorization: Bearer <token>`
145
+ - For Basic: `Authorization: Basic <base64(username:password)>`
146
+
147
+ Check that the token is not expired and the credentials are valid.
148
+
149
+ ### "[AuthController] Failed to init auth controller | Invalid injectable authentication service!"
150
+
151
+ **Cause:** The auth controller factory could not resolve the auth service from the DI container. The service key (default `'services.AuthenticationService'`) is not bound.
152
+
153
+ **Fix:** Register your `AuthenticationService` before registering the component:
154
+
155
+ ```typescript
156
+ this.service(AuthenticationService);
157
+ // Then register the component
158
+ this.component(AuthenticateComponent);
159
+ ```
160
+
161
+ ### "[JWTTokenService] Invalid jwtSecret" / "[JWTTokenService] Invalid applicationSecret" / "[JWTTokenService] Invalid getTokenExpiresFn"
162
+
163
+ **Cause:** The `JWTTokenService` constructor validates its injected options. These errors (status 500) occur when the service is instantiated with missing or falsy values. This is separate from the component-level `defineJWTAuth` validation and fires during DI resolution.
164
+
165
+ **Fix:** Ensure the bound `IJWTTokenServiceOptions` has all required fields populated with valid values.
166
+
167
+ ### "[BasicTokenService] Invalid verifyCredentials function"
168
+
169
+ **Cause:** The `BasicTokenService` constructor validates that the injected `verifyCredentials` option is present. This error (status 500) fires during DI resolution.
170
+
171
+ **Fix:** Ensure the bound `IBasicTokenServiceOptions` includes a `verifyCredentials` function.
172
+
173
+ ## See Also
174
+
175
+ - [Setup & Configuration](./) -- Binding keys, options interfaces, and initial setup
176
+ - [Usage & Examples](./usage) -- Securing routes, auth flows, and API endpoints
177
+ - [API Reference](./api) -- Architecture, service internals, and strategy registry