@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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -1,346 +1,111 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
import {
|
|
178
|
-
extraUserColumns,
|
|
179
|
-
extraRoleColumns,
|
|
180
|
-
extraPermissionColumns,
|
|
181
|
-
extraPolicyDefinitionColumns,
|
|
182
|
-
} from '@venizia/ignis';
|
|
38
|
+
this.component(AuthenticateComponent);
|
|
183
39
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
**JOSE standards**
|
|
231
68
|
|
|
232
|
-
|
|
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
|
-
|
|
75
|
+
## Common tasks
|
|
235
76
|
|
|
236
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
118
|
+
**Enable the built-in `/auth` controller.** Requires `jwtOptions` and a bound `IAuthService`.
|
|
354
119
|
|
|
355
120
|
```typescript
|
|
356
|
-
|
|
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
|
-
|
|
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
|
-
|
|
433
|
-
|
|
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: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
877
|
-
|
|
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
|
-
|
|
880
|
-
- [Security Guidelines](/best-practices/security-guidelines) -- Authentication best practices
|
|
165
|
+
**Files:**
|
|
881
166
|
|
|
882
|
-
-
|
|
883
|
-
|
|
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`
|