@adobe/spacecat-shared-http-utils 1.9.12 → 1.10.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [@adobe/spacecat-shared-http-utils-v1.10.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-http-utils-v1.10.0...@adobe/spacecat-shared-http-utils-v1.10.1) (2025-04-01)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **deps:** update external major (major) ([#674](https://github.com/adobe/spacecat-shared/issues/674)) ([285b37d](https://github.com/adobe/spacecat-shared/commit/285b37de9df42adb6a23694bcc699608e3b5b8fe))
7
+
8
+ # [@adobe/spacecat-shared-http-utils-v1.10.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-http-utils-v1.9.12...@adobe/spacecat-shared-http-utils-v1.10.0) (2025-03-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * jwt auth handler (WIP) ([#669](https://github.com/adobe/spacecat-shared/issues/669)) ([f3c8d84](https://github.com/adobe/spacecat-shared/commit/f3c8d84a724e9664cac995fe67af7af65800b16f))
14
+
1
15
  # [@adobe/spacecat-shared-http-utils-v1.9.12](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-http-utils-v1.9.11...@adobe/spacecat-shared-http-utils-v1.9.12) (2025-03-04)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-http-utils",
3
- "version": "1.9.12",
3
+ "version": "1.10.1",
4
4
  "description": "Shared modules of the Spacecat Services - HTTP Utils",
5
5
  "type": "module",
6
6
  "engines": {
@@ -42,6 +42,6 @@
42
42
  "@adobe/helix-shared-wrap": "2.0.2",
43
43
  "chai": "5.2.0",
44
44
  "chai-as-promised": "8.0.1",
45
- "sinon": "19.0.2"
45
+ "sinon": "20.0.0"
46
46
  }
47
47
  }
@@ -20,6 +20,7 @@ import {
20
20
 
21
21
  import configProd from './config/ims.js';
22
22
  import configDev from './config/ims-stg.js';
23
+ import { getBearerToken } from './utils/bearer.js';
23
24
 
24
25
  import AbstractHandler from './abstract.js';
25
26
  import AuthInfo from '../auth-info.js';
@@ -46,16 +47,6 @@ const loadConfig = (context) => {
46
47
  return isDev ? configDev : configProd;
47
48
  };
48
49
 
49
- const getBearerToken = (context) => {
50
- const authorizationHeader = context.pathInfo?.headers?.authorization || '';
51
-
52
- if (!authorizationHeader.startsWith('Bearer ')) {
53
- return null;
54
- }
55
-
56
- return authorizationHeader.replace('Bearer ', '');
57
- };
58
-
59
50
  const transformProfile = (payload) => {
60
51
  const profile = { ...payload };
61
52
 
@@ -65,6 +56,9 @@ const transformProfile = (payload) => {
65
56
  return profile;
66
57
  };
67
58
 
59
+ /**
60
+ * @deprecated Use JwtHandler instead in the context of IMS login with subsequent JWT exchange.
61
+ */
68
62
  export default class AdobeImsHandler extends AbstractHandler {
69
63
  constructor(log) {
70
64
  super('ims', log);
@@ -83,9 +77,9 @@ export default class AdobeImsHandler extends AbstractHandler {
83
77
  }
84
78
 
85
79
  async #validateToken(token, config) {
86
- const decoded = await decodeJwt(token);
87
- if (config.name !== decoded.as) {
88
- throw new Error(`Token not issued by expected idp: ${config.name} != ${decoded.as}`);
80
+ const claims = await decodeJwt(token);
81
+ if (config.name !== claims.as) {
82
+ throw new Error(`Token not issued by expected idp: ${config.name} != ${claims.as}`);
89
83
  }
90
84
 
91
85
  const jwks = await this.#getJwksUri(config);
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Copyright 2025 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { hasText } from '@adobe/spacecat-shared-utils';
14
+ import { importSPKI, jwtVerify } from 'jose';
15
+
16
+ import AbstractHandler from './abstract.js';
17
+ import AuthInfo from '../auth-info.js';
18
+ import { getBearerToken } from './utils/bearer.js';
19
+
20
+ const ALGORITHM_ES256 = 'ES256';
21
+ export const ISSUER = 'https://spacecat.experiencecloud.live';
22
+
23
+ export default class JwtHandler extends AbstractHandler {
24
+ constructor(log) {
25
+ super('jwt', log);
26
+ }
27
+
28
+ async #setup(context) {
29
+ const authPublicKey = context.env?.AUTH_PUBLIC_KEY;
30
+
31
+ if (!hasText(authPublicKey)) {
32
+ throw new Error('No public key provided');
33
+ }
34
+
35
+ this.authPublicKey = await importSPKI(authPublicKey, ALGORITHM_ES256);
36
+ }
37
+
38
+ async #validateToken(token) {
39
+ const verifiedToken = await jwtVerify(
40
+ token,
41
+ this.authPublicKey,
42
+ {
43
+ algorithms: [ALGORITHM_ES256], // force expected algorithm
44
+ clockTolerance: 5, // number of seconds to tolerate when checking the nbf and exp claims
45
+ complete: false, // only return the payload and not headers etc.
46
+ ignoreExpiration: false, // validate expiration
47
+ issuer: ISSUER, // validate issuer
48
+ },
49
+ );
50
+
51
+ return verifiedToken.payload;
52
+ }
53
+
54
+ async checkAuth(request, context) {
55
+ const authInfo = new AuthInfo()
56
+ .withType(this.name)
57
+ .withAuthenticated(false);
58
+
59
+ try {
60
+ await this.#setup(context);
61
+
62
+ const token = getBearerToken(context);
63
+
64
+ if (!hasText(token)) {
65
+ this.log('No bearer token provided', 'debug');
66
+ authInfo.withReason('No bearer token provided');
67
+ return authInfo;
68
+ }
69
+
70
+ const payload = await this.#validateToken(token);
71
+
72
+ return new AuthInfo()
73
+ .withType(this.name)
74
+ .withAuthenticated(true)
75
+ .withProfile(payload);
76
+ } catch (e) {
77
+ this.log(`Failed to validate token: ${e.message}`, 'error');
78
+ authInfo.withReason(e.message);
79
+ }
80
+
81
+ return authInfo;
82
+ }
83
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright 2025 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ export const getBearerToken = (context) => {
14
+ const authorizationHeader = context.pathInfo?.headers?.authorization || '';
15
+
16
+ if (!authorizationHeader.startsWith('Bearer ')) {
17
+ return null;
18
+ }
19
+
20
+ return authorizationHeader.replace('Bearer ', '');
21
+ };
@@ -80,3 +80,45 @@ export default class ExampleHandler extends AbstractHandler {
80
80
  }
81
81
  }
82
82
  ```
83
+
84
+ ## Provided Authentication Handlers
85
+
86
+ ### JWT Handler
87
+
88
+ The JWT (JSON Web Token) handler provides authentication using ES256-signed JSON Web Tokens. It validates tokens against a provided public key and ensures they're properly signed and not expired.
89
+
90
+ #### How It Works
91
+
92
+ 1. The handler extracts the bearer token from the request
93
+ 2. It validates the token using a public key (from the `AUTH_PUBLIC_KEY` environment variable)
94
+ 3. It verifies:
95
+ - The token is signed with the ES256 algorithm
96
+ - The token is not expired
97
+ - The token has the correct issuer (https://spacecat.experiencecloud.live)
98
+ 4. On successful validation, it returns the token payload as the user profile
99
+
100
+ #### Configuration
101
+
102
+ To use the JWT handler, you need to:
103
+
104
+ 1. Set the `AUTH_PUBLIC_KEY` environment variable with your SPKI-formatted public key
105
+ 2. Add the handler to your auth wrapper configuration
106
+
107
+ ```javascript
108
+ import { wrap } from '@adobe/helix-shared-wrap';
109
+ import auth from './auth-wrapper.js';
110
+ import JwtHandler from './handlers/jwt.js';
111
+
112
+ export const main = wrap(run)
113
+ .with(auth, { authHandlers: [JwtHandler] });
114
+ ```
115
+
116
+ #### Token Requirements
117
+
118
+ JWT tokens must:
119
+ - Be signed with the ES256 algorithm
120
+ - Include standard claims (exp, iat, iss)
121
+ - Use the issuer: `https://spacecat.experiencecloud.live`
122
+ - Be provided as a Bearer token in the Authorization header
123
+
124
+ Any additional claims in the JWT payload will be available in the `authInfo.profile` object after authentication.