A ready-to-deploy demo environment showcasing Ping Identity's CIAM capabilities using PingOne. Three mock healthcare websites authenticate through PingOne, demonstrating:
- User Registration via PingOne self-service
- Adaptive MFA (configurable in PingOne policies)
- Single Sign-On (SSO) across separate applications
Windows/Mac browser ---> Ubuntu VM (nginx + static sites)
https://IP1 -> BX Health Patient Portal (teal theme)
https://IP2 -> BX Health Scheduling (green theme)
https://IP3 -> BX Health Bill Pay (indigo theme)
Each site is a static HTML/CSS/JS app. Authentication is handled entirely client-side using OIDC Authorization Code flow with PKCE. No backend required.
- Ubuntu VM (22.04+ recommended) with 3 available IP addresses on the same subnet
- Network access from your browser machine to the VM
- A PingOne account with an environment created
- Log into PingOne Admin Console
- Create (or use) an environment
- Create 3 OIDC SPA applications: (OpenID Connect)
- BX Health Portal (redirect:
https://<IP1>/callback.html) - BX Health Scheduling (redirect:
https://<IP2>/callback.html) - BX Health Bill Pay (redirect:
https://<IP3>/callback.html)
- BX Health Portal (redirect:
- For each app:
- Set PKCE to S256 Required
- Add sign-off URL:
https://<IPx>/index.html - Note the Client ID
- Enable self-registration: Go to Authentication > Policies > edit the default policy > enable Registration
- Note your Environment ID (from Environment > Properties)
git clone https://github.com/sstrohmeyer/ping_demo.git
cd ping_demo
bash configure.shThis will prompt for your PingOne Environment ID, the 3 Client IDs, and the IPs to use.
sudo bash setup.shThis binds the IPs, generates a self-signed SSL cert, deploys the sites to nginx, and starts everything.
Copy /etc/ssl/bxhealth/bxhealth.crt to your browser machine and import it:
- Chrome/Edge (Windows): Import into
certmgr.msc> Trusted Root Certification Authorities - Chrome (Mac): Import into Keychain Access > System > Certificates, set to Always Trust
- Firefox: Either import via Settings > Certificates > Import, or set
security.enterprise_roots.enabled = trueinabout:configto use the OS store
- Open
https://<IP1>— BX Health Portal landing page - Click Sign In — redirects to PingOne hosted login
- After authenticating, you land on the dashboard
- Click Schedule Appointment — navigates to
https://<IP2>, already logged in (SSO) - Click Go to Bill Pay — navigates to
https://<IP3>, also already logged in
- User authenticates at PingOne via the Portal app
- PingOne creates a session cookie on
auth.pingone.com - When the user navigates to Scheduling or Bill Pay, that site calls PingOne's authorize endpoint with
prompt=none - Since a PingOne session already exists, tokens are returned silently — no login screen
- The user appears instantly logged in on the second site
ping_demo/
├── configure.sh # Interactive config (run first)
├── setup.sh # Deployment script (run with sudo)
├── bx-health-portal/ # Patient Portal site
│ ├── index.html # Landing page
│ ├── dashboard.html # Post-login dashboard
│ ├── callback.html # OIDC callback handler
│ ├── css/style.css # Teal theme
│ ├── js/auth.js # OIDC/PKCE library
│ ├── js/config.js # PingOne config (reference)
│ └── img/ # Logo + background
├── bx-health-scheduling/ # Scheduling site (green theme)
│ └── (same structure)
└── bx-health-billpay/ # Bill Pay site (indigo theme)
└── (same structure)
| Symptom | Cause | Fix |
|---|---|---|
| Default nginx page shows | Old nginx process has stale listeners | sudo systemctl stop nginx && sudo systemctl start nginx (full restart, not reload) |
| Buttons do nothing (no click response) | crypto.subtle unavailable (insecure context) |
Auth.js includes a pure JS SHA-256 fallback; ensure you're on HTTPS |
PING_CONFIG is not defined |
External script blocked by browser | Config is inlined in HTML pages to avoid this |
| PingOne returns 403 | Client ID mismatch or app not enabled | Verify client IDs and that the app is toggled ON in PingOne |
| Registration button shows login page | Self-registration not enabled | Enable in PingOne: Authentication > Policies > edit policy > enable Registration |
| Firefox cert warnings despite import | Firefox uses its own cert store | Set security.enterprise_roots.enabled = true in about:config, or import cert directly into Firefox |
| IP addresses not responding | IPs not bound to NIC | Run ip addr show to verify; IPs don't survive reboot unless added to netplan |
- Config inlining: PingOne config (
PING_CONFIG) is declared inline in each HTML page rather than loaded from an externalconfig.js. Browsers may block external script execution on self-signed certificate origins, even when the file is accessible. Inline scripts avoid this. - PKCE fallback: The
auth.jslibrary includes a pure JavaScript SHA-256 implementation as a fallback for environments wherecrypto.subtleis unavailable (which happens when the browser treats the page as an insecure context despite HTTPS). - nginx restart vs reload: After initial setup, always use
stop+startrather thanreload. A reload attempts to bind new listeners while old ones hold the ports, causingEADDRINUSEerrors that fail silently.