@platform-x/shp-auth-client 0.0.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.
Files changed (54) hide show
  1. package/README.md +304 -0
  2. package/dist/cache.d.ts +10 -0
  3. package/dist/cache.d.ts.map +1 -0
  4. package/dist/cache.js +28 -0
  5. package/dist/cache.js.map +1 -0
  6. package/dist/config-provider.d.ts +17 -0
  7. package/dist/config-provider.d.ts.map +1 -0
  8. package/dist/config-provider.js +54 -0
  9. package/dist/config-provider.js.map +1 -0
  10. package/dist/errors.d.ts +53 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +117 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/extract-site-name.d.ts +15 -0
  15. package/dist/extract-site-name.d.ts.map +1 -0
  16. package/dist/extract-site-name.js +28 -0
  17. package/dist/extract-site-name.js.map +1 -0
  18. package/dist/graphql-client.d.ts +14 -0
  19. package/dist/graphql-client.d.ts.map +1 -0
  20. package/dist/graphql-client.js +53 -0
  21. package/dist/graphql-client.js.map +1 -0
  22. package/dist/hep-secret-provider.d.ts +18 -0
  23. package/dist/hep-secret-provider.d.ts.map +1 -0
  24. package/dist/hep-secret-provider.js +31 -0
  25. package/dist/hep-secret-provider.js.map +1 -0
  26. package/dist/index.d.ts +15 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +47 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/keycloak-auth.d.ts +50 -0
  31. package/dist/keycloak-auth.d.ts.map +1 -0
  32. package/dist/keycloak-auth.js +92 -0
  33. package/dist/keycloak-auth.js.map +1 -0
  34. package/dist/keycloak-secrets-provisioner.d.ts +44 -0
  35. package/dist/keycloak-secrets-provisioner.d.ts.map +1 -0
  36. package/dist/keycloak-secrets-provisioner.js +94 -0
  37. package/dist/keycloak-secrets-provisioner.js.map +1 -0
  38. package/dist/resilience.d.ts +26 -0
  39. package/dist/resilience.d.ts.map +1 -0
  40. package/dist/resilience.js +84 -0
  41. package/dist/resilience.js.map +1 -0
  42. package/dist/tenant-config.d.ts +23 -0
  43. package/dist/tenant-config.d.ts.map +1 -0
  44. package/dist/tenant-config.js +136 -0
  45. package/dist/tenant-config.js.map +1 -0
  46. package/dist/tls-trust.d.ts +2 -0
  47. package/dist/tls-trust.d.ts.map +1 -0
  48. package/dist/tls-trust.js +27 -0
  49. package/dist/tls-trust.js.map +1 -0
  50. package/dist/types.d.ts +40 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +3 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +69 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-site-name.js","sourceRoot":"","sources":["../src/extract-site-name.ts"],"names":[],"mappings":";;AAQA,0CAUC;AAOD,sCAEC;AAzBD;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,GAAY;IAC1C,MAAM,UAAU,GACb,GAAG,CAAC,OAAO,CAAC,UAAU,CAAY;QAClC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAY;QACnC,GAAG,CAAC,QAAQ,CAAC;IAEf,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,6BAA6B;IAC7B,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAAC,QAAgB,EAAE,QAAgB,EAAE,GAAW;IAC3E,OAAO,GAAG,QAAQ,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import './tls-trust';
