feat(http-utils): add facsWrapper for hybrid FACS authorization#1717
feat(http-utils): add facsWrapper for hybrid FACS authorization#1717ravverma wants to merge 1 commit into
Conversation
Introduces the FACS authorization wrapper for the helix-shared-wrap `.with()` chain, plus the supporting resource resolver, state-layer helpers and route-pattern utilities. Enforces FACS permissions for external customer users per route (gated by a LaunchDarkly flag); internal identities and Adobe internal orgs bypass. Net-new, additive only: - src/auth/facs-wrapper.js - src/auth/facs-resource-resolver.js - src/auth/facs-state-layer.js (normalizeImsOrgId, findFacsResourceBinding) - src/auth/route-utils.js - barrel exports in index.js + index.d.ts type Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| tag: 'facs', | ||
| bypass: 'internal-identity', | ||
| method, | ||
| suffix, |
There was a problem hiding this comment.
issue (blocking): The LD flag evaluation hardcodes `${orgId}@AdobeOrg` via template literal. If getTenantIds() returns a pre-suffixed value (e.g. ACME@AdobeOrg), this produces ACME@AdobeOrg@AdobeOrg - the flag evaluates against a non-existent org, returns false, and FACS enforcement is silently bypassed.
Fix: normalizeImsOrgId(orgId) is already imported and used in step 7. Use it here too:
isFacsEnabled = await ldClient.isFlagEnabledForIMSOrg(flagKey, normalizeImsOrgId(orgId));| * @returns A wrapped handler. | ||
| */ | ||
| export function facsWrapper( | ||
| fn: Function, |
There was a problem hiding this comment.
issue (blocking): Record<string, string> does not match the actual shape. The real config is deeply nested (PRODUCTS_ROUTES, PRODUCTS_FACS_RESOURCE_PARAM_ALIASES, INTERNAL_ROUTES). TS consumers get no compile-time guidance - violates the repo rule that .d.ts files accurately describe public APIs.
|
Verdict: Request changes | Complexity: HIGH (large diff; auth/IMS risk flag) Adds Must fix1. LaunchDarkly org-ID double-suffix risk
The LD flag evaluation hardcodes Fix: isFacsEnabled = await ldClient.isFlagEnabledForIMSOrg(flagKey, normalizeImsOrgId(orgId));
2. TypeScript declaration does not reflect actual
|
What
Adds the FACS authorization wrapper (
facsWrapper) tospacecat-shared-http-utils, plus its supporting modules. This is the piece intentionally deferred from the minimal-scope shared PRs (#1711 / #1712) — it builds on theAuthInfoFACS helpers and constants already onmain.Changes (net-new, additive only)
src/auth/facs-wrapper.js— per-route FACS enforcement for external customer users; gated by a LaunchDarkly flag. Internal identities and Adobe internal orgs bypass.src/auth/facs-resource-resolver.js— resolves the ReBAC resource for a request; defers to the controller when no resource is resolvable.src/auth/facs-state-layer.js—normalizeImsOrgId(canonicalises bare ident →<ident>@<authSrc>) andfindFacsResourceBinding.src/auth/route-utils.js— route-pattern matching helpers.src/index.js/src/index.d.ts— barrel exports +facsWrappertype.No changes to
auth-info.js,constants.js, orhandlers/ims.js— those shipped in #1712 andmain's versions are preserved.Testing
npm test -w packages/spacecat-shared-http-utils→ 474 passing🤖 Generated with Claude Code