@salesforce/b2c-cli 2.3.0 → 2.4.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/bin/dev.js +8 -8
- package/bin/run.js +5 -7
- package/content/guidance/b2c/b2c-business-manager-extensions/SKILL.md +361 -0
- package/content/guidance/b2c/b2c-business-manager-extensions/references/EXTENSIONS-XML.md +458 -0
- package/content/guidance/b2c/b2c-controllers/SKILL.md +301 -0
- package/content/guidance/b2c/b2c-controllers/references/CLASSIC-PATTERNS.md +335 -0
- package/content/guidance/b2c/b2c-controllers/references/SFRA-PATTERNS.md +400 -0
- package/content/guidance/b2c/b2c-custom-api-development/SKILL.md +281 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/CONTRACT.md +142 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/IMPLEMENTATION.md +153 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/TESTING.md +118 -0
- package/content/guidance/b2c/b2c-custom-caches/SKILL.md +279 -0
- package/content/guidance/b2c/b2c-custom-job-steps/SKILL.md +520 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/CHUNK-ORIENTED.md +377 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/JOBS-XML.md +212 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/STEPTYPES-JSON.md +373 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/TASK-ORIENTED.md +344 -0
- package/content/guidance/b2c/b2c-custom-objects/SKILL.md +327 -0
- package/content/guidance/b2c/b2c-custom-objects/references/OCAPI-SEARCH.md +298 -0
- package/content/guidance/b2c/b2c-forms/SKILL.md +242 -0
- package/content/guidance/b2c/b2c-forms/references/FORM-XML.md +409 -0
- package/content/guidance/b2c/b2c-hooks/SKILL.md +500 -0
- package/content/guidance/b2c/b2c-hooks/references/OCAPI-SCAPI-HOOKS.md +403 -0
- package/content/guidance/b2c/b2c-hooks/references/ORDER-HOOK-LIFECYCLE.md +169 -0
- package/content/guidance/b2c/b2c-hooks/references/SYSTEM-HOOKS.md +433 -0
- package/content/guidance/b2c/b2c-isml/SKILL.md +320 -0
- package/content/guidance/b2c/b2c-isml/references/EXPRESSIONS.md +366 -0
- package/content/guidance/b2c/b2c-isml/references/TAGS.md +443 -0
- package/content/guidance/b2c/b2c-localization/SKILL.md +344 -0
- package/content/guidance/b2c/b2c-localization/references/PATTERNS.md +407 -0
- package/content/guidance/b2c/b2c-logging/SKILL.md +352 -0
- package/content/guidance/b2c/b2c-logging/references/LOG-FILES.md +282 -0
- package/content/guidance/b2c/b2c-metadata/SKILL.md +406 -0
- package/content/guidance/b2c/b2c-metadata/references/SYSTEM-OBJECTS.md +320 -0
- package/content/guidance/b2c/b2c-metadata/references/XML-EXAMPLES.md +366 -0
- package/content/guidance/b2c/b2c-onboarding/SKILL.md +154 -0
- package/content/guidance/b2c/b2c-ordering/SKILL.md +391 -0
- package/content/guidance/b2c/b2c-page-designer/SKILL.md +410 -0
- package/content/guidance/b2c/b2c-page-designer/references/ATTRIBUTE-TYPES.md +436 -0
- package/content/guidance/b2c/b2c-page-designer/references/META-DEFINITIONS.md +340 -0
- package/content/guidance/b2c/b2c-querying-data/SKILL.md +289 -0
- package/content/guidance/b2c/b2c-querying-data/references/PERFORMANCE-APIS.md +74 -0
- package/content/guidance/b2c/b2c-scapi-admin/SKILL.md +53 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/CLIENT-EXAMPLES.md +440 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/INTEGRATION-PATTERNS.md +518 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/OAUTH-SCOPES.md +337 -0
- package/content/guidance/b2c/b2c-scapi-shopper/SKILL.md +56 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/CHECKOUT-FLOW.md +466 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/CLIENT-EXAMPLES.md +351 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/COMMON-PATTERNS.md +390 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/SCOPES.md +290 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/SKILL.md +420 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/PASSKEYS.md +126 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/SESSION-BRIDGE.md +267 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/TOKEN-LIFECYCLE.md +367 -0
- package/content/guidance/b2c/b2c-webservices/SKILL.md +318 -0
- package/content/guidance/b2c/b2c-webservices/references/FTP-SERVICES.md +524 -0
- package/content/guidance/b2c/b2c-webservices/references/HTTP-SERVICES.md +578 -0
- package/content/guidance/b2c/b2c-webservices/references/SERVICES-XML.md +351 -0
- package/content/guidance/b2c/b2c-webservices/references/SOAP-SERVICES.md +587 -0
- package/content/guidance/b2c-cli/b2c-am/SKILL.md +277 -0
- package/content/guidance/b2c-cli/b2c-bm-users-roles/SKILL.md +210 -0
- package/content/guidance/b2c-cli/b2c-cap/SKILL.md +131 -0
- package/content/guidance/b2c-cli/b2c-cip/SKILL.md +116 -0
- package/content/guidance/b2c-cli/b2c-cip/references/KNOWN_TABLES.md +105 -0
- package/content/guidance/b2c-cli/b2c-cip/references/SALES_ANALYSIS.md +50 -0
- package/content/guidance/b2c-cli/b2c-cip/references/STARTER_QUERIES.md +147 -0
- package/content/guidance/b2c-cli/b2c-code/SKILL.md +146 -0
- package/content/guidance/b2c-cli/b2c-config/SKILL.md +462 -0
- package/content/guidance/b2c-cli/b2c-content/SKILL.md +176 -0
- package/content/guidance/b2c-cli/b2c-debug/SKILL.md +138 -0
- package/content/guidance/b2c-cli/b2c-docs/SKILL.md +301 -0
- package/content/guidance/b2c-cli/b2c-ecdn/SKILL.md +135 -0
- package/content/guidance/b2c-cli/b2c-ecdn/references/ADVANCED.md +97 -0
- package/content/guidance/b2c-cli/b2c-ecdn/references/SECURITY.md +75 -0
- package/content/guidance/b2c-cli/b2c-import-set-migrations/SKILL.md +265 -0
- package/content/guidance/b2c-cli/b2c-job/SKILL.md +64 -0
- package/content/guidance/b2c-cli/b2c-job/references/EXPORT.md +121 -0
- package/content/guidance/b2c-cli/b2c-job/references/IMPORT.md +55 -0
- package/content/guidance/b2c-cli/b2c-job/references/RUN-AND-MONITOR.md +119 -0
- package/content/guidance/b2c-cli/b2c-logs/SKILL.md +249 -0
- package/content/guidance/b2c-cli/b2c-metrics/SKILL.md +315 -0
- package/content/guidance/b2c-cli/b2c-mrt/SKILL.md +207 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/BUNDLE-COMMANDS.md +203 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/ENVIRONMENT-COMMANDS.md +218 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/PROJECT-COMMANDS.md +154 -0
- package/content/guidance/b2c-cli/b2c-sandbox/SKILL.md +112 -0
- package/content/guidance/b2c-cli/b2c-scapi-custom/SKILL.md +126 -0
- package/content/guidance/b2c-cli/b2c-scapi-schemas/SKILL.md +66 -0
- package/content/guidance/b2c-cli/b2c-scapi-schemas/references/CLI-EXAMPLES.md +108 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/SKILL.md +57 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/IMPORT-OPTIONS.md +118 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/METADATA-XML.md +381 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/WORKFLOWS.md +182 -0
- package/content/guidance/b2c-cli/b2c-sites/SKILL.md +112 -0
- package/content/guidance/b2c-cli/b2c-slas/SKILL.md +182 -0
- package/content/guidance/b2c-cli/b2c-webdav/SKILL.md +186 -0
- package/content/guidance/b2c-ops/b2c-checkout-triage/SKILL.md +68 -0
- package/content/guidance/b2c-ops/b2c-job-health/SKILL.md +75 -0
- package/content/guidance/b2c-ops/b2c-job-health/references/job-logs.md +21 -0
- package/content/guidance/b2c-ops/b2c-order-failure-triage/SKILL.md +66 -0
- package/content/guidance/b2c-ops/b2c-order-failure-triage/references/order-evidence.md +87 -0
- package/content/guidance/b2c-ops/b2c-production-triage/SKILL.md +87 -0
- package/content/guidance/b2c-ops/b2c-production-triage/references/escalation.md +57 -0
- package/content/guidance/index.json +1949 -0
- package/content/guidance/storefront-next/sfnext-accessibility/SKILL.md +103 -0
- package/content/guidance/storefront-next/sfnext-accessibility/references/checklist.md +64 -0
- package/content/guidance/storefront-next/sfnext-analytics-consent/SKILL.md +91 -0
- package/content/guidance/storefront-next/sfnext-analytics-consent/references/CUSTOM-ADAPTER.md +49 -0
- package/content/guidance/storefront-next/sfnext-authentication/SKILL.md +127 -0
- package/content/guidance/storefront-next/sfnext-authentication/references/COOKIES.md +39 -0
- package/content/guidance/storefront-next/sfnext-authentication/references/LOGIN-FLOWS.md +59 -0
- package/content/guidance/storefront-next/sfnext-commerce-features/SKILL.md +67 -0
- package/content/guidance/storefront-next/sfnext-commerce-features/references/FEATURE-PREREQUISITES.md +37 -0
- package/content/guidance/storefront-next/sfnext-components/SKILL.md +153 -0
- package/content/guidance/storefront-next/sfnext-components/references/COMPONENT-AUTHORING.md +118 -0
- package/content/guidance/storefront-next/sfnext-components/references/SHAPE-TOKENS.md +51 -0
- package/content/guidance/storefront-next/sfnext-components/references/STORYBOOK.md +54 -0
- package/content/guidance/storefront-next/sfnext-components/references/TOKEN-SYSTEM.md +53 -0
- package/content/guidance/storefront-next/sfnext-components/references/TROUBLESHOOTING.md +19 -0
- package/content/guidance/storefront-next/sfnext-configuration/SKILL.md +163 -0
- package/content/guidance/storefront-next/sfnext-configuration/references/ENV-VARIABLES.md +55 -0
- package/content/guidance/storefront-next/sfnext-configuration/references/MULTI-SITE-URLS.md +137 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/SKILL.md +140 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/ACTIONS.md +53 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/API-CLIENTS.md +34 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/LOADERS.md +73 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/SCAPI-FETCHER.md +39 -0
- package/content/guidance/storefront-next/sfnext-deployment/SKILL.md +127 -0
- package/content/guidance/storefront-next/sfnext-deployment/references/MRT-DEPLOYMENT.md +59 -0
- package/content/guidance/storefront-next/sfnext-extensions/SKILL.md +118 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/ACTION-HOOKS.md +57 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/BASE-AUDIT.md +68 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/CLI-AND-INSTALL.md +58 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/EXTENSION-EXAMPLES.md +79 -0
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/SKILL.md +93 -0
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/references/HYBRID-PROXY-CONFIG.md +86 -0
- package/content/guidance/storefront-next/sfnext-i18n/SKILL.md +152 -0
- package/content/guidance/storefront-next/sfnext-i18n/references/locale-config.md +35 -0
- package/content/guidance/storefront-next/sfnext-overview/SKILL.md +101 -0
- package/content/guidance/storefront-next/sfnext-page-designer/SKILL.md +202 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/BUSINESS-MANAGER.md +46 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/COMPONENT-REGISTRY.md +111 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/DECORATOR-PATTERNS.md +168 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/REVIEW-CHECKLIST.md +83 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/TROUBLESHOOTING.md +35 -0
- package/content/guidance/storefront-next/sfnext-performance/SKILL.md +102 -0
- package/content/guidance/storefront-next/sfnext-performance/references/PERFORMANCE-REVIEW-CHECKLIST.md +85 -0
- package/content/guidance/storefront-next/sfnext-performance/references/SUSPENSE-AND-STREAMING.md +58 -0
- package/content/guidance/storefront-next/sfnext-project-setup/SKILL.md +147 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/PROJECT-STRUCTURE.md +61 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/SCRIPTS.md +47 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/SFNEXT-CLI.md +63 -0
- package/content/guidance/storefront-next/sfnext-quality-gates/SKILL.md +70 -0
- package/content/guidance/storefront-next/sfnext-quality-gates/references/lint-and-budgets.md +39 -0
- package/content/guidance/storefront-next/sfnext-revalidation/SKILL.md +121 -0
- package/content/guidance/storefront-next/sfnext-revalidation/references/POLICIES-AND-TAGS.md +55 -0
- package/content/guidance/storefront-next/sfnext-routing/SKILL.md +123 -0
- package/content/guidance/storefront-next/sfnext-routing/references/ROUTE-CONVENTIONS.md +65 -0
- package/content/guidance/storefront-next/sfnext-routing/references/URLS-AND-SEO-ROUTES.md +22 -0
- package/content/guidance/storefront-next/sfnext-scapi/SKILL.md +126 -0
- package/content/guidance/storefront-next/sfnext-scapi/references/WORKED-EXAMPLE.md +39 -0
- package/content/guidance/storefront-next/sfnext-security/SKILL.md +129 -0
- package/content/guidance/storefront-next/sfnext-security/references/COOKIE-DOMAIN.md +39 -0
- package/content/guidance/storefront-next/sfnext-security/references/TURNSTILE.md +43 -0
- package/content/guidance/storefront-next/sfnext-seo/SKILL.md +95 -0
- package/content/guidance/storefront-next/sfnext-seo/references/MULTI-DOMAIN-BASE-PATH.md +17 -0
- package/content/guidance/storefront-next/sfnext-seo/references/SEO-ROUTES.md +31 -0
- package/content/guidance/storefront-next/sfnext-state-management/SKILL.md +114 -0
- package/content/guidance/storefront-next/sfnext-state-management/references/PATTERNS.md +45 -0
- package/content/guidance/storefront-next/sfnext-testing/SKILL.md +151 -0
- package/content/guidance/storefront-next/sfnext-testing/references/E2E.md +38 -0
- package/content/guidance/storefront-next/sfnext-testing/references/STORYBOOK-PATTERNS.md +97 -0
- package/content/guidance/storefront-next/sfnext-testing/references/UNIT-AND-ROUTE-TESTS.md +60 -0
- package/content/guidance/storefront-next/sfnext-theming/SKILL.md +104 -0
- package/content/guidance/storefront-next/sfnext-theming/references/REBRAND-CHECKLIST.md +29 -0
- package/dist/commands/cap/install.d.ts +1 -0
- package/dist/commands/cap/list.d.ts +1 -0
- package/dist/commands/cap/pull.d.ts +1 -0
- package/dist/commands/cap/tasks.d.ts +1 -0
- package/dist/commands/cap/uninstall.d.ts +1 -0
- package/dist/commands/cip/describe.d.ts +1 -0
- package/dist/commands/cip/query.d.ts +1 -0
- package/dist/commands/cip/report/bot-traffic-share.d.ts +1 -0
- package/dist/commands/cip/report/checkout-funnel-dropoff.d.ts +1 -0
- package/dist/commands/cip/report/controller-error-rate-trend.d.ts +1 -0
- package/dist/commands/cip/report/controller-health-scorecard.d.ts +1 -0
- package/dist/commands/cip/report/customer-registration-trends.d.ts +1 -0
- package/dist/commands/cip/report/discount-depth-breakdown.d.ts +1 -0
- package/dist/commands/cip/report/inventory-stockout-by-location.d.ts +1 -0
- package/dist/commands/cip/report/new-vs-returning-buyer-revenue.d.ts +1 -0
- package/dist/commands/cip/report/ocapi-client-usage.d.ts +1 -0
- package/dist/commands/cip/report/ocapi-requests.d.ts +1 -0
- package/dist/commands/cip/report/payment-method-performance.d.ts +1 -0
- package/dist/commands/cip/report/product-co-purchase-analysis.d.ts +1 -0
- package/dist/commands/cip/report/promotion-discount-analysis.d.ts +1 -0
- package/dist/commands/cip/report/promotion-roi-leaderboard.d.ts +1 -0
- package/dist/commands/cip/report/recommender-effectiveness.d.ts +1 -0
- package/dist/commands/cip/report/remote-include-performance.d.ts +1 -0
- package/dist/commands/cip/report/revenue-by-channel.d.ts +1 -0
- package/dist/commands/cip/report/sales-analytics.d.ts +1 -0
- package/dist/commands/cip/report/sales-summary.d.ts +1 -0
- package/dist/commands/cip/report/scapi-cache-hit-ratio.d.ts +1 -0
- package/dist/commands/cip/report/scapi-error-rate-by-status.d.ts +1 -0
- package/dist/commands/cip/report/scapi-latency-distribution.d.ts +1 -0
- package/dist/commands/cip/report/scapi-traffic-latency.d.ts +1 -0
- package/dist/commands/cip/report/search-query-performance.d.ts +1 -0
- package/dist/commands/cip/report/top-referrers.d.ts +1 -0
- package/dist/commands/cip/report/top-selling-products.d.ts +1 -0
- package/dist/commands/cip/report/zero-result-searches.d.ts +1 -0
- package/dist/commands/cip/tables.d.ts +1 -0
- package/dist/commands/code/activate.d.ts +1 -0
- package/dist/commands/code/delete.d.ts +1 -0
- package/dist/commands/code/deploy.d.ts +1 -0
- package/dist/commands/code/download.d.ts +1 -0
- package/dist/commands/code/watch.d.ts +1 -0
- package/dist/commands/commands/search.d.ts +35 -0
- package/dist/commands/commands/search.js +85 -0
- package/dist/commands/commands/search.js.map +1 -0
- package/dist/commands/content/export.d.ts +1 -0
- package/dist/commands/content/list.d.ts +1 -0
- package/dist/commands/content/validate.d.ts +1 -0
- package/dist/commands/debug/cli.d.ts +1 -0
- package/dist/commands/debug/index.d.ts +1 -0
- package/dist/commands/docs/cache.d.ts +1 -0
- package/dist/commands/docs/download.d.ts +1 -0
- package/dist/commands/docs/read.d.ts +1 -0
- package/dist/commands/docs/schema.d.ts +1 -0
- package/dist/commands/docs/search.d.ts +1 -0
- package/dist/commands/docs/skill.d.ts +41 -0
- package/dist/commands/docs/skill.js +183 -0
- package/dist/commands/docs/skill.js.map +1 -0
- package/dist/commands/ecdn/cache/purge.d.ts +1 -0
- package/dist/commands/ecdn/certificates/add.d.ts +1 -0
- package/dist/commands/ecdn/certificates/delete.d.ts +1 -0
- package/dist/commands/ecdn/certificates/list.d.ts +1 -0
- package/dist/commands/ecdn/certificates/update.d.ts +1 -0
- package/dist/commands/ecdn/certificates/validate.d.ts +1 -0
- package/dist/commands/ecdn/cipher-suites/get.d.ts +1 -0
- package/dist/commands/ecdn/cipher-suites/update.d.ts +1 -0
- package/dist/commands/ecdn/firewall/create.d.ts +1 -0
- package/dist/commands/ecdn/firewall/delete.d.ts +1 -0
- package/dist/commands/ecdn/firewall/get.d.ts +1 -0
- package/dist/commands/ecdn/firewall/list.d.ts +1 -0
- package/dist/commands/ecdn/firewall/reorder.d.ts +1 -0
- package/dist/commands/ecdn/firewall/update.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/create.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/delete.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/get.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/list.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/update.d.ts +1 -0
- package/dist/commands/ecdn/logpush/ownership.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/create.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/delete.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/get.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/rules/delete.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/rules/update.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/update.d.ts +1 -0
- package/dist/commands/ecdn/mtls/create.d.ts +1 -0
- package/dist/commands/ecdn/mtls/delete.d.ts +1 -0
- package/dist/commands/ecdn/mtls/get.d.ts +1 -0
- package/dist/commands/ecdn/mtls/issue.d.ts +1 -0
- package/dist/commands/ecdn/mtls/list.d.ts +1 -0
- package/dist/commands/ecdn/mtls/setup.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/delete.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/get.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/set.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/create.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/delete.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/list.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/create.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/delete.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/get.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/list.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/update.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/scripts/get.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/scripts/list.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/create.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/delete.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/get.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/list.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/update.d.ts +1 -0
- package/dist/commands/ecdn/security/get.d.ts +1 -0
- package/dist/commands/ecdn/security/update.d.ts +1 -0
- package/dist/commands/ecdn/speed/get.d.ts +1 -0
- package/dist/commands/ecdn/speed/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/groups/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/groups/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/managed-rules/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/managed-rules/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/migrate.d.ts +1 -0
- package/dist/commands/ecdn/waf/owasp/get.d.ts +1 -0
- package/dist/commands/ecdn/waf/owasp/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/get.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/rulesets/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/rulesets/update.d.ts +1 -0
- package/dist/commands/ecdn/zones/create.d.ts +1 -0
- package/dist/commands/ecdn/zones/list.d.ts +1 -0
- package/dist/commands/job/execution/delete.d.ts +1 -0
- package/dist/commands/job/export.d.ts +1 -0
- package/dist/commands/job/import-set.d.ts +1 -0
- package/dist/commands/job/import.d.ts +1 -0
- package/dist/commands/job/log.d.ts +1 -0
- package/dist/commands/job/run.d.ts +1 -0
- package/dist/commands/job/search.d.ts +1 -0
- package/dist/commands/job/wait.d.ts +1 -0
- package/dist/commands/logs/get.d.ts +1 -0
- package/dist/commands/logs/list.d.ts +1 -0
- package/dist/commands/logs/tail.d.ts +1 -0
- package/dist/commands/metrics/controller.d.ts +1 -0
- package/dist/commands/metrics/ecdn.d.ts +1 -0
- package/dist/commands/metrics/mrt.d.ts +1 -0
- package/dist/commands/metrics/ocapi.d.ts +1 -0
- package/dist/commands/metrics/overall.d.ts +1 -0
- package/dist/commands/metrics/sales.d.ts +1 -0
- package/dist/commands/metrics/scapi-hooks.d.ts +1 -0
- package/dist/commands/metrics/scapi.d.ts +1 -0
- package/dist/commands/metrics/third-party.d.ts +1 -0
- package/dist/commands/mrt/bundle/delete.d.ts +1 -0
- package/dist/commands/mrt/bundle/deploy.d.ts +1 -0
- package/dist/commands/mrt/bundle/download.d.ts +1 -0
- package/dist/commands/mrt/bundle/history.d.ts +1 -0
- package/dist/commands/mrt/bundle/list.d.ts +1 -0
- package/dist/commands/mrt/bundle/save.d.ts +1 -0
- package/dist/commands/mrt/bundle/upload-v2.d.ts +1 -0
- package/dist/commands/mrt/env/access-control/list.d.ts +1 -0
- package/dist/commands/mrt/env/b2c.d.ts +1 -0
- package/dist/commands/mrt/env/clone.d.ts +1 -0
- package/dist/commands/mrt/env/create.d.ts +1 -0
- package/dist/commands/mrt/env/delete.d.ts +1 -0
- package/dist/commands/mrt/env/get.d.ts +1 -0
- package/dist/commands/mrt/env/invalidate.d.ts +1 -0
- package/dist/commands/mrt/env/list.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/clone.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/create.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/delete.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/list.d.ts +1 -0
- package/dist/commands/mrt/env/update.d.ts +1 -0
- package/dist/commands/mrt/env/var/delete.d.ts +1 -0
- package/dist/commands/mrt/env/var/list.d.ts +1 -0
- package/dist/commands/mrt/env/var/push.d.ts +1 -0
- package/dist/commands/mrt/env/var/set.d.ts +1 -0
- package/dist/commands/mrt/org/b2c.d.ts +1 -0
- package/dist/commands/mrt/org/cert/create.d.ts +1 -0
- package/dist/commands/mrt/org/cert/delete.d.ts +1 -0
- package/dist/commands/mrt/org/cert/get.d.ts +1 -0
- package/dist/commands/mrt/org/cert/list.d.ts +1 -0
- package/dist/commands/mrt/org/cert/restart-validation.d.ts +1 -0
- package/dist/commands/mrt/org/list.d.ts +1 -0
- package/dist/commands/mrt/org/member/add.d.ts +1 -0
- package/dist/commands/mrt/org/member/get.d.ts +1 -0
- package/dist/commands/mrt/org/member/list.d.ts +1 -0
- package/dist/commands/mrt/org/member/remove.d.ts +1 -0
- package/dist/commands/mrt/org/member/update.d.ts +1 -0
- package/dist/commands/mrt/project/create.d.ts +1 -0
- package/dist/commands/mrt/project/delete.d.ts +1 -0
- package/dist/commands/mrt/project/get.d.ts +1 -0
- package/dist/commands/mrt/project/list.d.ts +1 -0
- package/dist/commands/mrt/project/member/add.d.ts +1 -0
- package/dist/commands/mrt/project/member/get.d.ts +1 -0
- package/dist/commands/mrt/project/member/list.d.ts +1 -0
- package/dist/commands/mrt/project/member/remove.d.ts +1 -0
- package/dist/commands/mrt/project/member/update.d.ts +1 -0
- package/dist/commands/mrt/project/notification/delete.d.ts +1 -0
- package/dist/commands/mrt/project/notification/get.d.ts +1 -0
- package/dist/commands/mrt/project/update.d.ts +1 -0
- package/dist/commands/mrt/save-credentials.d.ts +1 -0
- package/dist/commands/mrt/tail-logs.d.ts +1 -0
- package/dist/commands/mrt/user/api-key.d.ts +1 -0
- package/dist/commands/mrt/user/email-prefs.d.ts +1 -0
- package/dist/commands/mrt/user/profile.d.ts +1 -0
- package/dist/commands/scaffold/init.js +2 -1
- package/dist/commands/scaffold/init.js.map +1 -1
- package/dist/commands/scapi/custom/status.d.ts +1 -0
- package/dist/commands/scapi/schemas/get.d.ts +1 -0
- package/dist/commands/scapi/schemas/list.d.ts +1 -0
- package/dist/commands/setup/ide/tsserver-plugin.d.ts +1 -0
- package/dist/commands/setup/ide/vscode-types.d.ts +1 -0
- package/dist/commands/setup/index.js +4 -3
- package/dist/commands/setup/index.js.map +1 -1
- package/dist/commands/setup/inspect.d.ts +1 -0
- package/dist/commands/setup/inspect.js +17 -2
- package/dist/commands/setup/inspect.js.map +1 -1
- package/dist/commands/setup/instance/create.d.ts +1 -0
- package/dist/commands/setup/instance/list.d.ts +1 -0
- package/dist/commands/setup/instance/remove.d.ts +1 -0
- package/dist/commands/setup/instance/set-active.d.ts +1 -0
- package/dist/commands/setup/openshell.d.ts +1 -0
- package/dist/commands/setup/skills.d.ts +1 -0
- package/dist/commands/slas/client/create.d.ts +1 -0
- package/dist/commands/slas/client/delete.d.ts +1 -0
- package/dist/commands/slas/client/get.d.ts +1 -0
- package/dist/commands/slas/client/list.d.ts +1 -0
- package/dist/commands/slas/client/open.d.ts +1 -0
- package/dist/commands/slas/client/update.d.ts +1 -0
- package/dist/commands/slas/token.d.ts +1 -0
- package/dist/commands/slas/token.js +2 -1
- package/dist/commands/slas/token.js.map +1 -1
- package/dist/commands/webdav/get.d.ts +1 -0
- package/dist/commands/webdav/mkdir.d.ts +2 -0
- package/dist/commands/webdav/mkdir.js +5 -3
- package/dist/commands/webdav/mkdir.js.map +1 -1
- package/dist/commands/webdav/put.d.ts +2 -0
- package/dist/commands/webdav/put.js +5 -3
- package/dist/commands/webdav/put.js.map +1 -1
- package/dist/commands/webdav/rm.d.ts +1 -0
- package/dist/help.d.ts +25 -0
- package/dist/help.js +96 -0
- package/dist/help.js.map +1 -0
- package/dist/lib/scaffold/generate-helper.js +3 -2
- package/dist/lib/scaffold/generate-helper.js.map +1 -1
- package/dist/lib/skills.d.ts +19 -0
- package/dist/lib/skills.js +75 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/utils/cip/command.d.ts +1 -0
- package/dist/utils/ecdn/zone-command.d.ts +1 -0
- package/dist/utils/slas/client.d.ts +1 -0
- package/oclif.manifest.json +10994 -7653
- package/package.json +11 -5
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Session Bridge Reference
|
|
2
|
+
|
|
3
|
+
> Illustrative of the flow; confirm platform-agnostic API contract with `b2c docs read commerce-api/slas-session-bridge-auth`. PWA Kit examples use framework-specific convenience wrappers.
|
|
4
|
+
|
|
5
|
+
Detailed implementation patterns for session bridging between PWA Kit and SFRA.
|
|
6
|
+
|
|
7
|
+
## Token Types
|
|
8
|
+
|
|
9
|
+
| Token | Description | Duration |
|
|
10
|
+
|-------|-------------|----------|
|
|
11
|
+
| `dwsgst` | Guest session token | 30 minutes |
|
|
12
|
+
| `dwsrst` | Registered session token | 30 minutes |
|
|
13
|
+
| `dwsid` | SFRA session cookie | Session-based |
|
|
14
|
+
|
|
15
|
+
## PWA Kit to SFRA
|
|
16
|
+
|
|
17
|
+
### Step 1: Generate Bridge Tokens
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
// commerce-sdk-isomorphic or direct API call
|
|
21
|
+
async function generateBridgeTokens(shopperToken) {
|
|
22
|
+
const response = await shopperLogin.getSessionBridgeAccessToken({
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${shopperToken.access_token}`
|
|
25
|
+
},
|
|
26
|
+
body: {
|
|
27
|
+
channel_id: siteId,
|
|
28
|
+
login_id: shopperToken.customer_id
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
dwsgst: response.dwsgst,
|
|
34
|
+
dwsrst: response.dwsrst // Only present if logged in
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 2: Redirect with Tokens
|
|
40
|
+
|
|
41
|
+
Option A: URL Parameters (simple)
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
function redirectToSFRA(bridgeTokens, targetPath) {
|
|
45
|
+
const url = new URL(sfraBaseUrl + targetPath);
|
|
46
|
+
url.searchParams.set('dwsgst', bridgeTokens.dwsgst);
|
|
47
|
+
if (bridgeTokens.dwsrst) {
|
|
48
|
+
url.searchParams.set('dwsrst', bridgeTokens.dwsrst);
|
|
49
|
+
}
|
|
50
|
+
window.location.href = url.toString();
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Option B: POST Form (more secure)
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
function redirectViaPOST(bridgeTokens, targetPath) {
|
|
58
|
+
const form = document.createElement('form');
|
|
59
|
+
form.method = 'POST';
|
|
60
|
+
form.action = sfraBaseUrl + '/SessionBridge-Establish';
|
|
61
|
+
|
|
62
|
+
const addInput = (name, value) => {
|
|
63
|
+
const input = document.createElement('input');
|
|
64
|
+
input.type = 'hidden';
|
|
65
|
+
input.name = name;
|
|
66
|
+
input.value = value;
|
|
67
|
+
form.appendChild(input);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
addInput('dwsgst', bridgeTokens.dwsgst);
|
|
71
|
+
if (bridgeTokens.dwsrst) {
|
|
72
|
+
addInput('dwsrst', bridgeTokens.dwsrst);
|
|
73
|
+
}
|
|
74
|
+
addInput('redirect', targetPath);
|
|
75
|
+
|
|
76
|
+
document.body.appendChild(form);
|
|
77
|
+
form.submit();
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 3: SFRA Establishes Session
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// SFRA Controller: SessionBridge-Establish
|
|
85
|
+
var server = require('server');
|
|
86
|
+
var SLASBridge = require('*/cartridge/scripts/services/SLASBridge');
|
|
87
|
+
|
|
88
|
+
server.post('Establish', function(req, res, next) {
|
|
89
|
+
var dwsgst = req.form.dwsgst;
|
|
90
|
+
var dwsrst = req.form.dwsrst;
|
|
91
|
+
var redirect = req.form.redirect || '/';
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Exchange bridge tokens for session
|
|
95
|
+
var result = SLASBridge.establishSession(dwsgst, dwsrst);
|
|
96
|
+
|
|
97
|
+
if (result.success) {
|
|
98
|
+
// Session established, dwsid cookie is set by platform
|
|
99
|
+
res.redirect(redirect);
|
|
100
|
+
} else {
|
|
101
|
+
res.redirect('/login?error=bridge_failed');
|
|
102
|
+
}
|
|
103
|
+
} catch (e) {
|
|
104
|
+
res.redirect('/login?error=bridge_error');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
next();
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## SFRA to PWA Kit
|
|
112
|
+
|
|
113
|
+
### Step 1: Generate Bridge Tokens in SFRA
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
// SFRA Helper
|
|
117
|
+
var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry');
|
|
118
|
+
var Site = require('dw/system/Site');
|
|
119
|
+
|
|
120
|
+
function getBridgeTokensForPWA() {
|
|
121
|
+
var customer = session.customer;
|
|
122
|
+
var svc = LocalServiceRegistry.createService('slas.sessionbridge', {
|
|
123
|
+
createRequest: function(svc, args) {
|
|
124
|
+
svc.setRequestMethod('POST');
|
|
125
|
+
svc.addHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
126
|
+
|
|
127
|
+
var params = 'channel_id=' + Site.current.ID;
|
|
128
|
+
if (customer.authenticated) {
|
|
129
|
+
params += '&login_id=' + customer.profile.credentials.login;
|
|
130
|
+
}
|
|
131
|
+
return params;
|
|
132
|
+
},
|
|
133
|
+
parseResponse: function(svc, response) {
|
|
134
|
+
return JSON.parse(response.text);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
var result = svc.call();
|
|
139
|
+
return result.ok ? result.object : null;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Step 2: Redirect to PWA Kit
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// SFRA Controller: SessionBridge-ToPWA
|
|
147
|
+
server.get('ToPWA', function(req, res, next) {
|
|
148
|
+
var SLASBridge = require('*/cartridge/scripts/services/SLASBridge');
|
|
149
|
+
var targetPath = req.querystring.path || '/';
|
|
150
|
+
|
|
151
|
+
var bridgeTokens = SLASBridge.getBridgeTokensForPWA();
|
|
152
|
+
|
|
153
|
+
if (bridgeTokens) {
|
|
154
|
+
var pwaUrl = 'https://pwa.yoursite.com/session-bridge/callback';
|
|
155
|
+
pwaUrl += '?dwsgst=' + encodeURIComponent(bridgeTokens.dwsgst);
|
|
156
|
+
if (bridgeTokens.dwsrst) {
|
|
157
|
+
pwaUrl += '&dwsrst=' + encodeURIComponent(bridgeTokens.dwsrst);
|
|
158
|
+
}
|
|
159
|
+
pwaUrl += '&redirect=' + encodeURIComponent(targetPath);
|
|
160
|
+
|
|
161
|
+
res.redirect(pwaUrl);
|
|
162
|
+
} else {
|
|
163
|
+
res.redirect('https://pwa.yoursite.com' + targetPath);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
next();
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Step 3: PWA Kit Handles Callback
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
// PWA Kit: pages/session-bridge/callback.jsx
|
|
174
|
+
import { useEffect } from 'react';
|
|
175
|
+
import { useSearchParams, useNavigate } from 'react-router-dom';
|
|
176
|
+
import { useAuth } from '@salesforce/commerce-sdk-react';
|
|
177
|
+
|
|
178
|
+
export default function SessionBridgeCallback() {
|
|
179
|
+
const [searchParams] = useSearchParams();
|
|
180
|
+
const navigate = useNavigate();
|
|
181
|
+
const auth = useAuth();
|
|
182
|
+
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
async function establishSession() {
|
|
185
|
+
const dwsgst = searchParams.get('dwsgst');
|
|
186
|
+
const dwsrst = searchParams.get('dwsrst');
|
|
187
|
+
const redirect = searchParams.get('redirect') || '/';
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
// Exchange bridge tokens
|
|
191
|
+
await auth.loginSessionBridge({ dwsgst, dwsrst });
|
|
192
|
+
navigate(redirect);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error('Bridge failed:', error);
|
|
195
|
+
navigate('/login?error=bridge_failed');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
establishSession();
|
|
200
|
+
}, [searchParams]);
|
|
201
|
+
|
|
202
|
+
return <div>Establishing session...</div>;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Hybrid Navigation Component
|
|
207
|
+
|
|
208
|
+
```jsx
|
|
209
|
+
// PWA Kit: components/hybrid-link.jsx
|
|
210
|
+
import { useAuth } from '@salesforce/commerce-sdk-react';
|
|
211
|
+
|
|
212
|
+
export function HybridLink({ href, children, ...props }) {
|
|
213
|
+
const auth = useAuth();
|
|
214
|
+
|
|
215
|
+
const handleClick = async (e) => {
|
|
216
|
+
// Check if href is SFRA
|
|
217
|
+
if (href.includes('sfra.yoursite.com')) {
|
|
218
|
+
e.preventDefault();
|
|
219
|
+
|
|
220
|
+
// Get bridge tokens
|
|
221
|
+
const tokens = await auth.getSessionBridgeTokens();
|
|
222
|
+
|
|
223
|
+
// Build SFRA URL with tokens
|
|
224
|
+
const url = new URL(href);
|
|
225
|
+
url.searchParams.set('dwsgst', tokens.dwsgst);
|
|
226
|
+
if (tokens.dwsrst) {
|
|
227
|
+
url.searchParams.set('dwsrst', tokens.dwsrst);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
window.location.href = url.toString();
|
|
231
|
+
}
|
|
232
|
+
// Otherwise, let normal navigation happen
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<a href={href} onClick={handleClick} {...props}>
|
|
237
|
+
{children}
|
|
238
|
+
</a>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Error Handling
|
|
244
|
+
|
|
245
|
+
| Error | Cause | Solution |
|
|
246
|
+
|-------|-------|----------|
|
|
247
|
+
| `invalid_token` | Bridge token expired | Re-generate tokens |
|
|
248
|
+
| `session_mismatch` | Different customer | Clear and restart |
|
|
249
|
+
| `channel_mismatch` | Wrong site ID | Verify channel_id |
|
|
250
|
+
| `bridge_disabled` | Feature not enabled | Enable in SLAS config |
|
|
251
|
+
|
|
252
|
+
## Security Considerations
|
|
253
|
+
|
|
254
|
+
1. **Token Expiry**: Bridge tokens expire quickly (30 min) - generate fresh tokens before redirect
|
|
255
|
+
2. **HTTPS Only**: Always use HTTPS for token exchange
|
|
256
|
+
3. **Referrer Validation**: Validate source domain on callback endpoints
|
|
257
|
+
4. **Rate Limiting**: Implement rate limiting on bridge endpoints
|
|
258
|
+
5. **Token Usage**: Each bridge token should only be used once
|
|
259
|
+
|
|
260
|
+
## Testing
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Verify bridge token generation
|
|
264
|
+
curl -X POST "https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{orgId}/oauth2/session-bridge/token" \
|
|
265
|
+
-H "Authorization: Bearer {access_token}" \
|
|
266
|
+
-d "channel_id={siteId}&login_id={customerId}"
|
|
267
|
+
```
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Token Lifecycle Reference
|
|
2
|
+
|
|
3
|
+
Understanding token expiry, refresh patterns, and lifecycle management for SLAS.
|
|
4
|
+
|
|
5
|
+
## Token Types and Expiry
|
|
6
|
+
|
|
7
|
+
| Token Type | Default Expiry | Configurable | Use Case |
|
|
8
|
+
|------------|----------------|--------------|----------|
|
|
9
|
+
| Access Token | 30 minutes | Yes (SLAS Admin) | API requests |
|
|
10
|
+
| Refresh Token (Public) | 90 days | Yes | Token renewal |
|
|
11
|
+
| Refresh Token (Private) | 90 days | Yes | Token renewal |
|
|
12
|
+
| Session Bridge Token | 30 minutes | No | Cross-platform auth |
|
|
13
|
+
| Passwordless Token | 10 minutes | No | OTP verification |
|
|
14
|
+
|
|
15
|
+
## Access Token Lifecycle
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
[Issue] → [Active] → [Expired]
|
|
19
|
+
↓ ↓
|
|
20
|
+
Use Refresh
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Best Practices for Access Tokens
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
class TokenManager {
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.accessToken = null;
|
|
29
|
+
this.refreshToken = null;
|
|
30
|
+
this.expiresAt = null;
|
|
31
|
+
this.refreshThreshold = 5 * 60 * 1000; // 5 minutes
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isExpired() {
|
|
35
|
+
return Date.now() >= this.expiresAt;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
shouldRefresh() {
|
|
39
|
+
// Refresh when within threshold of expiry
|
|
40
|
+
return Date.now() >= (this.expiresAt - this.refreshThreshold);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async getValidToken() {
|
|
44
|
+
if (this.shouldRefresh()) {
|
|
45
|
+
await this.refresh();
|
|
46
|
+
}
|
|
47
|
+
return this.accessToken;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async refresh() {
|
|
51
|
+
const tokens = await this.slasRefresh(this.refreshToken);
|
|
52
|
+
this.setTokens(tokens);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setTokens(tokens) {
|
|
56
|
+
this.accessToken = tokens.access_token;
|
|
57
|
+
this.refreshToken = tokens.refresh_token;
|
|
58
|
+
this.expiresAt = Date.now() + (tokens.expires_in * 1000);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Refresh Token Patterns
|
|
64
|
+
|
|
65
|
+
### Public Client (Single-Use)
|
|
66
|
+
|
|
67
|
+
For browser-based apps without a backend:
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
// Refresh tokens are single-use - must store new token
|
|
71
|
+
async function refreshPublicClient(refreshToken) {
|
|
72
|
+
const response = await fetch(tokenEndpoint, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
75
|
+
body: new URLSearchParams({
|
|
76
|
+
grant_type: 'refresh_token',
|
|
77
|
+
refresh_token: refreshToken,
|
|
78
|
+
client_id: clientId,
|
|
79
|
+
channel_id: siteId
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const tokens = await response.json();
|
|
84
|
+
|
|
85
|
+
// IMPORTANT: Old refresh token is now invalid
|
|
86
|
+
// Must use the new refresh_token for next refresh
|
|
87
|
+
return {
|
|
88
|
+
accessToken: tokens.access_token,
|
|
89
|
+
refreshToken: tokens.refresh_token, // NEW token
|
|
90
|
+
expiresIn: tokens.expires_in
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Private Client (Reusable)
|
|
96
|
+
|
|
97
|
+
For server-side applications:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// Refresh tokens are reusable with client secret
|
|
101
|
+
async function refreshPrivateClient(refreshToken) {
|
|
102
|
+
const response = await fetch(tokenEndpoint, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
106
|
+
'Authorization': `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`
|
|
107
|
+
},
|
|
108
|
+
body: new URLSearchParams({
|
|
109
|
+
grant_type: 'refresh_token',
|
|
110
|
+
refresh_token: refreshToken,
|
|
111
|
+
channel_id: siteId
|
|
112
|
+
})
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const tokens = await response.json();
|
|
116
|
+
|
|
117
|
+
// Can reuse same refresh token (though new one may be provided)
|
|
118
|
+
return {
|
|
119
|
+
accessToken: tokens.access_token,
|
|
120
|
+
refreshToken: tokens.refresh_token || refreshToken,
|
|
121
|
+
expiresIn: tokens.expires_in
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Token Rotation Strategy
|
|
127
|
+
|
|
128
|
+
### Public Clients - Handle Race Conditions
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
class PublicTokenManager {
|
|
132
|
+
constructor() {
|
|
133
|
+
this.refreshPromise = null;
|
|
134
|
+
this.tokenVersion = 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async refresh() {
|
|
138
|
+
// Prevent multiple concurrent refreshes
|
|
139
|
+
if (this.refreshPromise) {
|
|
140
|
+
return this.refreshPromise;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const currentVersion = this.tokenVersion;
|
|
144
|
+
|
|
145
|
+
this.refreshPromise = this._doRefresh()
|
|
146
|
+
.finally(() => {
|
|
147
|
+
this.refreshPromise = null;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return this.refreshPromise;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async _doRefresh() {
|
|
154
|
+
const tokens = await refreshPublicClient(this.refreshToken);
|
|
155
|
+
|
|
156
|
+
// Check for race condition
|
|
157
|
+
if (this.tokenVersion !== currentVersion) {
|
|
158
|
+
// Another refresh happened, tokens might be stale
|
|
159
|
+
throw new Error('Token version mismatch');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.tokenVersion++;
|
|
163
|
+
this.setTokens(tokens);
|
|
164
|
+
return tokens;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Storage Strategies
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
// Browser - httpOnly cookie preferred (requires BFF)
|
|
173
|
+
// Fallback to localStorage with encryption
|
|
174
|
+
|
|
175
|
+
class TokenStorage {
|
|
176
|
+
// Option 1: httpOnly cookies via BFF
|
|
177
|
+
async storeViaBackend(tokens) {
|
|
178
|
+
await fetch('/api/auth/store-tokens', {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
credentials: 'include',
|
|
181
|
+
body: JSON.stringify(tokens)
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Option 2: localStorage (less secure)
|
|
186
|
+
storeLocally(tokens) {
|
|
187
|
+
// Consider encrypting before storage
|
|
188
|
+
localStorage.setItem('auth_tokens', JSON.stringify({
|
|
189
|
+
accessToken: tokens.access_token,
|
|
190
|
+
refreshToken: tokens.refresh_token,
|
|
191
|
+
expiresAt: Date.now() + (tokens.expires_in * 1000)
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Option 3: Memory only (cleared on refresh)
|
|
196
|
+
storeInMemory(tokens) {
|
|
197
|
+
this._tokens = tokens;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Session Timeout Handling
|
|
203
|
+
|
|
204
|
+
### Proactive Refresh
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
class SessionManager {
|
|
208
|
+
constructor() {
|
|
209
|
+
this.refreshTimer = null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
scheduleRefresh(expiresIn) {
|
|
213
|
+
// Clear existing timer
|
|
214
|
+
if (this.refreshTimer) {
|
|
215
|
+
clearTimeout(this.refreshTimer);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Schedule refresh 5 minutes before expiry
|
|
219
|
+
const refreshIn = (expiresIn - 300) * 1000;
|
|
220
|
+
|
|
221
|
+
this.refreshTimer = setTimeout(() => {
|
|
222
|
+
this.refresh().catch(this.handleRefreshError);
|
|
223
|
+
}, refreshIn);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
handleRefreshError(error) {
|
|
227
|
+
// Check if refresh token is also expired
|
|
228
|
+
if (error.message.includes('invalid_grant')) {
|
|
229
|
+
// Full re-authentication required
|
|
230
|
+
this.logout();
|
|
231
|
+
this.redirectToLogin();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Activity-Based Extension
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
class ActivityTracker {
|
|
241
|
+
constructor(tokenManager) {
|
|
242
|
+
this.tokenManager = tokenManager;
|
|
243
|
+
this.lastActivity = Date.now();
|
|
244
|
+
this.activityTimeout = 15 * 60 * 1000; // 15 minutes
|
|
245
|
+
|
|
246
|
+
this.setupListeners();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
setupListeners() {
|
|
250
|
+
['click', 'keydown', 'scroll', 'mousemove'].forEach(event => {
|
|
251
|
+
window.addEventListener(event, () => this.recordActivity(), { passive: true });
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
recordActivity() {
|
|
256
|
+
const now = Date.now();
|
|
257
|
+
if (now - this.lastActivity > 60000) { // Throttle to 1 minute
|
|
258
|
+
this.lastActivity = now;
|
|
259
|
+
this.checkTokenRefresh();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
checkTokenRefresh() {
|
|
264
|
+
if (this.tokenManager.shouldRefresh()) {
|
|
265
|
+
this.tokenManager.refresh();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
isInactive() {
|
|
270
|
+
return Date.now() - this.lastActivity > this.activityTimeout;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Guest to Registered Transition
|
|
276
|
+
|
|
277
|
+
When a guest user logs in, merge sessions:
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
async function loginWithSessionMerge(credentials, guestAccessToken) {
|
|
281
|
+
const response = await fetch(tokenEndpoint, {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
headers: {
|
|
284
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
285
|
+
// Include guest token for basket/session merge
|
|
286
|
+
'Authorization': `Bearer ${guestAccessToken}`
|
|
287
|
+
},
|
|
288
|
+
body: new URLSearchParams({
|
|
289
|
+
grant_type: 'password',
|
|
290
|
+
username: credentials.username,
|
|
291
|
+
password: credentials.password,
|
|
292
|
+
client_id: clientId,
|
|
293
|
+
channel_id: siteId
|
|
294
|
+
})
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Returns registered user tokens
|
|
298
|
+
// Guest basket is automatically merged
|
|
299
|
+
return response.json();
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Logout and Token Revocation
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
async function logout(accessToken, refreshToken) {
|
|
307
|
+
// Revoke refresh token
|
|
308
|
+
await fetch(
|
|
309
|
+
`https://${shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/${orgId}/oauth2/revoke`,
|
|
310
|
+
{
|
|
311
|
+
method: 'POST',
|
|
312
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
313
|
+
body: new URLSearchParams({
|
|
314
|
+
token: refreshToken,
|
|
315
|
+
token_type_hint: 'refresh_token',
|
|
316
|
+
client_id: clientId,
|
|
317
|
+
channel_id: siteId
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Clear local storage
|
|
323
|
+
localStorage.removeItem('auth_tokens');
|
|
324
|
+
|
|
325
|
+
// Clear cookies if using BFF
|
|
326
|
+
await fetch('/api/auth/logout', {
|
|
327
|
+
method: 'POST',
|
|
328
|
+
credentials: 'include'
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Error Handling Reference
|
|
334
|
+
|
|
335
|
+
| Error | Meaning | Action |
|
|
336
|
+
|-------|---------|--------|
|
|
337
|
+
| `invalid_grant` | Refresh token expired/revoked | Re-authenticate |
|
|
338
|
+
| `invalid_token` | Access token invalid | Try refresh |
|
|
339
|
+
| `expired_token` | Access token expired | Refresh token |
|
|
340
|
+
| `invalid_client` | Client credentials wrong | Check configuration |
|
|
341
|
+
| `unauthorized_client` | Client not allowed | Check SLAS client settings |
|
|
342
|
+
|
|
343
|
+
## Debugging Token Issues
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
// Decode JWT without validation (for debugging only)
|
|
347
|
+
function decodeToken(token) {
|
|
348
|
+
const parts = token.split('.');
|
|
349
|
+
if (parts.length !== 3) return null;
|
|
350
|
+
|
|
351
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
352
|
+
return {
|
|
353
|
+
subject: payload.sub,
|
|
354
|
+
issuer: payload.iss,
|
|
355
|
+
audience: payload.aud,
|
|
356
|
+
expiresAt: new Date(payload.exp * 1000),
|
|
357
|
+
issuedAt: new Date(payload.iat * 1000),
|
|
358
|
+
scopes: payload.scope?.split(' ') || []
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Check if token is expired
|
|
363
|
+
function isTokenExpired(token) {
|
|
364
|
+
const decoded = decodeToken(token);
|
|
365
|
+
return decoded && Date.now() >= decoded.expiresAt.getTime();
|
|
366
|
+
}
|
|
367
|
+
```
|