@adobe/helix-config 2.9.0 → 2.10.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.
@@ -0,0 +1 @@
1
+ npx lint-staged
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [2.10.0](https://github.com/adobe/helix-config/compare/v2.9.0...v2.10.0) (2024-04-26)
2
+
3
+
4
+ ### Features
5
+
6
+ * add support to migrate jwt api keys ([#59](https://github.com/adobe/helix-config/issues/59)) ([04adf04](https://github.com/adobe/helix-config/commit/04adf04c975ee38840a56f81aac8a1609959298a))
7
+
1
8
  # [2.9.0](https://github.com/adobe/helix-config/compare/v2.8.0...v2.9.0) (2024-04-25)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-config",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "Helix Config",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  "docs:types": "node ./test/dev/generate-types.js",
16
16
  "semantic-release": "semantic-release",
17
17
  "semantic-release-dry": "semantic-release --dry-run --branches $CI_BRANCH",
18
- "prepare": "husky install"
18
+ "prepare": "husky"
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
@@ -68,6 +68,7 @@
68
68
  "@smithy/node-http-handler": "2.5.0",
69
69
  "ajv": "8.12.0",
70
70
  "ajv-formats": "3.0.1",
71
+ "jose": "5.2.4",
71
72
  "mime": "4.0.1"
72
73
  }
73
74
  }
@@ -78,7 +78,11 @@ export function getAccessConfig(config, partition) {
78
78
  const cfg = {
79
79
  allow: toArray(access[partition]?.allow ?? access.allow),
80
80
  apiKeyId,
81
- tokenHash: apiKeyId.map((id) => tokens[id]?.hash).filter((hash) => !!hash),
81
+ tokenHash: apiKeyId
82
+ // token ids are always stored in base64url format, but legacy apiKeyIds are not
83
+ .map((jti) => jti.replaceAll('/', '_').replaceAll('+', '-'))
84
+ .map((id) => tokens[id]?.hash)
85
+ .filter((hash) => !!hash),
82
86
  clientCertDN: toArray(access[partition]?.clientCertDN ?? access.clientCertDN),
83
87
  };
84
88
  // if an allow is defined but no apiKeyId, create a fake one so that auth is still
@@ -16,6 +16,7 @@ import {
16
16
  createToken,
17
17
  jsonGet,
18
18
  jsonPut,
19
+ migrateToken,
19
20
  updateCodeSource,
20
21
  updateContentSource,
21
22
  } from './utils.js';
@@ -198,14 +199,21 @@ export class ConfigStore {
198
199
  // don't allow to update individual token
199
200
  throw new StatusCodeError(400, 'invalid object path.');
200
201
  } else if (frag.type === 'tokens') {
201
- // create new token
202
- const token = createToken(this.org);
202
+ // TODO: remove support after all helix4 projects are migrated
203
+ let token;
204
+ if (data.jwt) {
205
+ token = await migrateToken(this.org, data.jwt);
206
+ } else {
207
+ // create new token
208
+ token = createToken(this.org);
209
+ }
210
+
203
211
  data = {
204
212
  ...token,
205
213
  };
206
214
  // don't store token value in the config
207
215
  delete data.value;
208
- relPath = `tokens/${token.id}`;
216
+ relPath = ['tokens', token.id];
209
217
  frag.type = 'token';
210
218
  ret = token;
211
219
  // don't expose hash in return value
@@ -12,13 +12,15 @@
12
12
  /* eslint-disable no-param-reassign */
13
13
  import crypto from 'crypto';
14
14
  import { GitUrl } from '@adobe/helix-shared-git';
15
+ import { decodeJwt } from 'jose';
16
+ import { StatusCodeError } from './status-code-error.js';
15
17
 
16
18
  export function jsonGet(obj, path) {
17
19
  return path.split('/').reduce((o, p) => o[p], obj);
18
20
  }
19
21
 
20
22
  export function jsonPut(obj, path, value) {
21
- const parts = path.split('/');
23
+ const parts = Array.isArray(path) ? path : path.split('/');
22
24
  const last = parts.pop();
23
25
  const parent = parts.reduce((o, p) => {
24
26
  if (!(p in o)) {
@@ -134,3 +136,37 @@ export function createToken(key) {
134
136
  created,
135
137
  };
136
138
  }
139
+
140
+ /**
141
+ * migrates an existing jwt token
142
+ * @param key
143
+ * @param jwt
144
+ * @returns {Promise<object>}
145
+ */
146
+ export async function migrateToken(key, jwt) {
147
+ let payload;
148
+ try {
149
+ payload = await decodeJwt(jwt);
150
+ } catch (e) {
151
+ throw new StatusCodeError(400, `unable to migrate jwt: ${e.message}`);
152
+ }
153
+ const { jti } = payload;
154
+ if (!jti) {
155
+ throw new StatusCodeError(400, 'unable to migrate jwt: missing jti claim.');
156
+ }
157
+ const id = jti
158
+ .replaceAll('/', '_')
159
+ .replaceAll('+', '-');
160
+ const hash = crypto
161
+ .createHmac('sha512', key)
162
+ .update(jwt, 'utf-8')
163
+ .digest()
164
+ .toString('base64url');
165
+ const created = new Date().toISOString();
166
+ return {
167
+ id,
168
+ value: jwt,
169
+ hash,
170
+ created,
171
+ };
172
+ }