Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 34 additions & 21 deletions javascript/reactjs-todo-journey/client/components/journey/form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,39 @@ export default function Form({ action, bottomMessage, followUp, topMessage, jour
return <Confirmation callback={cb} inputName={name} key={name} />;
case 'SelectIdPCallback':
return <IdentityProvider callback={cb} inputName={name} key={name} />;
case 'MetadataCallback':
case 'HiddenValueCallback':
// No component to render
return;
default:
// If current callback is not supported, render a warning message
return <Unknown callback={cb} key={`unknown-${idx}`} />;
}
}

const callbacksForm = (
<form
className="cstm_form"
onSubmit={(event) => {
event.preventDefault();
// Indicate form processing
setSubmittingForm(true);
// set currently rendered step as step to be submitted
setSubmissionStep(renderStep);
}}
>
{formFailureMessage ? <Alert message={formFailureMessage} type="error" /> : null}
{
/**
* Map over the callbacks in renderStep and render the appropriate
* component for each one.
*/
renderStep?.callbacks.map(mapCallbacksToComponents)
}
<Button buttonText={formMetadata.buttonText} submittingForm={submittingForm} />
</form>
);

/**
* Render conditions for presenting appropriate views to user.
* First, we need to handle no "step", which means we are waiting for
Expand Down Expand Up @@ -186,7 +214,11 @@ export default function Form({ action, bottomMessage, followUp, topMessage, jour
<div className="cstm_form-icon align-self-center mb-3">
<FingerPrintIcon size="72px" />
</div>
<WebAuthnComponent step={renderStep} setSubmissionStep={setSubmissionStep} />
<WebAuthnComponent
step={renderStep}
setSubmissionStep={setSubmissionStep}
callbacksForm={callbacksForm}
/>
</>
);
} else if (
Expand All @@ -213,26 +245,7 @@ export default function Form({ action, bottomMessage, followUp, topMessage, jour
</div>
<h1 className={`text-center fs-2 mb-3 ${theme.textClass}`}>{formMetadata.titleText}</h1>
{topMessage}
<form
className="cstm_form"
onSubmit={(event) => {
event.preventDefault();
// Indicate form processing
setSubmittingForm(true);
// set currently rendered step as step to be submitted
setSubmissionStep(renderStep);
}}
>
{formFailureMessage ? <Alert message={formFailureMessage} type="error" /> : null}
{
/**
* Map over the callbacks in renderStep and render the appropriate
* component for each one.
*/
renderStep.callbacks.map(mapCallbacksToComponents)
}
<Button buttonText={formMetadata.buttonText} submittingForm={submittingForm} />
</form>
{callbacksForm}
{bottomMessage}
</Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export default function Text({ callback, inputName }) {
const policies = callback.getPolicies && callback.getPolicies();
const textInputLabel = callback.getPrompt();
const stringAttributeName = callback.getName && callback.getName();
const isNameCb =
callback.getType() === 'NameCallback' ||
callback.getType() === 'ValidatedCreateUsernameCallback';
const autocomplete = isNameCb
? callback.getOutputByName('autocompleteValues')?.join(' ')
: undefined;

let isRequired;
let Validation = null;
Expand Down Expand Up @@ -91,6 +97,7 @@ export default function Text({ callback, inputName }) {
placeholder={textInputLabel}
required={isRequired ? 'required' : ''}
type={stringAttributeName === 'mail' ? 'email' : 'text'}
autoComplete={autocomplete}
/>
<label htmlFor={inputName}>{textInputLabel}</label>
{Validation}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@
import { WebAuthn, WebAuthnStepType } from '@forgerock/journey-client/webauthn';
import { useEffect, useState, useContext } from 'react';
import { ThemeContext } from '../../context/theme.context';
import Alert from './alert';
/**
* @function WebAuthn - Footer React component
* @returns {Object} - React component object
*/

export default function WebAuthnComponent({ step, setSubmissionStep }) {
export default function WebAuthnComponent({ step, setSubmissionStep, callbacksForm }) {
const theme = useContext(ThemeContext);
const webAuthnStep = WebAuthn.getWebAuthnStepType(step);
const [autofill, setAutofill] = useState(false);
const [state, setState] = useState({
message: '',
header: '',
});
const [error, setError] = useState('');

useEffect(() => {
async function performWebAuthn() {
try {
Expand All @@ -32,23 +36,53 @@ export default function WebAuthnComponent({ step, setSubmissionStep }) {
message: 'Your device will be used to verify your identity',
});
await WebAuthn.register(step);
} else {
setSubmissionStep(step);
} else if (webAuthnStep === WebAuthnStepType.Authentication) {
setState({
header: 'Verifying your identity',
message: 'Use your device to verify your identity',
});
await WebAuthn.authenticate(step);

// (Optional) Check if the browser supports conditional mediation
const isConditionalSupported = await WebAuthn.isConditionalMediationSupported();

// Check if the PingAM authorization server allows conditional mediation
const metadataCallback = WebAuthn.getMetadataCallback(step);
const meta = metadataCallback?.getData();
const isConditionalMediation =
meta?.mediation === 'conditional' || meta?.conditional === true;

if (isConditionalSupported && isConditionalMediation) {
setAutofill(true);
const controller = new AbortController();
await WebAuthn.authenticate(step, controller.signal);
} else {
// Proceed to default WebAuthn authentication method if autofill isn't available
await WebAuthn.authenticate(step);
}

setSubmissionStep(step);
} else {
setError('Unknown WebAuthn step type');
}
setSubmissionStep(step);
// eslint-disable-next-line no-unused-vars
} catch (_) {
} catch (error) {
console.error(error.message);
setError(
`Failed to ${webAuthnStep === WebAuthnStepType.Registration ? 'register' : 'authenticate'}`,
);
setSubmissionStep(step);
Comment thread
vatsalparikh marked this conversation as resolved.
Resolved
}
}
performWebAuthn();
}, [setSubmissionStep, step, webAuthnStep]);

return (
if (error) {
return <Alert message={error} type="error" />;
}

return autofill ? (
callbacksForm
) : (
<p>
<span className="d-flex justify-content-center my-2">
<span className="cstm_loading-spinner spinner-border text-primary" role="status"></span>
Expand Down
Loading