@adatechnology/keycloak-admin 0.1.16 → 1.0.0-rc.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 +63 -0
- package/dist/index.d.ts +151 -84
- package/dist/index.js +295 -662
- package/package.json +28 -19
package/dist/index.js
CHANGED
|
@@ -1,702 +1,335 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
// src/keycloak-admin.constant.ts
|
|
2
|
+
var KEYCLOAK_ADMIN_GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials";
|
|
3
|
+
var KEYCLOAK_ADMIN_FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
|
4
|
+
var KEYCLOAK_ADMIN_JSON_CONTENT_TYPE = "application/json";
|
|
5
|
+
var KEYCLOAK_ADMIN_REDACTED = "[REDACTED]";
|
|
6
|
+
var KEYCLOAK_ADMIN_TOKEN_RENEWAL_SKEW_MS = 3e4;
|
|
7
|
+
var KEYCLOAK_ADMIN_ERROR_CODE = {
|
|
8
|
+
CONFIGURATION_INVALID: "KEYCLOAK_ADMIN_CONFIGURATION_INVALID",
|
|
9
|
+
REQUEST_FAILED: "KEYCLOAK_ADMIN_REQUEST_FAILED",
|
|
10
|
+
TOKEN_REQUEST_FAILED: "KEYCLOAK_ADMIN_TOKEN_REQUEST_FAILED",
|
|
11
|
+
TOKEN_RESPONSE_INVALID: "KEYCLOAK_ADMIN_TOKEN_RESPONSE_INVALID",
|
|
12
|
+
USER_ALREADY_EXISTS: "KEYCLOAK_ADMIN_USER_ALREADY_EXISTS",
|
|
13
|
+
USER_ID_MISSING: "KEYCLOAK_ADMIN_USER_ID_MISSING",
|
|
14
|
+
USER_NOT_FOUND: "KEYCLOAK_ADMIN_USER_NOT_FOUND"
|
|
9
15
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
KEYCLOAK_ADMIN_CLIENT: () => KEYCLOAK_ADMIN_CLIENT,
|
|
24
|
-
KEYCLOAK_ADMIN_CONFIG: () => KEYCLOAK_ADMIN_CONFIG,
|
|
25
|
-
KEYCLOAK_ADMIN_PROVIDER: () => KEYCLOAK_ADMIN_PROVIDER,
|
|
26
|
-
KeycloakAdminClient: () => KeycloakAdminClient,
|
|
27
|
-
KeycloakAdminError: () => KeycloakAdminError,
|
|
28
|
-
KeycloakAdminModule: () => KeycloakAdminModule,
|
|
29
|
-
validateKeycloakAdminConfig: () => validateKeycloakAdminConfig
|
|
30
|
-
});
|
|
31
|
-
module.exports = __toCommonJS(index_exports);
|
|
32
|
-
|
|
33
|
-
// src/keycloak-admin.module.ts
|
|
34
|
-
var import_common2 = require("@nestjs/common");
|
|
35
|
-
var import_http_client2 = require("@adatechnology/http-client");
|
|
36
|
-
|
|
37
|
-
// src/keycloak-admin.token.ts
|
|
38
|
-
var KEYCLOAK_ADMIN_CLIENT = "KEYCLOAK_ADMIN_CLIENT";
|
|
39
|
-
var KEYCLOAK_ADMIN_CONFIG = "KEYCLOAK_ADMIN_CONFIG";
|
|
40
|
-
var KEYCLOAK_ADMIN_PROVIDER = "KEYCLOAK_ADMIN_PROVIDER";
|
|
41
|
-
|
|
42
|
-
// src/keycloak-admin.client.ts
|
|
43
|
-
var import_common = require("@nestjs/common");
|
|
44
|
-
var import_http_client = require("@adatechnology/http-client");
|
|
45
|
-
var import_logger = require("@adatechnology/logger");
|
|
16
|
+
function buildKeycloakAdminEndpoints({ baseUrl, realm }) {
|
|
17
|
+
const origin = baseUrl.replace(/\/+$/, "");
|
|
18
|
+
const encodedRealm = encodeURIComponent(realm);
|
|
19
|
+
const users = `${origin}/admin/realms/${encodedRealm}/users`;
|
|
20
|
+
return {
|
|
21
|
+
token: `${origin}/realms/${encodedRealm}/protocol/openid-connect/token`,
|
|
22
|
+
user: (userId) => `${users}/${encodeURIComponent(userId)}`,
|
|
23
|
+
userPassword: (userId) => `${users}/${encodeURIComponent(userId)}/reset-password`,
|
|
24
|
+
users
|
|
25
|
+
};
|
|
26
|
+
}
|
|
46
27
|
|
|
47
|
-
// src/
|
|
48
|
-
var
|
|
49
|
-
statusCode;
|
|
28
|
+
// src/keycloak-admin.error.ts
|
|
29
|
+
var KeycloakAdminError = class _KeycloakAdminError extends Error {
|
|
50
30
|
code;
|
|
51
31
|
context;
|
|
52
|
-
|
|
53
|
-
|
|
32
|
+
status;
|
|
33
|
+
constructor({ code, context = {}, message, status }) {
|
|
34
|
+
super(message);
|
|
54
35
|
this.name = "KeycloakAdminError";
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.context = context;
|
|
38
|
+
this.status = status;
|
|
58
39
|
Object.setPrototypeOf(this, _KeycloakAdminError.prototype);
|
|
59
40
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
publishConfig: {
|
|
69
|
-
access: "public"
|
|
70
|
-
},
|
|
71
|
-
main: "dist/index.js",
|
|
72
|
-
module: "dist/index.mjs",
|
|
73
|
-
types: "dist/index.d.ts",
|
|
74
|
-
files: [
|
|
75
|
-
"dist"
|
|
76
|
-
],
|
|
77
|
-
scripts: {
|
|
78
|
-
build: "rm -rf dist && tsup && cp index.d.ts dist/index.d.ts",
|
|
79
|
-
"build:watch": "tsup --watch",
|
|
80
|
-
check: "tsc -p tsconfig.json --noEmit"
|
|
81
|
-
},
|
|
82
|
-
dependencies: {
|
|
83
|
-
"@adatechnology/http-client": "workspace:*",
|
|
84
|
-
"@adatechnology/logger": "workspace:*"
|
|
85
|
-
},
|
|
86
|
-
peerDependencies: {
|
|
87
|
-
"@nestjs/common": "^11.0.16",
|
|
88
|
-
"@nestjs/core": "^11"
|
|
89
|
-
},
|
|
90
|
-
devDependencies: {
|
|
91
|
-
"@adatechnology/shared": "workspace:*",
|
|
92
|
-
"@esbuild-plugins/tsconfig-paths": "^0.1.2",
|
|
93
|
-
"@swc/core": "^1.15.24",
|
|
94
|
-
tsup: "^8.5.1",
|
|
95
|
-
typescript: "^5.2.0"
|
|
41
|
+
toJSON() {
|
|
42
|
+
return {
|
|
43
|
+
code: this.code,
|
|
44
|
+
context: this.context,
|
|
45
|
+
message: this.message,
|
|
46
|
+
name: this.name,
|
|
47
|
+
status: this.status
|
|
48
|
+
};
|
|
96
49
|
}
|
|
97
50
|
};
|
|
51
|
+
function isKeycloakAdminError(value) {
|
|
52
|
+
return value instanceof KeycloakAdminError;
|
|
53
|
+
}
|
|
98
54
|
|
|
99
|
-
// src/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
var KEYCLOAK_ADMIN_BEARER_PREFIX = "Bearer ";
|
|
108
|
-
var KEYCLOAK_ADMIN_ENDPOINTS = {
|
|
109
|
-
MASTER_TOKEN: "/realms/master/protocol/openid-connect/token",
|
|
110
|
-
ADMIN_USERS: /* @__PURE__ */ __name((baseUrl, realm, userId) => userId ? `${baseUrl}/admin/realms/${realm}/users/${userId}` : `${baseUrl}/admin/realms/${realm}/users`, "ADMIN_USERS"),
|
|
111
|
-
ADMIN_RESET_PASSWORD: /* @__PURE__ */ __name((baseUrl, realm, userId) => `${baseUrl}/admin/realms/${realm}/users/${userId}/reset-password`, "ADMIN_RESET_PASSWORD"),
|
|
112
|
-
ADMIN_SEND_VERIFY_EMAIL: /* @__PURE__ */ __name((baseUrl, realm, userId) => `${baseUrl}/admin/realms/${realm}/users/${userId}/send-verify-email`, "ADMIN_SEND_VERIFY_EMAIL")
|
|
113
|
-
};
|
|
114
|
-
var KEYCLOAK_ADMIN_ERROR_CODES = {
|
|
115
|
-
ADMIN_TOKEN_ERROR: "KEYCLOAK_ADMIN_TOKEN_ERROR",
|
|
116
|
-
CREATE_USER_ERROR: "KEYCLOAK_CREATE_USER_ERROR",
|
|
117
|
-
UPDATE_USER_ERROR: "KEYCLOAK_UPDATE_USER_ERROR",
|
|
118
|
-
RESET_PASSWORD_ERROR: "KEYCLOAK_RESET_PASSWORD_ERROR",
|
|
119
|
-
TOGGLE_ENABLED_ERROR: "KEYCLOAK_TOGGLE_ENABLED_ERROR",
|
|
120
|
-
DELETE_USER_ERROR: "KEYCLOAK_DELETE_USER_ERROR",
|
|
121
|
-
UPDATE_ATTRIBUTES_ERROR: "KEYCLOAK_UPDATE_ATTRIBUTES_ERROR",
|
|
122
|
-
SEND_VERIFY_EMAIL_ERROR: "KEYCLOAK_SEND_VERIFY_EMAIL_ERROR"
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// src/utils/extract-http-error.ts
|
|
126
|
-
function extractHttpError(err) {
|
|
127
|
-
const unknownErr = err;
|
|
128
|
-
const response = unknownErr == null ? void 0 : unknownErr.response;
|
|
129
|
-
const responseData = response == null ? void 0 : response.data;
|
|
130
|
-
let statusCode = void 0;
|
|
131
|
-
if (typeof (unknownErr == null ? void 0 : unknownErr.status) === "number") {
|
|
132
|
-
statusCode = unknownErr.status;
|
|
133
|
-
} else if (typeof (response == null ? void 0 : response.status) === "number") {
|
|
134
|
-
statusCode = response.status;
|
|
55
|
+
// src/keycloak-admin.schema.ts
|
|
56
|
+
import { z } from "zod";
|
|
57
|
+
function isAbsoluteHttpUrl(value) {
|
|
58
|
+
try {
|
|
59
|
+
const url = new URL(value);
|
|
60
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
135
63
|
}
|
|
136
|
-
const details = responseData ?? (unknownErr == null ? void 0 : unknownErr.message);
|
|
137
|
-
const rawError = (responseData == null ? void 0 : responseData.error) ?? (responseData == null ? void 0 : responseData.errorMessage);
|
|
138
|
-
const errorCode = typeof rawError === "string" ? rawError : typeof (unknownErr == null ? void 0 : unknownErr.code) === "string" ? unknownErr.code : void 0;
|
|
139
|
-
return {
|
|
140
|
-
statusCode,
|
|
141
|
-
details,
|
|
142
|
-
errorCode
|
|
143
|
-
};
|
|
144
64
|
}
|
|
145
|
-
|
|
65
|
+
var keycloakAdminConfigSchema = z.object({
|
|
66
|
+
baseUrl: z.string().min(1).refine(isAbsoluteHttpUrl, { message: "must be an absolute http(s) URL" }),
|
|
67
|
+
clientId: z.string().min(1),
|
|
68
|
+
clientSecret: z.string().min(1),
|
|
69
|
+
realm: z.string().min(1)
|
|
70
|
+
});
|
|
71
|
+
function parseKeycloakAdminConfig(value) {
|
|
72
|
+
const parsed = keycloakAdminConfigSchema.safeParse(value);
|
|
73
|
+
if (parsed.success) return parsed.data;
|
|
74
|
+
throw new KeycloakAdminError({
|
|
75
|
+
code: KEYCLOAK_ADMIN_ERROR_CODE.CONFIGURATION_INVALID,
|
|
76
|
+
context: { fields: parsed.error.issues.map((issue) => issue.path.join(".")).sort() },
|
|
77
|
+
message: "Invalid Keycloak admin configuration"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
146
80
|
|
|
147
|
-
// src/keycloak-admin.
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
151
|
-
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;
|
|
152
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
81
|
+
// src/keycloak-admin.redaction.ts
|
|
82
|
+
function replaceAll({ secrets, text }) {
|
|
83
|
+
return secrets.reduce((current, secret) => current.split(secret).join(KEYCLOAK_ADMIN_REDACTED), text);
|
|
153
84
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
85
|
+
function redactDeep({ secrets, value }) {
|
|
86
|
+
if (typeof value === "string") return replaceAll({ secrets, text: value });
|
|
87
|
+
if (Array.isArray(value)) return value.map((item) => redactDeep({ secrets, value: item }));
|
|
88
|
+
if (value !== null && typeof value === "object") {
|
|
89
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactDeep({ secrets, value: item })]));
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
157
92
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
161
|
-
|
|
93
|
+
function createSecretRedactor(...secrets) {
|
|
94
|
+
const known = secrets.filter((secret) => typeof secret === "string" && secret !== "");
|
|
95
|
+
return {
|
|
96
|
+
text(value) {
|
|
97
|
+
return replaceAll({ secrets: known, text: value });
|
|
98
|
+
},
|
|
99
|
+
value(value) {
|
|
100
|
+
return redactDeep({ secrets: known, value });
|
|
101
|
+
},
|
|
102
|
+
with(...additional) {
|
|
103
|
+
return createSecretRedactor(...known, ...additional);
|
|
104
|
+
}
|
|
162
105
|
};
|
|
163
106
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
107
|
+
|
|
108
|
+
// src/keycloak-admin.response.ts
|
|
109
|
+
var MAX_DETAIL_LENGTH = 200;
|
|
110
|
+
async function readKeycloakDetail(response) {
|
|
111
|
+
const text = await response.text().catch(() => "");
|
|
112
|
+
if (text === "") return void 0;
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(text);
|
|
115
|
+
if (parsed === null || typeof parsed !== "object") return text.slice(0, MAX_DETAIL_LENGTH);
|
|
116
|
+
const payload = parsed;
|
|
117
|
+
const detail = payload.errorMessage ?? payload.error_description ?? payload.error;
|
|
118
|
+
return typeof detail === "string" ? detail.slice(0, MAX_DETAIL_LENGTH) : void 0;
|
|
119
|
+
} catch {
|
|
120
|
+
return text.slice(0, MAX_DETAIL_LENGTH);
|
|
174
121
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/keycloak-admin.token.ts
|
|
125
|
+
function createKeycloakTokenProvider({
|
|
126
|
+
config,
|
|
127
|
+
endpoint,
|
|
128
|
+
fetch: fetchImpl,
|
|
129
|
+
now,
|
|
130
|
+
redactor
|
|
131
|
+
}) {
|
|
132
|
+
let cached;
|
|
133
|
+
let inFlight;
|
|
134
|
+
function isFresh(state) {
|
|
135
|
+
return state.expiresAtMs - KEYCLOAK_ADMIN_TOKEN_RENEWAL_SKEW_MS > now();
|
|
188
136
|
}
|
|
189
|
-
async
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
137
|
+
async function requestToken() {
|
|
138
|
+
const requestedAtMs = now();
|
|
139
|
+
const response = await fetchImpl(endpoint, {
|
|
140
|
+
body: new URLSearchParams({
|
|
141
|
+
client_id: config.clientId,
|
|
142
|
+
client_secret: config.clientSecret,
|
|
143
|
+
grant_type: KEYCLOAK_ADMIN_GRANT_TYPE_CLIENT_CREDENTIALS
|
|
144
|
+
}),
|
|
145
|
+
headers: { "content-type": KEYCLOAK_ADMIN_FORM_CONTENT_TYPE },
|
|
146
|
+
method: "POST"
|
|
193
147
|
});
|
|
194
|
-
|
|
195
|
-
const body = new URLSearchParams();
|
|
196
|
-
body.append("grant_type", KEYCLOAK_ADMIN_GRANT_TYPE_PASSWORD);
|
|
197
|
-
body.append("client_id", KEYCLOAK_ADMIN_CLIENT_ID_ADMIN_CLI);
|
|
198
|
-
body.append("username", this.config.adminUser);
|
|
199
|
-
body.append("password", this.config.adminPassword);
|
|
200
|
-
try {
|
|
201
|
-
const response = await this.httpProvider.post({
|
|
202
|
-
url,
|
|
203
|
-
data: body,
|
|
204
|
-
config: {
|
|
205
|
-
headers: {
|
|
206
|
-
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_FORM
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
const result = {
|
|
211
|
-
accessToken: response.data.access_token,
|
|
212
|
-
expiresIn: response.data.expires_in,
|
|
213
|
-
tokenType: response.data.token_type
|
|
214
|
-
};
|
|
215
|
-
this.log("info", `${method} - Success`, method);
|
|
216
|
-
return result;
|
|
217
|
-
} catch (err) {
|
|
218
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
219
|
-
this.log("error", `${method} - Failed`, method, {
|
|
220
|
-
statusCode,
|
|
221
|
-
errorCode
|
|
222
|
-
});
|
|
148
|
+
if (!response.ok) {
|
|
223
149
|
throw new KeycloakAdminError({
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
url,
|
|
229
|
-
method: "POST",
|
|
230
|
-
details,
|
|
231
|
-
errorCode
|
|
232
|
-
}
|
|
150
|
+
code: KEYCLOAK_ADMIN_ERROR_CODE.TOKEN_REQUEST_FAILED,
|
|
151
|
+
context: redactor.value({ detail: await readKeycloakDetail(response), realm: config.realm }),
|
|
152
|
+
message: "Keycloak refused the service account token request",
|
|
153
|
+
status: response.status
|
|
233
154
|
});
|
|
234
155
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
var _a, _b;
|
|
238
|
-
const method = "createUser";
|
|
239
|
-
const { username, email, firstName, lastName, enabled, emailVerified, credentials, attributes, adminToken } = params;
|
|
240
|
-
this.log("info", `${method} - Start`, method, {
|
|
241
|
-
username,
|
|
242
|
-
email
|
|
243
|
-
});
|
|
244
|
-
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm);
|
|
245
|
-
const userData = {
|
|
246
|
-
username,
|
|
247
|
-
email,
|
|
248
|
-
firstName,
|
|
249
|
-
lastName,
|
|
250
|
-
enabled,
|
|
251
|
-
emailVerified,
|
|
252
|
-
...credentials ? {
|
|
253
|
-
credentials
|
|
254
|
-
} : {},
|
|
255
|
-
...attributes ? {
|
|
256
|
-
attributes
|
|
257
|
-
} : {}
|
|
258
|
-
};
|
|
259
|
-
try {
|
|
260
|
-
const response = await this.httpProvider.post({
|
|
261
|
-
url,
|
|
262
|
-
data: userData,
|
|
263
|
-
config: {
|
|
264
|
-
headers: {
|
|
265
|
-
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
266
|
-
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
const locationHeader = ((_a = response.headers) == null ? void 0 : _a["location"]) ?? ((_b = response.headers) == null ? void 0 : _b["Location"]);
|
|
271
|
-
const userId = typeof locationHeader === "string" ? locationHeader.split("/").pop() ?? "" : "";
|
|
272
|
-
this.log("info", `${method} - Success`, method, {
|
|
273
|
-
username,
|
|
274
|
-
email,
|
|
275
|
-
userId
|
|
276
|
-
});
|
|
277
|
-
return userId;
|
|
278
|
-
} catch (err) {
|
|
279
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
280
|
-
this.log("error", `${method} - Failed`, method, {
|
|
281
|
-
username,
|
|
282
|
-
email,
|
|
283
|
-
statusCode,
|
|
284
|
-
errorCode
|
|
285
|
-
});
|
|
156
|
+
const payload = await response.json();
|
|
157
|
+
if (typeof payload.access_token !== "string" || typeof payload.expires_in !== "number") {
|
|
286
158
|
throw new KeycloakAdminError({
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
url,
|
|
292
|
-
method: "POST",
|
|
293
|
-
username,
|
|
294
|
-
email,
|
|
295
|
-
details,
|
|
296
|
-
errorCode
|
|
297
|
-
}
|
|
159
|
+
code: KEYCLOAK_ADMIN_ERROR_CODE.TOKEN_RESPONSE_INVALID,
|
|
160
|
+
context: { realm: config.realm },
|
|
161
|
+
message: "Keycloak returned a token response without access_token or expires_in",
|
|
162
|
+
status: response.status
|
|
298
163
|
});
|
|
299
164
|
}
|
|
165
|
+
return {
|
|
166
|
+
accessToken: payload.access_token,
|
|
167
|
+
expiresAtMs: requestedAtMs + payload.expires_in * 1e3
|
|
168
|
+
};
|
|
300
169
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
config: {
|
|
313
|
-
headers: {
|
|
314
|
-
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
315
|
-
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
this.log("info", `${method} - Success`, method, {
|
|
320
|
-
userId
|
|
321
|
-
});
|
|
322
|
-
} catch (err) {
|
|
323
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
324
|
-
this.log("error", `${method} - Failed`, method, {
|
|
325
|
-
userId,
|
|
326
|
-
statusCode,
|
|
327
|
-
errorCode
|
|
328
|
-
});
|
|
329
|
-
throw new KeycloakAdminError({
|
|
330
|
-
message: "Keycloak update user failed",
|
|
331
|
-
statusCode: statusCode ?? 502,
|
|
332
|
-
code: KEYCLOAK_ADMIN_ERROR_CODES.UPDATE_USER_ERROR,
|
|
333
|
-
context: {
|
|
334
|
-
url,
|
|
335
|
-
method: "PUT",
|
|
336
|
-
userId,
|
|
337
|
-
details,
|
|
338
|
-
errorCode
|
|
339
|
-
}
|
|
340
|
-
});
|
|
170
|
+
return {
|
|
171
|
+
async getAccessToken() {
|
|
172
|
+
if (cached && isFresh(cached)) return cached.accessToken;
|
|
173
|
+
inFlight ??= requestToken();
|
|
174
|
+
try {
|
|
175
|
+
const state = await inFlight;
|
|
176
|
+
cached = state;
|
|
177
|
+
return state.accessToken;
|
|
178
|
+
} finally {
|
|
179
|
+
inFlight = void 0;
|
|
180
|
+
}
|
|
341
181
|
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/keycloak-admin.client.ts
|
|
186
|
+
var HTTP_CONFLICT = 409;
|
|
187
|
+
var HTTP_NOT_FOUND = 404;
|
|
188
|
+
function errorCodeOf(status) {
|
|
189
|
+
if (status === HTTP_NOT_FOUND) return KEYCLOAK_ADMIN_ERROR_CODE.USER_NOT_FOUND;
|
|
190
|
+
if (status === HTTP_CONFLICT) return KEYCLOAK_ADMIN_ERROR_CODE.USER_ALREADY_EXISTS;
|
|
191
|
+
return KEYCLOAK_ADMIN_ERROR_CODE.REQUEST_FAILED;
|
|
192
|
+
}
|
|
193
|
+
function normalizeAttributes(attributes) {
|
|
194
|
+
return Object.fromEntries(
|
|
195
|
+
Object.entries(attributes).map(([key, value]) => [key, typeof value === "string" ? [value] : [...value]])
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function userIdFromLocation(response) {
|
|
199
|
+
const location = response.headers.get("location");
|
|
200
|
+
const id = location?.split("/").filter(Boolean).at(-1);
|
|
201
|
+
if (id === void 0 || id === "") {
|
|
202
|
+
throw new KeycloakAdminError({
|
|
203
|
+
code: KEYCLOAK_ADMIN_ERROR_CODE.USER_ID_MISSING,
|
|
204
|
+
message: "Keycloak created the user without a usable Location header",
|
|
205
|
+
status: response.status
|
|
349
206
|
});
|
|
350
|
-
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_RESET_PASSWORD(this.config.baseUrl, this.config.realm, userId);
|
|
351
|
-
try {
|
|
352
|
-
await this.httpProvider.put({
|
|
353
|
-
url,
|
|
354
|
-
data: {
|
|
355
|
-
type: "password",
|
|
356
|
-
value: password,
|
|
357
|
-
temporary
|
|
358
|
-
},
|
|
359
|
-
config: {
|
|
360
|
-
headers: {
|
|
361
|
-
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
362
|
-
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
this.log("info", `${method} - Success`, method, {
|
|
367
|
-
userId
|
|
368
|
-
});
|
|
369
|
-
} catch (err) {
|
|
370
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
371
|
-
this.log("error", `${method} - Failed`, method, {
|
|
372
|
-
userId,
|
|
373
|
-
statusCode,
|
|
374
|
-
errorCode
|
|
375
|
-
});
|
|
376
|
-
throw new KeycloakAdminError({
|
|
377
|
-
message: "Keycloak reset password failed",
|
|
378
|
-
statusCode: statusCode ?? 502,
|
|
379
|
-
code: KEYCLOAK_ADMIN_ERROR_CODES.RESET_PASSWORD_ERROR,
|
|
380
|
-
context: {
|
|
381
|
-
url,
|
|
382
|
-
method: "PUT",
|
|
383
|
-
userId,
|
|
384
|
-
details,
|
|
385
|
-
errorCode
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
207
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
208
|
+
return id;
|
|
209
|
+
}
|
|
210
|
+
function createKeycloakAdminClient({
|
|
211
|
+
config: rawConfig,
|
|
212
|
+
fetch: injectedFetch,
|
|
213
|
+
now = Date.now
|
|
214
|
+
}) {
|
|
215
|
+
const config = parseKeycloakAdminConfig(rawConfig);
|
|
216
|
+
const endpoints = buildKeycloakAdminEndpoints(config);
|
|
217
|
+
const redactor = createSecretRedactor(config.clientSecret);
|
|
218
|
+
const fetchImpl = injectedFetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
219
|
+
const tokenProvider = createKeycloakTokenProvider({
|
|
220
|
+
config,
|
|
221
|
+
endpoint: endpoints.token,
|
|
222
|
+
fetch: fetchImpl,
|
|
223
|
+
now,
|
|
224
|
+
redactor
|
|
225
|
+
});
|
|
226
|
+
async function adminRequest({ body, method, secrets = [], url }) {
|
|
227
|
+
const accessToken = await tokenProvider.getAccessToken();
|
|
228
|
+
const response = await fetchImpl(url, {
|
|
229
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
230
|
+
headers: {
|
|
231
|
+
authorization: `Bearer ${accessToken}`,
|
|
232
|
+
...body === void 0 ? {} : { "content-type": KEYCLOAK_ADMIN_JSON_CONTENT_TYPE }
|
|
233
|
+
},
|
|
234
|
+
method
|
|
396
235
|
});
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
this.log("info", `${method} - Success`, method, {
|
|
412
|
-
userId,
|
|
413
|
-
enabled
|
|
414
|
-
});
|
|
415
|
-
} catch (err) {
|
|
416
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
417
|
-
this.log("error", `${method} - Failed`, method, {
|
|
418
|
-
userId,
|
|
419
|
-
enabled,
|
|
420
|
-
statusCode,
|
|
421
|
-
errorCode
|
|
422
|
-
});
|
|
423
|
-
throw new KeycloakAdminError({
|
|
424
|
-
message: "Keycloak toggle user enabled failed",
|
|
425
|
-
statusCode: statusCode ?? 502,
|
|
426
|
-
code: KEYCLOAK_ADMIN_ERROR_CODES.TOGGLE_ENABLED_ERROR,
|
|
427
|
-
context: {
|
|
428
|
-
url,
|
|
429
|
-
method: "PUT",
|
|
430
|
-
userId,
|
|
431
|
-
enabled,
|
|
432
|
-
details,
|
|
433
|
-
errorCode
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
async deleteUser(params) {
|
|
439
|
-
const method = "deleteUser";
|
|
440
|
-
const { userId, adminToken } = params;
|
|
441
|
-
this.log("info", `${method} - Start`, method, {
|
|
442
|
-
userId
|
|
236
|
+
if (response.ok) return response;
|
|
237
|
+
const callRedactor = redactor.with(accessToken, ...secrets);
|
|
238
|
+
throw new KeycloakAdminError({
|
|
239
|
+
code: errorCodeOf(response.status),
|
|
240
|
+
context: callRedactor.value({
|
|
241
|
+
detail: await readKeycloakDetail(response),
|
|
242
|
+
method,
|
|
243
|
+
realm: config.realm
|
|
244
|
+
}),
|
|
245
|
+
message: callRedactor.text(`Keycloak admin request failed with status ${response.status}`),
|
|
246
|
+
status: response.status
|
|
443
247
|
});
|
|
444
|
-
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm, userId);
|
|
445
|
-
try {
|
|
446
|
-
await this.httpProvider.delete({
|
|
447
|
-
url,
|
|
448
|
-
config: {
|
|
449
|
-
headers: {
|
|
450
|
-
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
this.log("info", `${method} - Success`, method, {
|
|
455
|
-
userId
|
|
456
|
-
});
|
|
457
|
-
} catch (err) {
|
|
458
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
459
|
-
this.log("error", `${method} - Failed`, method, {
|
|
460
|
-
userId,
|
|
461
|
-
statusCode,
|
|
462
|
-
errorCode
|
|
463
|
-
});
|
|
464
|
-
throw new KeycloakAdminError({
|
|
465
|
-
message: "Keycloak delete user failed",
|
|
466
|
-
statusCode: statusCode ?? 502,
|
|
467
|
-
code: KEYCLOAK_ADMIN_ERROR_CODES.DELETE_USER_ERROR,
|
|
468
|
-
context: {
|
|
469
|
-
url,
|
|
470
|
-
method: "DELETE",
|
|
471
|
-
userId,
|
|
472
|
-
details,
|
|
473
|
-
errorCode
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
248
|
}
|
|
478
|
-
async
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
249
|
+
async function setPassword({ password, temporary, userId }) {
|
|
250
|
+
await adminRequest({
|
|
251
|
+
body: { temporary, type: "password", value: password },
|
|
252
|
+
method: "PUT",
|
|
253
|
+
secrets: [password],
|
|
254
|
+
url: endpoints.userPassword(userId)
|
|
484
255
|
});
|
|
485
|
-
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm, userId);
|
|
486
|
-
try {
|
|
487
|
-
await this.httpProvider.put({
|
|
488
|
-
url,
|
|
489
|
-
data: {
|
|
490
|
-
attributes
|
|
491
|
-
},
|
|
492
|
-
config: {
|
|
493
|
-
headers: {
|
|
494
|
-
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
495
|
-
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
this.log("info", `${method} - Success`, method, {
|
|
500
|
-
userId
|
|
501
|
-
});
|
|
502
|
-
} catch (err) {
|
|
503
|
-
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
504
|
-
this.log("error", `${method} - Failed`, method, {
|
|
505
|
-
userId,
|
|
506
|
-
statusCode,
|
|
507
|
-
errorCode
|
|
508
|
-
});
|
|
509
|
-
throw new KeycloakAdminError({
|
|
510
|
-
message: "Keycloak update user attributes failed",
|
|
511
|
-
statusCode: statusCode ?? 502,
|
|
512
|
-
code: KEYCLOAK_ADMIN_ERROR_CODES.UPDATE_ATTRIBUTES_ERROR,
|
|
513
|
-
context: {
|
|
514
|
-
url,
|
|
515
|
-
method: "PUT",
|
|
516
|
-
userId,
|
|
517
|
-
details,
|
|
518
|
-
errorCode
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
256
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
257
|
+
return {
|
|
258
|
+
async createUser({
|
|
259
|
+
attributes,
|
|
260
|
+
email,
|
|
261
|
+
emailVerified,
|
|
262
|
+
enabled,
|
|
263
|
+
firstName,
|
|
264
|
+
lastName,
|
|
265
|
+
password,
|
|
266
|
+
username
|
|
267
|
+
}) {
|
|
268
|
+
const response = await adminRequest({
|
|
269
|
+
body: {
|
|
270
|
+
...attributes === void 0 ? {} : { attributes: normalizeAttributes(attributes) },
|
|
271
|
+
...password === void 0 ? {} : {
|
|
272
|
+
credentials: [{ temporary: password.temporary, type: "password", value: password.value }]
|
|
273
|
+
},
|
|
274
|
+
email,
|
|
275
|
+
emailVerified: emailVerified ?? false,
|
|
276
|
+
enabled: enabled ?? true,
|
|
277
|
+
firstName,
|
|
278
|
+
lastName,
|
|
279
|
+
username
|
|
280
|
+
},
|
|
281
|
+
method: "POST",
|
|
282
|
+
secrets: [password?.value],
|
|
283
|
+
url: endpoints.users
|
|
550
284
|
});
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
285
|
+
return { id: userIdFromLocation(response) };
|
|
286
|
+
},
|
|
287
|
+
async deleteUser({ userId }) {
|
|
288
|
+
await adminRequest({ method: "DELETE", url: endpoints.user(userId) });
|
|
289
|
+
},
|
|
290
|
+
async findUserByEmail({ email }) {
|
|
291
|
+
const query = new URLSearchParams({ email, exact: "true" });
|
|
292
|
+
const response = await adminRequest({ method: "GET", url: `${endpoints.users}?${query}` });
|
|
293
|
+
const found = await response.json();
|
|
294
|
+
return found.at(0);
|
|
295
|
+
},
|
|
296
|
+
/**
|
|
297
|
+
* Pede um a mais que o limite para saber se há próxima página sem uma segunda chamada — o
|
|
298
|
+
* Keycloak não devolve total, e contar o realm inteiro só para desenhar um botão é caro.
|
|
299
|
+
*/
|
|
300
|
+
async listUsers({ first = 0, limit = 100, search } = {}) {
|
|
301
|
+
const query = new URLSearchParams({ first: String(first), max: String(limit + 1) });
|
|
302
|
+
if (search !== void 0 && search !== "") query.set("search", search);
|
|
303
|
+
const response = await adminRequest({ method: "GET", url: `${endpoints.users}?${query}` });
|
|
304
|
+
const found = await response.json();
|
|
305
|
+
return { hasMore: found.length > limit, users: found.slice(0, limit) };
|
|
306
|
+
},
|
|
307
|
+
async setEnabled({ enabled, userId }) {
|
|
308
|
+
await adminRequest({ body: { enabled }, method: "PUT", url: endpoints.user(userId) });
|
|
309
|
+
},
|
|
310
|
+
setPassword,
|
|
311
|
+
async setTemporaryPassword({ password, userId }) {
|
|
312
|
+
await setPassword({ password, temporary: true, userId });
|
|
313
|
+
},
|
|
314
|
+
async updateAttributes({ attributes, userId }) {
|
|
315
|
+
await adminRequest({
|
|
316
|
+
body: { attributes: normalizeAttributes(attributes) },
|
|
317
|
+
method: "PUT",
|
|
318
|
+
url: endpoints.user(userId)
|
|
562
319
|
});
|
|
320
|
+
},
|
|
321
|
+
async updateUser({ user, userId }) {
|
|
322
|
+
await adminRequest({ body: user, method: "PUT", url: endpoints.user(userId) });
|
|
563
323
|
}
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
__name(_KeycloakAdminClient, "KeycloakAdminClient");
|
|
567
|
-
var KeycloakAdminClient = _KeycloakAdminClient;
|
|
568
|
-
KeycloakAdminClient = _ts_decorate([
|
|
569
|
-
(0, import_common.Injectable)(),
|
|
570
|
-
_ts_param(0, (0, import_common.Inject)(KEYCLOAK_ADMIN_CONFIG)),
|
|
571
|
-
_ts_param(1, (0, import_common.Inject)(import_http_client.HTTP_PROVIDER)),
|
|
572
|
-
_ts_param(2, (0, import_common.Optional)()),
|
|
573
|
-
_ts_param(2, (0, import_common.Inject)(import_logger.LOGGER_PROVIDER)),
|
|
574
|
-
_ts_metadata("design:type", Function),
|
|
575
|
-
_ts_metadata("design:paramtypes", [
|
|
576
|
-
typeof KeycloakAdminConfig === "undefined" ? Object : KeycloakAdminConfig,
|
|
577
|
-
typeof HttpProviderInterface === "undefined" ? Object : HttpProviderInterface,
|
|
578
|
-
typeof LoggerProviderInterface === "undefined" ? Object : LoggerProviderInterface
|
|
579
|
-
])
|
|
580
|
-
], KeycloakAdminClient);
|
|
581
|
-
|
|
582
|
-
// src/utils/validate-config.ts
|
|
583
|
-
function validateKeycloakAdminConfig(config) {
|
|
584
|
-
const issues = [];
|
|
585
|
-
if (!config) {
|
|
586
|
-
throw new KeycloakAdminError({
|
|
587
|
-
message: "KeycloakAdminConfig is required. Provide an object with baseUrl, realm, adminUser and adminPassword.",
|
|
588
|
-
statusCode: 400,
|
|
589
|
-
code: "KEYCLOAK_ADMIN_CONFIG_MISSING"
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
if (typeof config.baseUrl !== "string" || config.baseUrl.trim() === "") {
|
|
593
|
-
issues.push({
|
|
594
|
-
field: "baseUrl",
|
|
595
|
-
message: "baseUrl is required and must be a non-empty string (e.g. 'http://localhost:8081')"
|
|
596
|
-
});
|
|
597
|
-
} else if (!config.baseUrl.startsWith("http://") && !config.baseUrl.startsWith("https://")) {
|
|
598
|
-
issues.push({
|
|
599
|
-
field: "baseUrl",
|
|
600
|
-
message: `baseUrl must start with 'http://' or 'https://'. Received: '${config.baseUrl}'`
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
if (typeof config.realm !== "string" || config.realm.trim() === "") {
|
|
604
|
-
issues.push({
|
|
605
|
-
field: "realm",
|
|
606
|
-
message: "realm is required and must be a non-empty string (e.g. 'BACKEND')"
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
if (typeof config.adminUser !== "string" || config.adminUser.trim() === "") {
|
|
610
|
-
issues.push({
|
|
611
|
-
field: "adminUser",
|
|
612
|
-
message: "adminUser is required and must be a non-empty string"
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
if (typeof config.adminPassword !== "string" || config.adminPassword.trim() === "") {
|
|
616
|
-
issues.push({
|
|
617
|
-
field: "adminPassword",
|
|
618
|
-
message: "adminPassword is required and must be a non-empty string"
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
if (issues.length > 0) {
|
|
622
|
-
const summary = issues.map((i) => ` - ${i.field}: ${i.message}`).join("\n");
|
|
623
|
-
throw new KeycloakAdminError({
|
|
624
|
-
message: `KeycloakAdminConfig validation failed:
|
|
625
|
-
${summary}`,
|
|
626
|
-
statusCode: 400,
|
|
627
|
-
code: "KEYCLOAK_ADMIN_CONFIG_INVALID",
|
|
628
|
-
context: {
|
|
629
|
-
issues
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
__name(validateKeycloakAdminConfig, "validateKeycloakAdminConfig");
|
|
635
|
-
|
|
636
|
-
// src/keycloak-admin.module.ts
|
|
637
|
-
function _ts_decorate2(decorators, target, key, desc) {
|
|
638
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
639
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
640
|
-
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;
|
|
641
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
324
|
+
};
|
|
642
325
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
validateKeycloakAdminConfig(config);
|
|
647
|
-
return {
|
|
648
|
-
module: _KeycloakAdminModule,
|
|
649
|
-
global: true,
|
|
650
|
-
imports: [
|
|
651
|
-
import_http_client2.HttpModule.forRoot({
|
|
652
|
-
baseURL: config.baseUrl,
|
|
653
|
-
timeout: 5e3
|
|
654
|
-
}, {
|
|
655
|
-
logging: {
|
|
656
|
-
enabled: true,
|
|
657
|
-
includeBody: true,
|
|
658
|
-
context: "KeycloakAdminHttpClient",
|
|
659
|
-
environments: [
|
|
660
|
-
"development",
|
|
661
|
-
"test"
|
|
662
|
-
]
|
|
663
|
-
}
|
|
664
|
-
})
|
|
665
|
-
],
|
|
666
|
-
providers: [
|
|
667
|
-
{
|
|
668
|
-
provide: KEYCLOAK_ADMIN_CONFIG,
|
|
669
|
-
useValue: config
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
provide: KEYCLOAK_ADMIN_CLIENT,
|
|
673
|
-
useClass: KeycloakAdminClient
|
|
674
|
-
},
|
|
675
|
-
{
|
|
676
|
-
provide: KEYCLOAK_ADMIN_PROVIDER,
|
|
677
|
-
useExisting: KEYCLOAK_ADMIN_CLIENT
|
|
678
|
-
}
|
|
679
|
-
],
|
|
680
|
-
exports: [
|
|
681
|
-
KEYCLOAK_ADMIN_CLIENT,
|
|
682
|
-
KEYCLOAK_ADMIN_PROVIDER,
|
|
683
|
-
KEYCLOAK_ADMIN_CONFIG
|
|
684
|
-
]
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
__name(_KeycloakAdminModule, "KeycloakAdminModule");
|
|
689
|
-
var KeycloakAdminModule = _KeycloakAdminModule;
|
|
690
|
-
KeycloakAdminModule = _ts_decorate2([
|
|
691
|
-
(0, import_common2.Module)({})
|
|
692
|
-
], KeycloakAdminModule);
|
|
693
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
694
|
-
0 && (module.exports = {
|
|
695
|
-
KEYCLOAK_ADMIN_CLIENT,
|
|
696
|
-
KEYCLOAK_ADMIN_CONFIG,
|
|
697
|
-
KEYCLOAK_ADMIN_PROVIDER,
|
|
698
|
-
KeycloakAdminClient,
|
|
326
|
+
export {
|
|
327
|
+
KEYCLOAK_ADMIN_ERROR_CODE,
|
|
328
|
+
KEYCLOAK_ADMIN_TOKEN_RENEWAL_SKEW_MS,
|
|
699
329
|
KeycloakAdminError,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
330
|
+
buildKeycloakAdminEndpoints,
|
|
331
|
+
createKeycloakAdminClient,
|
|
332
|
+
isKeycloakAdminError,
|
|
333
|
+
keycloakAdminConfigSchema,
|
|
334
|
+
parseKeycloakAdminConfig
|
|
335
|
+
};
|