From ccb194eed7167853c22e480570ebd42859fbfeda Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Wed, 18 Sep 2024 17:42:37 -0400 Subject: [PATCH 1/5] [UID-159] Additional RTR debugging and configuration options --- src/settings/RefreshTokenRotation.js | 145 +++++++++++++++++++++------ translations/ui-developer/en.json | 6 +- 2 files changed, 117 insertions(+), 34 deletions(-) diff --git a/src/settings/RefreshTokenRotation.js b/src/settings/RefreshTokenRotation.js index 626c7f0..9529776 100644 --- a/src/settings/RefreshTokenRotation.js +++ b/src/settings/RefreshTokenRotation.js @@ -1,70 +1,94 @@ -import { useEffect, useState } from 'react'; +import { merge } from 'lodash'; import PropTypes from 'prop-types'; +import { useCallback, useEffect, useState } from 'react'; +import { Field, Form } from 'react-final-form'; import { FormattedMessage } from 'react-intl'; -import { - getTokenExpiry, - setTokenExpiry, -} from '@folio/stripes/core'; - -import { - Button, - LoadingPane, - Pane, - PaneHeader, -} from '@folio/stripes/components'; +import { getTokenExpiry, setTokenExpiry } from '@folio/stripes/core'; +import { Button, LoadingPane, Pane, PaneHeader, TextField } from '@folio/stripes/components'; +import { RTR_FORCE_REFRESH_EVENT } from '../../../stripes-core/src/components/Root/constants'; /** * manipulate AT/RT expiration dates in storage in order to test RTR. * @returns */ -const RefreshTokenRotation = () => { +const RefreshTokenRotation = ({ stripes }) => { const [isLoading, setIsLoading] = useState(true); const [tokenExpiration, setTokenExpiration] = useState({}); useEffect(() => { - getTokenExpiry() - .then(te => { - setTokenExpiration(te ?? { atExpires: -1, rtExpires: -1 }); - setIsLoading(false); - }); + setIsLoading(true); + getTokenExpiry().then((te) => { + setTokenExpiration(te ?? { atExpires: -1, rtExpires: -1 }); + setIsLoading(false); + }); }, []); /** * invalidateAT * return a promise to expire the AT in local storage */ - const invalidateAT = () => { - return getTokenExpiry() - .then(te => { - const expiration = { ...te }; - expiration.atExpires = -1; + const invalidateAT = useCallback(() => { + return getTokenExpiry().then((te) => { + const expiration = { ...te }; + expiration.atExpires = -1; - return setTokenExpiry(expiration); - }); - }; + return setTokenExpiry(expiration); + }); + }, []); /** * invalidateRT * return a promise to expire the AT and RT in local storage */ - const invalidateRT = () => { + const invalidateRT = useCallback(() => { const expiration = { atExpires: -1, rtExpires: -1, }; return setTokenExpiry(expiration); - }; + }, []); + + /** + * forceRefresh + * dispatch an event to force a token rotation + */ + const forceRefresh = useCallback( + () => window.dispatchEvent(new Event(RTR_FORCE_REFRESH_EVENT)), + [], + ); + + /** + * saveRtrConfig + * update stripes.config.rtr from form + */ + const saveRtrConfig = useCallback( + (values) => { + merge(stripes.config.rtr, { + idleSessionTTL: values.idleSessionTTL, + idleModalTTL: values.idleModalTTL, + rotationIntervalFraction: Number(values.rotationIntervalFraction), + activityEvents: values.activityEvents.split(',').map((e) => e.trim()), + }); + + forceRefresh(); + }, + [stripes, forceRefresh], + ); if (!isLoading) { return ( } />} + renderHeader={(renderProps) => ( + } /> + )} >
    -
  • stripes logs RTR events in the category rtr
  • +
  • + stripes logs RTR events in the category rtr +
{!isLoading && (
@@ -75,8 +99,55 @@ const RefreshTokenRotation = () => {
)}
- - + + + + + +
+ {({ handleSubmit, pristine, submitting }) => ( + + } + /> + } + /> + } + type="number" + step={0.01} + min={0} + /> + } + /> + + + )} +
); @@ -89,7 +160,15 @@ RefreshTokenRotation.propTypes = { stripes: PropTypes.shape({ okapi: PropTypes.shape({ tenant: PropTypes.string, - }) + }), + config: PropTypes.shape({ + rtr: PropTypes.shape({ + idleSessionTTL: PropTypes.string, + idleModalTTL: PropTypes.string, + rotationIntervalFraction: PropTypes.number, + activityEvents: PropTypes.arrayOf(PropTypes.string), + }), + }), }).isRequired, }; diff --git a/translations/ui-developer/en.json b/translations/ui-developer/en.json index 6323ba0..682362e 100644 --- a/translations/ui-developer/en.json +++ b/translations/ui-developer/en.json @@ -170,6 +170,11 @@ "rtr": "Refresh token rotation", "rtr.invalidateAT": "Invalidate access token", "rtr.invalidateRT": "Invalidate refresh token", + "rtr.forceRefresh": "Force refresh", + "rtr.idleSessionTTL": "idleSessionTTL: duration an idle session last before being killed (e.g. 1h, 1m, 5s, 10ms)", + "rtr.idleModalTTL": "idleModalTTL: duration the idle modal should be shown before session is killed (e.g. 1h, 1m, 5s, 10ms)", + "rtr.rotationIntervalFraction": "rotationIntervalFraction: decimal fraction of how early to refresh the access token (e.g. 0.6 is 60% into the lifetime of the token)", + "rtr.activityEvents": "activityEvents: which DOM events constitute user activity (comma-separated, e.g. 'mousemove,keydown')", "rtr.registerServiceWorker": "Register the service worker", "rtr.unregisterServiceWorker": "Unregister the service worker", @@ -192,5 +197,4 @@ "permission.settings.okapiConsole.modules": "Settings (developer): Can use the Okapi console's Modules tab", "permission.settings.userLocale": "Settings (developer): Can edit locale entries for any user", "permission.settings.okapiTimers": "Settings (developer): Can view okapi timers" - } From 72fb50a5886c859093d7f824288ba73eb97c8711 Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Thu, 19 Sep 2024 10:06:54 -0400 Subject: [PATCH 2/5] typo --- translations/ui-developer/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/ui-developer/en.json b/translations/ui-developer/en.json index 682362e..bd00c6a 100644 --- a/translations/ui-developer/en.json +++ b/translations/ui-developer/en.json @@ -171,7 +171,7 @@ "rtr.invalidateAT": "Invalidate access token", "rtr.invalidateRT": "Invalidate refresh token", "rtr.forceRefresh": "Force refresh", - "rtr.idleSessionTTL": "idleSessionTTL: duration an idle session last before being killed (e.g. 1h, 1m, 5s, 10ms)", + "rtr.idleSessionTTL": "idleSessionTTL: duration an idle session lasts before being killed (e.g. 1h, 1m, 5s, 10ms)", "rtr.idleModalTTL": "idleModalTTL: duration the idle modal should be shown before session is killed (e.g. 1h, 1m, 5s, 10ms)", "rtr.rotationIntervalFraction": "rotationIntervalFraction: decimal fraction of how early to refresh the access token (e.g. 0.6 is 60% into the lifetime of the token)", "rtr.activityEvents": "activityEvents: which DOM events constitute user activity (comma-separated, e.g. 'mousemove,keydown')", From f9759134a2bc63de16a57914f144be1682770897 Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Fri, 20 Sep 2024 10:22:05 -0400 Subject: [PATCH 3/5] cleanup --- src/settings/RefreshTokenRotation.js | 38 ++-------------------------- translations/ui-developer/en.json | 2 -- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/src/settings/RefreshTokenRotation.js b/src/settings/RefreshTokenRotation.js index 9529776..40a886b 100644 --- a/src/settings/RefreshTokenRotation.js +++ b/src/settings/RefreshTokenRotation.js @@ -4,9 +4,8 @@ import { useCallback, useEffect, useState } from 'react'; import { Field, Form } from 'react-final-form'; import { FormattedMessage } from 'react-intl'; -import { getTokenExpiry, setTokenExpiry } from '@folio/stripes/core'; +import { getTokenExpiry, RTR_CONSTANTS } from '@folio/stripes/core'; import { Button, LoadingPane, Pane, PaneHeader, TextField } from '@folio/stripes/components'; -import { RTR_FORCE_REFRESH_EVENT } from '../../../stripes-core/src/components/Root/constants'; /** * manipulate AT/RT expiration dates in storage in order to test RTR. @@ -24,38 +23,12 @@ const RefreshTokenRotation = ({ stripes }) => { }); }, []); - /** - * invalidateAT - * return a promise to expire the AT in local storage - */ - const invalidateAT = useCallback(() => { - return getTokenExpiry().then((te) => { - const expiration = { ...te }; - expiration.atExpires = -1; - - return setTokenExpiry(expiration); - }); - }, []); - - /** - * invalidateRT - * return a promise to expire the AT and RT in local storage - */ - const invalidateRT = useCallback(() => { - const expiration = { - atExpires: -1, - rtExpires: -1, - }; - - return setTokenExpiry(expiration); - }, []); - /** * forceRefresh * dispatch an event to force a token rotation */ const forceRefresh = useCallback( - () => window.dispatchEvent(new Event(RTR_FORCE_REFRESH_EVENT)), + () => window.dispatchEvent(new Event(RTR_CONSTANTS.RTR_FORCE_REFRESH_EVENT)), [], ); @@ -99,13 +72,6 @@ const RefreshTokenRotation = ({ stripes }) => { )}
- - - diff --git a/translations/ui-developer/en.json b/translations/ui-developer/en.json index bd00c6a..5991f93 100644 --- a/translations/ui-developer/en.json +++ b/translations/ui-developer/en.json @@ -168,8 +168,6 @@ "userLocale.numberingSystem": "Numbering system (override locale's default; details)", "rtr": "Refresh token rotation", - "rtr.invalidateAT": "Invalidate access token", - "rtr.invalidateRT": "Invalidate refresh token", "rtr.forceRefresh": "Force refresh", "rtr.idleSessionTTL": "idleSessionTTL: duration an idle session lasts before being killed (e.g. 1h, 1m, 5s, 10ms)", "rtr.idleModalTTL": "idleModalTTL: duration the idle modal should be shown before session is killed (e.g. 1h, 1m, 5s, 10ms)", From 8dcd6046ce701471573952fc71a9c2fecdbc5e4f Mon Sep 17 00:00:00 2001 From: Noah Overcash Date: Tue, 24 Sep 2024 20:34:26 -0400 Subject: [PATCH 4/5] add fixedLengthSessionWarningTTL --- src/settings/RefreshTokenRotation.js | 6 ++++++ translations/ui-developer/en.json | 1 + 2 files changed, 7 insertions(+) diff --git a/src/settings/RefreshTokenRotation.js b/src/settings/RefreshTokenRotation.js index 40a886b..0cef742 100644 --- a/src/settings/RefreshTokenRotation.js +++ b/src/settings/RefreshTokenRotation.js @@ -41,6 +41,7 @@ const RefreshTokenRotation = ({ stripes }) => { merge(stripes.config.rtr, { idleSessionTTL: values.idleSessionTTL, idleModalTTL: values.idleModalTTL, + fixedLengthSessionWarningTTL: values.fixedLengthSessionWarningTTL, rotationIntervalFraction: Number(values.rotationIntervalFraction), activityEvents: values.activityEvents.split(',').map((e) => e.trim()), }); @@ -95,6 +96,11 @@ const RefreshTokenRotation = ({ stripes }) => { name="idleModalTTL" label={} /> + } + /> Date: Wed, 9 Oct 2024 14:41:08 -0400 Subject: [PATCH 5/5] UID-159 copy instead of import Copying the value of the constant into this component, instead of importing it from stripes-core, gives us the same end result without requiring us to bump the peer-dep on stripes up from `^9.1.0` to `^9.2.0`, allowing this release to remain compatible with Q. Refs UID-159 --- src/settings/RefreshTokenRotation.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/settings/RefreshTokenRotation.js b/src/settings/RefreshTokenRotation.js index 0cef742..bf7b91a 100644 --- a/src/settings/RefreshTokenRotation.js +++ b/src/settings/RefreshTokenRotation.js @@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react'; import { Field, Form } from 'react-final-form'; import { FormattedMessage } from 'react-intl'; -import { getTokenExpiry, RTR_CONSTANTS } from '@folio/stripes/core'; +import { getTokenExpiry } from '@folio/stripes/core'; import { Button, LoadingPane, Pane, PaneHeader, TextField } from '@folio/stripes/components'; /** @@ -12,6 +12,17 @@ import { Button, LoadingPane, Pane, PaneHeader, TextField } from '@folio/stripes * @returns */ const RefreshTokenRotation = ({ stripes }) => { + // why WHY copy this string here instead of importing it from stripes-core? + // + // RTR_FORCE_REFRESH_EVENT will be present in stripes-core 10.2.0 (stripes + // 9.2.0). Importing it would force the stripes peer depedency to bump from + // ^9.1.0 to ^9.2.0.If we copy the string instead of importing it, we can + // remain compatible with 9.1.0. + // + // OK, compatibility is nice. But it's still gross, right? Yep, super gross. + // Aren't you nauseated? Yes, yes I am. 🤢🧼🛁 + const RTR_FORCE_REFRESH_EVENT = '@folio/stripes/core::RTRForceRefresh'; + const [isLoading, setIsLoading] = useState(true); const [tokenExpiration, setTokenExpiration] = useState({}); @@ -28,7 +39,7 @@ const RefreshTokenRotation = ({ stripes }) => { * dispatch an event to force a token rotation */ const forceRefresh = useCallback( - () => window.dispatchEvent(new Event(RTR_CONSTANTS.RTR_FORCE_REFRESH_EVENT)), + () => window.dispatchEvent(new Event(RTR_FORCE_REFRESH_EVENT)), [], );