diff --git a/javascript/package-lock.json b/javascript/package-lock.json index e0966a03..3bc9d5ff 100644 --- a/javascript/package-lock.json +++ b/javascript/package-lock.json @@ -5729,6 +5729,24 @@ "license": "MIT", "optional": true }, + "node_modules/@pingidentity/protect": { + "version": "0.0.0-beta.0", + "resolved": "http://localhost:4873/@pingidentity/protect/-/protect-0.0.0-beta.0.tgz", + "integrity": "sha512-/Tc51ybhx+v6T6KfCdQp0oyoqrPSAZPVQte9sW5O3vOink0viklsm0KvIpIXaqOD5wwS+gLgpvQXwELqZElw2A==", + "dependencies": { + "@forgerock/javascript-sdk": "4.7.0" + } + }, + "node_modules/@pingidentity/protect/node_modules/@forgerock/javascript-sdk": { + "version": "4.7.0", + "resolved": "http://localhost:4873/@forgerock/javascript-sdk/-/javascript-sdk-4.7.0.tgz", + "integrity": "sha512-0wpy2/ii9F9yKI3r+huqQtp6bVAeajf2+Llq25dvkfxQX19FKKi9KPPMF7JTVti6heYHyo36lxweB7xerB5UTQ==", + "license": "MIT", + "dependencies": { + "@reduxjs/toolkit": "^2.2.5", + "immer": "^10.1.1" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -5754,13 +5772,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.2" + "playwright": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -6468,9 +6486,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.6.tgz", - "integrity": "sha512-uYssdp9z5zH5GQ0L4zEJ2ZuavYsJwkozjiUzCRfGtaaQcyjAMJ34aP8idv61QlqTozu6kudyr6JMq9Chf09dfA==", + "version": "20.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz", + "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==", "dev": true, "license": "MIT", "dependencies": { @@ -10352,9 +10370,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.180", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz", - "integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==", + "version": "1.5.182", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", + "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", "dev": true, "license": "ISC" }, @@ -18515,13 +18533,13 @@ } }, "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", + "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -18534,9 +18552,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", + "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -20955,9 +20973,9 @@ } }, "node_modules/socks": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", - "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", + "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", "dev": true, "license": "MIT", "dependencies": { @@ -23183,9 +23201,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.100.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.0.tgz", - "integrity": "sha512-H8yBSBTk+BqxrINJnnRzaxU94SVP2bjd7WmA+PfCphoIdDpeQMJ77pq9/4I7xjLq38cB1bNKfzYPZu8pB3zKtg==", + "version": "5.100.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.1.tgz", + "integrity": "sha512-YJB/ESPUe2Locd0NKXmw72Dx8fZQk1gTzI6rc9TAT4+Sypbnhl8jd8RywB1bDsDF9Dy1RUR7gn3q/ZJTd0OZZg==", "dev": true, "license": "MIT", "dependencies": { @@ -24020,8 +24038,9 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@forgerock/davinci-client": "latest", + "@forgerock/davinci-client": "^0.0.0-beta-20250714191203", "@forgerock/javascript-sdk": "latest", + "@pingidentity/protect": "^0.0.0-beta.0", "cookie-parser": "^1.4.5", "cors": "^2.8.5", "dotenv": "^10.0.0", @@ -24068,6 +24087,60 @@ "webpack-dev-server": "^5.1.0" } }, + "reactjs-todo-davinci/node_modules/@forgerock/davinci-client": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/davinci-client/-/davinci-client-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-+CvwXJjrtol+avPX8aXLUOGUsSuzP3L3o9SSobpi86COWLwLMtIvMCbeW5sKCpQ92pmHFTwjHkBS/DeZmXvD8w==", + "dependencies": { + "@forgerock/sdk-logger": "0.0.0-beta-20250714191203", + "@forgerock/sdk-oidc": "0.0.0-beta-20250714191203", + "@forgerock/sdk-request-middleware": "0.0.0-beta-20250714191203", + "@forgerock/sdk-types": "0.0.0-beta-20250714191203", + "@forgerock/storage": "0.0.0-beta-20250714191203", + "@reduxjs/toolkit": "^2.8.2", + "immer": "^10.1.1" + } + }, + "reactjs-todo-davinci/node_modules/@forgerock/sdk-logger": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/sdk-logger/-/sdk-logger-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-kmjIIIRFQpxAfiHmN0SWdcsC0VR8ZiFmpL6AY8FLjYZ0mjkEomO81Rh0LbqZu/nXN/jgRxhV8CYreJyZU37eSA==" + }, + "reactjs-todo-davinci/node_modules/@forgerock/sdk-oidc": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/sdk-oidc/-/sdk-oidc-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-tvzgOEGVieKgJAnA11M3XFXIiei3SkUPlL1CwhCEyG0j6uyXySXtXTwBQJwUPHFR6d5vpiW8SYVpRfXRxXUAgw==", + "dependencies": { + "@forgerock/sdk-types": "0.0.0-beta-20250714191203", + "@forgerock/sdk-utilities": "0.0.0-beta-20250714191203" + } + }, + "reactjs-todo-davinci/node_modules/@forgerock/sdk-request-middleware": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/sdk-request-middleware/-/sdk-request-middleware-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-v82hC0Si5o7GhpKvEvuyKnDV+HpRX96wcjq/BFRMqk/NCDIue3o035BtAhC5q6obI3k46jHbl0r74QHcSE1JIw==", + "dependencies": { + "@reduxjs/toolkit": "^2.8.2" + } + }, + "reactjs-todo-davinci/node_modules/@forgerock/sdk-types": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/sdk-types/-/sdk-types-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-DahZ072A8YV3e3btmXiOWgzAB57dL6w/hLP/WRpIuCoTTictfBrRLtcgOW9RBd70hvO5HXC/vc/T+G0nn3ElCA==" + }, + "reactjs-todo-davinci/node_modules/@forgerock/sdk-utilities": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/sdk-utilities/-/sdk-utilities-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-Xd8k6q1aIB9sYZyluYNBGL7zE1Y/+AFrzrIvgIhzBVroiM5ba8cfkJc9VGhQ7cx/yxRgata9yHPOHpiZbLJu0w==" + }, + "reactjs-todo-davinci/node_modules/@forgerock/storage": { + "version": "0.0.0-beta-20250714191203", + "resolved": "http://localhost:4873/@forgerock/storage/-/storage-0.0.0-beta-20250714191203.tgz", + "integrity": "sha512-F+3hvR2cAqoT1x6O7ytz9cXCC3Id6JD1EApu2ViVBBH2e4i/dNwUqfqe9FIP7AjymoCLwpa9ACI530G9/UKwvw==", + "dependencies": { + "@forgerock/sdk-types": "0.0.0-beta-20250714191203" + } + }, "reactjs-todo-davinci/node_modules/@types/express-serve-static-core": { "version": "4.19.6", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", diff --git a/javascript/reactjs-todo-davinci/.env.example b/javascript/reactjs-todo-davinci/.env.example index f77764d4..368f0f57 100644 --- a/javascript/reactjs-todo-davinci/.env.example +++ b/javascript/reactjs-todo-davinci/.env.example @@ -4,4 +4,7 @@ DEVELOPMENT=$DEVELOPMENT PORT=$PORT WEB_OAUTH_CLIENT=$WEB_OAUTH_CLIENT SCOPE="openid profile email phone name revoke" -WELLKNOWN_URL=$WELLKNOWN_URL \ No newline at end of file +WELLKNOWN_URL=$WELLKNOWN_URL +INIT_PROTECT=$INIT_PROTECT # a boolean which if true will initialize protect at app bootstrap time, + # otherwise relies on the PingOne Protect collector for initialization +PINGONE_ENV_ID=$PINGONE_ENV_ID # required when ProtectCollector is present \ No newline at end of file diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js index b328e167..3a998f0f 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js @@ -41,7 +41,7 @@ export default function Form() { * and index 1 having the "setter" methods to set new state values. */ - const [{ theme }, methods] = useContext(AppContext); + const [{ theme, protectAPI }, methods] = useContext(AppContext); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); @@ -53,22 +53,56 @@ export default function Form() { useDavinci(); /** - * @function hasProtectCollector - Determines if there is a Protect SDK collector + * @function hasProtectTextCollector - Determines if there is a Protect SDK Text collector * @param {Object} collectors - An array of collectors from DaVinci - * @returns {boolean} - True if there is a Protect SDK collector otherwise false + * @returns {boolean} - True if there is a Protect SDK Text collector otherwise false */ - function hasProtectCollector(collectors) { + function hasProtectTextCollector(collectors) { return collectors?.some( (collector) => collector.type === 'TextCollector' && collector.name === 'protectsdk', ); } + /** + * @function hasProtectCollector - Determines if there is a ProtectCollector + * @param {Object} collectors - An array of collectors from DaVinci + * @returns {boolean} - True if there is a ProtectCollector otherwise false + */ + function hasProtectCollector(collectors) { + return collectors?.some((collector) => collector.type === 'ProtectCollector'); + } + + /** + * @function updateProtectCollector - Updates the ProtectCollector with data collected + * @param {Object} protectCollector - A ProtectCollector from DaVinci + * @returns {Promise} + */ + async function updateProtectCollector(protectCollector) { + /** + * Use the `getData()` method to retrieve the device profiling and behavioral data + * collected since initialization. Then set the data on the ProtectCollector. + */ + const data = await protectAPI.getData(); + const protectUpdater = updater(protectCollector); + const error = protectUpdater(data); + if (error && 'error' in error) { + console.error(`Error updating ProtectCollector: ${error.error.message}`); + } + } + /** * @function submitProtect - Handles Protect collector submission * @returns {Promise} */ async function submitProtect() { - await setNext(); + if (hasProtectTextCollector(collectors)) { + await setNext(); + } else if (hasProtectCollector(collectors)) { + const protectCollector = collectors.find( + (collector) => collector.type === 'ProtectCollector', + ); + await updateProtectCollector(protectCollector); + } } /** @@ -123,6 +157,9 @@ export default function Form() { setIsLoading(true); try { + // Submit Protect data if there is a Protect collector + await submitProtect(collectors); + // Get the next node in the flow await setNext(); } catch (error) { @@ -147,7 +184,14 @@ export default function Form() { const collectorName = collector.name; if (collector.type === 'TextCollector' && collector.name === 'protectsdk') { - return ; + return ( + + ); } switch (collector.type) { @@ -189,8 +233,11 @@ export default function Form() { submitForm={setNext} /> ); - case 'PROTECT': - return ; + // TODO: Do we need this? + // case 'PROTECT': + // return ; + case 'ProtectCollector': + return ; case 'SubmitCollector': return ; case 'FlowCollector': @@ -248,7 +295,7 @@ export default function Form() {
{formIcon(formAction)}

