@adobe/spacecat-shared-http-utils 1.12.1 → 1.13.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [@adobe/spacecat-shared-http-utils-v1.13.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-http-utils-v1.12.1...@adobe/spacecat-shared-http-utils-v1.13.0) (2025-05-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * add cookie auth handler ([#749](https://github.com/adobe/spacecat-shared/issues/749)) ([362184b](https://github.com/adobe/spacecat-shared/commit/362184b1339ccdaa25328228c906917721d86bd1))
7
+
1
8
  # [@adobe/spacecat-shared-http-utils-v1.12.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-http-utils-v1.12.0...@adobe/spacecat-shared-http-utils-v1.12.1) (2025-05-19)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-http-utils",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
4
  "description": "Shared modules of the Spacecat Services - HTTP Utils",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,88 @@
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 { getCookieValue } from './utils/cookie.js';
19
+
20
+ const ALGORITHM_ES256 = 'ES256';
21
+ export const ISSUER = 'https://spacecat.experiencecloud.live';
22
+
23
+ export default class CookieAuthHandler extends AbstractHandler {
24
+ constructor(log) {
25
+ super('cookieAuth', log);
26
+ }
27
+
28
+ async #setup(context) {
29
+ const authPublicKeyB64 = context.env?.AUTH_PUBLIC_KEY_B64;
30
+
31
+ if (!hasText(authPublicKeyB64)) {
32
+ throw new Error('No public key provided');
33
+ }
34
+
35
+ const authPublicKey = Buffer.from(authPublicKeyB64, 'base64').toString('utf-8');
36
+
37
+ this.authPublicKey = await importSPKI(authPublicKey, ALGORITHM_ES256);
38
+ }
39
+
40
+ async #validateToken(token) {
41
+ const verifiedToken = await jwtVerify(
42
+ token,
43
+ this.authPublicKey,
44
+ {
45
+ algorithms: [ALGORITHM_ES256], // force expected algorithm
46
+ clockTolerance: 5, // number of seconds to tolerate when checking the nbf and exp claims
47
+ complete: false, // only return the payload and not headers etc.
48
+ ignoreExpiration: false, // validate expiration
49
+ issuer: ISSUER, // validate issuer
50
+ },
51
+ );
52
+
53
+ verifiedToken.payload.tenants = verifiedToken.payload.tenants || [];
54
+
55
+ return verifiedToken.payload;
56
+ }
57
+
58
+ async checkAuth(request, context) {
59
+ try {
60
+ await this.#setup(context);
61
+
62
+ const sessionToken = getCookieValue(context, 'sessionToken');
63
+
64
+ if (!hasText(sessionToken)) {
65
+ this.log('No session token provided', 'debug');
66
+ return null;
67
+ }
68
+
69
+ const payload = await this.#validateToken(sessionToken);
70
+
71
+ const scopes = payload.is_admin ? [{ name: 'admin' }] : [];
72
+
73
+ scopes.push(...payload.tenants.map(
74
+ (tenant) => ({ name: 'user', domains: [tenant.id], subScopes: tenant.subServices }),
75
+ ));
76
+
77
+ return new AuthInfo()
78
+ .withType(this.name)
79
+ .withAuthenticated(true)
80
+ .withProfile(payload)
81
+ .withScopes(scopes);
82
+ } catch (e) {
83
+ this.log(`Failed to validate token: ${e.message}`, 'error');
84
+ }
85
+
86
+ return null;
87
+ }
88
+ }
@@ -0,0 +1,46 @@
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
+ /**
14
+ * Gets the raw cookie string from the context
15
+ * @param {Object} context - The context object containing pathInfo
16
+ * @returns {string|null} The cookie string or null if not found
17
+ */
18
+ export const getCookie = (context) => {
19
+ const cookie = context.pathInfo?.headers?.cookie || '';
20
+
21
+ if (!cookie) {
22
+ return null;
23
+ }
24
+
25
+ return cookie;
26
+ };
27
+
28
+ /**
29
+ * Parses and retrieves a specific cookie value by name
30
+ * @param {Object} context - The context object containing pathInfo
31
+ * @param {string} name - The name of the cookie to retrieve
32
+ * @returns {string|null} The cookie value or null if not found
33
+ */
34
+ export const getCookieValue = (context, name) => {
35
+ const cookieString = getCookie(context);
36
+ if (!cookieString) return null;
37
+
38
+ const cookies = cookieString.split(';');
39
+ for (const cookie of cookies) {
40
+ const [cookieName, cookieValue] = cookie.trim().split('=');
41
+ if (cookieName === name) {
42
+ return cookieValue;
43
+ }
44
+ }
45
+ return null;
46
+ };