@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.
- package/README.md +304 -0
- package/dist/cache.d.ts +10 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +28 -0
- package/dist/cache.js.map +1 -0
- package/dist/config-provider.d.ts +17 -0
- package/dist/config-provider.d.ts.map +1 -0
- package/dist/config-provider.js +54 -0
- package/dist/config-provider.js.map +1 -0
- package/dist/errors.d.ts +53 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +117 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract-site-name.d.ts +15 -0
- package/dist/extract-site-name.d.ts.map +1 -0
- package/dist/extract-site-name.js +28 -0
- package/dist/extract-site-name.js.map +1 -0
- package/dist/graphql-client.d.ts +14 -0
- package/dist/graphql-client.d.ts.map +1 -0
- package/dist/graphql-client.js +53 -0
- package/dist/graphql-client.js.map +1 -0
- package/dist/hep-secret-provider.d.ts +18 -0
- package/dist/hep-secret-provider.d.ts.map +1 -0
- package/dist/hep-secret-provider.js +31 -0
- package/dist/hep-secret-provider.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/keycloak-auth.d.ts +50 -0
- package/dist/keycloak-auth.d.ts.map +1 -0
- package/dist/keycloak-auth.js +92 -0
- package/dist/keycloak-auth.js.map +1 -0
- package/dist/keycloak-secrets-provisioner.d.ts +44 -0
- package/dist/keycloak-secrets-provisioner.d.ts.map +1 -0
- package/dist/keycloak-secrets-provisioner.js +94 -0
- package/dist/keycloak-secrets-provisioner.js.map +1 -0
- package/dist/resilience.d.ts +26 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +84 -0
- package/dist/resilience.js.map +1 -0
- package/dist/tenant-config.d.ts +23 -0
- package/dist/tenant-config.d.ts.map +1 -0
- package/dist/tenant-config.js +136 -0
- package/dist/tenant-config.js.map +1 -0
- package/dist/tls-trust.d.ts +2 -0
- package/dist/tls-trust.d.ts.map +1 -0
- package/dist/tls-trust.js +27 -0
- package/dist/tls-trust.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CircuitBreaker = void 0;
|
|
4
|
+
exports.withRetry = withRetry;
|
|
5
|
+
exports.withTimeout = withTimeout;
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
/**
|
|
8
|
+
* Retries an async operation with exponential back-off.
|
|
9
|
+
*
|
|
10
|
+
* @param fn - Async function to execute
|
|
11
|
+
* @param retries - Maximum retry attempts (default 3)
|
|
12
|
+
* @param baseMs - Initial delay in ms (default 200); doubles each attempt
|
|
13
|
+
* @param shouldRetry - Optional predicate; returning false stops retrying early
|
|
14
|
+
*/
|
|
15
|
+
async function withRetry(fn, retries = 3, baseMs = 200, shouldRetry) {
|
|
16
|
+
let lastError;
|
|
17
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
return await fn();
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
lastError = err;
|
|
23
|
+
if (attempt === retries)
|
|
24
|
+
break;
|
|
25
|
+
if (shouldRetry && !shouldRetry(err))
|
|
26
|
+
break;
|
|
27
|
+
await sleep(baseMs * 2 ** attempt);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw lastError;
|
|
31
|
+
}
|
|
32
|
+
/** Wraps an async function with a timeout. Throws if it takes longer than ms. */
|
|
33
|
+
async function withTimeout(fn, ms = 5000) {
|
|
34
|
+
return Promise.race([
|
|
35
|
+
fn(),
|
|
36
|
+
new Promise((_, reject) => setTimeout(() => reject(new errors_1.TimeoutError(`Operation timed out after ${ms}ms`)), ms)),
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Simple circuit breaker. Opens after `failureThreshold` consecutive failures
|
|
41
|
+
* and resets after `resetAfterMs`.
|
|
42
|
+
*/
|
|
43
|
+
class CircuitBreaker {
|
|
44
|
+
constructor(failureThreshold = 5, resetAfterMs = 30000) {
|
|
45
|
+
this.failureThreshold = failureThreshold;
|
|
46
|
+
this.resetAfterMs = resetAfterMs;
|
|
47
|
+
this.failures = 0;
|
|
48
|
+
this.openSince = null;
|
|
49
|
+
}
|
|
50
|
+
isOpen() {
|
|
51
|
+
if (this.openSince === null)
|
|
52
|
+
return false;
|
|
53
|
+
if (Date.now() - this.openSince >= this.resetAfterMs) {
|
|
54
|
+
this.failures = 0;
|
|
55
|
+
this.openSince = null;
|
|
56
|
+
return false; // half-open: allow one attempt
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
async call(fn) {
|
|
61
|
+
if (this.isOpen())
|
|
62
|
+
throw new errors_1.CircuitOpenError('Circuit breaker is open');
|
|
63
|
+
try {
|
|
64
|
+
const result = await fn();
|
|
65
|
+
this.failures = 0;
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
this.failures++;
|
|
70
|
+
if (this.failures >= this.failureThreshold)
|
|
71
|
+
this.openSince = Date.now();
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
reset() {
|
|
76
|
+
this.failures = 0;
|
|
77
|
+
this.openSince = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.CircuitBreaker = CircuitBreaker;
|
|
81
|
+
function sleep(ms) {
|
|
82
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=resilience.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilience.js","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":";;;AAUA,8BAkBC;AAGD,kCAOC;AAtCD,qCAA0D;AAE1D;;;;;;;GAOG;AACI,KAAK,UAAU,SAAS,CAC7B,EAAoB,EACpB,OAAO,GAAG,CAAC,EACX,MAAM,GAAG,GAAG,EACZ,WAAuC;IAEvC,IAAI,SAAkB,CAAC;IACvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,CAAC;YAChB,IAAI,OAAO,KAAK,OAAO;gBAAE,MAAM;YAC/B,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;gBAAE,MAAM;YAC5C,MAAM,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,iFAAiF;AAC1E,KAAK,UAAU,WAAW,CAAI,EAAoB,EAAE,EAAE,GAAG,IAAI;IAClE,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,EAAE,EAAE;QACJ,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,qBAAY,CAAC,6BAA6B,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CACpF;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAa,cAAc;IAIzB,YACmB,mBAAmB,CAAC,EACpB,eAAe,KAAM;QADrB,qBAAgB,GAAhB,gBAAgB,CAAI;QACpB,iBAAY,GAAZ,YAAY,CAAS;QALhC,aAAQ,GAAG,CAAC,CAAC;QACb,cAAS,GAAkB,IAAI,CAAC;IAKrC,CAAC;IAEJ,MAAM;QACJ,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACrD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,OAAO,KAAK,CAAC,CAAC,+BAA+B;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,EAAoB;QAChC,IAAI,IAAI,CAAC,MAAM,EAAE;YAAE,MAAM,IAAI,yBAAgB,CAAC,yBAAyB,CAAC,CAAC;QACzE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YAClB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,gBAAgB;gBAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACxE,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;CACF;AApCD,wCAoCC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConfigProvider, TenantConfig, TenantAuthInfo } from './types';
|
|
2
|
+
/** Override the config provider (useful for testing with mocks). */
|
|
3
|
+
export declare function setConfigProvider(p: ConfigProvider): void;
|
|
4
|
+
/**
|
|
5
|
+
* Main entry point. Fetches and caches tenant config for 5 minutes.
|
|
6
|
+
* @param siteName - Normalized site name e.g. 'KIWI'
|
|
7
|
+
*/
|
|
8
|
+
export declare function getTenantConfig(siteName: string): Promise<TenantConfig>;
|
|
9
|
+
/** Clear cached config for a tenant or all tenants. */
|
|
10
|
+
export declare function clearTenantCache(siteName?: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Looks up a site's entry in the AUTH_TENANTS_INFO secret (a base64-encoded
|
|
13
|
+
* JSON array). Returns `null` when the site isn't listed there, meaning it
|
|
14
|
+
* uses the shared realm rather than a dedicated one.
|
|
15
|
+
*
|
|
16
|
+
* Case-insensitive: `buildSecretId` already normalizes site names to
|
|
17
|
+
* uppercase for secret ids, so a caller passing e.g. 'kiwi' while
|
|
18
|
+
* AUTH_TENANTS_INFO stores 'KIWI' (or vice versa) must still match here —
|
|
19
|
+
* otherwise this silently returns null and the caller sees a confusing
|
|
20
|
+
* "missing tenant/client data" error instead of the real entry.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getClientTenantDetails(siteName: string): Promise<TenantAuthInfo | null>;
|
|
23
|
+
//# sourceMappingURL=tenant-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-config.d.ts","sourceRoot":"","sources":["../src/tenant-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAiC,cAAc,EAAE,MAAM,SAAS,CAAC;AAY3G,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI,CAIzD;AAqED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAU7E;AAED,uDAAuD;AACvD,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAOxD;AAaD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAI7F"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setConfigProvider = setConfigProvider;
|
|
4
|
+
exports.getTenantConfig = getTenantConfig;
|
|
5
|
+
exports.clearTenantCache = clearTenantCache;
|
|
6
|
+
exports.getClientTenantDetails = getClientTenantDetails;
|
|
7
|
+
const cache_1 = require("./cache");
|
|
8
|
+
const config_provider_1 = require("./config-provider");
|
|
9
|
+
const errors_1 = require("./errors");
|
|
10
|
+
const extract_site_name_1 = require("./extract-site-name");
|
|
11
|
+
const cache = new cache_1.TTLCache();
|
|
12
|
+
let provider = (0, config_provider_1.createConfigProvider)('auto');
|
|
13
|
+
// Single global secret (not per-site, so it bypasses buildSecretId) listing
|
|
14
|
+
// every dedicated-realm tenant. Cached under one fixed key since it's fetched
|
|
15
|
+
// and parsed as a whole rather than per site. Mirrors hep-delivery-service's
|
|
16
|
+
// SECRET_KEYS.AUTH_TENANTS_INFO / getClientTenantDetails (user_auth.services.ts).
|
|
17
|
+
const AUTH_TENANTS_INFO_SECRET_ID = 'AUTH_TENANTS_INFO';
|
|
18
|
+
const authTenantsCache = new cache_1.TTLCache();
|
|
19
|
+
/** Override the config provider (useful for testing with mocks). */
|
|
20
|
+
function setConfigProvider(p) {
|
|
21
|
+
provider = p;
|
|
22
|
+
cache.clear();
|
|
23
|
+
authTenantsCache.clear();
|
|
24
|
+
}
|
|
25
|
+
async function fetchShopifyConfig(siteName) {
|
|
26
|
+
const s = (key) => provider.get((0, extract_site_name_1.buildSecretId)(siteName, 'SHOPIFY', key));
|
|
27
|
+
const [adminApiAccessToken, isEnabledRaw, planName, clientId, clientSecret, storeDomain, storefrontApiVersion, storefrontPrivateToken, webhookHmacSecret,] = await Promise.all([
|
|
28
|
+
s('ADMIN_API_ACCESS_TOKEN'), s('IS_ENABLED'), s('PLAN_NAME'),
|
|
29
|
+
s('CLIENT_ID'), s('CLIENT_SECRET'), s('STORE_DOMAIN'),
|
|
30
|
+
s('STOREFRONT_API_VERSION'), s('STOREFRONT_PRIVATE_TOKEN'), s('WEBHOOK_HMAC_SECRET'),
|
|
31
|
+
]);
|
|
32
|
+
return {
|
|
33
|
+
adminApiAccessToken,
|
|
34
|
+
isEnabled: isEnabledRaw.toLowerCase() === 'true',
|
|
35
|
+
planName, clientId, clientSecret, storeDomain,
|
|
36
|
+
storefrontApiVersion, storefrontPrivateToken, webhookHmacSecret,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* realm/clientId/clientSecret and baseUrl are no longer read as their own
|
|
41
|
+
* `${SITENAME}_KEYCLOAK_*` secrets. Instead this mirrors how
|
|
42
|
+
* `@platform-x/hep-authentication-provider`'s `AuthenticationManager.iamAuthenticator`
|
|
43
|
+
* (see hep-delivery-service) resolves a tenant: realm/clientId/clientSecret
|
|
44
|
+
* come from the same AUTH_TENANTS_INFO entry it reads (via
|
|
45
|
+
* `getClientTenantDetails`), and baseUrl from the same `KEYCLOCK_HOST` env var
|
|
46
|
+
* the package itself reads. discoveryUrl/jwksUri/issuer follow Keycloak's
|
|
47
|
+
* fixed endpoint layout, derived from those two rather than stored — the same
|
|
48
|
+
* templates `generateKeycloakSecrets` uses to precompute them.
|
|
49
|
+
*
|
|
50
|
+
* callbackUrl/postLogoutRedirectUri stay as their own secrets, read under the
|
|
51
|
+
* `SHOPIFY` provider segment (`${SITENAME}_SHOPIFY_CALLBACK_URL` /
|
|
52
|
+
* `${SITENAME}_SHOPIFY_POST_LOGOUT_REDIRECT_URI`) rather than `KEYCLOAK` —
|
|
53
|
+
* they're Shopify-side external-IdP redirect URLs, not Keycloak realm data,
|
|
54
|
+
* so nothing about the tenant's realm can derive them.
|
|
55
|
+
*/
|
|
56
|
+
async function fetchKeycloakConfig(siteName) {
|
|
57
|
+
const tenant = await getClientTenantDetails(siteName);
|
|
58
|
+
const clientInfo = tenant?.clients?.[0];
|
|
59
|
+
const realm = tenant?.tenant;
|
|
60
|
+
if (!realm || !clientInfo?.client_id || !clientInfo?.client_secret) {
|
|
61
|
+
throw new errors_1.SecretNotFoundError(`AUTH_TENANTS_INFO entry for ${siteName} is missing tenant/client data`);
|
|
62
|
+
}
|
|
63
|
+
const baseUrl = (process.env.KEYCLOCK_HOST ?? '').replace(/\/+$/, '');
|
|
64
|
+
if (!baseUrl) {
|
|
65
|
+
throw new errors_1.SecretNotFoundError('KEYCLOCK_HOST');
|
|
66
|
+
}
|
|
67
|
+
const [callbackUrl, postLogoutRedirectUri] = await Promise.all([
|
|
68
|
+
provider.get((0, extract_site_name_1.buildSecretId)(siteName, 'SHOPIFY', 'CALLBACK_URL')),
|
|
69
|
+
provider.get((0, extract_site_name_1.buildSecretId)(siteName, 'SHOPIFY', 'POST_LOGOUT_REDIRECT_URI')),
|
|
70
|
+
]);
|
|
71
|
+
return {
|
|
72
|
+
baseUrl,
|
|
73
|
+
realm,
|
|
74
|
+
clientId: clientInfo.client_id,
|
|
75
|
+
clientSecret: clientInfo.client_secret,
|
|
76
|
+
discoveryUrl: `${baseUrl}/realms/${realm}/.well-known/openid-configuration`,
|
|
77
|
+
jwksUri: `${baseUrl}/realms/${realm}/protocol/openid-connect/certs`,
|
|
78
|
+
issuer: `${baseUrl}/realms/${realm}`,
|
|
79
|
+
callbackUrl,
|
|
80
|
+
postLogoutRedirectUri,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Main entry point. Fetches and caches tenant config for 5 minutes.
|
|
85
|
+
* @param siteName - Normalized site name e.g. 'KIWI'
|
|
86
|
+
*/
|
|
87
|
+
async function getTenantConfig(siteName) {
|
|
88
|
+
const cached = cache.get(siteName);
|
|
89
|
+
if (cached)
|
|
90
|
+
return cached;
|
|
91
|
+
const [shopify, keycloak] = await Promise.all([
|
|
92
|
+
fetchShopifyConfig(siteName),
|
|
93
|
+
fetchKeycloakConfig(siteName),
|
|
94
|
+
]);
|
|
95
|
+
const config = { siteName, shopify, keycloak };
|
|
96
|
+
cache.set(siteName, config);
|
|
97
|
+
return config;
|
|
98
|
+
}
|
|
99
|
+
/** Clear cached config for a tenant or all tenants. */
|
|
100
|
+
function clearTenantCache(siteName) {
|
|
101
|
+
if (siteName) {
|
|
102
|
+
cache.delete(siteName);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
cache.clear();
|
|
106
|
+
authTenantsCache.clear();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function fetchAuthTenantsInfo() {
|
|
110
|
+
const cached = authTenantsCache.get(AUTH_TENANTS_INFO_SECRET_ID);
|
|
111
|
+
if (cached)
|
|
112
|
+
return cached;
|
|
113
|
+
const raw = await provider.get(AUTH_TENANTS_INFO_SECRET_ID);
|
|
114
|
+
// 'ascii' to match hep-delivery-service's base64ToString/stringToBase64,
|
|
115
|
+
// which is how this secret is encoded on write.
|
|
116
|
+
const tenants = JSON.parse(Buffer.from(raw, 'base64').toString('ascii'));
|
|
117
|
+
authTenantsCache.set(AUTH_TENANTS_INFO_SECRET_ID, tenants);
|
|
118
|
+
return tenants;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Looks up a site's entry in the AUTH_TENANTS_INFO secret (a base64-encoded
|
|
122
|
+
* JSON array). Returns `null` when the site isn't listed there, meaning it
|
|
123
|
+
* uses the shared realm rather than a dedicated one.
|
|
124
|
+
*
|
|
125
|
+
* Case-insensitive: `buildSecretId` already normalizes site names to
|
|
126
|
+
* uppercase for secret ids, so a caller passing e.g. 'kiwi' while
|
|
127
|
+
* AUTH_TENANTS_INFO stores 'KIWI' (or vice versa) must still match here —
|
|
128
|
+
* otherwise this silently returns null and the caller sees a confusing
|
|
129
|
+
* "missing tenant/client data" error instead of the real entry.
|
|
130
|
+
*/
|
|
131
|
+
async function getClientTenantDetails(siteName) {
|
|
132
|
+
const tenants = await fetchAuthTenantsInfo();
|
|
133
|
+
const target = siteName.toUpperCase();
|
|
134
|
+
return tenants.find((tenant) => tenant.site_name.toUpperCase() === target) ?? null;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=tenant-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-config.js","sourceRoot":"","sources":["../src/tenant-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls-trust.d.ts","sourceRoot":"","sources":["../src/tls-trust.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
const node_tls_1 = __importDefault(require("node:tls"));
|
|
7
|
+
/**
|
|
8
|
+
* Extends Node's default TLS trust store with the OS/system certificate store.
|
|
9
|
+
*
|
|
10
|
+
* `getGraphQLClient` calls Shopify through Node's built-in `fetch`, which only
|
|
11
|
+
* trusts a small bundled CA list by default — it does NOT include a corporate
|
|
12
|
+
* proxy's TLS-interception root CA. That CA IS already in the OS trust store
|
|
13
|
+
* (that's what lets browsers on the same machine reach HTTPS sites), so pulling
|
|
14
|
+
* it in here — once, for every consumer of this package — fixes
|
|
15
|
+
* "unable to get local issuer certificate" against Shopify/Keycloak without
|
|
16
|
+
* every service needing its own `--use-system-ca` flag or `cacert.pem` file.
|
|
17
|
+
*
|
|
18
|
+
* Runs once at module load (Node caches modules), process-wide. Falls back to
|
|
19
|
+
* the bundled default list on a Node/platform without OS store integration.
|
|
20
|
+
*/
|
|
21
|
+
try {
|
|
22
|
+
node_tls_1.default.setDefaultCACertificates([...node_tls_1.default.getCACertificates('default'), ...node_tls_1.default.getCACertificates('system')]);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// No system store integration available — keep Node's bundled defaults.
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=tls-trust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls-trust.js","sourceRoot":"","sources":["../src/tls-trust.ts"],"names":[],"mappings":";;;;;AAAA,wDAA2B;AAE3B;;;;;;;;;;;;;GAaG;AACH,IAAI,CAAC;IACH,kBAAG,CAAC,wBAAwB,CAAC,CAAC,GAAG,kBAAG,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,GAAG,kBAAG,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1G,CAAC;AAAC,MAAM,CAAC;IACP,wEAAwE;AAC1E,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface ShopifyConfig {
|
|
2
|
+
adminApiAccessToken: string;
|
|
3
|
+
isEnabled: boolean;
|
|
4
|
+
planName: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret: string;
|
|
7
|
+
storeDomain: string;
|
|
8
|
+
storefrontApiVersion: string;
|
|
9
|
+
storefrontPrivateToken: string;
|
|
10
|
+
webhookHmacSecret: string;
|
|
11
|
+
}
|
|
12
|
+
export interface KeycloakConfig {
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
realm: string;
|
|
15
|
+
clientId: string;
|
|
16
|
+
clientSecret: string;
|
|
17
|
+
discoveryUrl: string;
|
|
18
|
+
jwksUri: string;
|
|
19
|
+
issuer: string;
|
|
20
|
+
callbackUrl: string;
|
|
21
|
+
postLogoutRedirectUri: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TenantConfig {
|
|
24
|
+
siteName: string;
|
|
25
|
+
shopify: ShopifyConfig;
|
|
26
|
+
keycloak: KeycloakConfig;
|
|
27
|
+
}
|
|
28
|
+
export interface ConfigProvider {
|
|
29
|
+
get(secretId: string): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* One entry from the AUTH_TENANTS_INFO secret — a base64-encoded JSON array
|
|
33
|
+
* listing every site that has a dedicated Keycloak realm. A site absent from
|
|
34
|
+
* this list uses the shared realm instead.
|
|
35
|
+
*/
|
|
36
|
+
export interface TenantAuthInfo {
|
|
37
|
+
site_name: string;
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@platform-x/shp-auth-client",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Shared Shopify/Keycloak tenant config resolver, resilient GraphQL client, and Keycloak token verification for Platform-X microservices",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"license": "UNLICENSED",
|
|
11
|
+
"author": "HCL-X",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/hcl-x/shp-auth-client.git"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/hcl-x/shp-auth-client#readme",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "node ./node_modules/typescript/bin/tsc -p tsconfig.build.json",
|
|
22
|
+
"dev": "node ./node_modules/typescript/bin/tsc -p tsconfig.build.json --watch",
|
|
23
|
+
"clean": "rm -rf dist",
|
|
24
|
+
"lint": "eslint .",
|
|
25
|
+
"format": "prettier --write .",
|
|
26
|
+
"test": "cross-env NODE_ENV=test jest --runInBand",
|
|
27
|
+
"test:watch": "jest --watch",
|
|
28
|
+
"typecheck": "node ./node_modules/typescript/bin/tsc --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"overrides": {
|
|
31
|
+
"axios": "^1.20.0",
|
|
32
|
+
"qs": "^6.16.0",
|
|
33
|
+
"uuid": "11.1.1",
|
|
34
|
+
"@platform-x/hep-authentication-provider": {
|
|
35
|
+
"uuid": "11.1.1"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@platform-x/hep-authentication-provider": "2.3.24",
|
|
40
|
+
"graphql": "^16.8.0",
|
|
41
|
+
"graphql-request": "^6.1.0",
|
|
42
|
+
"hep-secret-access": "^1.3.4",
|
|
43
|
+
"jsonwebtoken": "^9.0.3",
|
|
44
|
+
"jwks-rsa": "^3.2.2"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"express": "^4.18.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@eslint/js": "^9.18.0",
|
|
51
|
+
"@types/express": "^4.17.21",
|
|
52
|
+
"@types/jest": "^29.5.14",
|
|
53
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
54
|
+
"@types/node": "^22.10.0",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
56
|
+
"@typescript-eslint/parser": "^8.18.0",
|
|
57
|
+
"cross-env": "^7.0.3",
|
|
58
|
+
"eslint": "^9.18.0",
|
|
59
|
+
"eslint-config-prettier": "^9.1.0",
|
|
60
|
+
"eslint-import-resolver-typescript": "^3.10.1",
|
|
61
|
+
"eslint-plugin-import": "^2.31.0",
|
|
62
|
+
"globals": "^14.0.0",
|
|
63
|
+
"jest": "^29.7.0",
|
|
64
|
+
"prettier": "^3.4.2",
|
|
65
|
+
"ts-jest": "^29.2.5",
|
|
66
|
+
"ts-node": "^10.9.2",
|
|
67
|
+
"typescript": "^5.3.0"
|
|
68
|
+
}
|
|
69
|
+
}
|