@globaltracking/auth-middleware 2.0.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.
- package/README.md +372 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +110 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +42 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract-user.d.ts +10 -0
- package/dist/extract-user.d.ts.map +1 -0
- package/dist/extract-user.js +30 -0
- package/dist/extract-user.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/middlewares/authenticate.d.ts +10 -0
- package/dist/middlewares/authenticate.d.ts.map +1 -0
- package/dist/middlewares/authenticate.js +26 -0
- package/dist/middlewares/authenticate.js.map +1 -0
- package/dist/middlewares/error-handler.d.ts +12 -0
- package/dist/middlewares/error-handler.d.ts.map +1 -0
- package/dist/middlewares/error-handler.js +22 -0
- package/dist/middlewares/error-handler.js.map +1 -0
- package/dist/middlewares/require-permission.d.ts +12 -0
- package/dist/middlewares/require-permission.d.ts.map +1 -0
- package/dist/middlewares/require-permission.js +63 -0
- package/dist/middlewares/require-permission.js.map +1 -0
- package/dist/middlewares/require-role.d.ts +12 -0
- package/dist/middlewares/require-role.d.ts.map +1 -0
- package/dist/middlewares/require-role.js +30 -0
- package/dist/middlewares/require-role.js.map +1 -0
- package/dist/middlewares/require-self.d.ts +10 -0
- package/dist/middlewares/require-self.d.ts.map +1 -0
- package/dist/middlewares/require-self.js +40 -0
- package/dist/middlewares/require-self.js.map +1 -0
- package/dist/middlewares/require-tenant.d.ts +11 -0
- package/dist/middlewares/require-tenant.d.ts.map +1 -0
- package/dist/middlewares/require-tenant.js +25 -0
- package/dist/middlewares/require-tenant.js.map +1 -0
- package/dist/nestjs/constants.d.ts +5 -0
- package/dist/nestjs/constants.d.ts.map +1 -0
- package/dist/nestjs/constants.js +8 -0
- package/dist/nestjs/constants.js.map +1 -0
- package/dist/nestjs/decorators/index.d.ts +44 -0
- package/dist/nestjs/decorators/index.d.ts.map +1 -0
- package/dist/nestjs/decorators/index.js +61 -0
- package/dist/nestjs/decorators/index.js.map +1 -0
- package/dist/nestjs/filters/auth-exception.filter.d.ts +16 -0
- package/dist/nestjs/filters/auth-exception.filter.d.ts.map +1 -0
- package/dist/nestjs/filters/auth-exception.filter.js +37 -0
- package/dist/nestjs/filters/auth-exception.filter.js.map +1 -0
- package/dist/nestjs/gt-auth.module.d.ts +31 -0
- package/dist/nestjs/gt-auth.module.d.ts.map +1 -0
- package/dist/nestjs/gt-auth.module.js +102 -0
- package/dist/nestjs/gt-auth.module.js.map +1 -0
- package/dist/nestjs/guards/auth.guard.d.ts +13 -0
- package/dist/nestjs/guards/auth.guard.d.ts.map +1 -0
- package/dist/nestjs/guards/auth.guard.js +53 -0
- package/dist/nestjs/guards/auth.guard.js.map +1 -0
- package/dist/nestjs/guards/internal-only.guard.d.ts +15 -0
- package/dist/nestjs/guards/internal-only.guard.d.ts.map +1 -0
- package/dist/nestjs/guards/internal-only.guard.js +51 -0
- package/dist/nestjs/guards/internal-only.guard.js.map +1 -0
- package/dist/nestjs/guards/permissions.guard.d.ts +23 -0
- package/dist/nestjs/guards/permissions.guard.d.ts.map +1 -0
- package/dist/nestjs/guards/permissions.guard.js +134 -0
- package/dist/nestjs/guards/permissions.guard.js.map +1 -0
- package/dist/nestjs/index.d.ts +12 -0
- package/dist/nestjs/index.d.ts.map +1 -0
- package/dist/nestjs/index.js +40 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/interceptors/org-context.interceptor.d.ts +21 -0
- package/dist/nestjs/interceptors/org-context.interceptor.d.ts.map +1 -0
- package/dist/nestjs/interceptors/org-context.interceptor.js +63 -0
- package/dist/nestjs/interceptors/org-context.interceptor.js.map +1 -0
- package/dist/nestjs/middleware/trusted-headers.middleware.d.ts +15 -0
- package/dist/nestjs/middleware/trusted-headers.middleware.d.ts.map +1 -0
- package/dist/nestjs/middleware/trusted-headers.middleware.js +42 -0
- package/dist/nestjs/middleware/trusted-headers.middleware.js.map +1 -0
- package/dist/nestjs.d.ts +2 -0
- package/dist/nestjs.d.ts.map +1 -0
- package/dist/nestjs.js +18 -0
- package/dist/nestjs.js.map +1 -0
- package/dist/strategies/gateway-header.strategy.d.ts +13 -0
- package/dist/strategies/gateway-header.strategy.d.ts.map +1 -0
- package/dist/strategies/gateway-header.strategy.js +51 -0
- package/dist/strategies/gateway-header.strategy.js.map +1 -0
- package/dist/strategies/index.d.ts +5 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +10 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/jwt.strategy.d.ts +13 -0
- package/dist/strategies/jwt.strategy.d.ts.map +1 -0
- package/dist/strategies/jwt.strategy.js +94 -0
- package/dist/strategies/jwt.strategy.js.map +1 -0
- package/dist/strategies/strategy.interface.d.ts +15 -0
- package/dist/strategies/strategy.interface.d.ts.map +1 -0
- package/dist/strategies/strategy.interface.js +3 -0
- package/dist/strategies/strategy.interface.js.map +1 -0
- package/dist/strategies/trusted-headers.strategy.d.ts +16 -0
- package/dist/strategies/trusted-headers.strategy.d.ts.map +1 -0
- package/dist/strategies/trusted-headers.strategy.js +50 -0
- package/dist/strategies/trusted-headers.strategy.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/helpers.d.ts +14 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +26 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/jwt.d.ts +12 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +104 -0
- package/dist/utils/jwt.js.map +1 -0
- package/package.json +82 -0
package/README.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# @globaltracking/auth-middleware
|
|
2
|
+
|
|
3
|
+
Unified authentication and authorization middleware for the **Global Tracking** platform. Shared by all backend microservices — supports both **Express** and **NestJS**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Strategy pattern**: Gateway header, trusted headers, JWT — configurable per service
|
|
8
|
+
- **NestJS adapter**: `GtAuthModule`, guards, decorators, interceptors, exception filter
|
|
9
|
+
- **Express middleware**: `authenticate`, `requirePermission`, `requireRole`, `requireSelf`, `requireTenant`
|
|
10
|
+
- **Hybrid permission resolution**: JWT claims (fast) → custom resolver → RBAC HTTP call → deny (fail-closed)
|
|
11
|
+
- **Multi-tenant RLS**: `OrgContextInterceptor` sets `SET LOCAL app.current_org_id` for PostgreSQL Row-Level Security
|
|
12
|
+
- **Admin bypass**: Configurable `adminRoles[]` bypass all permission checks
|
|
13
|
+
- **TypeScript-first**: Full type definitions with Express `Request` augmentation
|
|
14
|
+
- **98%+ test coverage**: 89 tests across strategies, middlewares, and config
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @globaltracking/auth-middleware
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Peer dependencies:**
|
|
23
|
+
|
|
24
|
+
| Package | Required for | Optional? |
|
|
25
|
+
|---------|-------------|-----------|
|
|
26
|
+
| `express` ^4.18 | Express middleware | Required |
|
|
27
|
+
| `@nestjs/common` ^11.0 | NestJS adapter | Optional |
|
|
28
|
+
| `@nestjs/core` ^11.0 | NestJS adapter | Optional |
|
|
29
|
+
| `typeorm` ^0.3.0 | OrgContextInterceptor (RLS) | Optional |
|
|
30
|
+
| `rxjs` ^7.0 | OrgContextInterceptor | Optional |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## NestJS Integration (Recommended)
|
|
35
|
+
|
|
36
|
+
This is the primary integration path for Global Tracking microservices.
|
|
37
|
+
|
|
38
|
+
### 1. Import GtAuthModule in your AppModule
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Module } from '@nestjs/common';
|
|
42
|
+
import { ConfigService } from '@nestjs/config';
|
|
43
|
+
import { GtAuthModule } from '@globaltracking/auth-middleware/nestjs';
|
|
44
|
+
|
|
45
|
+
@Module({
|
|
46
|
+
imports: [
|
|
47
|
+
// ... ConfigModule, TypeOrmModule, ThrottlerModule ...
|
|
48
|
+
|
|
49
|
+
GtAuthModule.forRootAsync({
|
|
50
|
+
inject: [ConfigService],
|
|
51
|
+
useFactory: (config: ConfigService) => ({
|
|
52
|
+
strategies: ['trusted-headers'],
|
|
53
|
+
internalGatewayToken: config.get('INTERNAL_GATEWAY_TOKEN'),
|
|
54
|
+
adminRoles: ['system_admin', 'org_admin'],
|
|
55
|
+
rbacServiceUrl: config.get('RBAC_SERVICE_URL'),
|
|
56
|
+
}),
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
// ... domain modules ...
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
export class AppModule {}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**What `GtAuthModule` provides (globally):**
|
|
66
|
+
|
|
67
|
+
| Component | What it does |
|
|
68
|
+
|-----------|-------------|
|
|
69
|
+
| `GtTrustedHeadersMiddleware` | Extracts `req.user` from configured strategy chain (auto-applied to all routes) |
|
|
70
|
+
| `InternalOnlyGuard` | Validates `X-Gateway-Token` header — register as `APP_GUARD` |
|
|
71
|
+
| `GtPermissionsGuard` | Checks `@RequirePermissions()` — register as `APP_GUARD` |
|
|
72
|
+
| `OrgContextInterceptor` | Sets PostgreSQL RLS context — register as `APP_INTERCEPTOR` |
|
|
73
|
+
| `AuthExceptionFilter` | Catches `AuthError` and returns standard error envelope |
|
|
74
|
+
|
|
75
|
+
### 2. Use decorators in controllers
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import {
|
|
79
|
+
RequirePermissions,
|
|
80
|
+
CurrentUser,
|
|
81
|
+
CurrentOrg,
|
|
82
|
+
Public,
|
|
83
|
+
RequireRoles,
|
|
84
|
+
} from '@globaltracking/auth-middleware/nestjs';
|
|
85
|
+
|
|
86
|
+
@Controller('vehicles')
|
|
87
|
+
export class VehiclesController {
|
|
88
|
+
@Post()
|
|
89
|
+
@RequirePermissions('vehicles:create')
|
|
90
|
+
create(
|
|
91
|
+
@CurrentOrg() orgId: string,
|
|
92
|
+
@CurrentUser('userId') userId: string,
|
|
93
|
+
@Body() dto: CreateVehicleDto,
|
|
94
|
+
) {
|
|
95
|
+
// orgId and userId extracted from trusted headers
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Get(':id')
|
|
99
|
+
@RequirePermissions('vehicles:read')
|
|
100
|
+
findOne(
|
|
101
|
+
@CurrentOrg() orgId: string,
|
|
102
|
+
@Param('id', ParseUUIDPipe) id: string,
|
|
103
|
+
) { ... }
|
|
104
|
+
|
|
105
|
+
@Delete(':id')
|
|
106
|
+
@RequireRoles('system_admin')
|
|
107
|
+
remove(@Param('id', ParseUUIDPipe) id: string) { ... }
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Health endpoints skip auth
|
|
113
|
+
@Controller('health')
|
|
114
|
+
export class HealthController {
|
|
115
|
+
@Get()
|
|
116
|
+
@Public()
|
|
117
|
+
liveness() {
|
|
118
|
+
return { status: 'ok' };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. Register guards and interceptors
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// app.module.ts providers
|
|
127
|
+
import {
|
|
128
|
+
InternalOnlyGuard,
|
|
129
|
+
GtPermissionsGuard,
|
|
130
|
+
OrgContextInterceptor,
|
|
131
|
+
} from '@globaltracking/auth-middleware/nestjs';
|
|
132
|
+
|
|
133
|
+
providers: [
|
|
134
|
+
{ provide: APP_GUARD, useClass: InternalOnlyGuard },
|
|
135
|
+
{ provide: APP_GUARD, useClass: GtPermissionsGuard },
|
|
136
|
+
{ provide: APP_GUARD, useClass: ThrottlerGuard },
|
|
137
|
+
{ provide: APP_INTERCEPTOR, useClass: OrgContextInterceptor },
|
|
138
|
+
// ... your other interceptors
|
|
139
|
+
],
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 4. Add env vars
|
|
143
|
+
|
|
144
|
+
```env
|
|
145
|
+
INTERNAL_GATEWAY_TOKEN=your-32-char-min-secret
|
|
146
|
+
RBAC_SERVICE_URL=http://gt-rbac-service:3000 # optional, for permission resolution
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Special case: gt-rbac-service
|
|
150
|
+
|
|
151
|
+
The RBAC service **cannot call itself** for permission resolution. Instead, inject its own `ResolveService` via `permissionResolver`:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
GtAuthModule.forRootAsync({
|
|
155
|
+
imports: [ResolveModule],
|
|
156
|
+
inject: [ConfigService, ResolveService],
|
|
157
|
+
useFactory: (config: ConfigService, resolveService: ResolveService) => ({
|
|
158
|
+
strategies: ['trusted-headers'],
|
|
159
|
+
internalGatewayToken: config.get('INTERNAL_GATEWAY_TOKEN'),
|
|
160
|
+
adminRoles: ['system_admin', 'org_admin'],
|
|
161
|
+
permissionResolver: (orgId, userId, resource, action) =>
|
|
162
|
+
resolveService.checkPermission(orgId, userId, resource, action),
|
|
163
|
+
}),
|
|
164
|
+
}),
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Express Integration
|
|
170
|
+
|
|
171
|
+
For services that use plain Express (no NestJS):
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import express from 'express';
|
|
175
|
+
import {
|
|
176
|
+
initAuth,
|
|
177
|
+
authenticate,
|
|
178
|
+
requirePermission,
|
|
179
|
+
requireTenant,
|
|
180
|
+
authErrorHandler,
|
|
181
|
+
} from '@globaltracking/auth-middleware';
|
|
182
|
+
|
|
183
|
+
const app = express();
|
|
184
|
+
|
|
185
|
+
// Initialize once at startup
|
|
186
|
+
initAuth({
|
|
187
|
+
strategies: ['gateway-header', 'jwt'],
|
|
188
|
+
adminRoles: ['system_admin', 'org_admin'],
|
|
189
|
+
publicKeyPath: './keys/public.pem',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Protected route
|
|
193
|
+
app.get('/v1/vehicles',
|
|
194
|
+
authenticate,
|
|
195
|
+
requireTenant,
|
|
196
|
+
requirePermission('vehicles:read'),
|
|
197
|
+
async (req, res) => {
|
|
198
|
+
// req.user.userId, req.user.orgId, etc.
|
|
199
|
+
// req.tenantId set by requireTenant
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Error handler (must be last)
|
|
204
|
+
app.use(authErrorHandler);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Configuration
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
initAuth({
|
|
213
|
+
// Strategy chain — tried in order until one matches
|
|
214
|
+
strategies: ['gateway-header', 'jwt'], // default
|
|
215
|
+
|
|
216
|
+
// Gateway header name (GCP API Gateway)
|
|
217
|
+
gatewayHeaderName: 'x-apigateway-api-userinfo', // default
|
|
218
|
+
|
|
219
|
+
// JWT verification (for local dev / non-gateway)
|
|
220
|
+
jwtIssuer: 'globaltracking-auth', // default
|
|
221
|
+
publicKey: '-----BEGIN PUBLIC KEY-----\n...', // PEM string
|
|
222
|
+
publicKeyPath: './keys/public.pem', // or file path
|
|
223
|
+
|
|
224
|
+
// Roles that bypass all permission checks
|
|
225
|
+
adminRoles: ['system_admin', 'org_admin'], // default
|
|
226
|
+
|
|
227
|
+
// Trusted headers strategy config
|
|
228
|
+
internalGatewayToken: 'secret', // also reads env INTERNAL_GATEWAY_TOKEN
|
|
229
|
+
trustedHeaderNames: {
|
|
230
|
+
userId: 'x-user-id', // default
|
|
231
|
+
orgId: 'x-org-id', // default
|
|
232
|
+
userRole: 'x-user-role', // default
|
|
233
|
+
requestId: 'x-request-id', // default
|
|
234
|
+
gatewayToken: 'x-gateway-token', // default
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
// Permission resolution (NestJS GtPermissionsGuard)
|
|
238
|
+
rbacServiceUrl: 'http://gt-rbac-service:3000', // HTTP fallback
|
|
239
|
+
permissionResolver: async (orgId, userId, resource, action) => {
|
|
240
|
+
// Custom resolver (e.g., direct DB call)
|
|
241
|
+
return true;
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Public key resolution order: `publicKey` → `publicKeyPath` → `AUTH_PUBLIC_KEY` env → `AUTH_PUBLIC_KEY_PATH` env.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Auth Strategies
|
|
251
|
+
|
|
252
|
+
| Strategy | When used | What it reads |
|
|
253
|
+
|----------|-----------|---------------|
|
|
254
|
+
| `gateway-header` | Production (GCP API Gateway) | Base64-encoded JSON in `X-Apigateway-Api-Userinfo` |
|
|
255
|
+
| `trusted-headers` | NestJS services behind API Gateway | `X-User-Id`, `X-Org-Id`, `X-User-Role`, `X-Request-Id`, `X-Gateway-Token` |
|
|
256
|
+
| `jwt` | Local dev, non-gateway environments | `Authorization: Bearer <token>` (RS256 verification) |
|
|
257
|
+
|
|
258
|
+
The strategy chain iterates in the configured order. The first strategy whose `canHandle(req)` returns true is used.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## AuthUser Shape
|
|
263
|
+
|
|
264
|
+
Every authenticated request gets `req.user` populated with:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
interface AuthUser {
|
|
268
|
+
userId: string; // from JWT sub or X-User-Id
|
|
269
|
+
email: string; // from JWT email (empty in trusted-headers mode)
|
|
270
|
+
role: UserRole; // from JWT role or X-User-Role
|
|
271
|
+
orgId: string; // from JWT org_id or X-Org-Id
|
|
272
|
+
tenantId: string; // = orgId in trusted-headers mode
|
|
273
|
+
permissions: string[]; // from JWT claims (empty in trusted-headers mode)
|
|
274
|
+
requestId: string; // from X-Request-Id
|
|
275
|
+
authSource: AuthStrategy; // 'gateway-header' | 'trusted-headers' | 'jwt'
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Permission Resolution (GtPermissionsGuard)
|
|
282
|
+
|
|
283
|
+
The NestJS `GtPermissionsGuard` resolves permissions in 3 tiers:
|
|
284
|
+
|
|
285
|
+
1. **JWT claims** — If `user.permissions` is non-empty, check in-memory (fast path)
|
|
286
|
+
2. **permissionResolver** — If configured, call the function directly (for RBAC service)
|
|
287
|
+
3. **RBAC HTTP call** — If `rbacServiceUrl` is configured, POST to `/v1/resolve/check`
|
|
288
|
+
4. **Deny** — Fail-closed if no resolution mechanism is available
|
|
289
|
+
|
|
290
|
+
Admin roles (`config.adminRoles`) always bypass all checks.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Express Middlewares
|
|
295
|
+
|
|
296
|
+
| Middleware | Purpose |
|
|
297
|
+
|-----------|---------|
|
|
298
|
+
| `authenticate` | Extracts `req.user` from strategy chain |
|
|
299
|
+
| `requireRole(...roles)` | Checks `req.user.role` against allowed roles |
|
|
300
|
+
| `requirePermission(...perms)` | Requires ALL listed permissions (admin bypass) |
|
|
301
|
+
| `requireAnyPermission(...perms)` | Requires AT LEAST ONE permission (admin bypass) |
|
|
302
|
+
| `requireTenant` | Ensures `req.user.tenantId` exists, sets `req.tenantId` |
|
|
303
|
+
| `requireSelf(paramName?)` | Compares `req.user.userId` with route param (admin bypass) |
|
|
304
|
+
| `authErrorHandler` | Error handler for `AuthError` subclasses |
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Error Handling
|
|
309
|
+
|
|
310
|
+
All auth errors extend `AuthError` and produce the standard Global Tracking error envelope:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import { UnauthorizedError, ForbiddenError } from '@globaltracking/auth-middleware';
|
|
314
|
+
|
|
315
|
+
throw new UnauthorizedError('Token expired');
|
|
316
|
+
// → 401 { success: false, error: { code: 'UNAUTHORIZED', message: 'Token expired', statusCode: 401 } }
|
|
317
|
+
|
|
318
|
+
throw new ForbiddenError('Insufficient permissions', { required: ['vehicles:write'] });
|
|
319
|
+
// → 403 { success: false, error: { code: 'FORBIDDEN', message: 'Insufficient permissions', statusCode: 403 } }
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Utility Functions
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { extractUser, hasRole, hasPermission, hasAnyPermission } from '@globaltracking/auth-middleware';
|
|
328
|
+
|
|
329
|
+
const user = extractUser(req); // throws UnauthorizedError if missing
|
|
330
|
+
hasRole(user, 'system_admin'); // boolean
|
|
331
|
+
hasPermission(user, 'vehicles:read'); // all must match
|
|
332
|
+
hasAnyPermission(user, 'a:read', 'b:read'); // at least one
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Files Replaced Per Service
|
|
338
|
+
|
|
339
|
+
When a NestJS service adopts this package, these ~10 files can be deleted:
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
src/common/decorators/current-org.decorator.ts → @CurrentOrg from package
|
|
343
|
+
src/common/decorators/current-user.decorator.ts → @CurrentUser from package
|
|
344
|
+
src/common/decorators/permissions.decorator.ts → @RequirePermissions from package
|
|
345
|
+
src/common/decorators/public.decorator.ts → @Public from package
|
|
346
|
+
src/common/guards/internal-only.guard.ts → InternalOnlyGuard from package
|
|
347
|
+
src/common/guards/permissions.guard.ts → GtPermissionsGuard from package
|
|
348
|
+
src/common/interceptors/org-context.interceptor.ts → OrgContextInterceptor from package
|
|
349
|
+
src/common/middleware/trusted-headers.middleware.ts → GtTrustedHeadersMiddleware from package
|
|
350
|
+
src/common/interfaces/jwt-payload.interface.ts → AuthUser from package
|
|
351
|
+
src/common/interfaces/request-context.interface.ts → (Express augmentation from package)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Development
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
npm test # run tests with coverage (89 tests, 98%+ coverage)
|
|
360
|
+
npm run test:watch # watch mode
|
|
361
|
+
npm run build # compile to dist/
|
|
362
|
+
npm run clean # remove dist/
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Migration from v1.x
|
|
368
|
+
|
|
369
|
+
- `superAdminRole` → use `adminRoles: ['system_admin', 'org_admin']` (legacy `superAdminRole` still works)
|
|
370
|
+
- `RequestUser` type alias still exported for backward compatibility
|
|
371
|
+
- Error handler now returns `{ success: false, error: { code, message, statusCode } }` envelope instead of `{ error: message }`
|
|
372
|
+
- `extractUser()` now sets `authSource` and `requestId` on returned user
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AuthConfig, ResolvedAuthConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Initialize auth middleware configuration. Call once at app startup.
|
|
4
|
+
*/
|
|
5
|
+
export declare function initAuth(config?: AuthConfig): void;
|
|
6
|
+
export declare function getConfig(): ResolvedAuthConfig;
|
|
7
|
+
/** Reset config to defaults (used in tests) */
|
|
8
|
+
export declare function resetConfig(): void;
|
|
9
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAyBzD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,GAAE,UAAe,GAAG,IAAI,CAqCtD;AAED,wBAAgB,SAAS,IAAI,kBAAkB,CAE9C;AAED,+CAA+C;AAC/C,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.initAuth = initAuth;
|
|
37
|
+
exports.getConfig = getConfig;
|
|
38
|
+
exports.resetConfig = resetConfig;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const DEFAULT_TRUSTED_HEADERS = {
|
|
41
|
+
userId: 'x-user-id',
|
|
42
|
+
orgId: 'x-org-id',
|
|
43
|
+
userRole: 'x-user-role',
|
|
44
|
+
requestId: 'x-request-id',
|
|
45
|
+
gatewayToken: 'x-gateway-token',
|
|
46
|
+
};
|
|
47
|
+
const DEFAULT_CONFIG = {
|
|
48
|
+
gatewayHeaderName: 'x-apigateway-api-userinfo',
|
|
49
|
+
jwtIssuer: 'globaltracking-auth',
|
|
50
|
+
publicKey: '',
|
|
51
|
+
publicKeyPath: '',
|
|
52
|
+
strategies: ['gateway-header', 'jwt'],
|
|
53
|
+
adminRoles: ['system_admin', 'org_admin'],
|
|
54
|
+
internalGatewayToken: '',
|
|
55
|
+
trustedHeaderNames: { ...DEFAULT_TRUSTED_HEADERS },
|
|
56
|
+
rbacServiceUrl: '',
|
|
57
|
+
permissionResolver: undefined,
|
|
58
|
+
};
|
|
59
|
+
let currentConfig = { ...DEFAULT_CONFIG, trustedHeaderNames: { ...DEFAULT_TRUSTED_HEADERS } };
|
|
60
|
+
/**
|
|
61
|
+
* Initialize auth middleware configuration. Call once at app startup.
|
|
62
|
+
*/
|
|
63
|
+
function initAuth(config = {}) {
|
|
64
|
+
// Resolve adminRoles: explicit adminRoles > legacy superAdminRole > default
|
|
65
|
+
let adminRoles = DEFAULT_CONFIG.adminRoles;
|
|
66
|
+
if (config.adminRoles) {
|
|
67
|
+
adminRoles = config.adminRoles;
|
|
68
|
+
}
|
|
69
|
+
else if (config.superAdminRole) {
|
|
70
|
+
adminRoles = [config.superAdminRole];
|
|
71
|
+
}
|
|
72
|
+
currentConfig = {
|
|
73
|
+
gatewayHeaderName: config.gatewayHeaderName ?? DEFAULT_CONFIG.gatewayHeaderName,
|
|
74
|
+
jwtIssuer: config.jwtIssuer ?? DEFAULT_CONFIG.jwtIssuer,
|
|
75
|
+
publicKey: '',
|
|
76
|
+
publicKeyPath: '',
|
|
77
|
+
strategies: config.strategies ?? DEFAULT_CONFIG.strategies,
|
|
78
|
+
adminRoles,
|
|
79
|
+
internalGatewayToken: config.internalGatewayToken || process.env.INTERNAL_GATEWAY_TOKEN || '',
|
|
80
|
+
trustedHeaderNames: {
|
|
81
|
+
...DEFAULT_TRUSTED_HEADERS,
|
|
82
|
+
...config.trustedHeaderNames,
|
|
83
|
+
},
|
|
84
|
+
rbacServiceUrl: config.rbacServiceUrl ?? '',
|
|
85
|
+
permissionResolver: config.permissionResolver,
|
|
86
|
+
};
|
|
87
|
+
// Resolve public key: explicit config → env vars → empty (gateway-only mode)
|
|
88
|
+
if (config.publicKey) {
|
|
89
|
+
currentConfig.publicKey = config.publicKey;
|
|
90
|
+
}
|
|
91
|
+
else if (config.publicKeyPath) {
|
|
92
|
+
currentConfig.publicKey = fs.readFileSync(config.publicKeyPath, 'utf-8');
|
|
93
|
+
currentConfig.publicKeyPath = config.publicKeyPath;
|
|
94
|
+
}
|
|
95
|
+
else if (process.env.AUTH_PUBLIC_KEY) {
|
|
96
|
+
currentConfig.publicKey = process.env.AUTH_PUBLIC_KEY;
|
|
97
|
+
}
|
|
98
|
+
else if (process.env.AUTH_PUBLIC_KEY_PATH) {
|
|
99
|
+
currentConfig.publicKey = fs.readFileSync(process.env.AUTH_PUBLIC_KEY_PATH, 'utf-8');
|
|
100
|
+
currentConfig.publicKeyPath = process.env.AUTH_PUBLIC_KEY_PATH;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getConfig() {
|
|
104
|
+
return currentConfig;
|
|
105
|
+
}
|
|
106
|
+
/** Reset config to defaults (used in tests) */
|
|
107
|
+
function resetConfig() {
|
|
108
|
+
currentConfig = { ...DEFAULT_CONFIG, trustedHeaderNames: { ...DEFAULT_TRUSTED_HEADERS } };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base auth error class. Works in both Express and NestJS contexts.
|
|
3
|
+
* Contains a `toResponse()` method that produces the standard error envelope.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AuthError extends Error {
|
|
6
|
+
readonly statusCode: number;
|
|
7
|
+
readonly code: string;
|
|
8
|
+
constructor(message: string, statusCode: number, code: string);
|
|
9
|
+
/** Returns the standard Global Tracking error envelope */
|
|
10
|
+
toResponse(): {
|
|
11
|
+
success: false;
|
|
12
|
+
error: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
statusCode: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare class UnauthorizedError extends AuthError {
|
|
20
|
+
constructor(message?: string);
|
|
21
|
+
}
|
|
22
|
+
export declare class ForbiddenError extends AuthError {
|
|
23
|
+
readonly details?: Record<string, unknown>;
|
|
24
|
+
constructor(message?: string, details?: Record<string, unknown>);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQ7D,0DAA0D;IAC1D,UAAU,IAAI;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAU/F;AAED,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,OAAO,SAA4B;CAGhD;AAED,qBAAa,cAAe,SAAQ,SAAS;IAC3C,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEtC,OAAO,SAAkB,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAIzE"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForbiddenError = exports.UnauthorizedError = exports.AuthError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base auth error class. Works in both Express and NestJS contexts.
|
|
6
|
+
* Contains a `toResponse()` method that produces the standard error envelope.
|
|
7
|
+
*/
|
|
8
|
+
class AuthError extends Error {
|
|
9
|
+
constructor(message, statusCode, code) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
this.statusCode = statusCode;
|
|
13
|
+
this.code = code;
|
|
14
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
15
|
+
}
|
|
16
|
+
/** Returns the standard Global Tracking error envelope */
|
|
17
|
+
toResponse() {
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
error: {
|
|
21
|
+
code: this.code,
|
|
22
|
+
message: this.message,
|
|
23
|
+
statusCode: this.statusCode,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.AuthError = AuthError;
|
|
29
|
+
class UnauthorizedError extends AuthError {
|
|
30
|
+
constructor(message = 'Authentication required') {
|
|
31
|
+
super(message, 401, 'UNAUTHORIZED');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.UnauthorizedError = UnauthorizedError;
|
|
35
|
+
class ForbiddenError extends AuthError {
|
|
36
|
+
constructor(message = 'Access denied', details) {
|
|
37
|
+
super(message, 403, 'FORBIDDEN');
|
|
38
|
+
this.details = details;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.ForbiddenError = ForbiddenError;
|
|
42
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,SAAU,SAAQ,KAAK;IAIlC,YAAY,OAAe,EAAE,UAAkB,EAAE,IAAY;QAC3D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAED,0DAA0D;IAC1D,UAAU;QACR,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B;SACF,CAAC;IACJ,CAAC;CACF;AAvBD,8BAuBC;AAED,MAAa,iBAAkB,SAAQ,SAAS;IAC9C,YAAY,OAAO,GAAG,yBAAyB;QAC7C,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;IACtC,CAAC;CACF;AAJD,8CAIC;AAED,MAAa,cAAe,SAAQ,SAAS;IAG3C,YAAY,OAAO,GAAG,eAAe,EAAE,OAAiC;QACtE,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAPD,wCAOC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
import { AuthUser } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Extract AuthUser from a request using the configured strategy chain.
|
|
5
|
+
* Iterates through `config.strategies` in order, trying each until one succeeds.
|
|
6
|
+
*
|
|
7
|
+
* Throws UnauthorizedError if no strategy can handle the request.
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractUser(req: Request): AuthUser;
|
|
10
|
+
//# sourceMappingURL=extract-user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-user.d.ts","sourceRoot":"","sources":["../src/extract-user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,OAAO,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAYjD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAWlD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractUser = extractUser;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const gateway_header_strategy_1 = require("./strategies/gateway-header.strategy");
|
|
7
|
+
const trusted_headers_strategy_1 = require("./strategies/trusted-headers.strategy");
|
|
8
|
+
const jwt_strategy_1 = require("./strategies/jwt.strategy");
|
|
9
|
+
const strategyMap = {
|
|
10
|
+
'gateway-header': new gateway_header_strategy_1.GatewayHeaderStrategy(),
|
|
11
|
+
'trusted-headers': new trusted_headers_strategy_1.TrustedHeadersStrategy(),
|
|
12
|
+
'jwt': new jwt_strategy_1.JwtStrategy(),
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Extract AuthUser from a request using the configured strategy chain.
|
|
16
|
+
* Iterates through `config.strategies` in order, trying each until one succeeds.
|
|
17
|
+
*
|
|
18
|
+
* Throws UnauthorizedError if no strategy can handle the request.
|
|
19
|
+
*/
|
|
20
|
+
function extractUser(req) {
|
|
21
|
+
const config = (0, config_1.getConfig)();
|
|
22
|
+
for (const strategyName of config.strategies) {
|
|
23
|
+
const strategy = strategyMap[strategyName];
|
|
24
|
+
if (strategy && strategy.canHandle(req)) {
|
|
25
|
+
return strategy.extract(req);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
throw new errors_1.UnauthorizedError('No authentication credentials provided');
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=extract-user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-user.js","sourceRoot":"","sources":["../src/extract-user.ts"],"names":[],"mappings":";;AAqBA,kCAWC;AA/BD,qCAAqC;AACrC,qCAA6C;AAG7C,kFAA6E;AAC7E,oFAA+E;AAC/E,4DAAwD;AAExD,MAAM,WAAW,GAAiD;IAChE,gBAAgB,EAAE,IAAI,+CAAqB,EAAE;IAC7C,iBAAiB,EAAE,IAAI,iDAAsB,EAAE;IAC/C,KAAK,EAAE,IAAI,0BAAW,EAAE;CACzB,CAAC;AAEF;;;;;GAKG;AACH,SAAgB,WAAW,CAAC,GAAY;IACtC,MAAM,MAAM,GAAG,IAAA,kBAAS,GAAE,CAAC;IAE3B,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,MAAM,IAAI,0BAAiB,CAAC,wCAAwC,CAAC,CAAC;AACxE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { initAuth, getConfig, resetConfig } from './config';
|
|
2
|
+
export { AuthUser, RequestUser, AuthConfig, AuthStrategy, JwtPayload, UserRole, TrustedHeaderNames, ResolvedAuthConfig } from './types';
|
|
3
|
+
export { AuthError, UnauthorizedError, ForbiddenError } from './errors';
|
|
4
|
+
export { extractUser } from './extract-user';
|
|
5
|
+
export { AuthExtractionStrategy } from './strategies/strategy.interface';
|
|
6
|
+
export { GatewayHeaderStrategy } from './strategies/gateway-header.strategy';
|
|
7
|
+
export { TrustedHeadersStrategy } from './strategies/trusted-headers.strategy';
|
|
8
|
+
export { JwtStrategy } from './strategies/jwt.strategy';
|
|
9
|
+
export { authenticate } from './middlewares/authenticate';
|
|
10
|
+
export { requireRole } from './middlewares/require-role';
|
|
11
|
+
export { requirePermission, requireAnyPermission } from './middlewares/require-permission';
|
|
12
|
+
export { requireTenant } from './middlewares/require-tenant';
|
|
13
|
+
export { requireSelf } from './middlewares/require-self';
|
|
14
|
+
export { authErrorHandler } from './middlewares/error-handler';
|
|
15
|
+
export { hasRole, hasPermission, hasAnyPermission } from './utils/helpers';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGxI,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAGxE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAG/D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|