2
+ import { GraphQLClient, gql, ClientError } from 'graphql-request';
3
+ type ClientType = 'admin' | 'storefront';
4
+ /**
5
+ * Returns a cached GraphQL client for the given tenant and API type.
6
+ * admin → Shopify Admin API (uses adminApiAccessToken)
7
+ * storefront → Shopify Storefront API (uses storefrontPrivateToken)
8
+ */
9
+ export declare function getGraphQLClient(siteName: string, type?: ClientType): Promise<GraphQLClient>;
10
+ /** Clears cached GraphQL clients (call after tenant config changes). */
11
+ export declare function clearGraphQLClientCache(siteName?: string): void;
12
+ /** Re-exports for convenience */
13
+ export { gql, ClientError };
14
+ //# sourceMappingURL=graphql-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-client.d.ts","sourceRoot":"","sources":["../src/graphql-client.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAIlE,KAAK,UAAU,GAAG,OAAO,GAAG,YAAY,CAAC;AAQzC;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,UAAoB,GACzB,OAAO,CAAC,aAAa,CAAC,CAyBxB;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAO/D;AAED,iCAAiC;AACjC,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientError = exports.gql = void 0;
4
+ exports.getGraphQLClient = getGraphQLClient;
5
+ exports.clearGraphQLClientCache = clearGraphQLClientCache;
6
+ require("./tls-trust");
7
+ const graphql_request_1 = require("graphql-request");
8
+ Object.defineProperty(exports, "gql", { enumerable: true, get: function () { return graphql_request_1.gql; } });
9
+ Object.defineProperty(exports, "ClientError", { enumerable: true, get: function () { return graphql_request_1.ClientError; } });
10
+ const tenant_config_1 = require("./tenant-config");
11
+ const clientCache = new Map();
12
+ function clientKey(siteName, type) {
13
+ return `${siteName}:${type}`;
14
+ }
15
+ /**
16
+ * Returns a cached GraphQL client for the given tenant and API type.
17
+ * admin → Shopify Admin API (uses adminApiAccessToken)
18
+ * storefront → Shopify Storefront API (uses storefrontPrivateToken)
19
+ */
20
+ async function getGraphQLClient(siteName, type = 'admin') {
21
+ const key = clientKey(siteName, type);
22
+ if (clientCache.has(key))
23
+ return clientCache.get(key);
24
+ const config = await (0, tenant_config_1.getTenantConfig)(siteName);
25
+ const { storeDomain, storefrontApiVersion, adminApiAccessToken, storefrontPrivateToken } = config.shopify;
26
+ let url;
27
+ let headers;
28
+ if (type === 'admin') {
29
+ url = `https://${storeDomain}/admin/api/${storefrontApiVersion}/graphql.json`;
30
+ headers = { 'X-Shopify-Access-Token': adminApiAccessToken };
31
+ }
32
+ else {
33
+ url = `https://${storeDomain}/api/${storefrontApiVersion}/graphql.json`;
34
+ // KIWI_SHOPIFY_STOREFRONT_PRIVATE_TOKEN is a private (server-side) Storefront
35
+ // token, which Shopify authenticates via this header — NOT the public-token
36
+ // header (`X-Shopify-Storefront-Access-Token`), which returns 401 for it.
37
+ headers = { 'Shopify-Storefront-Private-Token': storefrontPrivateToken };
38
+ }
39
+ const client = new graphql_request_1.GraphQLClient(url, { headers });
40
+ clientCache.set(key, client);
41
+ return client;
42
+ }
43
+ /** Clears cached GraphQL clients (call after tenant config changes). */
44
+ function clearGraphQLClientCache(siteName) {
45
+ if (siteName) {
46
+ clientCache.delete(clientKey(siteName, 'admin'));
47
+ clientCache.delete(clientKey(siteName, 'storefront'));
48
+ }
49
+ else {
50
+ clientCache.clear();
51
+ }
52
+ }
53
+ //# sourceMappingURL=graphql-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-client.js","sourceRoot":"","sources":["../src/graphql-client.ts"],"names":[],"mappings":";;;AAkBA,4CA4BC;AAGD,0DAOC;AAxDD,uBAAqB;AACrB,qDAAkE;AA0DzD,oFA1De,qBAAG,OA0Df;AAAE,4FA1De,6BAAW,OA0Df;AAxDzB,mDAAkD;AAIlD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAC;AAErD,SAAS,SAAS,CAAC,QAAgB,EAAE,IAAgB;IACnD,OAAO,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,OAAmB,OAAO;IAE1B,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,WAAW,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;IAEvD,MAAM,MAAM,GAAG,MAAM,IAAA,+BAAe,EAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,EAAE,WAAW,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,GACtF,MAAM,CAAC,OAAO,CAAC;IAEjB,IAAI,GAAW,CAAC;IAChB,IAAI,OAA+B,CAAC;IAEpC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,GAAG,GAAG,WAAW,WAAW,cAAc,oBAAoB,eAAe,CAAC;QAC9E,OAAO,GAAG,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,CAAC;IAC9D,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,WAAW,WAAW,QAAQ,oBAAoB,eAAe,CAAC;QACxE,8EAA8E;QAC9E,4EAA4E;QAC5E,0EAA0E;QAC1E,OAAO,GAAG,EAAE,kCAAkC,EAAE,sBAAsB,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,+BAAa,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wEAAwE;AACxE,SAAgB,uBAAuB,CAAC,QAAiB;IACvD,IAAI,QAAQ,EAAE,CAAC;QACb,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IACxD,CAAC;SAAM,CAAC;QACN,WAAW,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ConfigProvider } from './types';
2
+ /**
3
+ * Wraps hep-secret-access's secretManager() behind our ConfigProvider
4
+ * interface (see hep-delivery-service's secret_key.services.ts for the
5
+ * reference usage). secretManager() itself picks env | gsm | csi based on
6
+ * process.env.SECRET_MODE at construction time — constructed once here and
7
+ * reused, so its underlying client (e.g. GCP Secret Manager) isn't rebuilt
8
+ * on every read.
9
+ *
10
+ * hep-secret-access's own EnvProvider returns '' on a missing key rather than
11
+ * throwing; we throw instead, so a misconfigured secret fails loudly here
12
+ * too, regardless of which mode is active.
13
+ */
14
+ export declare class HepSecretProvider implements ConfigProvider {
15
+ private readonly provider;
16
+ get(secretId: string): Promise<string>;
17
+ }
18
+ //# sourceMappingURL=hep-secret-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hep-secret-provider.d.ts","sourceRoot":"","sources":["../src/hep-secret-provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAEtC,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAO7C"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HepSecretProvider = void 0;
4
+ const hep_secret_access_1 = require("hep-secret-access");
5
+ const errors_1 = require("./errors");
6
+ /**
7
+ * Wraps hep-secret-access's secretManager() behind our ConfigProvider
8
+ * interface (see hep-delivery-service's secret_key.services.ts for the
9
+ * reference usage). secretManager() itself picks env | gsm | csi based on
10
+ * process.env.SECRET_MODE at construction time — constructed once here and
11
+ * reused, so its underlying client (e.g. GCP Secret Manager) isn't rebuilt
12
+ * on every read.
13
+ *
14
+ * hep-secret-access's own EnvProvider returns '' on a missing key rather than
15
+ * throwing; we throw instead, so a misconfigured secret fails loudly here
16
+ * too, regardless of which mode is active.
17
+ */
18
+ class HepSecretProvider {
19
+ constructor() {
20
+ this.provider = (0, hep_secret_access_1.secretManager)();
21
+ }
22
+ async get(secretId) {
23
+ const value = await this.provider.read(secretId);
24
+ if (!value) {
25
+ throw new errors_1.SecretNotFoundError(secretId);
26
+ }
27
+ return value;
28
+ }
29
+ }
30
+ exports.HepSecretProvider = HepSecretProvider;
31
+ //# sourceMappingURL=hep-secret-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hep-secret-provider.js","sourceRoot":"","sources":["../src/hep-secret-provider.ts"],"names":[],"mappings":";;;AAAA,yDAAkD;AAElD,qCAA+C;AAG/C;;;;;;;;;;;GAWG;AACH,MAAa,iBAAiB;IAA9B;QACmB,aAAQ,GAAG,IAAA,iCAAa,GAAE,CAAC;IAS9C,CAAC;IAPC,KAAK,CAAC,GAAG,CAAC,QAAgB;QACxB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,4BAAmB,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAVD,8CAUC"}
@@ -0,0 +1,15 @@
1
+ export { getTenantConfig, clearTenantCache, setConfigProvider, getClientTenantDetails } from './tenant-config';
2
+ export { extractSiteName, buildSecretId } from './extract-site-name';
3
+ export { generateKeycloakSecrets } from './keycloak-secrets-provisioner';
4
+ export type { KeycloakSecretsInput, KeycloakSecretsResult } from './keycloak-secrets-provisioner';
5
+ export { createConfigProvider } from './config-provider';
6
+ export type { ProviderMode } from './config-provider';
7
+ export { HepSecretProvider } from './hep-secret-provider';
8
+ export { TTLCache } from './cache';
9
+ export { getGraphQLClient, clearGraphQLClientCache, gql, ClientError } from './graphql-client';
10
+ export { withRetry, withTimeout, CircuitBreaker } from './resilience';
11
+ export { TenantNotFoundError, SecretNotFoundError, TokenExpiredError, TokenInvalidError, UnauthorizedError, CrossTenantAccessError, ForbiddenError, ShopifyApiError, ShopifyUserError, TimeoutError, CircuitOpenError, globalErrorHandler, } from './errors';
12
+ export type { TenantConfig, ShopifyConfig, KeycloakConfig, ConfigProvider, TenantAuthInfo } from './types';
13
+ export { extractBearer, verifyKeycloakToken, keycloakConfigForTenant, requireKeycloakAuth, } from './keycloak-auth';
14
+ export type { KeycloakTokenPayload, KeycloakVerifyConfig } from './keycloak-auth';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC/G,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,YAAY,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EACL,mBAAmB,EAAE,mBAAmB,EACxC,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACvD,sBAAsB,EAAE,cAAc,EACtC,eAAe,EAAE,gBAAgB,EACjC,YAAY,EAAE,gBAAgB,EAC9B,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC3G,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireKeycloakAuth = exports.keycloakConfigForTenant = exports.verifyKeycloakToken = exports.extractBearer = exports.globalErrorHandler = exports.CircuitOpenError = exports.TimeoutError = exports.ShopifyUserError = exports.ShopifyApiError = exports.ForbiddenError = exports.CrossTenantAccessError = exports.UnauthorizedError = exports.TokenInvalidError = exports.TokenExpiredError = exports.SecretNotFoundError = exports.TenantNotFoundError = exports.CircuitBreaker = exports.withTimeout = exports.withRetry = exports.ClientError = exports.gql = exports.clearGraphQLClientCache = exports.getGraphQLClient = exports.TTLCache = exports.HepSecretProvider = exports.createConfigProvider = exports.generateKeycloakSecrets = exports.buildSecretId = exports.extractSiteName = exports.getClientTenantDetails = exports.setConfigProvider = exports.clearTenantCache = exports.getTenantConfig = void 0;
4
+ var tenant_config_1 = require("./tenant-config");
5
+ Object.defineProperty(exports, "getTenantConfig", { enumerable: true, get: function () { return tenant_config_1.getTenantConfig; } });
6
+ Object.defineProperty(exports, "clearTenantCache", { enumerable: true, get: function () { return tenant_config_1.clearTenantCache; } });
7
+ Object.defineProperty(exports, "setConfigProvider", { enumerable: true, get: function () { return tenant_config_1.setConfigProvider; } });
8
+ Object.defineProperty(exports, "getClientTenantDetails", { enumerable: true, get: function () { return tenant_config_1.getClientTenantDetails; } });
9
+ var extract_site_name_1 = require("./extract-site-name");
10
+ Object.defineProperty(exports, "extractSiteName", { enumerable: true, get: function () { return extract_site_name_1.extractSiteName; } });
11
+ Object.defineProperty(exports, "buildSecretId", { enumerable: true, get: function () { return extract_site_name_1.buildSecretId; } });
12
+ var keycloak_secrets_provisioner_1 = require("./keycloak-secrets-provisioner");
13
+ Object.defineProperty(exports, "generateKeycloakSecrets", { enumerable: true, get: function () { return keycloak_secrets_provisioner_1.generateKeycloakSecrets; } });
14
+ var config_provider_1 = require("./config-provider");
15
+ Object.defineProperty(exports, "createConfigProvider", { enumerable: true, get: function () { return config_provider_1.createConfigProvider; } });
16
+ var hep_secret_provider_1 = require("./hep-secret-provider");
17
+ Object.defineProperty(exports, "HepSecretProvider", { enumerable: true, get: function () { return hep_secret_provider_1.HepSecretProvider; } });
18
+ var cache_1 = require("./cache");
19
+ Object.defineProperty(exports, "TTLCache", { enumerable: true, get: function () { return cache_1.TTLCache; } });
20
+ var graphql_client_1 = require("./graphql-client");
21
+ Object.defineProperty(exports, "getGraphQLClient", { enumerable: true, get: function () { return graphql_client_1.getGraphQLClient; } });
22
+ Object.defineProperty(exports, "clearGraphQLClientCache", { enumerable: true, get: function () { return graphql_client_1.clearGraphQLClientCache; } });
23
+ Object.defineProperty(exports, "gql", { enumerable: true, get: function () { return graphql_client_1.gql; } });
24
+ Object.defineProperty(exports, "ClientError", { enumerable: true, get: function () { return graphql_client_1.ClientError; } });
25
+ var resilience_1 = require("./resilience");
26
+ Object.defineProperty(exports, "withRetry", { enumerable: true, get: function () { return resilience_1.withRetry; } });
27
+ Object.defineProperty(exports, "withTimeout", { enumerable: true, get: function () { return resilience_1.withTimeout; } });
28
+ Object.defineProperty(exports, "CircuitBreaker", { enumerable: true, get: function () { return resilience_1.CircuitBreaker; } });
29
+ var errors_1 = require("./errors");
30
+ Object.defineProperty(exports, "TenantNotFoundError", { enumerable: true, get: function () { return errors_1.TenantNotFoundError; } });
31
+ Object.defineProperty(exports, "SecretNotFoundError", { enumerable: true, get: function () { return errors_1.SecretNotFoundError; } });
32
+ Object.defineProperty(exports, "TokenExpiredError", { enumerable: true, get: function () { return errors_1.TokenExpiredError; } });
33
+ Object.defineProperty(exports, "TokenInvalidError", { enumerable: true, get: function () { return errors_1.TokenInvalidError; } });
34
+ Object.defineProperty(exports, "UnauthorizedError", { enumerable: true, get: function () { return errors_1.UnauthorizedError; } });
35
+ Object.defineProperty(exports, "CrossTenantAccessError", { enumerable: true, get: function () { return errors_1.CrossTenantAccessError; } });
36
+ Object.defineProperty(exports, "ForbiddenError", { enumerable: true, get: function () { return errors_1.ForbiddenError; } });
37
+ Object.defineProperty(exports, "ShopifyApiError", { enumerable: true, get: function () { return errors_1.ShopifyApiError; } });
38
+ Object.defineProperty(exports, "ShopifyUserError", { enumerable: true, get: function () { return errors_1.ShopifyUserError; } });
39
+ Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_1.TimeoutError; } });
40
+ Object.defineProperty(exports, "CircuitOpenError", { enumerable: true, get: function () { return errors_1.CircuitOpenError; } });
41
+ Object.defineProperty(exports, "globalErrorHandler", { enumerable: true, get: function () { return errors_1.globalErrorHandler; } });
42
+ var keycloak_auth_1 = require("./keycloak-auth");
43
+ Object.defineProperty(exports, "extractBearer", { enumerable: true, get: function () { return keycloak_auth_1.extractBearer; } });
44
+ Object.defineProperty(exports, "verifyKeycloakToken", { enumerable: true, get: function () { return keycloak_auth_1.verifyKeycloakToken; } });
45
+ Object.defineProperty(exports, "keycloakConfigForTenant", { enumerable: true, get: function () { return keycloak_auth_1.keycloakConfigForTenant; } });
46
+ Object.defineProperty(exports, "requireKeycloakAuth", { enumerable: true, get: function () { return keycloak_auth_1.requireKeycloakAuth; } });
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAA+G;AAAtG,gHAAA,eAAe,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AAAE,kHAAA,iBAAiB,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AACrF,yDAAqE;AAA5D,oHAAA,eAAe,OAAA;AAAE,kHAAA,aAAa,OAAA;AACvC,+EAAyE;AAAhE,uIAAA,uBAAuB,OAAA;AAEhC,qDAAyD;AAAhD,uHAAA,oBAAoB,OAAA;AAE7B,6DAA0D;AAAjD,wHAAA,iBAAiB,OAAA;AAC1B,iCAAmC;AAA1B,iGAAA,QAAQ,OAAA;AACjB,mDAA+F;AAAtF,kHAAA,gBAAgB,OAAA;AAAE,yHAAA,uBAAuB,OAAA;AAAE,qGAAA,GAAG,OAAA;AAAE,6GAAA,WAAW,OAAA;AACpE,2CAAsE;AAA7D,uGAAA,SAAS,OAAA;AAAE,yGAAA,WAAW,OAAA;AAAE,4GAAA,cAAc,OAAA;AAC/C,mCAOkB;AANhB,6GAAA,mBAAmB,OAAA;AAAE,6GAAA,mBAAmB,OAAA;AACxC,2GAAA,iBAAiB,OAAA;AAAE,2GAAA,iBAAiB,OAAA;AAAE,2GAAA,iBAAiB,OAAA;AACvD,gHAAA,sBAAsB,OAAA;AAAE,wGAAA,cAAc,OAAA;AACtC,yGAAA,eAAe,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AACjC,sGAAA,YAAY,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AAC9B,4GAAA,kBAAkB,OAAA;AAGpB,iDAKyB;AAJvB,8GAAA,aAAa,OAAA;AACb,oHAAA,mBAAmB,OAAA;AACnB,wHAAA,uBAAuB,OAAA;AACvB,oHAAA,mBAAmB,OAAA"}
@@ -0,0 +1,50 @@
1
+ import type { NextFunction, Request, Response } from 'express';
2
+ export interface KeycloakTokenPayload {
3
+ sub: string;
4
+ preferred_username?: string;
5
+ email?: string;
6
+ realm_access?: {
7
+ roles: string[];
8
+ };
9
+ [claim: string]: unknown;
10
+ }
11
+ export interface KeycloakVerifyConfig {
12
+ issuer: string;
13
+ jwksUri: string;
14
+ /** Restrict verification to tokens whose `aud` matches. Omit to accept any audience. */
15
+ audience?: string;
16
+ }
17
+ /** Extracts the bearer token from an `Authorization` header. Throws `UnauthorizedError` if missing/malformed. */
18
+ export declare function extractBearer(authorization?: string): string;
19
+ /**
20
+ * Verifies a Keycloak-issued access token: RS256 signature (resolved against
21
+ * `config.jwksUri`), issuer, and — if given — audience. Throws
22
+ * `TokenExpiredError` / `TokenInvalidError` on failure.
23
+ */
24
+ export declare function verifyKeycloakToken(token: string, config: KeycloakVerifyConfig): Promise<KeycloakTokenPayload>;
25
+ /**
26
+ * Builds a `KeycloakVerifyConfig` from a tenant's resolved config (see
27
+ * `getTenantConfig`). `audience` is passed through as-is — it isn't part of
28
+ * `KeycloakConfig` since it's a per-caller restriction, not a per-tenant secret.
29
+ */
30
+ export declare function keycloakConfigForTenant(siteName: string, audience?: string): Promise<KeycloakVerifyConfig>;
31
+ /**
32
+ * Express middleware factory. Verifies the request's bearer token and
33
+ * attaches `{ sub, role }` to `req.user`, and the full decoded payload to
34
+ * `req.keycloakUser`. `config` may be a static `KeycloakVerifyConfig` (a
35
+ * service with its own fixed issuer/JWKS env vars), or a function — sync or
36
+ * async — to resolve one per request, e.g. `() => keycloakConfigForTenant('KIWI')`.
37
+ */
38
+ export declare function requireKeycloakAuth(config: KeycloakVerifyConfig | ((req: Request) => KeycloakVerifyConfig | Promise<KeycloakVerifyConfig>)): (req: Request, _res: Response, next: NextFunction) => void;
39
+ declare global {
40
+ namespace Express {
41
+ interface Request {
42
+ user?: {
43
+ sub: string;
44
+ role?: string;
45
+ };
46
+ keycloakUser?: KeycloakTokenPayload;
47
+ }
48
+ }
49
+ }
50
+ //# sourceMappingURL=keycloak-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-auth.d.ts","sourceRoot":"","sources":["../src/keycloak-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAO/D,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACnC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8B9G;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAGhH;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,oBAAoB,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC,IAE/F,KAAK,OAAO,EAAE,MAAM,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAYhE;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YACtC,YAAY,CAAC,EAAE,oBAAoB,CAAC;SACrC;KACF;CACF"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractBearer = extractBearer;
7
+ exports.verifyKeycloakToken = verifyKeycloakToken;
8
+ exports.keycloakConfigForTenant = keycloakConfigForTenant;
9
+ exports.requireKeycloakAuth = requireKeycloakAuth;
10
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
11
+ const jwks_rsa_1 = __importDefault(require("jwks-rsa"));
12
+ const errors_1 = require("./errors");
13
+ const tenant_config_1 = require("./tenant-config");
14
+ // One jwks-rsa client per distinct JWKS URI, reused across calls/requests —
15
+ // it has its own internal signing-key cache + rate limiter.
16
+ const jwksClients = new Map();
17
+ function getJwksClient(jwksUri) {
18
+ let client = jwksClients.get(jwksUri);
19
+ if (!client) {
20
+ client = (0, jwks_rsa_1.default)({ jwksUri, cache: true, rateLimit: true });
21
+ jwksClients.set(jwksUri, client);
22
+ }
23
+ return client;
24
+ }
25
+ /** Extracts the bearer token from an `Authorization` header. Throws `UnauthorizedError` if missing/malformed. */
26
+ function extractBearer(authorization) {
27
+ if (!authorization?.startsWith('Bearer ')) {
28
+ throw new errors_1.UnauthorizedError('Missing or malformed Authorization header');
29
+ }
30
+ return authorization.slice('Bearer '.length);
31
+ }
32
+ /**
33
+ * Verifies a Keycloak-issued access token: RS256 signature (resolved against
34
+ * `config.jwksUri`), issuer, and — if given — audience. Throws
35
+ * `TokenExpiredError` / `TokenInvalidError` on failure.
36
+ */
37
+ function verifyKeycloakToken(token, config) {
38
+ const client = getJwksClient(config.jwksUri);
39
+ const getSigningKey = (header, callback) => {
40
+ client.getSigningKey(header.kid, (err, key) => {
41
+ if (err || !key) {
42
+ callback(err ?? new Error('Unable to resolve signing key'));
43
+ return;
44
+ }
45
+ callback(null, key.getPublicKey());
46
+ });
47
+ };
48
+ return new Promise((resolve, reject) => {
49
+ jsonwebtoken_1.default.verify(token, getSigningKey, { algorithms: ['RS256'], issuer: config.issuer, audience: config.audience }, (err, decoded) => {
50
+ if (err) {
51
+ reject(err.name === 'TokenExpiredError' ? new errors_1.TokenExpiredError() : new errors_1.TokenInvalidError(err.message));
52
+ return;
53
+ }
54
+ if (!decoded || typeof decoded !== 'object') {
55
+ reject(new errors_1.TokenInvalidError());
56
+ return;
57
+ }
58
+ resolve(decoded);
59
+ });
60
+ });
61
+ }
62
+ /**
63
+ * Builds a `KeycloakVerifyConfig` from a tenant's resolved config (see
64
+ * `getTenantConfig`). `audience` is passed through as-is — it isn't part of
65
+ * `KeycloakConfig` since it's a per-caller restriction, not a per-tenant secret.
66
+ */
67
+ async function keycloakConfigForTenant(siteName, audience) {
68
+ const { keycloak } = await (0, tenant_config_1.getTenantConfig)(siteName);
69
+ return { issuer: keycloak.issuer, jwksUri: keycloak.jwksUri, audience };
70
+ }
71
+ /**
72
+ * Express middleware factory. Verifies the request's bearer token and
73
+ * attaches `{ sub, role }` to `req.user`, and the full decoded payload to
74
+ * `req.keycloakUser`. `config` may be a static `KeycloakVerifyConfig` (a
75
+ * service with its own fixed issuer/JWKS env vars), or a function — sync or
76
+ * async — to resolve one per request, e.g. `() => keycloakConfigForTenant('KIWI')`.
77
+ */
78
+ function requireKeycloakAuth(config) {
79
+ return (req, _res, next) => {
80
+ Promise.resolve()
81
+ .then(async () => {
82
+ const token = extractBearer(req.header('authorization'));
83
+ const resolvedConfig = typeof config === 'function' ? await config(req) : config;
84
+ const payload = await verifyKeycloakToken(token, resolvedConfig);
85
+ req.user = { sub: payload.sub, role: payload.realm_access?.roles?.[0] };
86
+ req.keycloakUser = payload;
87
+ })
88
+ .then(() => next())
89
+ .catch(next);
90
+ };
91
+ }
92
+ //# sourceMappingURL=keycloak-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-auth.js","sourceRoot":"","sources":["../src/keycloak-auth.ts"],"names":[],"mappings":";;;;;AAoCA,sCAKC;AAOD,kDA8BC;AAOD,0DAGC;AASD,kDAeC;AA/GD,gEAA4E;AAC5E,wDAAuD;AAEvD,qCAAmF;AACnF,mDAAkD;AAiBlD,4EAA4E;AAC5E,4DAA4D;AAC5D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;AAElD,SAAS,aAAa,CAAC,OAAe;IACpC,IAAI,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAA,kBAAU,EAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iHAAiH;AACjH,SAAgB,aAAa,CAAC,aAAsB;IAClD,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,0BAAiB,CAAC,2CAA2C,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,KAAa,EAAE,MAA4B;IAC7E,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAG,CAAC,MAAiB,EAAE,QAA4B,EAAQ,EAAE;QAC9E,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAChB,QAAQ,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YACD,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,sBAAG,CAAC,MAAM,CACR,KAAK,EACL,aAAa,EACb,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,EAC3E,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;YACf,IAAI,GAAG,EAAE,CAAC;gBACR,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,IAAI,0BAAiB,EAAE,CAAC,CAAC,CAAC,IAAI,0BAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;gBACxG,OAAO;YACT,CAAC;YACD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC5C,MAAM,CAAC,IAAI,0BAAiB,EAAE,CAAC,CAAC;gBAChC,OAAO;YACT,CAAC;YACD,OAAO,CAAC,OAA+B,CAAC,CAAC;QAC3C,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,uBAAuB,CAAC,QAAgB,EAAE,QAAiB;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,+BAAe,EAAC,QAAQ,CAAC,CAAC;IACrD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,mBAAmB,CACjC,MAAuG;IAEvG,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAQ,EAAE;QAChE,OAAO,CAAC,OAAO,EAAE;aACd,IAAI,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;YACzD,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACjF,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;YACjE,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxE,GAAG,CAAC,YAAY,GAAG,OAAO,CAAC;QAC7B,CAAC,CAAC;aACD,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;aAClB,KAAK,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ export interface KeycloakSecretsInput {
2
+ /** Tenant site name, e.g. 'KIWI'. Uppercased/lowercased as needed internally. */
3
+ siteName: string;
4
+ /** Keycloak server origin, e.g. 'https://dev.idp.hcl-x.com' (no trailing slash needed). */
5
+ idpBaseUrl: string;
6
+ /** Shopify's external-IdP OAuth callback URL for this store (app-specific, not part of the Keycloak realm). */
7
+ callbackUrl: string;
8
+ /** Shopify's post-logout redirect URL for this store (app-specific, not part of the Keycloak realm). */
9
+ postLogoutRedirectUri: string;
10
+ }
11
+ export interface KeycloakSecretsResult {
12
+ siteName: string;
13
+ /** Secret ids that were created/updated — never the values, so callers don't accidentally log them. */
14
+ secretIds: string[];
15
+ }
16
+ /**
17
+ * Creates/updates the 7 `${SITENAME}_KEYCLOAK_*` secrets (BASE_URL, REALM,
18
+ * CLIENT_ID, CLIENT_SECRET, DISCOVERY_URL, JWKS_URI, ISSUER) plus the 2
19
+ * `${SITENAME}_SHOPIFY_*` redirect-url secrets (CALLBACK_URL,
20
+ * POST_LOGOUT_REDIRECT_URI) that `getTenantConfig`'s `fetchKeycloakConfig`
21
+ * reads.
22
+ *
23
+ * `@platform-x/hep-authentication-provider`'s `AuthenticationManager` (the
24
+ * same package + version hep-prelem-registration depends on) is used to
25
+ * verify the site is a real, authenticatable tenant before writing anything —
26
+ * `iamAuthenticator` throws/returns falsy for a site missing from
27
+ * AUTH_TENANTS_INFO or with an unsupported auth_provider. It requires
28
+ * `initIAMSecrets` to have registered a secret-key service first (this
29
+ * function does so itself, scoped to just AUTH_TENANTS_INFO). Its public API
30
+ * doesn't expose the resolved realm/client_id/client_secret though (they're
31
+ * private fields on the KeyClockEndUserConnector it builds), so those values
32
+ * are read the same way `getClientTenantDetails` already does — straight from
33
+ * the AUTH_TENANTS_INFO secret, which is the one source both draw from.
34
+ *
35
+ * The remaining fields are either derived from Keycloak's standard endpoint
36
+ * layout (DISCOVERY_URL/JWKS_URI/ISSUER — same templates
37
+ * hep-authentication-provider and shp-auth-service's env.ts already use for
38
+ * the `kiwi` realm) or supplied by the caller (CALLBACK_URL/
39
+ * POST_LOGOUT_REDIRECT_URI — Shopify-side external-IdP redirect URLs, not
40
+ * Keycloak realm data, so they're written under the `SHOPIFY` provider
41
+ * segment instead and can't be resolved automatically).
42
+ */
43
+ export declare function generateKeycloakSecrets(input: KeycloakSecretsInput): Promise<KeycloakSecretsResult>;
44
+ //# sourceMappingURL=keycloak-secrets-provisioner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-secrets-provisioner.d.ts","sourceRoot":"","sources":["../src/keycloak-secrets-provisioner.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,+GAA+G;IAC/G,WAAW,EAAE,MAAM,CAAC;IACpB,wGAAwG;IACxG,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,uGAAuG;IACvG,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA+DzG"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateKeycloakSecrets = generateKeycloakSecrets;
4
+ const hep_authentication_provider_1 = require("@platform-x/hep-authentication-provider");
5
+ const hep_secret_access_1 = require("hep-secret-access");
6
+ const errors_1 = require("./errors");
7
+ const extract_site_name_1 = require("./extract-site-name");
8
+ const tenant_config_1 = require("./tenant-config");
9
+ /**
10
+ * Creates/updates the 7 `${SITENAME}_KEYCLOAK_*` secrets (BASE_URL, REALM,
11
+ * CLIENT_ID, CLIENT_SECRET, DISCOVERY_URL, JWKS_URI, ISSUER) plus the 2
12
+ * `${SITENAME}_SHOPIFY_*` redirect-url secrets (CALLBACK_URL,
13
+ * POST_LOGOUT_REDIRECT_URI) that `getTenantConfig`'s `fetchKeycloakConfig`
14
+ * reads.
15
+ *
16
+ * `@platform-x/hep-authentication-provider`'s `AuthenticationManager` (the
17
+ * same package + version hep-prelem-registration depends on) is used to
18
+ * verify the site is a real, authenticatable tenant before writing anything —
19
+ * `iamAuthenticator` throws/returns falsy for a site missing from
20
+ * AUTH_TENANTS_INFO or with an unsupported auth_provider. It requires
21
+ * `initIAMSecrets` to have registered a secret-key service first (this
22
+ * function does so itself, scoped to just AUTH_TENANTS_INFO). Its public API
23
+ * doesn't expose the resolved realm/client_id/client_secret though (they're
24
+ * private fields on the KeyClockEndUserConnector it builds), so those values
25
+ * are read the same way `getClientTenantDetails` already does — straight from
26
+ * the AUTH_TENANTS_INFO secret, which is the one source both draw from.
27
+ *
28
+ * The remaining fields are either derived from Keycloak's standard endpoint
29
+ * layout (DISCOVERY_URL/JWKS_URI/ISSUER — same templates
30
+ * hep-authentication-provider and shp-auth-service's env.ts already use for
31
+ * the `kiwi` realm) or supplied by the caller (CALLBACK_URL/
32
+ * POST_LOGOUT_REDIRECT_URI — Shopify-side external-IdP redirect URLs, not
33
+ * Keycloak realm data, so they're written under the `SHOPIFY` provider
34
+ * segment instead and can't be resolved automatically).
35
+ */
36
+ async function generateKeycloakSecrets(input) {
37
+ const siteName = input.siteName.toUpperCase();
38
+ const gsm = (0, hep_secret_access_1.secretManager)();
39
+ // iamAuthenticator() reads AUTH_TENANTS_INFO through a process-global secret
40
+ // service the consuming app must register once via initIAMSecrets (see
41
+ // hep-prelem-registration's secreteKeys.services.ts) — without it, it throws
42
+ // "IAM secrets not initialized" rather than resolving the tenant. Register a
43
+ // minimal one here, scoped to just the one secret iamAuthenticator reads,
44
+ // rather than hep-prelem-registration's readAllSecrets() (which lists every
45
+ // secret in the project — broader than this package's per-key philosophy;
46
+ // see README).
47
+ const rawAuthTenantsInfo = await gsm.read('AUTH_TENANTS_INFO');
48
+ (0, hep_authentication_provider_1.initIAMSecrets)({ getSecretKeys: () => Promise.resolve({ AUTH_TENANTS_INFO: rawAuthTenantsInfo }) });
49
+ const authManager = new hep_authentication_provider_1.AuthenticationManager();
50
+ // iamAuthenticator's return type is `any` upstream; treat it as unknown and
51
+ // only ever check truthiness — never read fields off it (see doc comment above).
52
+ const connector = await authManager.iamAuthenticator(siteName, true);
53
+ if (!connector) {
54
+ throw new errors_1.TenantNotFoundError(siteName);
55
+ }
56
+ const tenantInfo = await (0, tenant_config_1.getClientTenantDetails)(siteName);
57
+ const realm = tenantInfo?.tenant;
58
+ const clientInfo = tenantInfo?.clients?.[0];
59
+ if (!realm || !clientInfo?.client_id || !clientInfo?.client_secret) {
60
+ throw new errors_1.SecretNotFoundError(`AUTH_TENANTS_INFO entry for ${siteName} is missing tenant/client data`);
61
+ }
62
+ const baseUrl = input.idpBaseUrl.replace(/\/+$/, '');
63
+ const keycloakValues = {
64
+ BASE_URL: baseUrl,
65
+ REALM: realm,
66
+ CLIENT_ID: clientInfo.client_id,
67
+ CLIENT_SECRET: clientInfo.client_secret,
68
+ DISCOVERY_URL: `${baseUrl}/realms/${realm}/.well-known/openid-configuration`,
69
+ JWKS_URI: `${baseUrl}/realms/${realm}/protocol/openid-connect/certs`,
70
+ ISSUER: `${baseUrl}/realms/${realm}`,
71
+ };
72
+ // Shopify-side external-IdP redirect URLs — not Keycloak realm data, so
73
+ // written under the SHOPIFY provider segment (matches fetchKeycloakConfig).
74
+ const shopifyValues = {
75
+ CALLBACK_URL: input.callbackUrl,
76
+ POST_LOGOUT_REDIRECT_URI: input.postLogoutRedirectUri,
77
+ };
78
+ const secretIds = [];
79
+ for (const [key, value] of Object.entries(keycloakValues)) {
80
+ const secretId = (0, extract_site_name_1.buildSecretId)(siteName, 'KEYCLOAK', key);
81
+ await gsm.ensureSecret(secretId, { sitename: siteName.toLowerCase(), provider: 'keycloak' });
82
+ await gsm.addVersion(secretId, value);
83
+ secretIds.push(secretId);
84
+ }
85
+ for (const [key, value] of Object.entries(shopifyValues)) {
86
+ const secretId = (0, extract_site_name_1.buildSecretId)(siteName, 'SHOPIFY', key);
87
+ await gsm.ensureSecret(secretId, { sitename: siteName.toLowerCase(), provider: 'shopify' });
88
+ await gsm.addVersion(secretId, value);
89
+ secretIds.push(secretId);
90
+ }
91
+ (0, tenant_config_1.clearTenantCache)(siteName);
92
+ return { siteName, secretIds };
93
+ }
94
+ //# sourceMappingURL=keycloak-secrets-provisioner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak-secrets-provisioner.js","sourceRoot":"","sources":["../src/keycloak-secrets-provisioner.ts"],"names":[],"mappings":";;AAiEA,0DA+DC;AAhID,yFAAgG;AAChG,yDAAkD;AAElD,qCAAoE;AACpE,2DAAoD;AACpD,mDAA2E;AAiC3E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACI,KAAK,UAAU,uBAAuB,CAAC,KAA2B;IACvE,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAA,iCAAa,GAAwC,CAAC;IAElE,6EAA6E;IAC7E,uEAAuE;IACvE,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,0EAA0E;IAC1E,eAAe;IACf,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC/D,IAAA,4CAAc,EAAC,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,CAAC,EAAE,CAAC,CAAC;IAEpG,MAAM,WAAW,GAAG,IAAI,mDAAqB,EAAE,CAAC;IAChD,4EAA4E;IAC5E,iFAAiF;IACjF,MAAM,SAAS,GAAY,MAAM,WAAW,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,4BAAmB,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,IAAA,sCAAsB,EAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,UAAU,EAAE,MAA4B,CAAC;IACvD,MAAM,UAAU,GAAI,UAAU,EAAE,OAA6E,EAAE,CAAC,CAAC,CAAC,CAAC;IACnH,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,EAAE,SAAS,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,CAAC;QACnE,MAAM,IAAI,4BAAmB,CAAC,+BAA+B,QAAQ,gCAAgC,CAAC,CAAC;IACzG,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,cAAc,GAA2B;QAC7C,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,aAAa,EAAE,UAAU,CAAC,aAAa;QACvC,aAAa,EAAE,GAAG,OAAO,WAAW,KAAK,mCAAmC;QAC5E,QAAQ,EAAE,GAAG,OAAO,WAAW,KAAK,gCAAgC;QACpE,MAAM,EAAE,GAAG,OAAO,WAAW,KAAK,EAAE;KACrC,CAAC;IACF,wEAAwE;IACxE,4EAA4E;IAC5E,MAAM,aAAa,GAA2B;QAC5C,YAAY,EAAE,KAAK,CAAC,WAAW;QAC/B,wBAAwB,EAAE,KAAK,CAAC,qBAAqB;KACtD,CAAC;IAEF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAA,iCAAa,EAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAC1D,MAAM,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7F,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzD,MAAM,QAAQ,GAAG,IAAA,iCAAa,EAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QACzD,MAAM,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QAC5F,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;IAED,IAAA,gCAAgB,EAAC,QAAQ,CAAC,CAAC;IAC3B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Retries an async operation with exponential back-off.
3
+ *
4
+ * @param fn - Async function to execute
5
+ * @param retries - Maximum retry attempts (default 3)
6
+ * @param baseMs - Initial delay in ms (default 200); doubles each attempt
7
+ * @param shouldRetry - Optional predicate; returning false stops retrying early
8
+ */
9
+ export declare function withRetry<T>(fn: () => Promise<T>, retries?: number, baseMs?: number, shouldRetry?: (err: unknown) => boolean): Promise<T>;
10
+ /** Wraps an async function with a timeout. Throws if it takes longer than ms. */
11
+ export declare function withTimeout<T>(fn: () => Promise<T>, ms?: number): Promise<T>;
12
+ /**
13
+ * Simple circuit breaker. Opens after `failureThreshold` consecutive failures
14
+ * and resets after `resetAfterMs`.
15
+ */
16
+ export declare class CircuitBreaker {
17
+ private readonly failureThreshold;
18
+ private readonly resetAfterMs;
19
+ private failures;
20
+ private openSince;
21
+ constructor(failureThreshold?: number, resetAfterMs?: number);
22
+ isOpen(): boolean;
23
+ call<T>(fn: () => Promise<T>): Promise<T>;
24
+ reset(): void;
25
+ }
26
+ //# sourceMappingURL=resilience.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resilience.d.ts","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,SAAI,EACX,MAAM,SAAM,EACZ,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GACtC,OAAO,CAAC,CAAC,CAAC,CAaZ;AAED,iFAAiF;AACjF,wBAAsB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAOhF;AAED;;;GAGG;AACH,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAL/B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAuB;gBAGrB,gBAAgB,SAAI,EACpB,YAAY,SAAS;IAGxC,MAAM,IAAI,OAAO;IAUX,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAa/C,KAAK,IAAI,IAAI;CAId"}