11/*
2- * Copyright (c) 2025 Ping Identity Corporation. All rights reserved.
2+ * Copyright (c) 2025-2026 Ping Identity Corporation. All rights reserved.
33 *
44 * This software may be modified and distributed under the terms
55 * of the MIT license. See the LICENSE file for details.
66 */
77import './style.css' ;
88
99import { journey } from '@forgerock/journey-client' ;
10+ import { WebAuthn , WebAuthnStepType } from '@forgerock/journey-client/webauthn' ;
1011
11- import type { JourneyClient , RequestMiddleware } from '@forgerock/journey-client/types' ;
12+ import type {
13+ JourneyClient ,
14+ JourneyClientConfig ,
15+ RequestMiddleware ,
16+ } from '@forgerock/journey-client/types' ;
1217
1318import { renderCallbacks } from './callback-map.js' ;
19+ import { renderDeleteDevicesSection } from './components/webauthn-devices.js' ;
1420import { renderQRCodeStep } from './components/qr-code.js' ;
1521import { renderRecoveryCodesStep } from './components/recovery-codes.js' ;
22+ import {
23+ deleteAllDevices ,
24+ deleteDevicesInSession ,
25+ storeDevicesBeforeSession ,
26+ } from './services/delete-webauthn-devices.js' ;
27+ import { webauthnComponent } from './components/webauthn.js' ;
1628import { serverConfigs } from './server-configs.js' ;
1729
1830const qs = window . location . search ;
@@ -61,7 +73,12 @@ if (searchParams.get('middleware') === 'true') {
6173
6274 let journeyClient : JourneyClient ;
6375 try {
64- journeyClient = await journey ( { config : config , requestMiddleware } ) ;
76+ const journeyConfig : JourneyClientConfig = {
77+ serverConfig : {
78+ wellknown : config . serverConfig . wellknown ,
79+ } ,
80+ } ;
81+ journeyClient = await journey ( { config : journeyConfig , requestMiddleware } ) ;
6582 } catch ( error ) {
6683 const message = error instanceof Error ? error . message : 'Unknown error' ;
6784 console . error ( 'Failed to initialize journey client:' , message ) ;
@@ -70,34 +87,6 @@ if (searchParams.get('middleware') === 'true') {
7087 }
7188 let step = await journeyClient . start ( { journey : journeyName } ) ;
7289
73- function renderComplete ( ) {
74- if ( step ?. type !== 'LoginSuccess' ) {
75- throw new Error ( 'Expected step to be defined and of type LoginSuccess' ) ;
76- }
77-
78- const session = step . getSessionToken ( ) ;
79-
80- console . log ( `Session Token: ${ session || 'none' } ` ) ;
81-
82- journeyEl . innerHTML = `
83- <h2 id="completeHeader">Complete</h2>
84- <span id="sessionLabel">Session:</span>
85- <pre id="sessionToken" id="sessionToken">${ session } </pre>
86- <button type="button" id="logoutButton">Logout</button>
87- ` ;
88-
89- const loginBtn = document . getElementById ( 'logoutButton' ) as HTMLButtonElement ;
90- loginBtn . addEventListener ( 'click' , async ( ) => {
91- await journeyClient . terminate ( ) ;
92-
93- console . log ( 'Logout successful' ) ;
94-
95- step = await journeyClient . start ( { journey : journeyName } ) ;
96-
97- renderForm ( ) ;
98- } ) ;
99- }
100-
10190 function renderError ( ) {
10291 if ( step ?. type !== 'LoginFailure' ) {
10392 throw new Error ( 'Expected step to be defined and of type LoginFailure' ) ;
@@ -134,6 +123,18 @@ if (searchParams.get('middleware') === 'true') {
134123 renderQRCodeStep ( journeyEl , step ) || renderRecoveryCodesStep ( journeyEl , step ) ;
135124
136125 if ( ! stepRendered ) {
126+ const webAuthnStep = WebAuthn . getWebAuthnStepType ( step ) ;
127+ const isWebAuthn =
128+ webAuthnStep === WebAuthnStepType . Authentication ||
129+ webAuthnStep === WebAuthnStepType . Registration ;
130+ if ( isWebAuthn ) {
131+ const webauthnSucceeded = await webauthnComponent ( journeyEl , step , 0 ) ;
132+ if ( webauthnSucceeded ) {
133+ submitForm ( ) ;
134+ }
135+ return ; // prevent the rest of the function from running
136+ }
137+
137138 const callbacks = step . callbacks ;
138139 renderCallbacks ( journeyEl , callbacks , submitForm ) ;
139140 }
@@ -145,6 +146,50 @@ if (searchParams.get('middleware') === 'true') {
145146 journeyEl . appendChild ( submitBtn ) ;
146147 }
147148
149+ function renderComplete ( ) {
150+ if ( step ?. type !== 'LoginSuccess' ) {
151+ throw new Error ( 'Expected step to be defined and of type LoginSuccess' ) ;
152+ }
153+
154+ const session = step . getSessionToken ( ) ;
155+
156+ journeyEl . replaceChildren ( ) ;
157+
158+ renderDeleteDevicesSection (
159+ journeyEl ,
160+ ( ) => storeDevicesBeforeSession ( config ) ,
161+ ( ) => deleteDevicesInSession ( config ) ,
162+ ( ) => deleteAllDevices ( config ) ,
163+ ) ;
164+
165+ const sessionLabelEl = document . createElement ( 'span' ) ;
166+ sessionLabelEl . id = 'sessionLabel' ;
167+ sessionLabelEl . innerText = 'Session:' ;
168+
169+ const sessionTokenEl = document . createElement ( 'pre' ) ;
170+ sessionTokenEl . id = 'sessionToken' ;
171+ sessionTokenEl . textContent = session || 'none' ;
172+
173+ const logoutBtn = document . createElement ( 'button' ) ;
174+ logoutBtn . type = 'button' ;
175+ logoutBtn . id = 'logoutButton' ;
176+ logoutBtn . innerText = 'Logout' ;
177+
178+ journeyEl . appendChild ( sessionLabelEl ) ;
179+ journeyEl . appendChild ( sessionTokenEl ) ;
180+ journeyEl . appendChild ( logoutBtn ) ;
181+
182+ logoutBtn . addEventListener ( 'click' , async ( ) => {
183+ await journeyClient . terminate ( ) ;
184+
185+ console . log ( 'Logout successful' ) ;
186+
187+ step = await journeyClient . start ( { journey : journeyName } ) ;
188+
189+ renderForm ( ) ;
190+ } ) ;
191+ }
192+
148193 formEl . addEventListener ( 'submit' , async ( event ) => {
149194 event . preventDefault ( ) ;
150195
0 commit comments