|
| 1 | +/* |
| 2 | + * |
| 3 | + * Copyright © 2025 Ping Identity Corporation. All right reserved. |
| 4 | + * |
| 5 | + * This software may be modified and distributed under the terms |
| 6 | + * of the MIT license. See the LICENSE file for details. |
| 7 | + * |
| 8 | + */ |
| 9 | +import { oidcApp } from '../utils/oidc-app.js'; |
| 10 | + |
| 11 | +const AM_BASE = 'https://openam-sdks.forgeblocks.com/am'; |
| 12 | +const REALM = 'alpha'; |
| 13 | + |
| 14 | +const urlParams = new URLSearchParams(window.location.search); |
| 15 | +const wellknown = urlParams.get('wellknown'); |
| 16 | + |
| 17 | +const config = { |
| 18 | + clientId: 'ParClient', |
| 19 | + redirectUri: 'http://localhost:8443/par/', |
| 20 | + scope: 'openid profile email', |
| 21 | + par: true, |
| 22 | + serverConfig: { |
| 23 | + wellknown: |
| 24 | + wellknown || |
| 25 | + 'https://openam-sdks.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration', |
| 26 | + }, |
| 27 | +}; |
| 28 | + |
| 29 | +// Run journey Login to establish an AM session before background PAR auth |
| 30 | +async function runLoginJourney(username: string, password: string): Promise<void> { |
| 31 | + const authenticateUrl = `${AM_BASE}/json/realms/root/realms/${REALM}/authenticate?authIndexType=service&authIndexValue=Login`; |
| 32 | + |
| 33 | + // Step 1: start the journey |
| 34 | + const initRes = await fetch(authenticateUrl, { |
| 35 | + method: 'POST', |
| 36 | + credentials: 'include', |
| 37 | + headers: { 'Content-Type': 'application/json', 'Accept-API-Version': 'resource=2.1' }, |
| 38 | + body: '{}', |
| 39 | + }); |
| 40 | + const initJson = await initRes.json(); |
| 41 | + |
| 42 | + if (initJson.successUrl) return; // already authenticated |
| 43 | + |
| 44 | + // Fill NameCallback + PasswordCallback |
| 45 | + for (const cb of initJson.callbacks ?? []) { |
| 46 | + if (cb.type === 'NameCallback') cb.input[0].value = username; |
| 47 | + if (cb.type === 'PasswordCallback') cb.input[0].value = password; |
| 48 | + } |
| 49 | + |
| 50 | + // Step 2: submit credentials |
| 51 | + const submitRes = await fetch(authenticateUrl, { |
| 52 | + method: 'POST', |
| 53 | + credentials: 'include', |
| 54 | + headers: { 'Content-Type': 'application/json', 'Accept-API-Version': 'resource=2.1' }, |
| 55 | + body: JSON.stringify(initJson), |
| 56 | + }); |
| 57 | + const submitJson = await submitRes.json(); |
| 58 | + |
| 59 | + if (!submitJson.tokenId && !submitJson.successUrl) { |
| 60 | + throw new Error(submitJson.message || 'Login failed'); |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +const journeyForm = document.getElementById('journey-form') as HTMLFormElement; |
| 65 | +const journeyStatus = document.getElementById('journey-status') as HTMLParagraphElement; |
| 66 | +const backgroundBtn = document.getElementById('login-background') as HTMLButtonElement; |
| 67 | + |
| 68 | +journeyForm.addEventListener('submit', async (e) => { |
| 69 | + e.preventDefault(); |
| 70 | + const username = (document.getElementById('username') as HTMLInputElement).value; |
| 71 | + const password = (document.getElementById('password') as HTMLInputElement).value; |
| 72 | + journeyStatus.textContent = 'Logging in…'; |
| 73 | + try { |
| 74 | + await runLoginJourney(username, password); |
| 75 | + journeyStatus.textContent = '✓ Session established — background login now available.'; |
| 76 | + backgroundBtn.disabled = false; |
| 77 | + } catch (err) { |
| 78 | + journeyStatus.textContent = `✗ ${err instanceof Error ? err.message : 'Login failed'}`; |
| 79 | + } |
| 80 | +}); |
| 81 | + |
| 82 | +oidcApp({ config, urlParams }); |
0 commit comments