@adatechnology/auth-keycloak 0.0.3 → 0.0.6
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/dist/index.d.ts +114 -6
- package/dist/index.js +743 -16
- package/package.json +6 -6
- package/dist/errors/keycloak-error.d.ts +0 -11
- package/dist/errors/keycloak-error.js +0 -20
- package/dist/errors/keycloak-error.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/keycloak.client.d.ts +0 -27
- package/dist/keycloak.client.js +0 -320
- package/dist/keycloak.client.js.map +0 -1
- package/dist/keycloak.http.interceptor.d.ts +0 -9
- package/dist/keycloak.http.interceptor.js +0 -37
- package/dist/keycloak.http.interceptor.js.map +0 -1
- package/dist/keycloak.interface.d.ts +0 -74
- package/dist/keycloak.interface.js +0 -3
- package/dist/keycloak.interface.js.map +0 -1
- package/dist/keycloak.module.d.ts +0 -6
- package/dist/keycloak.module.js +0 -63
- package/dist/keycloak.module.js.map +0 -1
- package/dist/keycloak.token.d.ts +0 -3
- package/dist/keycloak.token.js +0 -7
- package/dist/keycloak.token.js.map +0 -1
- package/dist/roles.decorator.d.ts +0 -19
- package/dist/roles.decorator.js +0 -34
- package/dist/roles.decorator.js.map +0 -1
- package/dist/roles.guard.d.ts +0 -10
- package/dist/roles.guard.js +0 -103
- package/dist/roles.guard.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,743 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
+
for (let key of __getOwnPropNames(from))
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
+
mod
|
|
29
|
+
));
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
32
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
33
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
34
|
+
if (decorator = decorators[i])
|
|
35
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
36
|
+
if (kind && result) __defProp(target, key, result);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
40
|
+
|
|
41
|
+
// ../shared/dist/types.js
|
|
42
|
+
var require_types = __commonJS({
|
|
43
|
+
"../shared/dist/types.js"(exports2) {
|
|
44
|
+
"use strict";
|
|
45
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ../shared/dist/utils.js
|
|
50
|
+
var require_utils = __commonJS({
|
|
51
|
+
"../shared/dist/utils.js"(exports2) {
|
|
52
|
+
"use strict";
|
|
53
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
54
|
+
exports2.noop = noop;
|
|
55
|
+
exports2.prefixWith = prefixWith;
|
|
56
|
+
function noop() {
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
function prefixWith(prefix, value) {
|
|
60
|
+
return `${prefix}-${value}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ../shared/dist/errors/base-app-error.js
|
|
66
|
+
var require_base_app_error = __commonJS({
|
|
67
|
+
"../shared/dist/errors/base-app-error.js"(exports2) {
|
|
68
|
+
"use strict";
|
|
69
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
70
|
+
exports2.BaseAppError = void 0;
|
|
71
|
+
var BaseAppError2 = class extends Error {
|
|
72
|
+
code;
|
|
73
|
+
status;
|
|
74
|
+
context;
|
|
75
|
+
constructor(params) {
|
|
76
|
+
var _a;
|
|
77
|
+
super(params.message);
|
|
78
|
+
this.name = new.target.name;
|
|
79
|
+
this.status = params.status;
|
|
80
|
+
this.code = params.code;
|
|
81
|
+
this.context = params.context;
|
|
82
|
+
const capturable = Error;
|
|
83
|
+
(_a = capturable.captureStackTrace) == null ? void 0 : _a.call(capturable, this, this.constructor);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
exports2.BaseAppError = BaseAppError2;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// ../shared/dist/errors/errors.constants.js
|
|
91
|
+
var require_errors_constants = __commonJS({
|
|
92
|
+
"../shared/dist/errors/errors.constants.js"(exports2) {
|
|
93
|
+
"use strict";
|
|
94
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
95
|
+
exports2.SHARED_INTERNAL_FRAME_RE = exports2.SHARED_ERROR_MESSAGES = exports2.SHARED_ERRORS = void 0;
|
|
96
|
+
exports2.SHARED_ERRORS = {
|
|
97
|
+
DEFAULT_STATUS: 502,
|
|
98
|
+
INTERNAL_STATUS: 500
|
|
99
|
+
};
|
|
100
|
+
exports2.SHARED_ERROR_MESSAGES = {
|
|
101
|
+
UPSTREAM_ERROR: "Upstream error",
|
|
102
|
+
NO_RESPONSE: "No response from upstream service",
|
|
103
|
+
UNEXPECTED_ERROR: "Unexpected error",
|
|
104
|
+
MAPPING_FAILURE: "Error mapping failure"
|
|
105
|
+
};
|
|
106
|
+
exports2.SHARED_INTERNAL_FRAME_RE = /node_modules|internal|\(internal|axios|packages\/http|@adatechnology\/http-client/;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ../shared/dist/errors/error-mapper.service.js
|
|
111
|
+
var require_error_mapper_service = __commonJS({
|
|
112
|
+
"../shared/dist/errors/error-mapper.service.js"(exports2) {
|
|
113
|
+
"use strict";
|
|
114
|
+
var __decorate = exports2 && exports2.__decorate || function(decorators, target, key, desc) {
|
|
115
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
116
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
117
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
118
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
119
|
+
};
|
|
120
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
121
|
+
exports2.ErrorMapperService = void 0;
|
|
122
|
+
var common_1 = require("@nestjs/common");
|
|
123
|
+
var base_app_error_1 = require_base_app_error();
|
|
124
|
+
var errors_constants_1 = require_errors_constants();
|
|
125
|
+
var ErrorMapperService = class ErrorMapperService {
|
|
126
|
+
/**
|
|
127
|
+
* Map an upstream/internal error to a BaseAppError with normalized fields.
|
|
128
|
+
* Keeps a small context to help tracing origin without leaking secrets.
|
|
129
|
+
*/
|
|
130
|
+
mapUpstreamError(err) {
|
|
131
|
+
if (err instanceof base_app_error_1.BaseAppError)
|
|
132
|
+
return err;
|
|
133
|
+
try {
|
|
134
|
+
const obj = err ?? void 0;
|
|
135
|
+
const context = {};
|
|
136
|
+
if (obj && typeof obj.stack === "string") {
|
|
137
|
+
const frames = this.parseStack(obj.stack);
|
|
138
|
+
if (frames.length) {
|
|
139
|
+
context.stack = frames;
|
|
140
|
+
const origin = frames.find((f) => !this.isInternalFrame(f.file));
|
|
141
|
+
if (origin)
|
|
142
|
+
context.origin = origin;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (obj && typeof obj.config === "object" && obj.config !== null) {
|
|
146
|
+
const cfg = obj.config;
|
|
147
|
+
context.url = typeof cfg.url === "string" ? cfg.url : typeof cfg.baseURL === "string" ? cfg.baseURL : void 0;
|
|
148
|
+
context.method = typeof cfg.method === "string" ? cfg.method : void 0;
|
|
149
|
+
}
|
|
150
|
+
if (obj && typeof obj.response === "object" && obj.response !== null) {
|
|
151
|
+
const resp = obj.response;
|
|
152
|
+
const status = typeof resp.status === "number" ? resp.status : errors_constants_1.SHARED_ERRORS.DEFAULT_STATUS;
|
|
153
|
+
const data = resp.data;
|
|
154
|
+
const message = data && typeof data.message === "string" ? data.message : typeof obj.message === "string" ? obj.message : errors_constants_1.SHARED_ERROR_MESSAGES.UPSTREAM_ERROR;
|
|
155
|
+
const code = typeof obj.code === "string" ? obj.code : void 0;
|
|
156
|
+
return new base_app_error_1.BaseAppError({
|
|
157
|
+
message,
|
|
158
|
+
status,
|
|
159
|
+
code,
|
|
160
|
+
context
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (obj && typeof obj.request === "object") {
|
|
164
|
+
const code = typeof obj.code === "string" ? obj.code : void 0;
|
|
165
|
+
return new base_app_error_1.BaseAppError({
|
|
166
|
+
message: errors_constants_1.SHARED_ERROR_MESSAGES.NO_RESPONSE,
|
|
167
|
+
status: errors_constants_1.SHARED_ERRORS.DEFAULT_STATUS,
|
|
168
|
+
code,
|
|
169
|
+
context
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return new base_app_error_1.BaseAppError({
|
|
173
|
+
message: obj && typeof obj.message === "string" ? obj.message : errors_constants_1.SHARED_ERROR_MESSAGES.UNEXPECTED_ERROR,
|
|
174
|
+
status: errors_constants_1.SHARED_ERRORS.INTERNAL_STATUS,
|
|
175
|
+
code: typeof (obj == null ? void 0 : obj.code) === "string" ? obj.code : void 0,
|
|
176
|
+
context
|
|
177
|
+
});
|
|
178
|
+
} catch (e) {
|
|
179
|
+
return new base_app_error_1.BaseAppError({
|
|
180
|
+
message: errors_constants_1.SHARED_ERROR_MESSAGES.MAPPING_FAILURE,
|
|
181
|
+
status: errors_constants_1.SHARED_ERRORS.INTERNAL_STATUS,
|
|
182
|
+
context: { original: String(err) }
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
parseStack(stack) {
|
|
187
|
+
const lines = stack.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
188
|
+
const frames = [];
|
|
189
|
+
const re = /^at\s+(?:(.*?)\s+\()?(.*?):(\d+):(\d+)\)?$/;
|
|
190
|
+
for (const line of lines) {
|
|
191
|
+
const m = re.exec(line);
|
|
192
|
+
if (m) {
|
|
193
|
+
const fn = m[1] || void 0;
|
|
194
|
+
const file = m[2];
|
|
195
|
+
const lineNum = parseInt(m[3], 10);
|
|
196
|
+
const colNum = parseInt(m[4], 10);
|
|
197
|
+
frames.push({ fn, file, line: lineNum, column: colNum });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return frames;
|
|
201
|
+
}
|
|
202
|
+
isInternalFrame(file) {
|
|
203
|
+
if (!file)
|
|
204
|
+
return false;
|
|
205
|
+
return errors_constants_1.SHARED_INTERNAL_FRAME_RE.test(file);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
exports2.ErrorMapperService = ErrorMapperService;
|
|
209
|
+
exports2.ErrorMapperService = ErrorMapperService = __decorate([
|
|
210
|
+
(0, common_1.Injectable)()
|
|
211
|
+
], ErrorMapperService);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// ../shared/dist/errors/index.js
|
|
216
|
+
var require_errors = __commonJS({
|
|
217
|
+
"../shared/dist/errors/index.js"(exports2) {
|
|
218
|
+
"use strict";
|
|
219
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
220
|
+
if (k2 === void 0) k2 = k;
|
|
221
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
222
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
223
|
+
desc = { enumerable: true, get: function() {
|
|
224
|
+
return m[k];
|
|
225
|
+
} };
|
|
226
|
+
}
|
|
227
|
+
Object.defineProperty(o, k2, desc);
|
|
228
|
+
}) : (function(o, m, k, k2) {
|
|
229
|
+
if (k2 === void 0) k2 = k;
|
|
230
|
+
o[k2] = m[k];
|
|
231
|
+
}));
|
|
232
|
+
var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
|
|
233
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
234
|
+
};
|
|
235
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
236
|
+
__exportStar(require_base_app_error(), exports2);
|
|
237
|
+
__exportStar(require_error_mapper_service(), exports2);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ../shared/dist/index.js
|
|
242
|
+
var require_dist = __commonJS({
|
|
243
|
+
"../shared/dist/index.js"(exports2) {
|
|
244
|
+
"use strict";
|
|
245
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
246
|
+
if (k2 === void 0) k2 = k;
|
|
247
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
248
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
249
|
+
desc = { enumerable: true, get: function() {
|
|
250
|
+
return m[k];
|
|
251
|
+
} };
|
|
252
|
+
}
|
|
253
|
+
Object.defineProperty(o, k2, desc);
|
|
254
|
+
}) : (function(o, m, k, k2) {
|
|
255
|
+
if (k2 === void 0) k2 = k;
|
|
256
|
+
o[k2] = m[k];
|
|
257
|
+
}));
|
|
258
|
+
var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
|
|
259
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
260
|
+
};
|
|
261
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
262
|
+
__exportStar(require_types(), exports2);
|
|
263
|
+
__exportStar(require_utils(), exports2);
|
|
264
|
+
__exportStar(require_errors(), exports2);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// src/index.ts
|
|
269
|
+
var index_exports = {};
|
|
270
|
+
__export(index_exports, {
|
|
271
|
+
KEYCLOAK_CLIENT: () => KEYCLOAK_CLIENT,
|
|
272
|
+
KEYCLOAK_CONFIG: () => KEYCLOAK_CONFIG,
|
|
273
|
+
KEYCLOAK_HTTP_INTERCEPTOR: () => KEYCLOAK_HTTP_INTERCEPTOR,
|
|
274
|
+
KeycloakError: () => KeycloakError,
|
|
275
|
+
KeycloakModule: () => KeycloakModule,
|
|
276
|
+
Roles: () => Roles,
|
|
277
|
+
RolesGuard: () => RolesGuard
|
|
278
|
+
});
|
|
279
|
+
module.exports = __toCommonJS(index_exports);
|
|
280
|
+
|
|
281
|
+
// src/keycloak.module.ts
|
|
282
|
+
var import_common5 = require("@nestjs/common");
|
|
283
|
+
var import_core2 = require("@nestjs/core");
|
|
284
|
+
var import_http_client2 = require("@adatechnology/http-client");
|
|
285
|
+
var import_logger2 = require("@adatechnology/logger");
|
|
286
|
+
|
|
287
|
+
// src/keycloak.client.ts
|
|
288
|
+
var import_common = require("@nestjs/common");
|
|
289
|
+
var import_http_client = require("@adatechnology/http-client");
|
|
290
|
+
var import_logger = require("@adatechnology/logger");
|
|
291
|
+
|
|
292
|
+
// src/errors/keycloak-error.ts
|
|
293
|
+
var KeycloakError = class _KeycloakError extends Error {
|
|
294
|
+
statusCode;
|
|
295
|
+
details;
|
|
296
|
+
keycloakError;
|
|
297
|
+
constructor(message, opts) {
|
|
298
|
+
super(message);
|
|
299
|
+
this.name = "KeycloakError";
|
|
300
|
+
this.statusCode = opts == null ? void 0 : opts.statusCode;
|
|
301
|
+
this.details = opts == null ? void 0 : opts.details;
|
|
302
|
+
this.keycloakError = opts == null ? void 0 : opts.keycloakError;
|
|
303
|
+
Object.setPrototypeOf(this, _KeycloakError.prototype);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// src/keycloak.client.ts
|
|
308
|
+
var LIB_NAME = "@adatechnology/auth-keycloak";
|
|
309
|
+
var LIB_VERSION = "0.0.2";
|
|
310
|
+
function extractErrorInfo(err) {
|
|
311
|
+
var _a, _b, _c, _d, _e;
|
|
312
|
+
const statusCode = (err == null ? void 0 : err.status) ?? ((_a = err == null ? void 0 : err.response) == null ? void 0 : _a.status);
|
|
313
|
+
const details = ((_b = err == null ? void 0 : err.response) == null ? void 0 : _b.data) ?? ((_c = err == null ? void 0 : err.context) == null ? void 0 : _c.data) ?? (err == null ? void 0 : err.context) ?? (err == null ? void 0 : err.details);
|
|
314
|
+
const errorCode = (err == null ? void 0 : err.code) ?? ((_e = (_d = err == null ? void 0 : err.response) == null ? void 0 : _d.data) == null ? void 0 : _e.error);
|
|
315
|
+
let keycloakError = void 0;
|
|
316
|
+
if (details && typeof details === "object" && details !== null) {
|
|
317
|
+
const raw = details.error;
|
|
318
|
+
if (typeof raw === "string") {
|
|
319
|
+
keycloakError = raw;
|
|
320
|
+
} else if (raw) {
|
|
321
|
+
try {
|
|
322
|
+
keycloakError = JSON.stringify(raw);
|
|
323
|
+
} catch {
|
|
324
|
+
keycloakError = String(raw);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
statusCode,
|
|
330
|
+
details: details ?? (err == null ? void 0 : err.message),
|
|
331
|
+
keycloakError: keycloakError ?? (errorCode ? `NETWORK_ERROR_${String(errorCode)}` : void 0)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
var KeycloakClient = class {
|
|
335
|
+
constructor(config, httpProvider, logger) {
|
|
336
|
+
this.config = config;
|
|
337
|
+
this.httpProvider = httpProvider;
|
|
338
|
+
this.logger = logger;
|
|
339
|
+
}
|
|
340
|
+
tokenCache = null;
|
|
341
|
+
tokenPromise = null;
|
|
342
|
+
log(level, message, libMethod, meta) {
|
|
343
|
+
if (!this.logger) return;
|
|
344
|
+
const httpCtx = (0, import_http_client.getHttpRequestContext)();
|
|
345
|
+
const requestId = httpCtx == null ? void 0 : httpCtx.requestId;
|
|
346
|
+
const source = (httpCtx == null ? void 0 : httpCtx.className) && (httpCtx == null ? void 0 : httpCtx.methodName) ? `${httpCtx.className}.${httpCtx.methodName}` : void 0;
|
|
347
|
+
const payload = {
|
|
348
|
+
message,
|
|
349
|
+
context: "KeycloakClient",
|
|
350
|
+
lib: LIB_NAME,
|
|
351
|
+
libVersion: LIB_VERSION,
|
|
352
|
+
libMethod,
|
|
353
|
+
source,
|
|
354
|
+
requestId,
|
|
355
|
+
meta
|
|
356
|
+
};
|
|
357
|
+
if (level === "debug") this.logger.debug(payload);
|
|
358
|
+
else if (level === "info") this.logger.info(payload);
|
|
359
|
+
else if (level === "warn") this.logger.warn(payload);
|
|
360
|
+
else if (level === "error") this.logger.error(payload);
|
|
361
|
+
}
|
|
362
|
+
async getAccessToken() {
|
|
363
|
+
const method = "getAccessToken";
|
|
364
|
+
this.log("debug", `${method} - Start`, method);
|
|
365
|
+
const now = Date.now();
|
|
366
|
+
if (this.tokenCache && now < this.tokenCache.expiresAt) {
|
|
367
|
+
this.log("debug", `${method} - Returning cached token`, method);
|
|
368
|
+
return this.tokenCache.token;
|
|
369
|
+
}
|
|
370
|
+
if (this.tokenPromise) {
|
|
371
|
+
this.log("debug", `${method} - Waiting for existing token request`, method);
|
|
372
|
+
return this.tokenPromise;
|
|
373
|
+
}
|
|
374
|
+
this.tokenPromise = (async () => {
|
|
375
|
+
try {
|
|
376
|
+
const tokenResponse = await this.requestToken();
|
|
377
|
+
const expiresAt = this.config.tokenCacheTtl ? Date.now() + this.config.tokenCacheTtl : Date.now() + (tokenResponse.expires_in - 60) * 1e3;
|
|
378
|
+
this.tokenCache = { token: tokenResponse.access_token, expiresAt };
|
|
379
|
+
this.log("debug", `${method} - Token obtained and cached`, method);
|
|
380
|
+
return tokenResponse.access_token;
|
|
381
|
+
} finally {
|
|
382
|
+
this.tokenPromise = null;
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
385
|
+
return this.tokenPromise;
|
|
386
|
+
}
|
|
387
|
+
async getTokenWithCredentials(params) {
|
|
388
|
+
const method = "getTokenWithCredentials";
|
|
389
|
+
const { username } = params;
|
|
390
|
+
this.log("info", `${method} - Start for user: ${username}`, method);
|
|
391
|
+
const { password } = params;
|
|
392
|
+
const tokenUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token`;
|
|
393
|
+
const body = new URLSearchParams();
|
|
394
|
+
body.append("client_id", this.config.credentials.clientId);
|
|
395
|
+
body.append("grant_type", "password");
|
|
396
|
+
body.append("username", username);
|
|
397
|
+
body.append("password", password);
|
|
398
|
+
if (this.config.credentials.clientSecret) {
|
|
399
|
+
body.append("client_secret", this.config.credentials.clientSecret);
|
|
400
|
+
}
|
|
401
|
+
body.append("scope", KeycloakClient.scopesToString(this.config.scopes));
|
|
402
|
+
try {
|
|
403
|
+
const response = await this.httpProvider.post({
|
|
404
|
+
url: tokenUrl,
|
|
405
|
+
data: body,
|
|
406
|
+
config: {
|
|
407
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
408
|
+
logContext: { className: "KeycloakClient", methodName: method }
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
this.log("info", `${method} - Success for user: ${username}`, method);
|
|
412
|
+
return response.data;
|
|
413
|
+
} catch (err) {
|
|
414
|
+
const { statusCode, details, keycloakError } = extractErrorInfo(err);
|
|
415
|
+
this.log("error", `${method} - Failed for user: ${username}`, method, { statusCode, keycloakError });
|
|
416
|
+
throw new KeycloakError("Failed to obtain token with credentials", {
|
|
417
|
+
statusCode,
|
|
418
|
+
details,
|
|
419
|
+
keycloakError
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async requestToken() {
|
|
424
|
+
const method = "requestToken";
|
|
425
|
+
this.log("debug", `${method} - Start`, method);
|
|
426
|
+
const tokenUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token`;
|
|
427
|
+
const data = new URLSearchParams();
|
|
428
|
+
data.append("client_id", this.config.credentials.clientId);
|
|
429
|
+
data.append("grant_type", this.config.credentials.grantType);
|
|
430
|
+
if (this.config.credentials.clientSecret) {
|
|
431
|
+
data.append("client_secret", this.config.credentials.clientSecret);
|
|
432
|
+
}
|
|
433
|
+
if (this.config.credentials.grantType === "password") {
|
|
434
|
+
if (this.config.credentials.username && this.config.credentials.password) {
|
|
435
|
+
data.append("username", this.config.credentials.username);
|
|
436
|
+
data.append("password", this.config.credentials.password);
|
|
437
|
+
data.append("scope", KeycloakClient.scopesToString(this.config.scopes));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
const response = await this.httpProvider.post({
|
|
442
|
+
url: tokenUrl,
|
|
443
|
+
data,
|
|
444
|
+
config: {
|
|
445
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
446
|
+
logContext: { className: "KeycloakClient", methodName: method }
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
this.log("debug", `${method} - Success`, method);
|
|
450
|
+
return response.data;
|
|
451
|
+
} catch (err) {
|
|
452
|
+
const { statusCode, details, keycloakError } = extractErrorInfo(err);
|
|
453
|
+
this.log("error", `${method} - Failed`, method, { statusCode, keycloakError });
|
|
454
|
+
throw new KeycloakError("Failed to request token", {
|
|
455
|
+
statusCode,
|
|
456
|
+
details,
|
|
457
|
+
keycloakError
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async refreshToken(refreshToken) {
|
|
462
|
+
const method = "refreshToken";
|
|
463
|
+
this.log("debug", `${method} - Start`, method);
|
|
464
|
+
const tokenUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token`;
|
|
465
|
+
const data = new URLSearchParams();
|
|
466
|
+
data.append("client_id", this.config.credentials.clientId);
|
|
467
|
+
data.append("grant_type", "refresh_token");
|
|
468
|
+
data.append("refresh_token", refreshToken);
|
|
469
|
+
if (this.config.credentials.clientSecret) {
|
|
470
|
+
data.append("client_secret", this.config.credentials.clientSecret);
|
|
471
|
+
}
|
|
472
|
+
try {
|
|
473
|
+
const response = await this.httpProvider.post({
|
|
474
|
+
url: tokenUrl,
|
|
475
|
+
data,
|
|
476
|
+
config: {
|
|
477
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
478
|
+
logContext: { className: "KeycloakClient", methodName: method }
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
const expiresAt = this.config.tokenCacheTtl ? Date.now() + this.config.tokenCacheTtl : Date.now() + (response.data.expires_in - 60) * 1e3;
|
|
482
|
+
this.tokenCache = { token: response.data.access_token, expiresAt };
|
|
483
|
+
this.log("debug", `${method} - Success`, method);
|
|
484
|
+
return response.data;
|
|
485
|
+
} catch (err) {
|
|
486
|
+
const { statusCode, details, keycloakError } = extractErrorInfo(err);
|
|
487
|
+
this.log("error", `${method} - Failed`, method, { statusCode, keycloakError });
|
|
488
|
+
throw new KeycloakError("Failed to refresh token", {
|
|
489
|
+
statusCode,
|
|
490
|
+
details,
|
|
491
|
+
keycloakError
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async validateToken(token) {
|
|
496
|
+
var _a;
|
|
497
|
+
const method = "validateToken";
|
|
498
|
+
this.log("debug", `${method} - Start`, method);
|
|
499
|
+
try {
|
|
500
|
+
const introspectUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/token/introspect`;
|
|
501
|
+
const data = new URLSearchParams();
|
|
502
|
+
data.append("token", token);
|
|
503
|
+
data.append("client_id", this.config.credentials.clientId);
|
|
504
|
+
if (this.config.credentials.clientSecret) {
|
|
505
|
+
data.append("client_secret", this.config.credentials.clientSecret);
|
|
506
|
+
}
|
|
507
|
+
const response = await this.httpProvider.post({
|
|
508
|
+
url: introspectUrl,
|
|
509
|
+
data,
|
|
510
|
+
config: {
|
|
511
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
512
|
+
logContext: { className: "KeycloakClient", methodName: method }
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
const active = ((_a = response.data) == null ? void 0 : _a.active) === true;
|
|
516
|
+
this.log("debug", `${method} - Success (Active: ${active})`, method);
|
|
517
|
+
return active;
|
|
518
|
+
} catch (error) {
|
|
519
|
+
const { statusCode, details, keycloakError } = extractErrorInfo(error);
|
|
520
|
+
this.log("error", `${method} - Failed`, method, { statusCode, keycloakError });
|
|
521
|
+
throw new KeycloakError("Token introspection failed", {
|
|
522
|
+
statusCode,
|
|
523
|
+
details,
|
|
524
|
+
keycloakError
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async getUserInfo(token) {
|
|
529
|
+
const method = "getUserInfo";
|
|
530
|
+
this.log("debug", `${method} - Start`, method);
|
|
531
|
+
const userInfoUrl = `${this.config.baseUrl}/realms/${this.config.realm}/protocol/openid-connect/userinfo`;
|
|
532
|
+
try {
|
|
533
|
+
const response = await this.httpProvider.get({
|
|
534
|
+
url: userInfoUrl,
|
|
535
|
+
config: {
|
|
536
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
537
|
+
logContext: { className: "KeycloakClient", methodName: method }
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
this.log("debug", `${method} - Success`, method);
|
|
541
|
+
return response.data;
|
|
542
|
+
} catch (err) {
|
|
543
|
+
const { statusCode, details, keycloakError } = extractErrorInfo(err);
|
|
544
|
+
this.log("error", `${method} - Failed`, method, { statusCode, keycloakError });
|
|
545
|
+
throw new KeycloakError("Failed to retrieve userinfo", {
|
|
546
|
+
statusCode,
|
|
547
|
+
details,
|
|
548
|
+
keycloakError
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
clearTokenCache() {
|
|
553
|
+
this.tokenCache = null;
|
|
554
|
+
}
|
|
555
|
+
static maskToken(token, visibleChars = 8) {
|
|
556
|
+
if (!token || typeof token !== "string") return "";
|
|
557
|
+
return token.length <= visibleChars ? token : `${token.slice(0, visibleChars)}...`;
|
|
558
|
+
}
|
|
559
|
+
static scopesToString(scopes) {
|
|
560
|
+
if (!scopes) return "openid profile email";
|
|
561
|
+
return Array.isArray(scopes) ? scopes.join(" ") : String(scopes);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
KeycloakClient = __decorateClass([
|
|
565
|
+
(0, import_common.Injectable)(),
|
|
566
|
+
__decorateParam(1, (0, import_common.Inject)(import_http_client.HTTP_PROVIDER)),
|
|
567
|
+
__decorateParam(2, (0, import_common.Optional)()),
|
|
568
|
+
__decorateParam(2, (0, import_common.Inject)(import_logger.LOGGER_PROVIDER))
|
|
569
|
+
], KeycloakClient);
|
|
570
|
+
|
|
571
|
+
// src/keycloak.http.interceptor.ts
|
|
572
|
+
var import_common2 = require("@nestjs/common");
|
|
573
|
+
var KeycloakHttpInterceptor = class {
|
|
574
|
+
constructor() {
|
|
575
|
+
}
|
|
576
|
+
intercept(context, next) {
|
|
577
|
+
const request = context.switchToHttp().getRequest();
|
|
578
|
+
if (typeof request.url === "string" && !request.url.includes("keycloak")) {
|
|
579
|
+
}
|
|
580
|
+
return next.handle();
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
KeycloakHttpInterceptor = __decorateClass([
|
|
584
|
+
(0, import_common2.Injectable)()
|
|
585
|
+
], KeycloakHttpInterceptor);
|
|
586
|
+
|
|
587
|
+
// src/roles.guard.ts
|
|
588
|
+
var import_common4 = require("@nestjs/common");
|
|
589
|
+
var import_core = require("@nestjs/core");
|
|
590
|
+
|
|
591
|
+
// src/roles.decorator.ts
|
|
592
|
+
var import_common3 = require("@nestjs/common");
|
|
593
|
+
var ROLES_META_KEY = "roles";
|
|
594
|
+
function Roles(...args) {
|
|
595
|
+
let payload;
|
|
596
|
+
if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0])) {
|
|
597
|
+
payload = args[0];
|
|
598
|
+
} else {
|
|
599
|
+
const roles = [].concat(
|
|
600
|
+
...args.map((a) => Array.isArray(a) ? a : String(a))
|
|
601
|
+
);
|
|
602
|
+
payload = { roles };
|
|
603
|
+
}
|
|
604
|
+
payload.mode = payload.mode ?? "any";
|
|
605
|
+
payload.type = payload.type ?? "both";
|
|
606
|
+
return (0, import_common3.SetMetadata)(ROLES_META_KEY, payload);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/keycloak.token.ts
|
|
610
|
+
var KEYCLOAK_CONFIG = "KEYCLOAK_CONFIG";
|
|
611
|
+
var KEYCLOAK_CLIENT = "KEYCLOAK_CLIENT";
|
|
612
|
+
var KEYCLOAK_HTTP_INTERCEPTOR = "KEYCLOAK_HTTP_INTERCEPTOR";
|
|
613
|
+
|
|
614
|
+
// src/roles.guard.ts
|
|
615
|
+
var import_shared = __toESM(require_dist());
|
|
616
|
+
var RolesGuard = class {
|
|
617
|
+
constructor(reflector, config) {
|
|
618
|
+
this.reflector = reflector;
|
|
619
|
+
this.config = config;
|
|
620
|
+
}
|
|
621
|
+
canActivate(context) {
|
|
622
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
623
|
+
const meta = this.reflector.get(ROLES_META_KEY, context.getHandler()) || this.reflector.get(ROLES_META_KEY, context.getClass());
|
|
624
|
+
if (!meta || !meta.roles || meta.roles.length === 0) return true;
|
|
625
|
+
const req = context.switchToHttp().getRequest();
|
|
626
|
+
const authHeader = ((_a = req.headers) == null ? void 0 : _a.authorization) || ((_b = req.headers) == null ? void 0 : _b.Authorization);
|
|
627
|
+
const token = authHeader ? String(authHeader).split(" ")[1] : (_c = req.query) == null ? void 0 : _c.token;
|
|
628
|
+
if (!token)
|
|
629
|
+
throw new import_shared.BaseAppError({
|
|
630
|
+
message: "Authorization token not provided",
|
|
631
|
+
status: 403,
|
|
632
|
+
code: "FORBIDDEN_MISSING_TOKEN",
|
|
633
|
+
context: {}
|
|
634
|
+
});
|
|
635
|
+
const payload = this.decodeJwtPayload(token);
|
|
636
|
+
const availableRoles = /* @__PURE__ */ new Set();
|
|
637
|
+
if (((_d = payload == null ? void 0 : payload.realm_access) == null ? void 0 : _d.roles) && Array.isArray(payload.realm_access.roles)) {
|
|
638
|
+
payload.realm_access.roles.forEach((r) => availableRoles.add(r));
|
|
639
|
+
}
|
|
640
|
+
const clientId = (_f = (_e = this.config) == null ? void 0 : _e.credentials) == null ? void 0 : _f.clientId;
|
|
641
|
+
if (clientId && ((_h = (_g = payload == null ? void 0 : payload.resource_access) == null ? void 0 : _g[clientId]) == null ? void 0 : _h.roles)) {
|
|
642
|
+
payload.resource_access[clientId].roles.forEach(
|
|
643
|
+
(r) => availableRoles.add(r)
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
if (meta.type === "both" && (payload == null ? void 0 : payload.resource_access)) {
|
|
647
|
+
Object.values(payload.resource_access).forEach((entry) => {
|
|
648
|
+
if ((entry == null ? void 0 : entry.roles) && Array.isArray(entry.roles)) {
|
|
649
|
+
entry.roles.forEach(
|
|
650
|
+
(r) => availableRoles.add(r)
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const required = meta.roles || [];
|
|
656
|
+
const hasMatch = required.map((r) => availableRoles.has(r));
|
|
657
|
+
const result = meta.mode === "all" ? hasMatch.every(Boolean) : hasMatch.some(Boolean);
|
|
658
|
+
if (!result)
|
|
659
|
+
throw new import_shared.BaseAppError({
|
|
660
|
+
message: "Insufficient roles",
|
|
661
|
+
status: 403,
|
|
662
|
+
code: "FORBIDDEN_INSUFFICIENT_ROLES",
|
|
663
|
+
context: { required }
|
|
664
|
+
});
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
decodeJwtPayload(token) {
|
|
668
|
+
try {
|
|
669
|
+
const parts = token.split(".");
|
|
670
|
+
if (parts.length < 2) return {};
|
|
671
|
+
const payload = parts[1];
|
|
672
|
+
const BufferCtor = globalThis.Buffer;
|
|
673
|
+
if (!BufferCtor) return {};
|
|
674
|
+
const decoded = BufferCtor.from(payload, "base64").toString("utf8");
|
|
675
|
+
return JSON.parse(decoded);
|
|
676
|
+
} catch (e) {
|
|
677
|
+
return {};
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
RolesGuard = __decorateClass([
|
|
682
|
+
(0, import_common4.Injectable)(),
|
|
683
|
+
__decorateParam(0, (0, import_common4.Inject)(import_core.Reflector)),
|
|
684
|
+
__decorateParam(1, (0, import_common4.Optional)()),
|
|
685
|
+
__decorateParam(1, (0, import_common4.Inject)(KEYCLOAK_CONFIG))
|
|
686
|
+
], RolesGuard);
|
|
687
|
+
|
|
688
|
+
// src/keycloak.module.ts
|
|
689
|
+
var KeycloakModule = class {
|
|
690
|
+
static forRoot(config, httpConfig) {
|
|
691
|
+
return {
|
|
692
|
+
module: KeycloakModule,
|
|
693
|
+
global: true,
|
|
694
|
+
imports: [
|
|
695
|
+
import_http_client2.HttpModule.forRoot(
|
|
696
|
+
httpConfig || { baseURL: config.baseUrl, timeout: 5e3 },
|
|
697
|
+
{
|
|
698
|
+
logging: {
|
|
699
|
+
enabled: true,
|
|
700
|
+
includeBody: true,
|
|
701
|
+
context: "KeycloakHttpClient",
|
|
702
|
+
environments: ["development", "test"]
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
)
|
|
706
|
+
],
|
|
707
|
+
providers: [
|
|
708
|
+
{ provide: import_core2.Reflector, useClass: import_core2.Reflector },
|
|
709
|
+
{ provide: KEYCLOAK_CONFIG, useValue: config },
|
|
710
|
+
{
|
|
711
|
+
provide: KEYCLOAK_CLIENT,
|
|
712
|
+
useFactory: (cfg, httpProvider, logger) => new KeycloakClient(cfg, httpProvider, logger),
|
|
713
|
+
inject: [KEYCLOAK_CONFIG, import_http_client2.HTTP_PROVIDER, { token: import_logger2.LOGGER_PROVIDER, optional: true }]
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
provide: KEYCLOAK_HTTP_INTERCEPTOR,
|
|
717
|
+
useFactory: () => new KeycloakHttpInterceptor()
|
|
718
|
+
},
|
|
719
|
+
RolesGuard
|
|
720
|
+
],
|
|
721
|
+
exports: [
|
|
722
|
+
import_core2.Reflector,
|
|
723
|
+
KEYCLOAK_CLIENT,
|
|
724
|
+
KEYCLOAK_HTTP_INTERCEPTOR,
|
|
725
|
+
KEYCLOAK_CONFIG,
|
|
726
|
+
RolesGuard
|
|
727
|
+
]
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
KeycloakModule = __decorateClass([
|
|
732
|
+
(0, import_common5.Module)({})
|
|
733
|
+
], KeycloakModule);
|
|
734
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
735
|
+
0 && (module.exports = {
|
|
736
|
+
KEYCLOAK_CLIENT,
|
|
737
|
+
KEYCLOAK_CONFIG,
|
|
738
|
+
KEYCLOAK_HTTP_INTERCEPTOR,
|
|
739
|
+
KeycloakError,
|
|
740
|
+
KeycloakModule,
|
|
741
|
+
Roles,
|
|
742
|
+
RolesGuard
|
|
743
|
+
});
|