- {hasProtectCollector(collectors) ? '' : formName} + {hasProtectTextCollector(collectors) || hasProtectCollector(collectors) ? '' : formName}

{/* * Map over the collectors and render the appropriate diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/protect.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/protect.js index de3163bd..2b10c1ab 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/protect.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/protect.js @@ -7,29 +7,70 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; +import { protect } from '@pingidentity/protect'; +import { INIT_PROTECT, PINGONE_ENV_ID } from '../../constants.js'; +import { AppContext } from '../../global-state.js'; import Loading from '../utilities/loading.js'; -export default function Protect({ updater, submit }) { +export default function Protect({ collector, updater, submit }) { const [loading, setLoading] = useState(true); - /** - * The protect collector is sent with the first node of the flow, but - * it is not needed. It is a self-submitting node which requires no - * user interaction. While you would normally load the Protect module - * here and wait for a response, we instead mock the response with a - * dummy value and update the collector. Then call the - * submit function to proceed with the flow. - */ + const [loadingMessage, setLoadingMessage] = useState(''); + // eslint-disable-next-line no-unused-vars + const [state, { setProtectAPI }] = useContext(AppContext); + useEffect(() => { - async function handleProtect() { + async function handleProtectTextCollector() { + /** + * A TextCollector with name protectsdk is sent with the first node of the flow, + * but it is not needed. It is a self-submitting node which requires no + * user interaction. While you would normally load the Protect module + * here and wait for a response, we instead mock the response with a + * dummy value and update the collector. Then call the + * submit function to proceed with the flow. + */ updater('fakeprofile'); setLoading(false); if (submit !== undefined) { await submit(); } } - handleProtect(); - }, [updater, submit]); - return loading ? : null; + async function handleProtectCollector() { + try { + if (!INIT_PROTECT && !state.protectAPI) { + /** + * If the INIT_PROTECT flag is false, rely on the configuration from the PingOne + * Protect Collector's output to initialize the Protect API. Then call the API's + * start method to begin collecting data. + */ + setLoadingMessage('Initializing PingOne Protect...'); + + const config = collector.output.config; + const protectAPI = await protect({ + envId: PINGONE_ENV_ID, + behavioralDataCollection: config.behavioralDataCollection, + // universalDeviceIdentification: config.universalDeviceIdentification, // This feature is not yet supported + }); + + setProtectAPI(protectAPI); + await protectAPI.start(); + console.log('PingOne Protect initialized by collector. Collecting data...'); + } + } catch (err) { + console.error(`Failed to initialize PingOne Protect for data collection`, err); + } finally { + setLoadingMessage(''); + setLoading(false); + } + } + + if (collector.type === 'TextCollector' && collector.name === 'protectsdk') { + handleProtectTextCollector(); + } else if (collector.type === 'ProtectCollector') { + handleProtectCollector(); + } + }, [updater, submit, collector, state.protectAPI]); + + return loading ? : null; } diff --git a/javascript/reactjs-todo-davinci/client/constants.js b/javascript/reactjs-todo-davinci/client/constants.js index 40aaf2b8..9e9b496d 100755 --- a/javascript/reactjs-todo-davinci/client/constants.js +++ b/javascript/reactjs-todo-davinci/client/constants.js @@ -14,3 +14,5 @@ export const DEBUGGER = process.env.DEBUGGER_OFF === 'false'; export const WEB_OAUTH_CLIENT = process.env.WEB_OAUTH_CLIENT; export const SCOPE = process.env.SCOPE; export const WELLKNOWN_URL = process.env.WELLKNOWN_URL; +export const INIT_PROTECT = process.env.INIT_PROTECT === 'true'; +export const PINGONE_ENV_ID = process.env.PINGONE_ENV_ID; diff --git a/javascript/reactjs-todo-davinci/client/global-state.js b/javascript/reactjs-todo-davinci/client/global-state.js index 348c8abc..1c42a352 100755 --- a/javascript/reactjs-todo-davinci/client/global-state.js +++ b/javascript/reactjs-todo-davinci/client/global-state.js @@ -20,9 +20,16 @@ import { DEBUGGER } from './constants'; * @param {Object} props.isAuthenticated - Boolean value of user's auth status * @param {Object} props.prefersDarkTheme - User theme setting * @param {Object} props.username - User's username + * @param {Object} props.protectAPI - A set of methods to interact with PingOne Protect * @returns {Array} - Global state values and state methods */ -export function useGlobalStateMgmt({ email, isAuthenticated, prefersDarkTheme, username }) { +export function useGlobalStateMgmt({ + email, + isAuthenticated, + prefersDarkTheme, + username, + protectAPI, +}) { /** * Create state properties for "global" state. * Using internal names that differ from external to prevent shadowing. @@ -32,6 +39,7 @@ export function useGlobalStateMgmt({ email, isAuthenticated, prefersDarkTheme, u const [authenticated, setAuthentication] = useState(isAuthenticated || false); const [mail, setEmail] = useState(email || ''); const [name, setUser] = useState(username || ''); + const [protectAPIMethods, setProtectAPI] = useState(protectAPI || null); let theme; @@ -119,11 +127,13 @@ export function useGlobalStateMgmt({ email, isAuthenticated, prefersDarkTheme, u email: mail, theme, username: name, + protectAPI: protectAPIMethods, }, { setAuthentication: setAuthenticationWrapper, setEmail: setEmailWrapper, setUser: setUserWrapper, + setProtectAPI, }, ]; } diff --git a/javascript/reactjs-todo-davinci/client/index.js b/javascript/reactjs-todo-davinci/client/index.js index 7d66eff4..01c9e125 100755 --- a/javascript/reactjs-todo-davinci/client/index.js +++ b/javascript/reactjs-todo-davinci/client/index.js @@ -9,11 +9,12 @@ */ import { Config, TokenStorage } from '@forgerock/javascript-sdk'; +import { protect } from '@pingidentity/protect'; import React from 'react'; import ReactDOM from 'react-dom/client'; import createConfig from './utilities/create-config'; import Router from './router'; -import { DEBUGGER } from './constants'; +import { DEBUGGER, INIT_PROTECT, PINGONE_ENV_ID } from './constants'; import { AppContext, useGlobalStateMgmt } from './global-state'; /** @@ -62,6 +63,27 @@ const config = createConfig(); console.error(`Error: token retrieval for hydration; ${err}`); } + /** + * If the INIT_PROTECT flag is set, initialize PingOne Protect as early as + * possible in the application for data collection. The PingOne environment ID + * is required while all other options in the configuration are optional. + */ + let protectAPI; + if (INIT_PROTECT) { + if (!PINGONE_ENV_ID) { + console.error('Missing PingOne environment ID for Protect initialization'); + } else { + try { + protectAPI = await protect({ envId: PINGONE_ENV_ID }); + // Call `start()` to begin collecting data + await protectAPI.start(); + console.log('PingOne Protect initialized at bootstrap. Collecting data...'); + } catch (error) { + console.error(`Error initializing PingOne Protect: ${error}`); + } + } + } + /** * Pull custom values from outside of the app to (re)hydrate state. */ @@ -93,6 +115,7 @@ const config = createConfig(); isAuthenticated, prefersDarkTheme, username, + protectAPI, }); return ( diff --git a/javascript/reactjs-todo-davinci/package.json b/javascript/reactjs-todo-davinci/package.json index c2294088..1391ceaa 100644 --- a/javascript/reactjs-todo-davinci/package.json +++ b/javascript/reactjs-todo-davinci/package.json @@ -39,8 +39,9 @@ "webpack-dev-server": "^5.1.0" }, "dependencies": { - "@forgerock/davinci-client": "latest", + "@forgerock/davinci-client": "^0.0.0-beta-20250714191203", "@forgerock/javascript-sdk": "latest", + "@pingidentity/protect": "^0.0.0-beta.0", "cookie-parser": "^1.4.5", "cors": "^2.8.5", "dotenv": "^10.0.0", diff --git a/javascript/reactjs-todo-davinci/webpack.config.js b/javascript/reactjs-todo-davinci/webpack.config.js index 72d608f6..ffaeea27 100644 --- a/javascript/reactjs-todo-davinci/webpack.config.js +++ b/javascript/reactjs-todo-davinci/webpack.config.js @@ -15,6 +15,8 @@ module.exports = () => { const WEB_OAUTH_CLIENT = process.env.WEB_OAUTH_CLIENT || localEnv.WEB_OAUTH_CLIENT; const SCOPE = process.env.SCOPE || localEnv.SCOPE; const WELLKNOWN_URL = process.env.WELLKNOWN_URL || localEnv.WELLKNOWN_URL; + const INIT_PROTECT = process.env.INIT_PROTECT || localEnv.INIT_PROTECT; + const PINGONE_ENV_ID = process.env.PINGONE_ENV_ID || localEnv.PINGONE_ENV_ID; return { // Point to the top level source file @@ -106,6 +108,8 @@ module.exports = () => { 'process.env.WEB_OAUTH_CLIENT': JSON.stringify(WEB_OAUTH_CLIENT), 'process.env.SCOPE': JSON.stringify(SCOPE), 'process.env.WELLKNOWN_URL': JSON.stringify(WELLKNOWN_URL), + 'process.env.INIT_PROTECT': JSON.stringify(INIT_PROTECT), + 'process.env.PINGONE_ENV_ID': JSON.stringify(PINGONE_ENV_ID), }), ], };