@adatechnology/keycloak-admin 0.1.1 → 0.1.3
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 +91 -0
- package/dist/index.js +601 -0
- package/package.json +2 -2
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { DynamicModule } from "@nestjs/common";
|
|
2
|
+
|
|
3
|
+
export interface KeycloakAdminConfig {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
realm: string;
|
|
6
|
+
adminUser: string;
|
|
7
|
+
adminPassword: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface GetAdminTokenResult {
|
|
11
|
+
accessToken: string;
|
|
12
|
+
expiresIn: number;
|
|
13
|
+
tokenType: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UpdateUserParams {
|
|
17
|
+
userId: string;
|
|
18
|
+
userData: Record<string, unknown>;
|
|
19
|
+
adminToken: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ResetPasswordParams {
|
|
23
|
+
userId: string;
|
|
24
|
+
password: string;
|
|
25
|
+
temporary: boolean;
|
|
26
|
+
adminToken: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ToggleUserEnabledParams {
|
|
30
|
+
userId: string;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
adminToken: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DeleteUserParams {
|
|
36
|
+
userId: string;
|
|
37
|
+
adminToken: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface UpdateUserAttributesParams {
|
|
41
|
+
userId: string;
|
|
42
|
+
attributes: Record<string, string | string[]>;
|
|
43
|
+
adminToken: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface SendVerifyEmailParams {
|
|
47
|
+
userId: string;
|
|
48
|
+
adminToken: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface KeycloakAdminClientInterface {
|
|
52
|
+
getAdminToken(): Promise<GetAdminTokenResult>;
|
|
53
|
+
updateUser(params: UpdateUserParams): Promise<void>;
|
|
54
|
+
resetPassword(params: ResetPasswordParams): Promise<void>;
|
|
55
|
+
toggleUserEnabled(params: ToggleUserEnabledParams): Promise<void>;
|
|
56
|
+
deleteUser(params: DeleteUserParams): Promise<void>;
|
|
57
|
+
updateUserAttributes(params: UpdateUserAttributesParams): Promise<void>;
|
|
58
|
+
sendVerifyEmail(params: SendVerifyEmailParams): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class KeycloakAdminError extends Error {
|
|
62
|
+
readonly statusCode?: number;
|
|
63
|
+
readonly code?: string;
|
|
64
|
+
readonly context?: Record<string, unknown>;
|
|
65
|
+
constructor(params: {
|
|
66
|
+
message: string;
|
|
67
|
+
statusCode?: number;
|
|
68
|
+
code?: string;
|
|
69
|
+
context?: Record<string, unknown>;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class KeycloakAdminClient implements KeycloakAdminClientInterface {
|
|
74
|
+
getAdminToken(): Promise<GetAdminTokenResult>;
|
|
75
|
+
updateUser(params: UpdateUserParams): Promise<void>;
|
|
76
|
+
resetPassword(params: ResetPasswordParams): Promise<void>;
|
|
77
|
+
toggleUserEnabled(params: ToggleUserEnabledParams): Promise<void>;
|
|
78
|
+
deleteUser(params: DeleteUserParams): Promise<void>;
|
|
79
|
+
updateUserAttributes(params: UpdateUserAttributesParams): Promise<void>;
|
|
80
|
+
sendVerifyEmail(params: SendVerifyEmailParams): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class KeycloakAdminModule {
|
|
84
|
+
static forRoot(config: KeycloakAdminConfig): DynamicModule;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function validateKeycloakAdminConfig(config: KeycloakAdminConfig): void;
|
|
88
|
+
|
|
89
|
+
export const KEYCLOAK_ADMIN_CLIENT = "KEYCLOAK_ADMIN_CLIENT";
|
|
90
|
+
export const KEYCLOAK_ADMIN_CONFIG = "KEYCLOAK_ADMIN_CONFIG";
|
|
91
|
+
export const KEYCLOAK_ADMIN_PROVIDER = "KEYCLOAK_ADMIN_PROVIDER";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
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");
|
|
46
|
+
|
|
47
|
+
// src/errors/keycloak-admin.error.ts
|
|
48
|
+
var _KeycloakAdminError = class _KeycloakAdminError extends Error {
|
|
49
|
+
statusCode;
|
|
50
|
+
code;
|
|
51
|
+
context;
|
|
52
|
+
constructor(params) {
|
|
53
|
+
super(params.message);
|
|
54
|
+
this.name = "KeycloakAdminError";
|
|
55
|
+
this.statusCode = params.statusCode;
|
|
56
|
+
this.code = params.code;
|
|
57
|
+
this.context = params.context;
|
|
58
|
+
Object.setPrototypeOf(this, _KeycloakAdminError.prototype);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
__name(_KeycloakAdminError, "KeycloakAdminError");
|
|
62
|
+
var KeycloakAdminError = _KeycloakAdminError;
|
|
63
|
+
|
|
64
|
+
// src/constants/keycloak-admin.constants.ts
|
|
65
|
+
var KEYCLOAK_ADMIN_LIB_NAME = "@adatechnology/keycloak-admin";
|
|
66
|
+
var KEYCLOAK_ADMIN_LIB_VERSION = "0.1.2";
|
|
67
|
+
var KEYCLOAK_ADMIN_GRANT_TYPE_PASSWORD = "password";
|
|
68
|
+
var KEYCLOAK_ADMIN_CLIENT_ID_ADMIN_CLI = "admin-cli";
|
|
69
|
+
var KEYCLOAK_ADMIN_CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
|
|
70
|
+
var KEYCLOAK_ADMIN_CONTENT_TYPE_JSON = "application/json";
|
|
71
|
+
var KEYCLOAK_ADMIN_AUTHORIZATION_HEADER = "Authorization";
|
|
72
|
+
var KEYCLOAK_ADMIN_BEARER_PREFIX = "Bearer ";
|
|
73
|
+
var KEYCLOAK_ADMIN_ENDPOINTS = {
|
|
74
|
+
MASTER_TOKEN: "/realms/master/protocol/openid-connect/token",
|
|
75
|
+
ADMIN_USERS: /* @__PURE__ */ __name((baseUrl, realm, userId) => `${baseUrl}/admin/realms/${realm}/users/${userId}`, "ADMIN_USERS"),
|
|
76
|
+
ADMIN_RESET_PASSWORD: /* @__PURE__ */ __name((baseUrl, realm, userId) => `${baseUrl}/admin/realms/${realm}/users/${userId}/reset-password`, "ADMIN_RESET_PASSWORD"),
|
|
77
|
+
ADMIN_SEND_VERIFY_EMAIL: /* @__PURE__ */ __name((baseUrl, realm, userId) => `${baseUrl}/admin/realms/${realm}/users/${userId}/send-verify-email`, "ADMIN_SEND_VERIFY_EMAIL")
|
|
78
|
+
};
|
|
79
|
+
var KEYCLOAK_ADMIN_ERROR_CODES = {
|
|
80
|
+
ADMIN_TOKEN_ERROR: "KEYCLOAK_ADMIN_TOKEN_ERROR",
|
|
81
|
+
UPDATE_USER_ERROR: "KEYCLOAK_UPDATE_USER_ERROR",
|
|
82
|
+
RESET_PASSWORD_ERROR: "KEYCLOAK_RESET_PASSWORD_ERROR",
|
|
83
|
+
TOGGLE_ENABLED_ERROR: "KEYCLOAK_TOGGLE_ENABLED_ERROR",
|
|
84
|
+
DELETE_USER_ERROR: "KEYCLOAK_DELETE_USER_ERROR",
|
|
85
|
+
UPDATE_ATTRIBUTES_ERROR: "KEYCLOAK_UPDATE_ATTRIBUTES_ERROR",
|
|
86
|
+
SEND_VERIFY_EMAIL_ERROR: "KEYCLOAK_SEND_VERIFY_EMAIL_ERROR"
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/utils/extract-http-error.ts
|
|
90
|
+
function extractHttpError(err) {
|
|
91
|
+
const unknownErr = err;
|
|
92
|
+
const response = unknownErr == null ? void 0 : unknownErr.response;
|
|
93
|
+
const responseData = response == null ? void 0 : response.data;
|
|
94
|
+
let statusCode = void 0;
|
|
95
|
+
if (typeof (unknownErr == null ? void 0 : unknownErr.status) === "number") {
|
|
96
|
+
statusCode = unknownErr.status;
|
|
97
|
+
} else if (typeof (response == null ? void 0 : response.status) === "number") {
|
|
98
|
+
statusCode = response.status;
|
|
99
|
+
}
|
|
100
|
+
const details = responseData ?? (unknownErr == null ? void 0 : unknownErr.message);
|
|
101
|
+
const rawError = (responseData == null ? void 0 : responseData.error) ?? (responseData == null ? void 0 : responseData.errorMessage);
|
|
102
|
+
const errorCode = typeof rawError === "string" ? rawError : typeof (unknownErr == null ? void 0 : unknownErr.code) === "string" ? unknownErr.code : void 0;
|
|
103
|
+
return {
|
|
104
|
+
statusCode,
|
|
105
|
+
details,
|
|
106
|
+
errorCode
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
__name(extractHttpError, "extractHttpError");
|
|
110
|
+
|
|
111
|
+
// src/keycloak-admin.client.ts
|
|
112
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
113
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
114
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
115
|
+
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;
|
|
116
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
117
|
+
}
|
|
118
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
119
|
+
function _ts_metadata(k, v) {
|
|
120
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
121
|
+
}
|
|
122
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
123
|
+
function _ts_param(paramIndex, decorator) {
|
|
124
|
+
return function(target, key) {
|
|
125
|
+
decorator(target, key, paramIndex);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
__name(_ts_param, "_ts_param");
|
|
129
|
+
var _KeycloakAdminClient = class _KeycloakAdminClient {
|
|
130
|
+
config;
|
|
131
|
+
httpProvider;
|
|
132
|
+
logger;
|
|
133
|
+
className = this.constructor.name;
|
|
134
|
+
constructor(config, httpProvider, logger) {
|
|
135
|
+
this.config = config;
|
|
136
|
+
this.httpProvider = httpProvider;
|
|
137
|
+
this.logger = logger;
|
|
138
|
+
}
|
|
139
|
+
log(level, message, libMethod, meta) {
|
|
140
|
+
if (!this.logger) return;
|
|
141
|
+
const payload = {
|
|
142
|
+
message,
|
|
143
|
+
context: this.className,
|
|
144
|
+
lib: KEYCLOAK_ADMIN_LIB_NAME,
|
|
145
|
+
libVersion: KEYCLOAK_ADMIN_LIB_VERSION,
|
|
146
|
+
libMethod,
|
|
147
|
+
meta
|
|
148
|
+
};
|
|
149
|
+
if (level === "info") this.logger.info(payload);
|
|
150
|
+
else if (level === "warn") this.logger.warn(payload);
|
|
151
|
+
else if (level === "error") this.logger.error(payload);
|
|
152
|
+
}
|
|
153
|
+
async getAdminToken() {
|
|
154
|
+
const method = "getAdminToken";
|
|
155
|
+
this.log("info", `${method} - Start`, method, {
|
|
156
|
+
realm: this.config.realm
|
|
157
|
+
});
|
|
158
|
+
const url = `${this.config.baseUrl}${KEYCLOAK_ADMIN_ENDPOINTS.MASTER_TOKEN}`;
|
|
159
|
+
const body = new URLSearchParams();
|
|
160
|
+
body.append("grant_type", KEYCLOAK_ADMIN_GRANT_TYPE_PASSWORD);
|
|
161
|
+
body.append("client_id", KEYCLOAK_ADMIN_CLIENT_ID_ADMIN_CLI);
|
|
162
|
+
body.append("username", this.config.adminUser);
|
|
163
|
+
body.append("password", this.config.adminPassword);
|
|
164
|
+
try {
|
|
165
|
+
const response = await this.httpProvider.post({
|
|
166
|
+
url,
|
|
167
|
+
data: body,
|
|
168
|
+
config: {
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_FORM
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
const result = {
|
|
175
|
+
accessToken: response.data.access_token,
|
|
176
|
+
expiresIn: response.data.expires_in,
|
|
177
|
+
tokenType: response.data.token_type
|
|
178
|
+
};
|
|
179
|
+
this.log("info", `${method} - Success`, method);
|
|
180
|
+
return result;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
183
|
+
this.log("error", `${method} - Failed`, method, {
|
|
184
|
+
statusCode,
|
|
185
|
+
errorCode
|
|
186
|
+
});
|
|
187
|
+
throw new KeycloakAdminError({
|
|
188
|
+
message: "Keycloak admin token request failed",
|
|
189
|
+
statusCode: statusCode ?? 502,
|
|
190
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.ADMIN_TOKEN_ERROR,
|
|
191
|
+
context: {
|
|
192
|
+
url,
|
|
193
|
+
method: "POST",
|
|
194
|
+
details,
|
|
195
|
+
errorCode
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async updateUser(params) {
|
|
201
|
+
const method = "updateUser";
|
|
202
|
+
const { userId, userData, adminToken } = params;
|
|
203
|
+
this.log("info", `${method} - Start`, method, {
|
|
204
|
+
userId
|
|
205
|
+
});
|
|
206
|
+
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm, userId);
|
|
207
|
+
try {
|
|
208
|
+
await this.httpProvider.put({
|
|
209
|
+
url,
|
|
210
|
+
data: userData,
|
|
211
|
+
config: {
|
|
212
|
+
headers: {
|
|
213
|
+
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
214
|
+
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
this.log("info", `${method} - Success`, method, {
|
|
219
|
+
userId
|
|
220
|
+
});
|
|
221
|
+
} catch (err) {
|
|
222
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
223
|
+
this.log("error", `${method} - Failed`, method, {
|
|
224
|
+
userId,
|
|
225
|
+
statusCode,
|
|
226
|
+
errorCode
|
|
227
|
+
});
|
|
228
|
+
throw new KeycloakAdminError({
|
|
229
|
+
message: "Keycloak update user failed",
|
|
230
|
+
statusCode: statusCode ?? 502,
|
|
231
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.UPDATE_USER_ERROR,
|
|
232
|
+
context: {
|
|
233
|
+
url,
|
|
234
|
+
method: "PUT",
|
|
235
|
+
userId,
|
|
236
|
+
details,
|
|
237
|
+
errorCode
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async resetPassword(params) {
|
|
243
|
+
const method = "resetPassword";
|
|
244
|
+
const { userId, password, temporary, adminToken } = params;
|
|
245
|
+
this.log("info", `${method} - Start`, method, {
|
|
246
|
+
userId,
|
|
247
|
+
temporary
|
|
248
|
+
});
|
|
249
|
+
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_RESET_PASSWORD(this.config.baseUrl, this.config.realm, userId);
|
|
250
|
+
try {
|
|
251
|
+
await this.httpProvider.put({
|
|
252
|
+
url,
|
|
253
|
+
data: {
|
|
254
|
+
type: "password",
|
|
255
|
+
value: password,
|
|
256
|
+
temporary
|
|
257
|
+
},
|
|
258
|
+
config: {
|
|
259
|
+
headers: {
|
|
260
|
+
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
261
|
+
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
this.log("info", `${method} - Success`, method, {
|
|
266
|
+
userId
|
|
267
|
+
});
|
|
268
|
+
} catch (err) {
|
|
269
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
270
|
+
this.log("error", `${method} - Failed`, method, {
|
|
271
|
+
userId,
|
|
272
|
+
statusCode,
|
|
273
|
+
errorCode
|
|
274
|
+
});
|
|
275
|
+
throw new KeycloakAdminError({
|
|
276
|
+
message: "Keycloak reset password failed",
|
|
277
|
+
statusCode: statusCode ?? 502,
|
|
278
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.RESET_PASSWORD_ERROR,
|
|
279
|
+
context: {
|
|
280
|
+
url,
|
|
281
|
+
method: "PUT",
|
|
282
|
+
userId,
|
|
283
|
+
details,
|
|
284
|
+
errorCode
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async toggleUserEnabled(params) {
|
|
290
|
+
const method = "toggleUserEnabled";
|
|
291
|
+
const { userId, enabled, adminToken } = params;
|
|
292
|
+
this.log("info", `${method} - Start`, method, {
|
|
293
|
+
userId,
|
|
294
|
+
enabled
|
|
295
|
+
});
|
|
296
|
+
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm, userId);
|
|
297
|
+
try {
|
|
298
|
+
await this.httpProvider.put({
|
|
299
|
+
url,
|
|
300
|
+
data: {
|
|
301
|
+
enabled
|
|
302
|
+
},
|
|
303
|
+
config: {
|
|
304
|
+
headers: {
|
|
305
|
+
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
306
|
+
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
this.log("info", `${method} - Success`, method, {
|
|
311
|
+
userId,
|
|
312
|
+
enabled
|
|
313
|
+
});
|
|
314
|
+
} catch (err) {
|
|
315
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
316
|
+
this.log("error", `${method} - Failed`, method, {
|
|
317
|
+
userId,
|
|
318
|
+
enabled,
|
|
319
|
+
statusCode,
|
|
320
|
+
errorCode
|
|
321
|
+
});
|
|
322
|
+
throw new KeycloakAdminError({
|
|
323
|
+
message: "Keycloak toggle user enabled failed",
|
|
324
|
+
statusCode: statusCode ?? 502,
|
|
325
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.TOGGLE_ENABLED_ERROR,
|
|
326
|
+
context: {
|
|
327
|
+
url,
|
|
328
|
+
method: "PUT",
|
|
329
|
+
userId,
|
|
330
|
+
enabled,
|
|
331
|
+
details,
|
|
332
|
+
errorCode
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async deleteUser(params) {
|
|
338
|
+
const method = "deleteUser";
|
|
339
|
+
const { userId, adminToken } = params;
|
|
340
|
+
this.log("info", `${method} - Start`, method, {
|
|
341
|
+
userId
|
|
342
|
+
});
|
|
343
|
+
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm, userId);
|
|
344
|
+
try {
|
|
345
|
+
await this.httpProvider.delete({
|
|
346
|
+
url,
|
|
347
|
+
config: {
|
|
348
|
+
headers: {
|
|
349
|
+
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
this.log("info", `${method} - Success`, method, {
|
|
354
|
+
userId
|
|
355
|
+
});
|
|
356
|
+
} catch (err) {
|
|
357
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
358
|
+
this.log("error", `${method} - Failed`, method, {
|
|
359
|
+
userId,
|
|
360
|
+
statusCode,
|
|
361
|
+
errorCode
|
|
362
|
+
});
|
|
363
|
+
throw new KeycloakAdminError({
|
|
364
|
+
message: "Keycloak delete user failed",
|
|
365
|
+
statusCode: statusCode ?? 502,
|
|
366
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.DELETE_USER_ERROR,
|
|
367
|
+
context: {
|
|
368
|
+
url,
|
|
369
|
+
method: "DELETE",
|
|
370
|
+
userId,
|
|
371
|
+
details,
|
|
372
|
+
errorCode
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async updateUserAttributes(params) {
|
|
378
|
+
const method = "updateUserAttributes";
|
|
379
|
+
const { userId, attributes, adminToken } = params;
|
|
380
|
+
this.log("info", `${method} - Start`, method, {
|
|
381
|
+
userId,
|
|
382
|
+
attributeKeys: Object.keys(attributes)
|
|
383
|
+
});
|
|
384
|
+
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_USERS(this.config.baseUrl, this.config.realm, userId);
|
|
385
|
+
try {
|
|
386
|
+
await this.httpProvider.put({
|
|
387
|
+
url,
|
|
388
|
+
data: {
|
|
389
|
+
attributes
|
|
390
|
+
},
|
|
391
|
+
config: {
|
|
392
|
+
headers: {
|
|
393
|
+
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
394
|
+
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
this.log("info", `${method} - Success`, method, {
|
|
399
|
+
userId
|
|
400
|
+
});
|
|
401
|
+
} catch (err) {
|
|
402
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
403
|
+
this.log("error", `${method} - Failed`, method, {
|
|
404
|
+
userId,
|
|
405
|
+
statusCode,
|
|
406
|
+
errorCode
|
|
407
|
+
});
|
|
408
|
+
throw new KeycloakAdminError({
|
|
409
|
+
message: "Keycloak update user attributes failed",
|
|
410
|
+
statusCode: statusCode ?? 502,
|
|
411
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.UPDATE_ATTRIBUTES_ERROR,
|
|
412
|
+
context: {
|
|
413
|
+
url,
|
|
414
|
+
method: "PUT",
|
|
415
|
+
userId,
|
|
416
|
+
details,
|
|
417
|
+
errorCode
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async sendVerifyEmail(params) {
|
|
423
|
+
const method = "sendVerifyEmail";
|
|
424
|
+
const { userId, adminToken } = params;
|
|
425
|
+
this.log("info", `${method} - Start`, method, {
|
|
426
|
+
userId
|
|
427
|
+
});
|
|
428
|
+
const url = KEYCLOAK_ADMIN_ENDPOINTS.ADMIN_SEND_VERIFY_EMAIL(this.config.baseUrl, this.config.realm, userId);
|
|
429
|
+
try {
|
|
430
|
+
await this.httpProvider.put({
|
|
431
|
+
url,
|
|
432
|
+
data: {},
|
|
433
|
+
config: {
|
|
434
|
+
headers: {
|
|
435
|
+
[KEYCLOAK_ADMIN_AUTHORIZATION_HEADER]: `${KEYCLOAK_ADMIN_BEARER_PREFIX}${adminToken}`,
|
|
436
|
+
"Content-Type": KEYCLOAK_ADMIN_CONTENT_TYPE_JSON
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
this.log("info", `${method} - Success`, method, {
|
|
441
|
+
userId
|
|
442
|
+
});
|
|
443
|
+
} catch (err) {
|
|
444
|
+
const { statusCode, details, errorCode } = extractHttpError(err);
|
|
445
|
+
this.log("error", `${method} - Failed`, method, {
|
|
446
|
+
userId,
|
|
447
|
+
statusCode,
|
|
448
|
+
errorCode
|
|
449
|
+
});
|
|
450
|
+
throw new KeycloakAdminError({
|
|
451
|
+
message: "Keycloak send verify email failed",
|
|
452
|
+
statusCode: statusCode ?? 502,
|
|
453
|
+
code: KEYCLOAK_ADMIN_ERROR_CODES.SEND_VERIFY_EMAIL_ERROR,
|
|
454
|
+
context: {
|
|
455
|
+
url,
|
|
456
|
+
method: "PUT",
|
|
457
|
+
userId,
|
|
458
|
+
details,
|
|
459
|
+
errorCode
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
__name(_KeycloakAdminClient, "KeycloakAdminClient");
|
|
466
|
+
var KeycloakAdminClient = _KeycloakAdminClient;
|
|
467
|
+
KeycloakAdminClient = _ts_decorate([
|
|
468
|
+
(0, import_common.Injectable)(),
|
|
469
|
+
_ts_param(0, (0, import_common.Inject)(KEYCLOAK_ADMIN_CONFIG)),
|
|
470
|
+
_ts_param(1, (0, import_common.Inject)(import_http_client.HTTP_PROVIDER)),
|
|
471
|
+
_ts_param(2, (0, import_common.Optional)()),
|
|
472
|
+
_ts_param(2, (0, import_common.Inject)(import_logger.LOGGER_PROVIDER)),
|
|
473
|
+
_ts_metadata("design:type", Function),
|
|
474
|
+
_ts_metadata("design:paramtypes", [
|
|
475
|
+
typeof KeycloakAdminConfig === "undefined" ? Object : KeycloakAdminConfig,
|
|
476
|
+
typeof HttpProviderInterface === "undefined" ? Object : HttpProviderInterface,
|
|
477
|
+
typeof LoggerProviderInterface === "undefined" ? Object : LoggerProviderInterface
|
|
478
|
+
])
|
|
479
|
+
], KeycloakAdminClient);
|
|
480
|
+
|
|
481
|
+
// src/utils/validate-config.ts
|
|
482
|
+
function validateKeycloakAdminConfig(config) {
|
|
483
|
+
const issues = [];
|
|
484
|
+
if (!config) {
|
|
485
|
+
throw new KeycloakAdminError({
|
|
486
|
+
message: "KeycloakAdminConfig is required. Provide an object with baseUrl, realm, adminUser and adminPassword.",
|
|
487
|
+
statusCode: 400,
|
|
488
|
+
code: "KEYCLOAK_ADMIN_CONFIG_MISSING"
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
if (typeof config.baseUrl !== "string" || config.baseUrl.trim() === "") {
|
|
492
|
+
issues.push({
|
|
493
|
+
field: "baseUrl",
|
|
494
|
+
message: "baseUrl is required and must be a non-empty string (e.g. 'http://localhost:8081')"
|
|
495
|
+
});
|
|
496
|
+
} else if (!config.baseUrl.startsWith("http://") && !config.baseUrl.startsWith("https://")) {
|
|
497
|
+
issues.push({
|
|
498
|
+
field: "baseUrl",
|
|
499
|
+
message: `baseUrl must start with 'http://' or 'https://'. Received: '${config.baseUrl}'`
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
if (typeof config.realm !== "string" || config.realm.trim() === "") {
|
|
503
|
+
issues.push({
|
|
504
|
+
field: "realm",
|
|
505
|
+
message: "realm is required and must be a non-empty string (e.g. 'BACKEND')"
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (typeof config.adminUser !== "string" || config.adminUser.trim() === "") {
|
|
509
|
+
issues.push({
|
|
510
|
+
field: "adminUser",
|
|
511
|
+
message: "adminUser is required and must be a non-empty string"
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
if (typeof config.adminPassword !== "string" || config.adminPassword.trim() === "") {
|
|
515
|
+
issues.push({
|
|
516
|
+
field: "adminPassword",
|
|
517
|
+
message: "adminPassword is required and must be a non-empty string"
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (issues.length > 0) {
|
|
521
|
+
const summary = issues.map((i) => ` - ${i.field}: ${i.message}`).join("\n");
|
|
522
|
+
throw new KeycloakAdminError({
|
|
523
|
+
message: `KeycloakAdminConfig validation failed:
|
|
524
|
+
${summary}`,
|
|
525
|
+
statusCode: 400,
|
|
526
|
+
code: "KEYCLOAK_ADMIN_CONFIG_INVALID",
|
|
527
|
+
context: {
|
|
528
|
+
issues
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
__name(validateKeycloakAdminConfig, "validateKeycloakAdminConfig");
|
|
534
|
+
|
|
535
|
+
// src/keycloak-admin.module.ts
|
|
536
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
537
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
538
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
539
|
+
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;
|
|
540
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
541
|
+
}
|
|
542
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
543
|
+
var _KeycloakAdminModule = class _KeycloakAdminModule {
|
|
544
|
+
static forRoot(config) {
|
|
545
|
+
validateKeycloakAdminConfig(config);
|
|
546
|
+
return {
|
|
547
|
+
module: _KeycloakAdminModule,
|
|
548
|
+
global: true,
|
|
549
|
+
imports: [
|
|
550
|
+
import_http_client2.HttpModule.forRoot({
|
|
551
|
+
baseURL: config.baseUrl,
|
|
552
|
+
timeout: 5e3
|
|
553
|
+
}, {
|
|
554
|
+
logging: {
|
|
555
|
+
enabled: true,
|
|
556
|
+
includeBody: true,
|
|
557
|
+
context: "KeycloakAdminHttpClient",
|
|
558
|
+
environments: [
|
|
559
|
+
"development",
|
|
560
|
+
"test"
|
|
561
|
+
]
|
|
562
|
+
}
|
|
563
|
+
})
|
|
564
|
+
],
|
|
565
|
+
providers: [
|
|
566
|
+
{
|
|
567
|
+
provide: KEYCLOAK_ADMIN_CONFIG,
|
|
568
|
+
useValue: config
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
provide: KEYCLOAK_ADMIN_CLIENT,
|
|
572
|
+
useClass: KeycloakAdminClient
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
provide: KEYCLOAK_ADMIN_PROVIDER,
|
|
576
|
+
useExisting: KEYCLOAK_ADMIN_CLIENT
|
|
577
|
+
}
|
|
578
|
+
],
|
|
579
|
+
exports: [
|
|
580
|
+
KEYCLOAK_ADMIN_CLIENT,
|
|
581
|
+
KEYCLOAK_ADMIN_PROVIDER,
|
|
582
|
+
KEYCLOAK_ADMIN_CONFIG
|
|
583
|
+
]
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
__name(_KeycloakAdminModule, "KeycloakAdminModule");
|
|
588
|
+
var KeycloakAdminModule = _KeycloakAdminModule;
|
|
589
|
+
KeycloakAdminModule = _ts_decorate2([
|
|
590
|
+
(0, import_common2.Module)({})
|
|
591
|
+
], KeycloakAdminModule);
|
|
592
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
593
|
+
0 && (module.exports = {
|
|
594
|
+
KEYCLOAK_ADMIN_CLIENT,
|
|
595
|
+
KEYCLOAK_ADMIN_CONFIG,
|
|
596
|
+
KEYCLOAK_ADMIN_PROVIDER,
|
|
597
|
+
KeycloakAdminClient,
|
|
598
|
+
KeycloakAdminError,
|
|
599
|
+
KeycloakAdminModule,
|
|
600
|
+
validateKeycloakAdminConfig
|
|
601
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adatechnology/keycloak-admin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@adatechnology/http-client": "0.0.
|
|
14
|
+
"@adatechnology/http-client": "0.0.11",
|
|
15
15
|
"@adatechnology/logger": "0.0.10"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|