@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
@@ -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