@adatechnology/auth-keycloak 0.0.8 → 0.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 +31 -1
- package/dist/index.d.ts +143 -1
- package/dist/index.js +323 -80
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,7 +10,8 @@ e um interceptor opcional. O módulo foi projetado para ser usado junto ao `@ada
|
|
|
10
10
|
- `KeycloakModule` — módulo principal. Suporta `KeycloakModule.forRoot(config?)`.
|
|
11
11
|
- `KEYCLOAK_CLIENT` — provider token para injetar o cliente Keycloak (`@Inject(KEYCLOAK_CLIENT)`).
|
|
12
12
|
- `KEYCLOAK_HTTP_INTERCEPTOR` — provider token para injetar o interceptor (opcional).
|
|
13
|
-
- `
|
|
13
|
+
- `BearerTokenGuard` — guard que valida o token Bearer via introspecção no Keycloak (401 em falha).
|
|
14
|
+
- `Roles` / `RolesGuard` — decorator e guard para autorização baseada em roles (403 em falha).
|
|
14
15
|
- `KeycloakError` — classe de erro tipada com `statusCode` e `details`.
|
|
15
16
|
|
|
16
17
|
### Instalação
|
|
@@ -128,6 +129,35 @@ Resultado no log:
|
|
|
128
129
|
[MyController.getToken][InMemoryCacheProvider.set] → token cached
|
|
129
130
|
```
|
|
130
131
|
|
|
132
|
+
### BearerTokenGuard — autenticação B2B via introspecção
|
|
133
|
+
|
|
134
|
+
Valida que o header `Authorization: Bearer <token>` contém um token ativo chamando
|
|
135
|
+
`POST /token/introspect` no Keycloak. Use sempre em conjunto com `RolesGuard` em rotas B2B.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { Controller, Headers, Post, UseGuards } from "@nestjs/common";
|
|
139
|
+
import { BearerTokenGuard, Roles, RolesGuard } from "@adatechnology/auth-keycloak";
|
|
140
|
+
|
|
141
|
+
@Controller("orders")
|
|
142
|
+
export class OrdersController {
|
|
143
|
+
@Post()
|
|
144
|
+
@Roles("manage-requests")
|
|
145
|
+
@UseGuards(BearerTokenGuard, RolesGuard)
|
|
146
|
+
create(@Headers("x-user-id") keycloakId: string) {}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Por que dois guards em sequência?**
|
|
151
|
+
|
|
152
|
+
| Guard | Mecanismo | HTTP? | Falha |
|
|
153
|
+
|---|---|---|---|
|
|
154
|
+
| `BearerTokenGuard` | `POST /token/introspect` ao Keycloak | Sim | 401 — token inativo/expirado/forjado |
|
|
155
|
+
| `RolesGuard` | Decode local do payload JWT | Não | 403 — permissão insuficiente |
|
|
156
|
+
|
|
157
|
+
O `RolesGuard` sozinho **não é seguro** para autenticação: ele apenas decodifica o payload JWT
|
|
158
|
+
sem verificar a assinatura, o que significa que um token forjado passaria. O `BearerTokenGuard`
|
|
159
|
+
é quem garante autenticidade via introspecção.
|
|
160
|
+
|
|
131
161
|
### Autorização com @Roles
|
|
132
162
|
|
|
133
163
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _nestjs_common from '@nestjs/common';
|
|
2
2
|
import { DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
3
3
|
import { AxiosRequestConfig, AxiosInstance } from 'axios';
|
|
4
|
+
import { LoggerProviderInterface } from '@adatechnology/logger';
|
|
4
5
|
import { Reflector } from '@nestjs/core';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -87,6 +88,32 @@ declare class KeycloakModule {
|
|
|
87
88
|
static forRoot(config: KeycloakConfig, httpConfig?: AxiosRequestConfig | AxiosInstance): DynamicModule;
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Guard that validates the Bearer token in the Authorization header via
|
|
93
|
+
* Keycloak token introspection (POST /token/introspect).
|
|
94
|
+
*
|
|
95
|
+
* Use together with RolesGuard for B2B (service-to-service) routes that trust
|
|
96
|
+
* an X-User-Id header injected by an upstream authenticated service:
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* @Roles('manage-requests')
|
|
101
|
+
* @UseGuards(BearerTokenGuard, RolesGuard)
|
|
102
|
+
* async create(@Headers('x-user-id') keycloakId: string) {}
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* Execution order:
|
|
106
|
+
* 1. BearerTokenGuard — validates token is active (HTTP → Keycloak) → 401 on failure
|
|
107
|
+
* 2. RolesGuard — checks roles from decoded JWT payload (local) → 403 on failure
|
|
108
|
+
*/
|
|
109
|
+
declare class BearerTokenGuard implements CanActivate {
|
|
110
|
+
private readonly keycloakClient?;
|
|
111
|
+
private readonly logger?;
|
|
112
|
+
constructor(keycloakClient?: KeycloakClientInterface, logger?: LoggerProviderInterface);
|
|
113
|
+
private log;
|
|
114
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
115
|
+
}
|
|
116
|
+
|
|
90
117
|
declare const KEYCLOAK_CONFIG = "KEYCLOAK_CONFIG";
|
|
91
118
|
declare const KEYCLOAK_CLIENT = "KEYCLOAK_CLIENT";
|
|
92
119
|
declare const KEYCLOAK_HTTP_INTERCEPTOR = "KEYCLOAK_HTTP_INTERCEPTOR";
|
|
@@ -110,6 +137,23 @@ type RolesOptions = {
|
|
|
110
137
|
*/
|
|
111
138
|
declare function Roles(...args: Array<string | string[] | RolesOptions>): _nestjs_common.CustomDecorator<string>;
|
|
112
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Guard that checks whether the current request has the required roles.
|
|
142
|
+
*
|
|
143
|
+
* Supports two auth paths transparently:
|
|
144
|
+
*
|
|
145
|
+
* 1. **Kong path** (user-facing, preferred):
|
|
146
|
+
* Kong validates the token, removes Authorization, and injects:
|
|
147
|
+
* - `X-User-Id` — keycloak sub
|
|
148
|
+
* - `X-User-Roles` — comma-separated realm roles
|
|
149
|
+
* The guard reads roles from `X-User-Roles` header.
|
|
150
|
+
*
|
|
151
|
+
* 2. **B2B path** (service-to-service, e.g. BFF → API):
|
|
152
|
+
* The caller sends a service account JWT in the Authorization header.
|
|
153
|
+
* The guard decodes the JWT locally and reads `realm_access.roles`.
|
|
154
|
+
*
|
|
155
|
+
* Priority: Kong header → JWT fallback.
|
|
156
|
+
*/
|
|
113
157
|
declare class RolesGuard implements CanActivate {
|
|
114
158
|
private readonly reflector;
|
|
115
159
|
private readonly config?;
|
|
@@ -118,6 +162,104 @@ declare class RolesGuard implements CanActivate {
|
|
|
118
162
|
private decodeJwtPayload;
|
|
119
163
|
}
|
|
120
164
|
|
|
165
|
+
/**
|
|
166
|
+
* B2B Guard — service-to-service routes (e.g. BFF → API, Worker → API).
|
|
167
|
+
*
|
|
168
|
+
* The caller must send a valid service account token (client_credentials)
|
|
169
|
+
* in the Authorization header. Delegates full token validation to
|
|
170
|
+
* BearerTokenGuard (Keycloak introspection).
|
|
171
|
+
*
|
|
172
|
+
* The caller is also expected to forward X-User-Id so the API knows which
|
|
173
|
+
* end-user context the call belongs to.
|
|
174
|
+
*
|
|
175
|
+
* Use for routes that are ONLY called by internal services, not by end users.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```ts
|
|
179
|
+
* @Post('internal/notify')
|
|
180
|
+
* @Roles('send-notifications')
|
|
181
|
+
* @UseGuards(B2BGuard, RolesGuard)
|
|
182
|
+
* async notify(@AuthUser() keycloakId: string) { ... }
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
declare class B2BGuard implements CanActivate {
|
|
186
|
+
private readonly bearerTokenGuard;
|
|
187
|
+
constructor(bearerTokenGuard: BearerTokenGuard);
|
|
188
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* B2C Guard — user-facing routes via Kong.
|
|
193
|
+
*
|
|
194
|
+
* Kong validated the JWT (JWKS local), removed the Authorization header,
|
|
195
|
+
* and injected X-User-Id + X-User-Roles. This guard simply asserts those
|
|
196
|
+
* headers are present — it does NOT re-validate any token.
|
|
197
|
+
*
|
|
198
|
+
* Use for routes that are ONLY called by end users through Kong.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* @Get('me')
|
|
203
|
+
* @Roles('user-manager')
|
|
204
|
+
* @UseGuards(B2CGuard, RolesGuard)
|
|
205
|
+
* async getMe(@AuthUser() keycloakId: string) { ... }
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare class B2CGuard implements CanActivate {
|
|
209
|
+
canActivate(context: ExecutionContext): boolean;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* ApiAuthGuard — composite guard for routes that accept both paths.
|
|
214
|
+
*
|
|
215
|
+
* Detects which path the request is coming from and delegates accordingly:
|
|
216
|
+
*
|
|
217
|
+
* - **B2B path** (Authorization header present):
|
|
218
|
+
* Service-to-service call (e.g. BFF → API). Delegates to B2BGuard,
|
|
219
|
+
* which validates the service account token via BearerTokenGuard.
|
|
220
|
+
*
|
|
221
|
+
* - **B2C path** (X-User-Id header present, no Authorization):
|
|
222
|
+
* User call routed by Kong. Delegates to B2CGuard,
|
|
223
|
+
* which asserts Kong identity headers are present.
|
|
224
|
+
*
|
|
225
|
+
* Use when the same route must be reachable both from Kong (users) and
|
|
226
|
+
* from internal services (BFF, Worker). If the route is exclusive to one
|
|
227
|
+
* path, prefer B2BGuard or B2CGuard directly for explicit intent.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* @Get('me')
|
|
232
|
+
* @Roles('user-manager')
|
|
233
|
+
* @UseGuards(ApiAuthGuard, RolesGuard)
|
|
234
|
+
* async getMe(@AuthUser() keycloakId: string) { ... }
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
declare class ApiAuthGuard implements CanActivate {
|
|
238
|
+
private readonly b2bGuard;
|
|
239
|
+
private readonly b2cGuard;
|
|
240
|
+
constructor(b2bGuard: B2BGuard, b2cGuard: B2CGuard);
|
|
241
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Parameter decorator that extracts the authenticated user's Keycloak ID
|
|
246
|
+
* from the `X-User-Id` header injected by Kong after token validation.
|
|
247
|
+
*
|
|
248
|
+
* In the B2B path (service-to-service), the caller is responsible for
|
|
249
|
+
* forwarding the same header.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* @Get('me')
|
|
254
|
+
* @Roles('user-manager')
|
|
255
|
+
* @UseGuards(RolesGuard)
|
|
256
|
+
* async getMe(@AuthUser() keycloakId: string) {
|
|
257
|
+
* return this.userService.getUserByKeycloakId(keycloakId);
|
|
258
|
+
* }
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
declare const AuthUser: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
262
|
+
|
|
121
263
|
declare class KeycloakError extends Error {
|
|
122
264
|
readonly statusCode?: number;
|
|
123
265
|
readonly details?: unknown;
|
|
@@ -129,4 +271,4 @@ declare class KeycloakError extends Error {
|
|
|
129
271
|
});
|
|
130
272
|
}
|
|
131
273
|
|
|
132
|
-
export { KEYCLOAK_CLIENT, KEYCLOAK_CONFIG, KEYCLOAK_HTTP_INTERCEPTOR, KEYCLOAK_PROVIDER, type KeycloakClientInterface, type KeycloakConfig, KeycloakError, KeycloakModule, type KeycloakProviderInterface, type KeycloakTokenResponse, Roles, RolesGuard };
|
|
274
|
+
export { ApiAuthGuard, AuthUser, B2BGuard, B2CGuard, BearerTokenGuard, KEYCLOAK_CLIENT, KEYCLOAK_CONFIG, KEYCLOAK_HTTP_INTERCEPTOR, KEYCLOAK_PROVIDER, type KeycloakClientInterface, type KeycloakConfig, KeycloakError, KeycloakModule, type KeycloakProviderInterface, type KeycloakTokenResponse, Roles, RolesGuard };
|
package/dist/index.js
CHANGED
|
@@ -68,7 +68,7 @@ var require_base_app_error = __commonJS({
|
|
|
68
68
|
"use strict";
|
|
69
69
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
70
70
|
exports2.BaseAppError = void 0;
|
|
71
|
-
var
|
|
71
|
+
var BaseAppError5 = class extends Error {
|
|
72
72
|
code;
|
|
73
73
|
status;
|
|
74
74
|
context;
|
|
@@ -83,7 +83,7 @@ var require_base_app_error = __commonJS({
|
|
|
83
83
|
(_a = capturable.captureStackTrace) == null ? void 0 : _a.call(capturable, this, this.constructor);
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
|
-
exports2.BaseAppError =
|
|
86
|
+
exports2.BaseAppError = BaseAppError5;
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
|
|
@@ -268,6 +268,11 @@ var require_dist = __commonJS({
|
|
|
268
268
|
// src/index.ts
|
|
269
269
|
var index_exports = {};
|
|
270
270
|
__export(index_exports, {
|
|
271
|
+
ApiAuthGuard: () => ApiAuthGuard,
|
|
272
|
+
AuthUser: () => AuthUser,
|
|
273
|
+
B2BGuard: () => B2BGuard,
|
|
274
|
+
B2CGuard: () => B2CGuard,
|
|
275
|
+
BearerTokenGuard: () => BearerTokenGuard,
|
|
271
276
|
KEYCLOAK_CLIENT: () => KEYCLOAK_CLIENT,
|
|
272
277
|
KEYCLOAK_CONFIG: () => KEYCLOAK_CONFIG,
|
|
273
278
|
KEYCLOAK_HTTP_INTERCEPTOR: () => KEYCLOAK_HTTP_INTERCEPTOR,
|
|
@@ -280,16 +285,174 @@ __export(index_exports, {
|
|
|
280
285
|
module.exports = __toCommonJS(index_exports);
|
|
281
286
|
|
|
282
287
|
// src/keycloak.module.ts
|
|
283
|
-
var
|
|
288
|
+
var import_common6 = require("@nestjs/common");
|
|
284
289
|
var import_core2 = require("@nestjs/core");
|
|
285
|
-
var
|
|
286
|
-
var
|
|
290
|
+
var import_http_client3 = require("@adatechnology/http-client");
|
|
291
|
+
var import_logger3 = require("@adatechnology/logger");
|
|
287
292
|
var import_cache3 = require("@adatechnology/cache");
|
|
288
293
|
|
|
289
|
-
// src/
|
|
294
|
+
// src/bearer-token.guard.ts
|
|
290
295
|
var import_common = require("@nestjs/common");
|
|
291
|
-
var import_http_client = require("@adatechnology/http-client");
|
|
292
296
|
var import_logger = require("@adatechnology/logger");
|
|
297
|
+
var import_http_client = require("@adatechnology/http-client");
|
|
298
|
+
var import_shared = __toESM(require_dist());
|
|
299
|
+
|
|
300
|
+
// src/keycloak.token.ts
|
|
301
|
+
var KEYCLOAK_CONFIG = "KEYCLOAK_CONFIG";
|
|
302
|
+
var KEYCLOAK_CLIENT = "KEYCLOAK_CLIENT";
|
|
303
|
+
var KEYCLOAK_HTTP_INTERCEPTOR = "KEYCLOAK_HTTP_INTERCEPTOR";
|
|
304
|
+
var KEYCLOAK_PROVIDER = "KEYCLOAK_PROVIDER";
|
|
305
|
+
|
|
306
|
+
// package.json
|
|
307
|
+
var package_default = {
|
|
308
|
+
name: "@adatechnology/auth-keycloak",
|
|
309
|
+
version: "0.1.1",
|
|
310
|
+
publishConfig: {
|
|
311
|
+
access: "public"
|
|
312
|
+
},
|
|
313
|
+
main: "dist/index.js",
|
|
314
|
+
module: "dist/index.mjs",
|
|
315
|
+
types: "dist/index.d.ts",
|
|
316
|
+
files: [
|
|
317
|
+
"dist"
|
|
318
|
+
],
|
|
319
|
+
scripts: {
|
|
320
|
+
build: "rm -rf dist && tsup",
|
|
321
|
+
"build:watch": "tsup --watch",
|
|
322
|
+
check: "tsc -p tsconfig.json --noEmit",
|
|
323
|
+
test: 'echo "no tests"'
|
|
324
|
+
},
|
|
325
|
+
dependencies: {
|
|
326
|
+
"@adatechnology/cache": "workspace:*",
|
|
327
|
+
"@adatechnology/http-client": "workspace:*",
|
|
328
|
+
"@adatechnology/logger": "workspace:*"
|
|
329
|
+
},
|
|
330
|
+
peerDependencies: {
|
|
331
|
+
"@nestjs/common": "^11.0.16",
|
|
332
|
+
"@nestjs/core": "^11"
|
|
333
|
+
},
|
|
334
|
+
devDependencies: {
|
|
335
|
+
"@adatechnology/shared": "workspace:*",
|
|
336
|
+
"@esbuild-plugins/tsconfig-paths": "^0.1.2",
|
|
337
|
+
tsup: "^8.5.1",
|
|
338
|
+
typescript: "^5.2.0"
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// src/keycloak.constants.ts
|
|
343
|
+
var LIB_NAME = package_default.name;
|
|
344
|
+
var LIB_VERSION = package_default.version;
|
|
345
|
+
var TOKEN_CACHE_KEY = "keycloak:access_token";
|
|
346
|
+
var LOG_CONTEXT = {
|
|
347
|
+
KEYCLOAK_CLIENT: "KeycloakClient",
|
|
348
|
+
BEARER_TOKEN_GUARD: "BearerTokenGuard"
|
|
349
|
+
};
|
|
350
|
+
var HTTP_STATUS = {
|
|
351
|
+
UNAUTHORIZED: 401,
|
|
352
|
+
FORBIDDEN: 403
|
|
353
|
+
};
|
|
354
|
+
var BEARER_ERROR_CODE = {
|
|
355
|
+
MISSING_TOKEN: "UNAUTHORIZED_MISSING_TOKEN",
|
|
356
|
+
KEYCLOAK_NOT_CONFIGURED: "UNAUTHORIZED_KEYCLOAK_NOT_CONFIGURED",
|
|
357
|
+
TOKEN_VALIDATION_FAILED: "UNAUTHORIZED_TOKEN_VALIDATION_FAILED",
|
|
358
|
+
INACTIVE_TOKEN: "UNAUTHORIZED_INACTIVE_TOKEN"
|
|
359
|
+
};
|
|
360
|
+
var ROLES_ERROR_CODE = {
|
|
361
|
+
MISSING_TOKEN: "FORBIDDEN_MISSING_TOKEN",
|
|
362
|
+
INSUFFICIENT_ROLES: "FORBIDDEN_INSUFFICIENT_ROLES"
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// src/bearer-token.guard.ts
|
|
366
|
+
var BearerTokenGuard = class {
|
|
367
|
+
constructor(keycloakClient, logger) {
|
|
368
|
+
this.keycloakClient = keycloakClient;
|
|
369
|
+
this.logger = logger;
|
|
370
|
+
}
|
|
371
|
+
log(level, message, libMethod, meta) {
|
|
372
|
+
if (!this.logger) return;
|
|
373
|
+
const loggerCtx = (0, import_logger.getContext)();
|
|
374
|
+
const httpCtx = (0, import_http_client.getHttpRequestContext)();
|
|
375
|
+
const logContext = loggerCtx == null ? void 0 : loggerCtx.logContext;
|
|
376
|
+
const requestId = (loggerCtx == null ? void 0 : loggerCtx.requestId) ?? (httpCtx == null ? void 0 : httpCtx.requestId);
|
|
377
|
+
const source = (logContext == null ? void 0 : logContext.className) && (logContext == null ? void 0 : logContext.methodName) ? `${logContext.className}.${logContext.methodName}` : (httpCtx == null ? void 0 : httpCtx.className) && (httpCtx == null ? void 0 : httpCtx.methodName) ? `${httpCtx.className}.${httpCtx.methodName}` : void 0;
|
|
378
|
+
const payload = {
|
|
379
|
+
message,
|
|
380
|
+
context: LOG_CONTEXT.BEARER_TOKEN_GUARD,
|
|
381
|
+
lib: LIB_NAME,
|
|
382
|
+
libVersion: LIB_VERSION,
|
|
383
|
+
libMethod,
|
|
384
|
+
source,
|
|
385
|
+
requestId,
|
|
386
|
+
meta
|
|
387
|
+
};
|
|
388
|
+
if (level === "debug") this.logger.debug(payload);
|
|
389
|
+
else if (level === "info") this.logger.info(payload);
|
|
390
|
+
else if (level === "warn") this.logger.warn(payload);
|
|
391
|
+
else if (level === "error") this.logger.error(payload);
|
|
392
|
+
}
|
|
393
|
+
async canActivate(context) {
|
|
394
|
+
var _a, _b;
|
|
395
|
+
const method = "canActivate";
|
|
396
|
+
this.log("debug", `${method} - Start`, method);
|
|
397
|
+
const request = context.switchToHttp().getRequest();
|
|
398
|
+
const authorization = ((_a = request.headers) == null ? void 0 : _a.authorization) ?? ((_b = request.headers) == null ? void 0 : _b.Authorization);
|
|
399
|
+
if (!(authorization == null ? void 0 : authorization.startsWith("Bearer "))) {
|
|
400
|
+
this.log("warn", `${method} - Missing or invalid Authorization header`, method);
|
|
401
|
+
throw new import_shared.BaseAppError({
|
|
402
|
+
message: "Missing or invalid Authorization header",
|
|
403
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
404
|
+
code: BEARER_ERROR_CODE.MISSING_TOKEN,
|
|
405
|
+
context: {}
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
if (!this.keycloakClient) {
|
|
409
|
+
this.log("error", `${method} - Keycloak client not configured`, method);
|
|
410
|
+
throw new import_shared.BaseAppError({
|
|
411
|
+
message: "Keycloak client not configured",
|
|
412
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
413
|
+
code: BEARER_ERROR_CODE.KEYCLOAK_NOT_CONFIGURED,
|
|
414
|
+
context: {}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const token = authorization.slice(7);
|
|
418
|
+
let isValid;
|
|
419
|
+
try {
|
|
420
|
+
isValid = await this.keycloakClient.validateToken(token);
|
|
421
|
+
} catch (err) {
|
|
422
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
423
|
+
this.log("error", `${method} - Token validation failed`, method, { detail });
|
|
424
|
+
throw new import_shared.BaseAppError({
|
|
425
|
+
message: "Token validation failed",
|
|
426
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
427
|
+
code: BEARER_ERROR_CODE.TOKEN_VALIDATION_FAILED,
|
|
428
|
+
context: { detail }
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
if (!isValid) {
|
|
432
|
+
this.log("warn", `${method} - Inactive or expired token`, method);
|
|
433
|
+
throw new import_shared.BaseAppError({
|
|
434
|
+
message: "Inactive or expired token",
|
|
435
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
436
|
+
code: BEARER_ERROR_CODE.INACTIVE_TOKEN,
|
|
437
|
+
context: {}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
this.log("debug", `${method} - Token valid`, method);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
BearerTokenGuard = __decorateClass([
|
|
445
|
+
(0, import_common.Injectable)(),
|
|
446
|
+
__decorateParam(0, (0, import_common.Optional)()),
|
|
447
|
+
__decorateParam(0, (0, import_common.Inject)(KEYCLOAK_CLIENT)),
|
|
448
|
+
__decorateParam(1, (0, import_common.Optional)()),
|
|
449
|
+
__decorateParam(1, (0, import_common.Inject)(import_logger.LOGGER_PROVIDER))
|
|
450
|
+
], BearerTokenGuard);
|
|
451
|
+
|
|
452
|
+
// src/keycloak.client.ts
|
|
453
|
+
var import_common2 = require("@nestjs/common");
|
|
454
|
+
var import_http_client2 = require("@adatechnology/http-client");
|
|
455
|
+
var import_logger2 = require("@adatechnology/logger");
|
|
293
456
|
var import_cache = require("@adatechnology/cache");
|
|
294
457
|
var import_cache2 = require("@adatechnology/cache");
|
|
295
458
|
|
|
@@ -309,9 +472,6 @@ var KeycloakError = class _KeycloakError extends Error {
|
|
|
309
472
|
};
|
|
310
473
|
|
|
311
474
|
// src/keycloak.client.ts
|
|
312
|
-
var LIB_NAME = "@adatechnology/auth-keycloak";
|
|
313
|
-
var LIB_VERSION = "0.0.7";
|
|
314
|
-
var TOKEN_CACHE_KEY = "keycloak:access_token";
|
|
315
475
|
function extractErrorInfo(err) {
|
|
316
476
|
var _a, _b, _c, _d, _e;
|
|
317
477
|
const statusCode = (err == null ? void 0 : err.status) ?? ((_a = err == null ? void 0 : err.response) == null ? void 0 : _a.status);
|
|
@@ -347,14 +507,14 @@ var KeycloakClient = class {
|
|
|
347
507
|
tokenPromise = null;
|
|
348
508
|
log(level, message, libMethod, meta) {
|
|
349
509
|
if (!this.logger) return;
|
|
350
|
-
const loggerCtx = (0,
|
|
351
|
-
const httpCtx = (0,
|
|
510
|
+
const loggerCtx = (0, import_logger2.getContext)();
|
|
511
|
+
const httpCtx = (0, import_http_client2.getHttpRequestContext)();
|
|
352
512
|
const logContext = loggerCtx == null ? void 0 : loggerCtx.logContext;
|
|
353
513
|
const requestId = (loggerCtx == null ? void 0 : loggerCtx.requestId) ?? (httpCtx == null ? void 0 : httpCtx.requestId);
|
|
354
514
|
const source = (logContext == null ? void 0 : logContext.className) && (logContext == null ? void 0 : logContext.methodName) ? `${logContext.className}.${logContext.methodName}` : (httpCtx == null ? void 0 : httpCtx.className) && (httpCtx == null ? void 0 : httpCtx.methodName) ? `${httpCtx.className}.${httpCtx.methodName}` : void 0;
|
|
355
515
|
const payload = {
|
|
356
516
|
message,
|
|
357
|
-
context:
|
|
517
|
+
context: LOG_CONTEXT.KEYCLOAK_CLIENT,
|
|
358
518
|
lib: LIB_NAME,
|
|
359
519
|
libVersion: LIB_VERSION,
|
|
360
520
|
libMethod,
|
|
@@ -413,7 +573,7 @@ var KeycloakClient = class {
|
|
|
413
573
|
data: body,
|
|
414
574
|
config: {
|
|
415
575
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
416
|
-
logContext: { className:
|
|
576
|
+
logContext: { className: LOG_CONTEXT.KEYCLOAK_CLIENT, methodName: method }
|
|
417
577
|
}
|
|
418
578
|
});
|
|
419
579
|
this.log("info", `${method} - Success for user: ${username}`, method);
|
|
@@ -451,7 +611,7 @@ var KeycloakClient = class {
|
|
|
451
611
|
data,
|
|
452
612
|
config: {
|
|
453
613
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
454
|
-
logContext: { className:
|
|
614
|
+
logContext: { className: LOG_CONTEXT.KEYCLOAK_CLIENT, methodName: method }
|
|
455
615
|
}
|
|
456
616
|
});
|
|
457
617
|
this.log("debug", `${method} - Success`, method);
|
|
@@ -483,7 +643,7 @@ var KeycloakClient = class {
|
|
|
483
643
|
data,
|
|
484
644
|
config: {
|
|
485
645
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
486
|
-
logContext: { className:
|
|
646
|
+
logContext: { className: LOG_CONTEXT.KEYCLOAK_CLIENT, methodName: method }
|
|
487
647
|
}
|
|
488
648
|
});
|
|
489
649
|
const ttlSeconds = this.config.tokenCacheTtl ? Math.floor(this.config.tokenCacheTtl / 1e3) : response.data.expires_in - 60;
|
|
@@ -517,7 +677,7 @@ var KeycloakClient = class {
|
|
|
517
677
|
data,
|
|
518
678
|
config: {
|
|
519
679
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
520
|
-
logContext: { className:
|
|
680
|
+
logContext: { className: LOG_CONTEXT.KEYCLOAK_CLIENT, methodName: method }
|
|
521
681
|
}
|
|
522
682
|
});
|
|
523
683
|
const active = ((_a = response.data) == null ? void 0 : _a.active) === true;
|
|
@@ -542,7 +702,7 @@ var KeycloakClient = class {
|
|
|
542
702
|
url: userInfoUrl,
|
|
543
703
|
config: {
|
|
544
704
|
headers: { Authorization: `Bearer ${token}` },
|
|
545
|
-
logContext: { className:
|
|
705
|
+
logContext: { className: LOG_CONTEXT.KEYCLOAK_CLIENT, methodName: method }
|
|
546
706
|
}
|
|
547
707
|
});
|
|
548
708
|
this.log("debug", `${method} - Success`, method);
|
|
@@ -566,16 +726,16 @@ var KeycloakClient = class {
|
|
|
566
726
|
}
|
|
567
727
|
};
|
|
568
728
|
KeycloakClient = __decorateClass([
|
|
569
|
-
(0,
|
|
570
|
-
__decorateParam(1, (0,
|
|
571
|
-
__decorateParam(2, (0,
|
|
572
|
-
__decorateParam(2, (0,
|
|
573
|
-
__decorateParam(3, (0,
|
|
574
|
-
__decorateParam(3, (0,
|
|
729
|
+
(0, import_common2.Injectable)(),
|
|
730
|
+
__decorateParam(1, (0, import_common2.Inject)(import_http_client2.HTTP_PROVIDER)),
|
|
731
|
+
__decorateParam(2, (0, import_common2.Optional)()),
|
|
732
|
+
__decorateParam(2, (0, import_common2.Inject)(import_logger2.LOGGER_PROVIDER)),
|
|
733
|
+
__decorateParam(3, (0, import_common2.Optional)()),
|
|
734
|
+
__decorateParam(3, (0, import_common2.Inject)(import_cache2.CACHE_PROVIDER))
|
|
575
735
|
], KeycloakClient);
|
|
576
736
|
|
|
577
737
|
// src/keycloak.http.interceptor.ts
|
|
578
|
-
var
|
|
738
|
+
var import_common3 = require("@nestjs/common");
|
|
579
739
|
var KeycloakHttpInterceptor = class {
|
|
580
740
|
constructor() {
|
|
581
741
|
}
|
|
@@ -587,15 +747,15 @@ var KeycloakHttpInterceptor = class {
|
|
|
587
747
|
}
|
|
588
748
|
};
|
|
589
749
|
KeycloakHttpInterceptor = __decorateClass([
|
|
590
|
-
(0,
|
|
750
|
+
(0, import_common3.Injectable)()
|
|
591
751
|
], KeycloakHttpInterceptor);
|
|
592
752
|
|
|
593
753
|
// src/roles.guard.ts
|
|
594
|
-
var
|
|
754
|
+
var import_common5 = require("@nestjs/common");
|
|
595
755
|
var import_core = require("@nestjs/core");
|
|
596
756
|
|
|
597
757
|
// src/roles.decorator.ts
|
|
598
|
-
var
|
|
758
|
+
var import_common4 = require("@nestjs/common");
|
|
599
759
|
var ROLES_META_KEY = "roles";
|
|
600
760
|
function Roles(...args) {
|
|
601
761
|
let payload;
|
|
@@ -609,66 +769,65 @@ function Roles(...args) {
|
|
|
609
769
|
}
|
|
610
770
|
payload.mode = payload.mode ?? "any";
|
|
611
771
|
payload.type = payload.type ?? "both";
|
|
612
|
-
return (0,
|
|
772
|
+
return (0, import_common4.SetMetadata)(ROLES_META_KEY, payload);
|
|
613
773
|
}
|
|
614
774
|
|
|
615
|
-
// src/keycloak.token.ts
|
|
616
|
-
var KEYCLOAK_CONFIG = "KEYCLOAK_CONFIG";
|
|
617
|
-
var KEYCLOAK_CLIENT = "KEYCLOAK_CLIENT";
|
|
618
|
-
var KEYCLOAK_HTTP_INTERCEPTOR = "KEYCLOAK_HTTP_INTERCEPTOR";
|
|
619
|
-
var KEYCLOAK_PROVIDER = "KEYCLOAK_PROVIDER";
|
|
620
|
-
|
|
621
775
|
// src/roles.guard.ts
|
|
622
|
-
var
|
|
776
|
+
var import_shared2 = __toESM(require_dist());
|
|
623
777
|
var RolesGuard = class {
|
|
624
778
|
constructor(reflector, config) {
|
|
625
779
|
this.reflector = reflector;
|
|
626
780
|
this.config = config;
|
|
627
781
|
}
|
|
628
782
|
canActivate(context) {
|
|
629
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
783
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
630
784
|
const meta = this.reflector.get(ROLES_META_KEY, context.getHandler()) || this.reflector.get(ROLES_META_KEY, context.getClass());
|
|
631
785
|
if (!meta || !meta.roles || meta.roles.length === 0) return true;
|
|
632
786
|
const req = context.switchToHttp().getRequest();
|
|
633
|
-
const
|
|
634
|
-
const token = authHeader ? String(authHeader).split(" ")[1] : (_c = req.query) == null ? void 0 : _c.token;
|
|
635
|
-
if (!token)
|
|
636
|
-
throw new import_shared.BaseAppError({
|
|
637
|
-
message: "Authorization token not provided",
|
|
638
|
-
status: 403,
|
|
639
|
-
code: "FORBIDDEN_MISSING_TOKEN",
|
|
640
|
-
context: {}
|
|
641
|
-
});
|
|
642
|
-
const payload = this.decodeJwtPayload(token);
|
|
787
|
+
const required = meta.roles;
|
|
643
788
|
const availableRoles = /* @__PURE__ */ new Set();
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
789
|
+
const kongRolesHeader = ((_a = req.headers) == null ? void 0 : _a["x-user-roles"]) || ((_b = req.headers) == null ? void 0 : _b["X-User-Roles"]);
|
|
790
|
+
if (kongRolesHeader) {
|
|
791
|
+
kongRolesHeader.split(",").map((r) => r.trim()).filter(Boolean).forEach((r) => availableRoles.add(r));
|
|
792
|
+
} else {
|
|
793
|
+
const authHeader = ((_c = req.headers) == null ? void 0 : _c.authorization) || ((_d = req.headers) == null ? void 0 : _d.Authorization);
|
|
794
|
+
const token = authHeader ? String(authHeader).split(" ")[1] : (_e = req.query) == null ? void 0 : _e.token;
|
|
795
|
+
if (!token) {
|
|
796
|
+
throw new import_shared2.BaseAppError({
|
|
797
|
+
message: "Authorization token not provided",
|
|
798
|
+
status: HTTP_STATUS.FORBIDDEN,
|
|
799
|
+
code: ROLES_ERROR_CODE.MISSING_TOKEN,
|
|
800
|
+
context: {}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
const payload = this.decodeJwtPayload(token);
|
|
804
|
+
if (((_f = payload == null ? void 0 : payload.realm_access) == null ? void 0 : _f.roles) && Array.isArray(payload.realm_access.roles)) {
|
|
805
|
+
payload.realm_access.roles.forEach((r) => availableRoles.add(r));
|
|
806
|
+
}
|
|
807
|
+
const clientId = (_h = (_g = this.config) == null ? void 0 : _g.credentials) == null ? void 0 : _h.clientId;
|
|
808
|
+
if (clientId && ((_j = (_i = payload == null ? void 0 : payload.resource_access) == null ? void 0 : _i[clientId]) == null ? void 0 : _j.roles)) {
|
|
809
|
+
payload.resource_access[clientId].roles.forEach(
|
|
810
|
+
(r) => availableRoles.add(r)
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
if (meta.type === "both" && (payload == null ? void 0 : payload.resource_access)) {
|
|
814
|
+
Object.values(payload.resource_access).forEach((entry) => {
|
|
815
|
+
if ((entry == null ? void 0 : entry.roles) && Array.isArray(entry.roles)) {
|
|
816
|
+
entry.roles.forEach((r) => availableRoles.add(r));
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
661
820
|
}
|
|
662
|
-
const required = meta.roles || [];
|
|
663
821
|
const hasMatch = required.map((r) => availableRoles.has(r));
|
|
664
822
|
const result = meta.mode === "all" ? hasMatch.every(Boolean) : hasMatch.some(Boolean);
|
|
665
|
-
if (!result)
|
|
666
|
-
throw new
|
|
823
|
+
if (!result) {
|
|
824
|
+
throw new import_shared2.BaseAppError({
|
|
667
825
|
message: "Insufficient roles",
|
|
668
|
-
status:
|
|
669
|
-
code:
|
|
826
|
+
status: HTTP_STATUS.FORBIDDEN,
|
|
827
|
+
code: ROLES_ERROR_CODE.INSUFFICIENT_ROLES,
|
|
670
828
|
context: { required }
|
|
671
829
|
});
|
|
830
|
+
}
|
|
672
831
|
return true;
|
|
673
832
|
}
|
|
674
833
|
decodeJwtPayload(token) {
|
|
@@ -680,16 +839,16 @@ var RolesGuard = class {
|
|
|
680
839
|
if (!BufferCtor) return {};
|
|
681
840
|
const decoded = BufferCtor.from(payload, "base64").toString("utf8");
|
|
682
841
|
return JSON.parse(decoded);
|
|
683
|
-
} catch
|
|
842
|
+
} catch {
|
|
684
843
|
return {};
|
|
685
844
|
}
|
|
686
845
|
}
|
|
687
846
|
};
|
|
688
847
|
RolesGuard = __decorateClass([
|
|
689
|
-
(0,
|
|
690
|
-
__decorateParam(0, (0,
|
|
691
|
-
__decorateParam(1, (0,
|
|
692
|
-
__decorateParam(1, (0,
|
|
848
|
+
(0, import_common5.Injectable)(),
|
|
849
|
+
__decorateParam(0, (0, import_common5.Inject)(import_core.Reflector)),
|
|
850
|
+
__decorateParam(1, (0, import_common5.Optional)()),
|
|
851
|
+
__decorateParam(1, (0, import_common5.Inject)(KEYCLOAK_CONFIG))
|
|
693
852
|
], RolesGuard);
|
|
694
853
|
|
|
695
854
|
// src/keycloak.module.ts
|
|
@@ -699,7 +858,7 @@ var KeycloakModule = class {
|
|
|
699
858
|
module: KeycloakModule,
|
|
700
859
|
global: true,
|
|
701
860
|
imports: [
|
|
702
|
-
|
|
861
|
+
import_http_client3.HttpModule.forRoot(
|
|
703
862
|
httpConfig || { baseURL: config.baseUrl, timeout: 5e3 },
|
|
704
863
|
{
|
|
705
864
|
logging: {
|
|
@@ -719,8 +878,8 @@ var KeycloakModule = class {
|
|
|
719
878
|
useFactory: (cfg, httpProvider, logger, cacheProvider) => new KeycloakClient(cfg, httpProvider, logger, cacheProvider),
|
|
720
879
|
inject: [
|
|
721
880
|
KEYCLOAK_CONFIG,
|
|
722
|
-
|
|
723
|
-
{ token:
|
|
881
|
+
import_http_client3.HTTP_PROVIDER,
|
|
882
|
+
{ token: import_logger3.LOGGER_PROVIDER, optional: true },
|
|
724
883
|
{ token: import_cache3.CACHE_PROVIDER, optional: true }
|
|
725
884
|
]
|
|
726
885
|
},
|
|
@@ -732,7 +891,8 @@ var KeycloakModule = class {
|
|
|
732
891
|
provide: KEYCLOAK_HTTP_INTERCEPTOR,
|
|
733
892
|
useFactory: () => new KeycloakHttpInterceptor()
|
|
734
893
|
},
|
|
735
|
-
RolesGuard
|
|
894
|
+
RolesGuard,
|
|
895
|
+
BearerTokenGuard
|
|
736
896
|
],
|
|
737
897
|
exports: [
|
|
738
898
|
import_core2.Reflector,
|
|
@@ -740,16 +900,99 @@ var KeycloakModule = class {
|
|
|
740
900
|
KEYCLOAK_PROVIDER,
|
|
741
901
|
KEYCLOAK_HTTP_INTERCEPTOR,
|
|
742
902
|
KEYCLOAK_CONFIG,
|
|
743
|
-
RolesGuard
|
|
903
|
+
RolesGuard,
|
|
904
|
+
BearerTokenGuard
|
|
744
905
|
]
|
|
745
906
|
};
|
|
746
907
|
}
|
|
747
908
|
};
|
|
748
909
|
KeycloakModule = __decorateClass([
|
|
749
|
-
(0,
|
|
910
|
+
(0, import_common6.Module)({})
|
|
750
911
|
], KeycloakModule);
|
|
912
|
+
|
|
913
|
+
// src/b2b.guard.ts
|
|
914
|
+
var import_common7 = require("@nestjs/common");
|
|
915
|
+
var B2BGuard = class {
|
|
916
|
+
constructor(bearerTokenGuard) {
|
|
917
|
+
this.bearerTokenGuard = bearerTokenGuard;
|
|
918
|
+
}
|
|
919
|
+
canActivate(context) {
|
|
920
|
+
return Promise.resolve(this.bearerTokenGuard.canActivate(context));
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
B2BGuard = __decorateClass([
|
|
924
|
+
(0, import_common7.Injectable)()
|
|
925
|
+
], B2BGuard);
|
|
926
|
+
|
|
927
|
+
// src/b2c.guard.ts
|
|
928
|
+
var import_common8 = require("@nestjs/common");
|
|
929
|
+
var import_shared3 = __toESM(require_dist());
|
|
930
|
+
var B2CGuard = class {
|
|
931
|
+
canActivate(context) {
|
|
932
|
+
var _a, _b;
|
|
933
|
+
const request = context.switchToHttp().getRequest();
|
|
934
|
+
const userId = ((_a = request.headers) == null ? void 0 : _a["x-user-id"]) ?? ((_b = request.headers) == null ? void 0 : _b["X-User-Id"]);
|
|
935
|
+
if (userId) return true;
|
|
936
|
+
throw new import_shared3.BaseAppError({
|
|
937
|
+
message: "Missing Kong identity headers (X-User-Id). Route requires user authentication via Kong.",
|
|
938
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
939
|
+
code: BEARER_ERROR_CODE.MISSING_TOKEN,
|
|
940
|
+
context: {}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
B2CGuard = __decorateClass([
|
|
945
|
+
(0, import_common8.Injectable)()
|
|
946
|
+
], B2CGuard);
|
|
947
|
+
|
|
948
|
+
// src/api-auth.guard.ts
|
|
949
|
+
var import_common9 = require("@nestjs/common");
|
|
950
|
+
var import_shared4 = __toESM(require_dist());
|
|
951
|
+
var ApiAuthGuard = class {
|
|
952
|
+
constructor(b2bGuard, b2cGuard) {
|
|
953
|
+
this.b2bGuard = b2bGuard;
|
|
954
|
+
this.b2cGuard = b2cGuard;
|
|
955
|
+
}
|
|
956
|
+
async canActivate(context) {
|
|
957
|
+
var _a, _b, _c, _d;
|
|
958
|
+
const request = context.switchToHttp().getRequest();
|
|
959
|
+
const authHeader = ((_a = request.headers) == null ? void 0 : _a.authorization) ?? ((_b = request.headers) == null ? void 0 : _b.Authorization);
|
|
960
|
+
if (authHeader == null ? void 0 : authHeader.toLowerCase().startsWith("bearer ")) {
|
|
961
|
+
return this.b2bGuard.canActivate(context);
|
|
962
|
+
}
|
|
963
|
+
const userId = ((_c = request.headers) == null ? void 0 : _c["x-user-id"]) ?? ((_d = request.headers) == null ? void 0 : _d["X-User-Id"]);
|
|
964
|
+
if (userId) {
|
|
965
|
+
return this.b2cGuard.canActivate(context);
|
|
966
|
+
}
|
|
967
|
+
throw new import_shared4.BaseAppError({
|
|
968
|
+
message: "Unauthorized: missing Authorization header (B2B) or Kong identity headers (B2C)",
|
|
969
|
+
status: HTTP_STATUS.UNAUTHORIZED,
|
|
970
|
+
code: BEARER_ERROR_CODE.MISSING_TOKEN,
|
|
971
|
+
context: {}
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
ApiAuthGuard = __decorateClass([
|
|
976
|
+
(0, import_common9.Injectable)()
|
|
977
|
+
], ApiAuthGuard);
|
|
978
|
+
|
|
979
|
+
// src/auth-user.decorator.ts
|
|
980
|
+
var import_common10 = require("@nestjs/common");
|
|
981
|
+
var AuthUser = (0, import_common10.createParamDecorator)(
|
|
982
|
+
(_data, ctx) => {
|
|
983
|
+
var _a, _b;
|
|
984
|
+
const request = ctx.switchToHttp().getRequest();
|
|
985
|
+
const raw = ((_a = request.headers) == null ? void 0 : _a["x-user-id"]) ?? ((_b = request.headers) == null ? void 0 : _b["X-User-Id"]);
|
|
986
|
+
return Array.isArray(raw) ? raw[0] : raw ?? "";
|
|
987
|
+
}
|
|
988
|
+
);
|
|
751
989
|
// Annotate the CommonJS export names for ESM import in node:
|
|
752
990
|
0 && (module.exports = {
|
|
991
|
+
ApiAuthGuard,
|
|
992
|
+
AuthUser,
|
|
993
|
+
B2BGuard,
|
|
994
|
+
B2CGuard,
|
|
995
|
+
BearerTokenGuard,
|
|
753
996
|
KEYCLOAK_CLIENT,
|
|
754
997
|
KEYCLOAK_CONFIG,
|
|
755
998
|
KEYCLOAK_HTTP_INTERCEPTOR,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adatechnology/auth-keycloak",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@adatechnology/cache": "0.0.
|
|
15
|
-
"@adatechnology/http-client": "0.0.
|
|
16
|
-
"@adatechnology/logger": "0.0.
|
|
14
|
+
"@adatechnology/cache": "0.0.8",
|
|
15
|
+
"@adatechnology/http-client": "0.0.9",
|
|
16
|
+
"@adatechnology/logger": "0.0.7"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"@nestjs/common": "^11.0.16",
|