@keycloak/keycloak-admin-client 21.1.2 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -107,20 +107,20 @@ setInterval(() => kcAdminClient.auth(credentials), 58 * 1000); // 58 seconds
107
107
  To build the source do a build:
108
108
 
109
109
  ```bash
110
- npm run build
110
+ pnpm run build
111
111
  ```
112
112
 
113
113
  Start the Keycloak server:
114
114
 
115
115
  ```bash
116
- npm run server:start
116
+ pnpm run server:start
117
117
  ```
118
118
 
119
119
  If you started your container manually make sure there is an admin user named 'admin' with password 'admin'.
120
120
  Then start the tests with:
121
121
 
122
122
  ```bash
123
- npm test
123
+ pnpm test
124
124
  ```
125
125
 
126
126
  ## Supported APIs
@@ -6,14 +6,14 @@ export var DecisionStrategy;
6
6
  DecisionStrategy["AFFIRMATIVE"] = "AFFIRMATIVE";
7
7
  DecisionStrategy["UNANIMOUS"] = "UNANIMOUS";
8
8
  DecisionStrategy["CONSENSUS"] = "CONSENSUS";
9
- })(DecisionStrategy = DecisionStrategy || (DecisionStrategy = {}));
9
+ })(DecisionStrategy || (DecisionStrategy = {}));
10
10
  export var DecisionEffect;
11
11
  (function (DecisionEffect) {
12
12
  DecisionEffect["Permit"] = "PERMIT";
13
13
  DecisionEffect["Deny"] = "DENY";
14
- })(DecisionEffect = DecisionEffect || (DecisionEffect = {}));
14
+ })(DecisionEffect || (DecisionEffect = {}));
15
15
  export var Logic;
16
16
  (function (Logic) {
17
17
  Logic["POSITIVE"] = "POSITIVE";
18
18
  Logic["NEGATIVE"] = "NEGATIVE";
19
- })(Logic = Logic || (Logic = {}));
19
+ })(Logic || (Logic = {}));
@@ -8,4 +8,4 @@ export var RequiredActionAlias;
8
8
  RequiredActionAlias["CONFIGURE_TOTP"] = "CONFIGURE_TOTP";
9
9
  RequiredActionAlias["UPDATE_PASSWORD"] = "UPDATE_PASSWORD";
10
10
  RequiredActionAlias["TERMS_AND_CONDITIONS"] = "TERMS_AND_CONDITIONS";
