Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/libratel/wp-integration-for-shopify/archive/refs/heads/main.zipPortal Identity Bridge (Phase 6)
WordPress plugin for secure Shopify/SparkLayer -> WordPress identity bridging, customer mapping, protected portal access, SparkLayer launch links, partner materials delivery, backorders dashboards/exports, and operations hardening.
Phase 6 Scope
- JWT-only handoff intake endpoint in WordPress.
- HS256 JWT verification with expiry + issuer checks.
- One-time replay protection via
jtitransient lock. - Required stable customer identity claim (
shopify_customer_idorcustomer_id). - Shopify customer/company -> WordPress user mapping table.
- Guarded auto-link/auto-create WordPress users from verified claims.
- Protected route guard with destination-aware sign-in redirect.
- Authorization helpers for customer/company scope checks.
- SparkLayer launch entry handler (
?pib_launch=1&dest=/...). - Launch-link shortcode for dashboard buttons.
- Partner materials custom content type with company-scoped access.
- Secure material download endpoint.
- Backorders dashboard shortcode with customer/company-scoped records.
- Backorders CSV export endpoint with same scope enforcement.
- External backorders API adapter + normalization layer.
- Backorders view/export audit hooks.
- Audit logging module + retention policy.
- Admin tools page for mappings and event logs.
- Managed session TTL + periodic revalidation.
- Local/coordinated logout handling.
- Auth intake rate-limiting by client IP.
- CSRF nonce enforcement for sensitive exports/downloads.
- Operational health checks module with actionable status levels.
- Health diagnostics tab in admin tools.
- Downloadable support snapshot JSON for incident/debug workflows.
Module Folders
Classes are organized by module under includes/:
core/(plugin bootstrap, installer, settings)auth/(auth intake, JWT verification, sign-in, session policy)security/(destination and security helpers)mapping/(customer mapping and authorization)access/(protected route gating)spark/(SparkLayer launch entry)materials/(partner materials module)backorders/(backorders data source and dashboard)portal/(extensible portal module registry + dashboard renderer)admin/(audit logs and admin tools)
Module Lifecycle (Prompt 1)
The plugin now boots modules through PIB_Module_Registry (see includes/core/class-pib-module-registry.php) instead of hardcoded direct instantiation.
Built-in modules are still loaded in the same order:
- settings
- customer_mapping
- protected_access
- spark_launch
- materials
- backorders
- portal_modules
- audit_log
- operations_health
- session_policy
- admin_tools
- auth_intake
- signin_link
Lifecycle hooks for extension modules:
pib_register_modules(register or replace modules before boot)pib_modules_registered(all module definitions ready)pib_before_modules_boot(just before module factories run)pib_module_booted(fires per module)pib_modules_booted(all modules booted)
Settings Architecture (Prompt 2)
Prompt 2 introduces a schema-driven settings contract:
includes/core/class-pib-settings-contract.phpis now the single source of truth for:- settings schema (key/type/default/section/metadata)
- normalization/sanitization
- validation and diagnostics payloads
- Runtime reads (
PIB_Plugin::get_settings()) and admin saves now use this same contract path. - Settings storage remains backward-compatible as a flat
pib_settingsoption array.
Admin settings are grouped into these sections:
- Identity/Auth Bridge
- Routing & Access
- User Mapping
- Session & Logout
- Backorders
- Operations & Audit
Strict save policy:
- Critical misconfiguration blocks persistence for that save request (all-or-nothing).
- Warning-level checks still allow save and are shown via WordPress settings notices.
Diagnostics panel:
- Rendered directly on
Settings -> Portal Bridge. - Shows overall health (
Healthy,Warnings,Action Needed), checklist results, computed references, and redacted effective values.
Extensibility hooks:
pib_settings_schemapib_settings_validation_resultspib_settings_diagnostics_checks
Protected Route Management (Prompt 4)
- Route matching now goes through
PIB_Protected_Route_Policyfor both runtime enforcement and admin diagnostics. - Protected access supports explicit unprotected prefixes (
Unprotected Route Prefixes) to keep login/failure routes reachable. - Route validation now flags misconfigurations such as protected failure destinations and global unprotected
/. - Admin tools include
Observability > Route Accesswith:- route access assistant summary + direct fix links
- risk cards (
Action Needed,Warning,OK) - risk checks table with actionable next steps
- destination tester with expected visitor behavior
- collapsible policy inputs + sample route matrix
Extensibility (Prompt 5)
- Portal features are now registered through
PIB_Portal_Modules. - Built-in modules (
materials,backorders) register themselves via the same extension API used by custom modules. - New dashboard shortcode:
[portal_dashboard]
- Core extension hooks:
pib_register_portal_modulespib_portal_modules_registeredpib_portal_modulepib_portal_modulespib_portal_module_card_markuppib_portal_dashboard_markup
- Optional module definition key:
contexts(array of context keys; empty means all contexts)- common contexts:
portal_dashboard,admin_tools,settings_diagnostics
- common contexts:
Operational Health (Prompt 6)
PIB_Operations_Healthadds runtime health checks for:- settings diagnostics status
- required table availability
- cron retention schedule
- required module registration/boot state
- mapping population trends
- auth activity and success/failure signal in the last 24 hours
- Admin tools now include
Observability > Healthwith:- overall status (
Healthy,Warnings,Action Needed) - check summary cards and 24h runtime signals
- detailed check matrix with direct action links
- read-only snapshot preview
- one-click snapshot download (
JSON)
- overall status (
- New extension hooks:
pib_operations_health_checkspib_operations_health_snapshot
Installation
- Copy
portal-identity-bridgeintowp-content/plugins/. - Activate Portal Identity Bridge in WordPress admin.
- Go to
Settings -> Portal Bridgeand configure:- Shopify Portal URL
- Client ID
- Shared Secret
- Issuer (
iss) - Require Company ID Claim
- Allowed Destination Prefixes
Auth Intake Endpoint
WordPress endpoint:
/wp-admin/admin-post.php?action=pib_auth_intake
The same URL is shown in plugin settings as Intake URL. Recommended transport is POST with form fields:
token(signed JWT)state(launch-bound one-time state)
Legacy query-signature handoff (customer_id/company_id/email/dest/ts/sig) is disabled.
Expected Token Claims
Minimum required claims:
exp(unix timestamp)jti(unique token id)iss(trusted token issuer)aud(trusted token audience)shopify_customer_idorcustomer_id(stable customer identifier)company_id(required account/company identifier)dest(internal destination path, example/dashboard/backorders?status=open)state(required in default strict handoff mode; should mirror launchstate)
Optional/used in Phase 5:
iat,nbfwp_user_id(optional explicit user hint)email(recommended; used for auto-link/auto-create and ongoing WP email sync)customer_email(alias accepted whenemailis not present)email_verified(optional trusted boolean for seamless first-time activation)email_verification_source(optional; useshopify_adminwhen verification came from Shopify Admin lookup)
Mapping guardrails:
- Email-based auto-link is blocked when target WP user is already actively mapped to a different Shopify customer.
- Existing blocked/archived customer mappings are not automatically reactivated during auth intake.
- If Shopify email claim changes, plugin attempts to sync mapped WordPress user email to keep customer identity aligned.
Sign-In Shortcode
Use on portal login page:
[portal_sign_in]
Optional:
[portal_sign_in label="Continue to Partner Portal"]
[portal_sign_in dest="/dashboard/backorders"]
The shortcode sends users to Shopify issuer URL with:
return_url(WordPress intake callback URL)dest(validated internal destination)intake(WordPress intake callback URL)client_id(expected token audience)state(one-time browser-bound handoff state)source=partner_portal
Logout shortcode:
[portal_logout_link]
SparkLayer Launch
Launch URL pattern:
https://partners.example.com/?pib_launch=1&dest=/materials
If user is already authenticated and mapped, launch redirects directly to destination. If not, launch redirects to Shopify sign-in handoff while preserving destination.
Launch helper shortcode:
[portal_launch_link dest="/materials" label="Partner Materials"]
Mapping Table
Custom table: {wp_prefix}pib_customer_map
Core fields:
shopify_customer_idcompany_idwp_user_idemailstatuscreated_at,updated_at,last_seen_at
Unique key: (shopify_customer_id, company_id)
New Settings (Phase 5)
Protected Route PrefixesUnprotected Route PrefixesAuto-create WordPress UsersAuto-link Existing Users by EmailRequire Email Verification for First-Time UsersAuto-Activate First-Time Users From Verified Shopify ClaimNew User RoleRequire Active Mapping on Protected RoutesRequire Managed Session on Protected Routes
Default role behavior for auto-created users:
shopify_customer(created on activation/upgrade if missing)- Additional role available for future workflows:
internal_employee Enable SparkLayer Launch EntrySpark Launch Allowed PrefixesManaged Session TTL (hours)Enable Periodic RevalidationRevalidation Interval (minutes)Enable Coordinated LogoutUpstream Logout URLUpstream Logout Return ParamRequire Handoff State VerificationHandoff State TTL (seconds)Auth Failure LimitAuth Failure Window (minutes)Allow Legacy JWT Query TransportEnable Audit LoggingAudit Log Retention (days)
Session guardrails:
- Protected routes can require a bridge-managed WordPress session (
Require Managed Session on Protected Routes). - Managed sessions are identity-bound (customer/company) and expire if session identity no longer matches active mapping context.
Protected route management:
- Route policy is centralized in
PIB_Protected_Route_Policy. - Protected prefixes are evaluated first, then unprotected prefixes can explicitly bypass route gating.
Failure Destinationshould resolve as public to avoid redirect loops.- Extension hooks:
pib_protected_route_prefixespib_unprotected_route_prefixespib_protected_route_evaluationpib_is_destination_protectedpib_protected_route_samples
Logout/session strategy:
- Explicit logout always clears local WordPress managed session markers and ends the WP session.
- Default local logout redirects to the configured failure/login destination with
?pib_logged_out=1. - Coordinated logout (optional) redirects to upstream logout and uses the local logged-out URL as the return target.
- Audit events are emitted for
logout_requested,logout_completed, andsession_expired.
Partner Materials
- Admin content type:
Partner Materials(pib_material). - Each material stores:
- File URL
- Allowed company IDs (optional, one per line)
- Frontend listing shortcode:
[portal_materials]
- Download endpoint:
/wp-admin/admin-post.php?action=pib_material_download&material_id=<ID>- Enforces login + active mapping + company entitlement.
Backorders
- Dashboard shortcode:
[portal_backorders]
- Export endpoint:
/wp-admin/admin-post.php?action=pib_backorders_export&pib_nonce=...- Enforces login + active mapping + nonce.
- API adapter settings:
Backorders API EndpointBackorders API TokenBackorders API Timeout (seconds)Backorders Default Limit
- Primary hook points:
pib_backorders_source_recordspib_backorders_request_argspib_backorders_viewpib_backorders_exportpib_backorders_error
Admin Tools
- WordPress admin page:
Portal Bridge -> Tools
- Tabs:
Overview(operational entry cards for daily workflows)Mappings(search/filter + activate/block/archive actions)Failures(unmatched/failed mapping + auth resolution records with guided actions)Health(runtime checks + support snapshot preview/download)Audit Logs(search/filter + clear action)Advanced(expert panels)
- Advanced panels:
Route Access(route policy overview + destination tester + sample route matrix)Extensions(plugin module lifecycle + registered portal extension modules)Token CheckSetup AssistantBridge ContractBridge SimulatorDiagnostics
- Mapping operations:
- Create/link/update mappings from admin form (customer/company -> WP user by ID/login/email)
- Actionable prefill from failures tab into mapping create/link form
Bridge Simulator (Prompt 1)
- In
Portal Bridge -> Tools -> Advanced, admins can launch a short-lived test handoff token via Bridge Simulator (Prompt 1). - This simulates the Shopify bridge handoff into WordPress intake via auto-submit POST (
token+state). - It is intended for validating WordPress intake/mapping/session flow before external Shopify route wiring is complete.
Bridge Contract (Prompt 2)
Portal Bridge -> Tools -> Advancedincludes Bridge Contract (Prompt 2).- It shows:
- canonical launch URL generated from configured settings
- expected Shopify intake handoff shape (POST URL + form fields)
- required JWT claims and expected values (
iss,aud,exp,jti,shopify_customer_id,company_id,dest,state) - implementation wiring map (what to configure in WordPress, Shopify App Proxy, and token-minting endpoint)
- copy/paste Cloudflare Worker token-minting script prefilled from current WP settings
- required worker environment variable matrix (
WP_JWT_SECRET,WP_ISSUER,WP_AUDIENCE,COMPANY_ID, plus Shopify lookup vars for seamless first-time activation) - optional Shopify theme button snippet for manual launch testing
- Outbound sign-in URL now includes:
return_url,intake,dest,client_id,state, andsource=partner_portal.
Setup Assistant (Prompt 3)
Portal Bridge -> Tools -> Advancedincludes Setup Assistant (Prompt 3).- It provides a click-by-click beginner sequence:
- confirm WordPress bridge values (
Portal URL,Client ID,Shared Secret,Issuer, intake URL) - configure Shopify App Proxy (
Prefix,Subpath, customer-facing URL vs proxy target URL) - configure token-minting endpoint env vars
- run verification links (
Shopify Bridge Test,Internal Token Test,Token Check,Mapping Failures)
- confirm WordPress bridge values (
- It is intended to remove ambiguity between:
- storefront/app-proxy URL (
/apps/<subpath>) - backend proxy target endpoint (worker/server URL)
- storefront/app-proxy URL (
Shopify Handoff Implementation (Platform-Agnostic)
This plugin does not passively detect Shopify login state across domains. Shopify-side route/app logic must actively perform JWT handoff into WordPress intake.
Expected implementation flow in Shopify app route:
- Receive launch parameters from WordPress:
return_urlintakedestclient_idstatesource=partner_portal
- Resolve authenticated Shopify customer context.
- Build a short-lived HS256 JWT using the same shared secret as WordPress
token_secret. - Include required claims:
iss(must exactly equal WordPress Issuer setting)aud(must includeclient_id)exp,iat,nbfjti(unique per token)shopify_customer_id(orcustomer_id)company_id(when WordPress requires company claim)dest(must match allowed destination prefixes)state(must mirror inbound launchstate)email(recommended for first-time auto-link/auto-create and identity sync)email_verified+email_verification_source=shopify_admin(optional trusted signal for seamless first-time activation)
- Return an auto-submit HTML form that POSTs to WordPress intake with:
token=<jwt>state=<state>
Operational notes:
- Issuer comparison is exact; normalize trailing slash behavior intentionally.
- Audience must include configured Client ID.
- Missing
company_idfails intake whenRequire Company ID Claimis enabled. - Normal Shopify login without the JWT redirect does not create a WordPress session.
Prompt 2 Migration Checklist
- Deploy updated token-minting endpoint that:
- preserves launch
state - includes
statein JWT claims - submits
token+stateto WordPress intake via POST
- preserves launch
- Confirm WordPress settings:
Require Handoff State Verification= enabledAllow Legacy JWT Query Transport= disabled
- If your external route is not updated yet, temporarily enable
Allow Legacy JWT Query Transportduring cutover. - Re-run smoke tests below and verify recent
auth_successevents in Audit Logs.
Bridge Smoke Test Checklist
- Run Internal Token Test in WordPress settings to verify intake/mapping/session path.
- Run Test Shopify Bridge Route from settings (or protected portal route) and confirm handoff submits into WordPress intake.
- Check
Portal Bridge -> Tools -> Failuresforauth_failure/mapping_resolution_deniedreason codes. - Check
Portal Bridge -> Tools -> Audit Logsfor recentauth_successorauth_failureevents.