@venizia/ignis-docs 0.0.5 → 0.0.6-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/package.json +1 -1
- package/wiki/best-practices/architectural-patterns.md +0 -2
- package/wiki/best-practices/architecture-decisions.md +0 -8
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/code-style-standards/index.md +0 -1
- package/wiki/best-practices/code-style-standards/tooling.md +0 -3
- package/wiki/best-practices/contribution-workflow.md +12 -12
- package/wiki/best-practices/index.md +4 -14
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/guides/core-concepts/application/bootstrapping.md +6 -7
- package/wiki/guides/core-concepts/components-guide.md +1 -1
- package/wiki/guides/core-concepts/components.md +2 -2
- package/wiki/guides/core-concepts/dependency-injection.md +4 -5
- package/wiki/guides/core-concepts/persistent/datasources.md +4 -5
- package/wiki/guides/core-concepts/services.md +1 -1
- package/wiki/guides/get-started/5-minute-quickstart.md +4 -5
- package/wiki/guides/get-started/philosophy.md +12 -24
- package/wiki/guides/index.md +2 -9
- package/wiki/guides/reference/mcp-docs-server.md +13 -13
- package/wiki/guides/tutorials/building-a-crud-api.md +10 -10
- package/wiki/guides/tutorials/complete-installation.md +11 -12
- package/wiki/guides/tutorials/ecommerce-api.md +3 -3
- package/wiki/guides/tutorials/realtime-chat.md +6 -6
- package/wiki/guides/tutorials/testing.md +4 -5
- package/wiki/index.md +8 -14
- package/wiki/references/base/bootstrapping.md +0 -3
- package/wiki/references/base/components.md +2 -2
- package/wiki/references/base/controllers.md +0 -1
- package/wiki/references/base/datasources.md +1 -1
- package/wiki/references/base/dependency-injection.md +2 -2
- package/wiki/references/base/filter-system/default-filter.md +2 -3
- package/wiki/references/base/filter-system/index.md +1 -1
- package/wiki/references/base/filter-system/quick-reference.md +0 -14
- package/wiki/references/base/middlewares.md +0 -8
- package/wiki/references/base/providers.md +0 -9
- package/wiki/references/base/repositories/advanced.md +1 -1
- package/wiki/references/base/repositories/mixins.md +2 -3
- package/wiki/references/base/services.md +0 -1
- package/wiki/references/components/authentication/api.md +444 -0
- package/wiki/references/components/authentication/errors.md +177 -0
- package/wiki/references/components/authentication/index.md +571 -0
- package/wiki/references/components/authentication/usage.md +781 -0
- package/wiki/references/components/health-check.md +292 -103
- package/wiki/references/components/index.md +14 -12
- package/wiki/references/components/mail/api.md +505 -0
- package/wiki/references/components/mail/errors.md +176 -0
- package/wiki/references/components/mail/index.md +535 -0
- package/wiki/references/components/mail/usage.md +404 -0
- package/wiki/references/components/request-tracker.md +229 -25
- package/wiki/references/components/socket-io/api.md +1051 -0
- package/wiki/references/components/socket-io/errors.md +119 -0
- package/wiki/references/components/socket-io/index.md +410 -0
- package/wiki/references/components/socket-io/usage.md +322 -0
- package/wiki/references/components/static-asset/api.md +261 -0
- package/wiki/references/components/static-asset/errors.md +89 -0
- package/wiki/references/components/static-asset/index.md +617 -0
- package/wiki/references/components/static-asset/usage.md +364 -0
- package/wiki/references/components/swagger.md +390 -110
- package/wiki/references/components/template/api-page.md +125 -0
- package/wiki/references/components/template/errors-page.md +100 -0
- package/wiki/references/components/template/index.md +104 -0
- package/wiki/references/components/template/setup-page.md +134 -0
- package/wiki/references/components/template/single-page.md +132 -0
- package/wiki/references/components/template/usage-page.md +127 -0
- package/wiki/references/components/websocket/api.md +508 -0
- package/wiki/references/components/websocket/errors.md +123 -0
- package/wiki/references/components/websocket/index.md +453 -0
- package/wiki/references/components/websocket/usage.md +475 -0
- package/wiki/references/helpers/cron/index.md +224 -0
- package/wiki/references/helpers/crypto/index.md +537 -0
- package/wiki/references/helpers/env/index.md +214 -0
- package/wiki/references/helpers/error/index.md +232 -0
- package/wiki/references/helpers/index.md +16 -15
- package/wiki/references/helpers/inversion/index.md +608 -0
- package/wiki/references/helpers/logger/index.md +600 -0
- package/wiki/references/helpers/network/api.md +986 -0
- package/wiki/references/helpers/network/index.md +620 -0
- package/wiki/references/helpers/queue/index.md +589 -0
- package/wiki/references/helpers/redis/index.md +495 -0
- package/wiki/references/helpers/socket-io/api.md +497 -0
- package/wiki/references/helpers/socket-io/index.md +513 -0
- package/wiki/references/helpers/storage/api.md +705 -0
- package/wiki/references/helpers/storage/index.md +583 -0
- package/wiki/references/helpers/template/index.md +66 -0
- package/wiki/references/helpers/template/single-page.md +126 -0
- package/wiki/references/helpers/testing/index.md +510 -0
- package/wiki/references/helpers/types/index.md +512 -0
- package/wiki/references/helpers/uid/index.md +272 -0
- package/wiki/references/helpers/websocket/api.md +736 -0
- package/wiki/references/helpers/websocket/index.md +574 -0
- package/wiki/references/helpers/worker-thread/index.md +470 -0
- package/wiki/references/index.md +2 -9
- package/wiki/references/quick-reference.md +3 -18
- package/wiki/references/utilities/jsx.md +1 -8
- package/wiki/references/utilities/statuses.md +0 -7
- package/wiki/references/components/authentication.md +0 -476
- package/wiki/references/components/mail.md +0 -687
- package/wiki/references/components/socket-io.md +0 -562
- package/wiki/references/components/static-asset.md +0 -1277
- package/wiki/references/helpers/cron.md +0 -108
- package/wiki/references/helpers/crypto.md +0 -132
- package/wiki/references/helpers/env.md +0 -83
- package/wiki/references/helpers/error.md +0 -97
- package/wiki/references/helpers/inversion.md +0 -176
- package/wiki/references/helpers/logger.md +0 -296
- package/wiki/references/helpers/network.md +0 -396
- package/wiki/references/helpers/queue.md +0 -150
- package/wiki/references/helpers/redis.md +0 -142
- package/wiki/references/helpers/socket-io.md +0 -932
- package/wiki/references/helpers/storage.md +0 -665
- package/wiki/references/helpers/testing.md +0 -133
- package/wiki/references/helpers/types.md +0 -167
- package/wiki/references/helpers/uid.md +0 -167
- package/wiki/references/helpers/worker-thread.md +0 -178
- package/wiki/references/src-details/boot.md +0 -379
- package/wiki/references/src-details/core.md +0 -263
- package/wiki/references/src-details/dev-configs.md +0 -298
- package/wiki/references/src-details/docs.md +0 -71
- package/wiki/references/src-details/helpers.md +0 -211
- package/wiki/references/src-details/index.md +0 -86
- package/wiki/references/src-details/inversion.md +0 -340
|
@@ -241,7 +241,6 @@ await productRepository.update(productId, {
|
|
|
241
241
|
});
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
-
---
|
|
245
244
|
|
|
246
245
|
## Status Groups
|
|
247
246
|
|
|
@@ -278,7 +277,6 @@ const activeJobs = jobs.filter(job =>
|
|
|
278
277
|
const canRetry = !Statuses.FAILED_SCHEME_SET.has(task.status);
|
|
279
278
|
```
|
|
280
279
|
|
|
281
|
-
---
|
|
282
280
|
|
|
283
281
|
## Validation Methods
|
|
284
282
|
|
|
@@ -330,7 +328,6 @@ const activeUsers = users.filter(user => Statuses.isActive(user.status));
|
|
|
330
328
|
const pendingOrders = orders.filter(order => Statuses.isPending(order.status));
|
|
331
329
|
```
|
|
332
330
|
|
|
333
|
-
---
|
|
334
331
|
|
|
335
332
|
## Specialized Status Classes
|
|
336
333
|
|
|
@@ -452,7 +449,6 @@ const oauthUser = await userRepository.create({
|
|
|
452
449
|
});
|
|
453
450
|
```
|
|
454
451
|
|
|
455
|
-
---
|
|
456
452
|
|
|
457
453
|
## Usage Examples
|
|
458
454
|
|
|
@@ -606,7 +602,6 @@ class TaskService extends BaseService {
|
|
|
606
602
|
}
|
|
607
603
|
```
|
|
608
604
|
|
|
609
|
-
---
|
|
610
605
|
|
|
611
606
|
## Binding Namespaces
|
|
612
607
|
|
|
@@ -659,7 +654,6 @@ const app = container.get(CoreBindings.APPLICATION_INSTANCE);
|
|
|
659
654
|
const config = container.get(CoreBindings.APPLICATION_CONFIG);
|
|
660
655
|
```
|
|
661
656
|
|
|
662
|
-
---
|
|
663
657
|
|
|
664
658
|
## Best Practices
|
|
665
659
|
|
|
@@ -724,7 +718,6 @@ if (isTerminal) {
|
|
|
724
718
|
}
|
|
725
719
|
```
|
|
726
720
|
|
|
727
|
-
---
|
|
728
721
|
|
|
729
722
|
## See Also
|
|
730
723
|
|
|
@@ -1,476 +0,0 @@
|
|
|
1
|
-
# Authentication Component
|
|
2
|
-
|
|
3
|
-
JWT and Basic authentication system for Ignis applications with multi-strategy support.
|
|
4
|
-
|
|
5
|
-
## Quick Reference
|
|
6
|
-
|
|
7
|
-
| Component | Purpose |
|
|
8
|
-
|-----------|---------|
|
|
9
|
-
| **AuthenticateComponent** | Main component registering auth services and controllers |
|
|
10
|
-
| **AuthenticationStrategyRegistry** | Singleton managing available auth strategies |
|
|
11
|
-
| **JWTAuthenticationStrategy** | JWT verification using `JWTTokenService` |
|
|
12
|
-
| **BasicAuthenticationStrategy** | Basic HTTP authentication using `BasicTokenService` |
|
|
13
|
-
| **JWTTokenService** | Generate, verify, encrypt/decrypt JWT tokens |
|
|
14
|
-
| **BasicTokenService** | Extract and verify Basic auth credentials |
|
|
15
|
-
| **IAuthService** | Interface for custom auth implementation (sign-in, sign-up) |
|
|
16
|
-
| **defineAuthController** | Factory function for creating custom auth controllers |
|
|
17
|
-
|
|
18
|
-
### Key Environment Variables
|
|
19
|
-
|
|
20
|
-
| Variable | Purpose | Required |
|
|
21
|
-
|----------|---------|----------|
|
|
22
|
-
| `APP_ENV_APPLICATION_SECRET` | Encrypt JWT payload | Required for JWT |
|
|
23
|
-
| `APP_ENV_JWT_SECRET` | Sign and verify JWT signature | Required for JWT |
|
|
24
|
-
| `APP_ENV_JWT_EXPIRES_IN` | Token expiration (seconds) | Optional |
|
|
25
|
-
|
|
26
|
-
### Binding Keys
|
|
27
|
-
|
|
28
|
-
The authentication component uses **separate binding keys** for each configuration type:
|
|
29
|
-
|
|
30
|
-
| Binding Key | Type | Description |
|
|
31
|
-
|-------------|------|-------------|
|
|
32
|
-
| `AuthenticateBindingKeys.REST_OPTIONS` | `TAuthenticationRestOptions` | REST controller configuration |
|
|
33
|
-
| `AuthenticateBindingKeys.JWT_OPTIONS` | `IJWTTokenServiceOptions` | JWT token configuration |
|
|
34
|
-
| `AuthenticateBindingKeys.BASIC_OPTIONS` | `IBasicTokenServiceOptions` | Basic auth configuration |
|
|
35
|
-
|
|
36
|
-
### REST Options Configuration
|
|
37
|
-
|
|
38
|
-
| Option | Type | Description |
|
|
39
|
-
|--------|------|-------------|
|
|
40
|
-
| `useAuthController` | `boolean` | Enable/disable built-in auth controller (default: `false`) |
|
|
41
|
-
| `controllerOpts` | `TDefineAuthControllerOpts` | Configuration for built-in auth controller (required if `useAuthController` is `true`) |
|
|
42
|
-
| `controllerOpts.restPath` | `string` | Base path for auth endpoints (default: `/auth`) |
|
|
43
|
-
| `controllerOpts.serviceKey` | `string` | Dependency injection key for auth service |
|
|
44
|
-
| `controllerOpts.requireAuthenticatedSignUp` | `boolean` | Whether sign-up requires authentication (default: `false`) |
|
|
45
|
-
| `controllerOpts.payload` | `object` | Custom Zod schemas for request/response payloads |
|
|
46
|
-
|
|
47
|
-
::: warning IMPORTANT
|
|
48
|
-
At least one of `JWT_OPTIONS` or `BASIC_OPTIONS` must be bound. If neither is configured, the component will throw an error.
|
|
49
|
-
:::
|
|
50
|
-
|
|
51
|
-
### Route Configuration Options
|
|
52
|
-
|
|
53
|
-
| Option | Type | Description |
|
|
54
|
-
|--------|------|-------------|
|
|
55
|
-
| `authStrategies` | `TAuthStrategy[]` | Array of strategy names to use (e.g., `['jwt']`, `['jwt', 'basic']`) |
|
|
56
|
-
| `authMode` | `'any' \| 'all'` | How to handle multiple strategies (default: `'any'`) |
|
|
57
|
-
| `skipAuth` | `boolean` | Skip authentication for this route (default: `false`) |
|
|
58
|
-
|
|
59
|
-
### Auth Modes
|
|
60
|
-
|
|
61
|
-
| Mode | Behavior |
|
|
62
|
-
|------|----------|
|
|
63
|
-
| `'any'` | First successful strategy wins (fallback mode) |
|
|
64
|
-
| `'all'` | All strategies must pass (MFA mode) |
|
|
65
|
-
|
|
66
|
-
## Architecture Components
|
|
67
|
-
|
|
68
|
-
- **`AuthenticateComponent`**: Registers all necessary services and optionally the authentication controller
|
|
69
|
-
- **`AuthenticationStrategyRegistry`**: Singleton managing authentication strategies
|
|
70
|
-
- **`JWTAuthenticationStrategy`**: JWT strategy implementation using `JWTTokenService`
|
|
71
|
-
- **`BasicAuthenticationStrategy`**: Basic HTTP auth strategy using `BasicTokenService`
|
|
72
|
-
- **`JWTTokenService`**: Generates, verifies, encrypts/decrypts JWT payloads
|
|
73
|
-
- **`BasicTokenService`**: Extracts and verifies Basic auth credentials
|
|
74
|
-
- **`defineAuthController`**: Factory function to create customizable authentication controller
|
|
75
|
-
- **Protected Routes**: Use `authStrategies` and `authMode` in route configs to secure endpoints
|
|
76
|
-
|
|
77
|
-
## Implementation Details
|
|
78
|
-
|
|
79
|
-
### Tech Stack
|
|
80
|
-
|
|
81
|
-
- **Hono**
|
|
82
|
-
- **`jose`:** For JWT signing, verification, and encryption.
|
|
83
|
-
- **`@venizia/ignis`**: The core framework.
|
|
84
|
-
|
|
85
|
-
### Configuration
|
|
86
|
-
|
|
87
|
-
Configure the authentication feature using environment variables:
|
|
88
|
-
|
|
89
|
-
- `APP_ENV_APPLICATION_SECRET`: A secret for encrypting the JWT payload.
|
|
90
|
-
- `APP_ENV_JWT_SECRET`: The secret for signing and verifying the JWT signature.
|
|
91
|
-
- `APP_ENV_JWT_EXPIRES_IN`: The JWT expiration time in seconds.
|
|
92
|
-
|
|
93
|
-
::: danger SECURITY NOTE
|
|
94
|
-
Both `APP_ENV_APPLICATION_SECRET` and `APP_ENV_JWT_SECRET` are **mandatory** when using JWT authentication. For security purposes, you must set these to strong, unique secret values.
|
|
95
|
-
:::
|
|
96
|
-
|
|
97
|
-
**Example `.env` file:**
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
APP_ENV_APPLICATION_SECRET=your-strong-application-secret
|
|
101
|
-
APP_ENV_JWT_SECRET=your-strong-jwt-secret
|
|
102
|
-
APP_ENV_JWT_EXPIRES_IN=86400
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Code Samples
|
|
106
|
-
|
|
107
|
-
#### 1. Registering the Authentication Component
|
|
108
|
-
|
|
109
|
-
In `src/application.ts`, register the `AuthenticateComponent` and authentication strategies.
|
|
110
|
-
|
|
111
|
-
**JWT Only Setup:**
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// src/application.ts
|
|
115
|
-
import {
|
|
116
|
-
AuthenticateComponent,
|
|
117
|
-
AuthenticateBindingKeys,
|
|
118
|
-
Authentication,
|
|
119
|
-
AuthenticationStrategyRegistry,
|
|
120
|
-
IJWTTokenServiceOptions,
|
|
121
|
-
JWTAuthenticationStrategy,
|
|
122
|
-
BaseApplication,
|
|
123
|
-
ValueOrPromise,
|
|
124
|
-
} from '@venizia/ignis';
|
|
125
|
-
import { AuthenticationService } from './services';
|
|
126
|
-
|
|
127
|
-
export class Application extends BaseApplication {
|
|
128
|
-
registerAuth() {
|
|
129
|
-
this.service(AuthenticationService);
|
|
130
|
-
|
|
131
|
-
// Bind JWT options
|
|
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
|
-
this.component(AuthenticateComponent);
|
|
139
|
-
AuthenticationStrategyRegistry.getInstance().register({
|
|
140
|
-
container: this,
|
|
141
|
-
strategies: [
|
|
142
|
-
{ name: Authentication.STRATEGY_JWT, strategy: JWTAuthenticationStrategy },
|
|
143
|
-
],
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
preConfigure(): ValueOrPromise<void> {
|
|
148
|
-
this.registerAuth();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**Basic Auth Only Setup:**
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
import {
|
|
157
|
-
AuthenticateComponent,
|
|
158
|
-
AuthenticateBindingKeys,
|
|
159
|
-
Authentication,
|
|
160
|
-
AuthenticationStrategyRegistry,
|
|
161
|
-
BasicAuthenticationStrategy,
|
|
162
|
-
IBasicTokenServiceOptions,
|
|
163
|
-
BaseApplication,
|
|
164
|
-
} from '@venizia/ignis';
|
|
165
|
-
|
|
166
|
-
export class Application extends BaseApplication {
|
|
167
|
-
registerAuth() {
|
|
168
|
-
// Bind Basic auth options
|
|
169
|
-
this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
|
|
170
|
-
verifyCredentials: async (opts) => {
|
|
171
|
-
const { credentials, context } = opts;
|
|
172
|
-
// Your verification logic here
|
|
173
|
-
const user = await this.userRepo.findByUsername(credentials.username);
|
|
174
|
-
if (user && await bcrypt.compare(credentials.password, user.passwordHash)) {
|
|
175
|
-
return { userId: user.id, roles: user.roles };
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
this.component(AuthenticateComponent);
|
|
182
|
-
AuthenticationStrategyRegistry.getInstance().register({
|
|
183
|
-
container: this,
|
|
184
|
-
strategies: [
|
|
185
|
-
{ name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
|
|
186
|
-
],
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
**Combined JWT + Basic Auth Setup (with fallback):**
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
import {
|
|
196
|
-
AuthenticateComponent,
|
|
197
|
-
AuthenticateBindingKeys,
|
|
198
|
-
Authentication,
|
|
199
|
-
AuthenticationStrategyRegistry,
|
|
200
|
-
BasicAuthenticationStrategy,
|
|
201
|
-
JWTAuthenticationStrategy,
|
|
202
|
-
IJWTTokenServiceOptions,
|
|
203
|
-
IBasicTokenServiceOptions,
|
|
204
|
-
TAuthenticationRestOptions,
|
|
205
|
-
BaseApplication,
|
|
206
|
-
} from '@venizia/ignis';
|
|
207
|
-
|
|
208
|
-
export class Application extends BaseApplication {
|
|
209
|
-
registerAuth() {
|
|
210
|
-
this.service(AuthenticationService);
|
|
211
|
-
|
|
212
|
-
// Bind REST options (for auth controller)
|
|
213
|
-
this.bind<TAuthenticationRestOptions>({ key: AuthenticateBindingKeys.REST_OPTIONS }).toValue({
|
|
214
|
-
useAuthController: true,
|
|
215
|
-
controllerOpts: {
|
|
216
|
-
restPath: '/auth',
|
|
217
|
-
payload: {
|
|
218
|
-
signIn: {
|
|
219
|
-
request: { schema: SignInRequestSchema },
|
|
220
|
-
response: { schema: SignInResponseSchema },
|
|
221
|
-
},
|
|
222
|
-
signUp: {
|
|
223
|
-
request: { schema: SignUpRequestSchema },
|
|
224
|
-
response: { schema: SignUpResponseSchema },
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// Bind JWT options
|
|
231
|
-
this.bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS }).toValue({
|
|
232
|
-
applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
|
|
233
|
-
jwtSecret: process.env.APP_ENV_JWT_SECRET,
|
|
234
|
-
getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Bind Basic auth options
|
|
238
|
-
this.bind<IBasicTokenServiceOptions>({ key: AuthenticateBindingKeys.BASIC_OPTIONS }).toValue({
|
|
239
|
-
verifyCredentials: async (opts) => {
|
|
240
|
-
const authenticateService = this.get<AuthenticationService>({
|
|
241
|
-
key: BindingKeys.build({
|
|
242
|
-
namespace: BindingNamespaces.SERVICE,
|
|
243
|
-
key: AuthenticationService.name,
|
|
244
|
-
}),
|
|
245
|
-
});
|
|
246
|
-
return authenticateService.signIn(opts.context, {
|
|
247
|
-
identifier: { scheme: 'username', value: opts.credentials.username },
|
|
248
|
-
credential: { scheme: 'basic', value: opts.credentials.password },
|
|
249
|
-
});
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
this.component(AuthenticateComponent);
|
|
254
|
-
|
|
255
|
-
// Register multiple strategies at once
|
|
256
|
-
AuthenticationStrategyRegistry.getInstance().register({
|
|
257
|
-
container: this,
|
|
258
|
-
strategies: [
|
|
259
|
-
{ name: Authentication.STRATEGY_JWT, strategy: JWTAuthenticationStrategy },
|
|
260
|
-
{ name: Authentication.STRATEGY_BASIC, strategy: BasicAuthenticationStrategy },
|
|
261
|
-
],
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
#### 2. Basic Authentication Verification Function
|
|
268
|
-
|
|
269
|
-
The `verifyCredentials` function receives an options object with credentials and context:
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
type TBasicAuthVerifyFn = (opts: {
|
|
273
|
-
credentials: { username: string; password: string };
|
|
274
|
-
context: Context;
|
|
275
|
-
}) => Promise<IAuthUser | null>;
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
Example implementation:
|
|
279
|
-
|
|
280
|
-
```typescript
|
|
281
|
-
basicOptions: {
|
|
282
|
-
verifyCredentials: async (opts) => {
|
|
283
|
-
const { credentials, context } = opts;
|
|
284
|
-
|
|
285
|
-
// Look up user by username
|
|
286
|
-
const user = await userRepo.findByUsername(credentials.username);
|
|
287
|
-
|
|
288
|
-
if (!user) {
|
|
289
|
-
return null; // User not found
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Verify password
|
|
293
|
-
const isValid = await bcrypt.compare(credentials.password, user.passwordHash);
|
|
294
|
-
|
|
295
|
-
if (!isValid) {
|
|
296
|
-
return null; // Invalid password
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Return user info (must include userId)
|
|
300
|
-
return {
|
|
301
|
-
userId: user.id,
|
|
302
|
-
roles: user.roles,
|
|
303
|
-
// ... any additional fields
|
|
304
|
-
};
|
|
305
|
-
},
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
#### 3. Implementing an AuthenticationService
|
|
310
|
-
|
|
311
|
-
The `AuthenticateComponent` depends on a service that implements the `IAuthService` interface.
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
// src/services/authentication.service.ts
|
|
315
|
-
import {
|
|
316
|
-
BaseService,
|
|
317
|
-
inject,
|
|
318
|
-
IAuthService,
|
|
319
|
-
IJWTTokenPayload,
|
|
320
|
-
JWTTokenService,
|
|
321
|
-
TSignInRequest,
|
|
322
|
-
getError,
|
|
323
|
-
} from '@venizia/ignis';
|
|
324
|
-
import { Context } from 'hono';
|
|
325
|
-
|
|
326
|
-
export class AuthenticationService extends BaseService implements IAuthService {
|
|
327
|
-
constructor(
|
|
328
|
-
@inject({ key: 'services.JWTTokenService' })
|
|
329
|
-
private _jwtTokenService: JWTTokenService,
|
|
330
|
-
) {
|
|
331
|
-
super({ scope: AuthenticationService.name });
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
async signIn(context: Context, opts: TSignInRequest): Promise<{ token: string }> {
|
|
335
|
-
const { identifier, credential } = opts;
|
|
336
|
-
|
|
337
|
-
// Your custom logic here
|
|
338
|
-
const user = await this.userRepo.findByIdentifier(identifier);
|
|
339
|
-
|
|
340
|
-
if (!user || !await this.verifyCredential(credential, user)) {
|
|
341
|
-
throw getError({ message: 'Invalid credentials' });
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const payload: IJWTTokenPayload = {
|
|
345
|
-
userId: user.id,
|
|
346
|
-
roles: user.roles,
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
const token = await this._jwtTokenService.generate({ payload });
|
|
350
|
-
return { token };
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async signUp(context: Context, opts: any): Promise<any> {
|
|
354
|
-
// Implement your sign-up logic
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
async changePassword(context: Context, opts: any): Promise<any> {
|
|
358
|
-
// Implement your change password logic
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
#### 4. Securing Routes
|
|
364
|
-
|
|
365
|
-
Use `authStrategies` and `authMode` in route configurations:
|
|
366
|
-
|
|
367
|
-
**Single Strategy:**
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
const SECURE_ROUTE_CONFIG = {
|
|
371
|
-
path: '/secure-data',
|
|
372
|
-
method: HTTP.Methods.GET,
|
|
373
|
-
authStrategies: [Authentication.STRATEGY_JWT],
|
|
374
|
-
responses: jsonResponse({
|
|
375
|
-
description: 'Protected data',
|
|
376
|
-
schema: z.object({ message: z.string() }),
|
|
377
|
-
}),
|
|
378
|
-
} as const;
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
**Multiple Strategies with Fallback (any mode):**
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
const FALLBACK_AUTH_CONFIG = {
|
|
385
|
-
path: '/api/data',
|
|
386
|
-
method: HTTP.Methods.GET,
|
|
387
|
-
authStrategies: [Authentication.STRATEGY_JWT, Authentication.STRATEGY_BASIC],
|
|
388
|
-
authMode: 'any', // First successful strategy wins (default)
|
|
389
|
-
responses: jsonResponse({
|
|
390
|
-
description: 'Data accessible via JWT or Basic auth',
|
|
391
|
-
schema: z.object({ data: z.any() }),
|
|
392
|
-
}),
|
|
393
|
-
} as const;
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
**Multiple Strategies with MFA (all mode):**
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
const MFA_CONFIG = {
|
|
400
|
-
path: '/admin/sensitive',
|
|
401
|
-
method: HTTP.Methods.POST,
|
|
402
|
-
authStrategies: [Authentication.STRATEGY_JWT, Authentication.STRATEGY_MFA],
|
|
403
|
-
authMode: 'all', // All strategies must pass
|
|
404
|
-
responses: jsonResponse({
|
|
405
|
-
description: 'Requires both JWT and MFA',
|
|
406
|
-
schema: z.object({ success: z.boolean() }),
|
|
407
|
-
}),
|
|
408
|
-
} as const;
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
**Skipping Authentication:**
|
|
412
|
-
|
|
413
|
-
```typescript
|
|
414
|
-
const PUBLIC_ROUTE_CONFIG = {
|
|
415
|
-
path: '/public',
|
|
416
|
-
method: HTTP.Methods.GET,
|
|
417
|
-
skipAuth: true, // Bypass authentication even if controller has default auth
|
|
418
|
-
responses: jsonResponse({
|
|
419
|
-
description: 'Public endpoint',
|
|
420
|
-
schema: z.object({ message: z.string() }),
|
|
421
|
-
}),
|
|
422
|
-
} as const;
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
#### 5. Accessing the Current User in Context
|
|
426
|
-
|
|
427
|
-
After authentication, the user payload is available on the Hono `Context`:
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
import { Context } from 'hono';
|
|
431
|
-
import { Authentication, IJWTTokenPayload } from '@venizia/ignis';
|
|
432
|
-
|
|
433
|
-
// Inside a route handler
|
|
434
|
-
const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload | undefined;
|
|
435
|
-
|
|
436
|
-
if (user) {
|
|
437
|
-
console.log('Authenticated user ID:', user.userId);
|
|
438
|
-
console.log('User roles:', user.roles);
|
|
439
|
-
}
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
#### 6. Dynamic Skip Authentication
|
|
443
|
-
|
|
444
|
-
Use `Authentication.SKIP_AUTHENTICATION` to dynamically skip auth in middleware:
|
|
445
|
-
|
|
446
|
-
```typescript
|
|
447
|
-
import { Authentication } from '@venizia/ignis';
|
|
448
|
-
import { createMiddleware } from 'hono/factory';
|
|
449
|
-
|
|
450
|
-
const conditionalAuthMiddleware = createMiddleware(async (c, next) => {
|
|
451
|
-
// Skip auth for certain conditions
|
|
452
|
-
if (c.req.header('X-API-Key') === 'valid-api-key') {
|
|
453
|
-
c.set(Authentication.SKIP_AUTHENTICATION, true);
|
|
454
|
-
}
|
|
455
|
-
return next();
|
|
456
|
-
});
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
## See Also
|
|
460
|
-
|
|
461
|
-
- **Related Concepts:**
|
|
462
|
-
- [Components Overview](/guides/core-concepts/components) - Component system basics
|
|
463
|
-
- [Controllers](/guides/core-concepts/controllers) - Protecting routes with auth
|
|
464
|
-
|
|
465
|
-
- **Other Components:**
|
|
466
|
-
- [Components Index](./index) - All built-in components
|
|
467
|
-
|
|
468
|
-
- **References:**
|
|
469
|
-
- [Middlewares](/references/base/middlewares) - Custom authentication middleware
|
|
470
|
-
- [Crypto Helper](/references/helpers/crypto) - Password hashing utilities
|
|
471
|
-
|
|
472
|
-
- **Best Practices:**
|
|
473
|
-
- [Security Guidelines](/best-practices/security-guidelines) - Authentication best practices
|
|
474
|
-
|
|
475
|
-
- **Tutorials:**
|
|
476
|
-
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Adding authentication
|