11
- })(RequiredActionAlias = RequiredActionAlias || (RequiredActionAlias = {}));
11
+ })(RequiredActionAlias || (RequiredActionAlias = {}));
@@ -87,4 +87,6 @@ export interface ProtocolMapperTypeRepresentation {
87
87
  export interface CryptoInfoRepresentation {
88
88
  cryptoProvider: string;
89
89
  supportedKeystoreTypes: string[];
90
+ clientSignatureSymmetricAlgorithms: string[];
91
+ clientSignatureAsymmetricAlgorithms: string[];
90
92
  }
@@ -20,17 +20,17 @@ export class Agent {
20
20
  return async (payload = {}, options) => {
21
21
  const baseParams = this.getBaseParams?.() ?? {};
22
22
  // Filter query parameters by queryParamKeys
23
- const queryParams = queryParamKeys
24
- ? pick(payload, queryParamKeys)
25
- : undefined;
23
+ const queryParams = queryParamKeys.length > 0 ? pick(payload, queryParamKeys) : undefined;
26
24
  // Add filtered payload parameters to base parameters
27
25
  const allUrlParamKeys = [...Object.keys(baseParams), ...urlParamKeys];
28
26
  const urlParams = { ...baseParams, ...pick(payload, allUrlParamKeys) };
29
- // Omit url parameters and query parameters from payload
30
- const omittedKeys = ignoredKeys
31
- ? [...allUrlParamKeys, ...queryParamKeys].filter((key) => !ignoredKeys.includes(key))
32
- : [...allUrlParamKeys, ...queryParamKeys];
33
- payload = omit(payload, omittedKeys);
27
+ if (!(payload instanceof FormData)) {
28
+ // Omit url parameters and query parameters from payload
29
+ const omittedKeys = ignoredKeys
30
+ ? [...allUrlParamKeys, ...queryParamKeys].filter((key) => !ignoredKeys.includes(key))
31
+ : [...allUrlParamKeys, ...queryParamKeys];
32
+ payload = omit(payload, omittedKeys);
33
+ }
34
34
  // Transform keys of both payload and queryParams
35
35
  if (keyTransform) {
36
36
  this.transformKey(payload, keyTransform);
@@ -27,6 +27,9 @@ export declare class Groups extends Resource<{
27
27
  }) | undefined, options?: Pick<import("./agent.js").RequestArgs, "catchNotFound"> | undefined) => Promise<{
28
28
  id: string;
29
29
  }>;
30
+ updateRoot: (payload?: (GroupRepresentation & {
31
+ realm?: string | undefined;
32
+ }) | undefined, options?: Pick<import("./agent.js").RequestArgs, "catchNotFound"> | undefined) => Promise<void>;
30
33
  /**
31
34
  * Single user
32
35
  */
@@ -53,6 +56,7 @@ export declare class Groups extends Resource<{
53
56
  /**
54
57
  * Set or create child.
55
58
  * This will just set the parent if it exists. Create it and set the parent if the group doesn’t exist.
59
+ * @deprecated Use `createChildGroup` or `updateChildGroup` instead.
56
60
  */
57
61
  setOrCreateChild: (query: {
58
62
  id: string;
@@ -61,6 +65,25 @@ export declare class Groups extends Resource<{
61
65
  }, payload: GroupRepresentation) => Promise<{
62
66
  id: string;
63
67
  }>;
68
+ /**
69
+ * Creates a child group on the specified parent group. If the group already exists, then an error is returned.
70
+ */
71
+ createChildGroup: (query: {
72
+ id: string;
73
+ } & {
74
+ realm?: string | undefined;
75
+ }, payload: Omit<GroupRepresentation, "id">) => Promise<{
76
+ id: string;
77
+ }>;
78
+ /**
79
+ * Updates a child group on the specified parent group. If the group doesn’t exist, then an error is returned.
80
+ * Can be used to move a group from one parent to another.
81
+ */
82
+ updateChildGroup: (query: {
83
+ id: string;
84
+ } & {
85
+ realm?: string | undefined;
86
+ }, payload: GroupRepresentation) => Promise<void>;
64
87
  /**
65
88
  * Members
66
89
  */
@@ -7,6 +7,9 @@ export class Groups extends Resource {
7
7
  method: "POST",
8
8
  returnResourceIdInLocationHeader: { field: "id" },
9
9
  });
10
+ updateRoot = this.makeRequest({
11
+ method: "POST",
12
+ });
10
13
  /**
11
14
  * Single user
12
15
  */
@@ -33,6 +36,7 @@ export class Groups extends Resource {
33
36
  /**
34
37
  * Set or create child.
35
38
  * This will just set the parent if it exists. Create it and set the parent if the group doesn’t exist.
39
+ * @deprecated Use `createChildGroup` or `updateChildGroup` instead.
36
40
  */
37
41
  setOrCreateChild = this.makeUpdateRequest({
38
42
  method: "POST",
@@ -40,6 +44,24 @@ export class Groups extends Resource {
40
44
  urlParamKeys: ["id"],
41
45
  returnResourceIdInLocationHeader: { field: "id" },
42
46
  });
47
+ /**
48
+ * Creates a child group on the specified parent group. If the group already exists, then an error is returned.
49
+ */
50
+ createChildGroup = this.makeUpdateRequest({
51
+ method: "POST",
52
+ path: "/{id}/children",
53
+ urlParamKeys: ["id"],
54
+ returnResourceIdInLocationHeader: { field: "id" },
55
+ });
56
+ /**
57
+ * Updates a child group on the specified parent group. If the group doesn’t exist, then an error is returned.
58
+ * Can be used to move a group from one parent to another.
59
+ */
60
+ updateChildGroup = this.makeUpdateRequest({
61
+ method: "POST",
62
+ path: "/{id}/children",
63
+ urlParamKeys: ["id"],
64
+ });
43
65
  /**
44
66
  * Members
45
67
  */
@@ -75,10 +75,10 @@ export declare class IdentityProviders extends Resource<{
75
75
  } & {
76
76
  realm?: string | undefined;
77
77
  }) | undefined, options?: Pick<import("./agent.js").RequestArgs, "catchNotFound"> | undefined) => Promise<Record<string, IdentityProviderMapperTypeRepresentation>>;
78
- importFromUrl: (payload?: ({
78
+ importFromUrl: (payload?: ((FormData | {
79
79
  fromUrl: string;
80
80
  providerId: string;
81
- } & {
81
+ }) & {
82
82
  realm?: string | undefined;
83
83
  }) | undefined, options?: Pick<import("./agent.js").RequestArgs, "catchNotFound"> | undefined) => Promise<Record<string, string>>;
84
84
  updatePermission: (query: {
@@ -122,8 +122,8 @@ export class Realms extends Resource {
122
122
  */
123
123
  removeSession = this.makeRequest({
124
124
  method: "DELETE",
125
- path: "/{realm}/sessions/{session}",
126
- urlParamKeys: ["realm", "session"],
125
+ path: "/{realm}/sessions/{sessionId}",
126
+ urlParamKeys: ["realm", "sessionId"],
127
127
  catchNotFound: true,
128
128
  });
129
129
  /**
package/lib/utils/auth.js CHANGED
@@ -2,6 +2,14 @@ import camelize from "camelize-ts";
2
2
  import { defaultBaseUrl, defaultRealm } from "./constants.js";
3
3
  import { fetchWithError } from "./fetchWithError.js";
4
4
  import { stringifyQueryParams } from "./stringifyQueryParams.js";
5
+ // See: https://developer.mozilla.org/en-US/docs/Glossary/Base64
6
+ const bytesToBase64 = (bytes) => btoa(Array.from(bytes, (byte) => String.fromCodePoint(byte)).join(""));
7
+ const toBase64 = (input) => bytesToBase64(new TextEncoder().encode(input));
8
+ // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986
9
+ const encodeRFC3986URIComponent = (input) => encodeURIComponent(input).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
10
+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
11
+ // Specifically, the section on encoding `application/x-www-form-urlencoded`.
12
+ const encodeFormURIComponent = (data) => encodeRFC3986URIComponent(data).replaceAll("%20", "+");
5
13
  export const getToken = async (settings) => {
6
14
  // Construct URL
7
15
  const baseUrl = settings.baseUrl || defaultBaseUrl;
@@ -28,7 +36,11 @@ export const getToken = async (settings) => {
28
36
  const options = settings.requestOptions ?? {};
29
37
  const headers = new Headers(options.headers);
30
38
  if (credentials.clientSecret) {
31
- headers.set("Authorization", `Basic ${btoa(`${credentials.clientId}:${credentials.clientSecret}`)}`);
39
+ // See: https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
40
+ const username = encodeFormURIComponent(credentials.clientId);
41
+ const password = encodeFormURIComponent(credentials.clientSecret);
42
+ // See: https://datatracker.ietf.org/doc/html/rfc2617#section-2
43
+ headers.set("authorization", `Basic ${toBase64(`${username}:${password}`)}`);
32
44
  }
33
45
  headers.set("content-type", "application/x-www-form-urlencoded");
34
46
  const response = await fetchWithError(url, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keycloak/keycloak-admin-client",
3
- "version": "21.1.2",
3
+ "version": "22.0.0",
4
4
  "description": "A client to interact with Keycloak's Administration API",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -11,12 +11,6 @@
11
11
  "engines": {
12
12
  "node": ">=18"
13
13
  },
14
- "scripts": {
15
- "build": "wireit",
16
- "lint": "wireit",
17
- "test": "wireit",
18
- "prepublishOnly": "npm run build"
19
- },
20
14
  "wireit": {
21
15
  "build": {
22
16
  "command": "tsc --pretty",
@@ -37,17 +31,17 @@
37
31
  }
38
32
  },
39
33
  "dependencies": {
40
- "camelize-ts": "^2.5.0",
34
+ "camelize-ts": "^3.0.0",
41
35
  "lodash-es": "^4.17.21",
42
36
  "url-join": "^5.0.0",
43
37
  "url-template": "^3.1.0"
44
38
  },
45
39
  "devDependencies": {
46
- "@faker-js/faker": "^7.6.0",
47
- "@types/chai": "^4.3.4",
40
+ "@faker-js/faker": "^8.0.2",
41
+ "@types/chai": "^4.3.5",
48
42
  "@types/lodash-es": "^4.17.7",
49
43
  "@types/mocha": "^10.0.1",
50
- "@types/node": "^18.15.11",
44
+ "@types/node": "^20.4.1",
51
45
  "chai": "^4.3.7",
52
46
  "mocha": "^10.2.0",
53
47
  "ts-node": "^10.9.1"
@@ -61,5 +55,10 @@
61
55
  "type": "git",
62
56
  "url": "https://github.com/keycloak/keycloak.git"
63
57
  },
64
- "homepage": "https://www.keycloak.org/"
65
- }
58
+ "homepage": "https://www.keycloak.org/",
59
+ "scripts": {
60
+ "build": "wireit",
61
+ "lint": "wireit",
62
+ "test": "wireit"
63
+ }
64
+ }