mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 08:43:58 +00:00
[Web] Make language on mfa verify step dialog more clear (#20825)
* update text on dialog * tweak language and overwrite error message * address comments * fix 'enter' to submit on newdevice form
This commit is contained in:
parent
f93d401df2
commit
6edb26c60f
|
@ -118,138 +118,143 @@ export function AddDevice({
|
|||
onClose={onClose}
|
||||
open={true}
|
||||
>
|
||||
<DialogHeader style={{ flexDirection: 'column' }}>
|
||||
<DialogTitle>Add New Two-Factor Device</DialogTitle>
|
||||
</DialogHeader>
|
||||
{addDeviceAttempt.status === 'failed' && (
|
||||
<Danger mt={2} width="100%">
|
||||
{addDeviceAttempt.statusText}
|
||||
</Danger>
|
||||
)}
|
||||
{fetchQrCodeAttempt.status === 'failed' && (
|
||||
<Danger mt={2} width="100%">
|
||||
{fetchQrCodeAttempt.statusText}
|
||||
</Danger>
|
||||
)}
|
||||
<DialogContent>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
bg="primary.light"
|
||||
borderRadius={8}
|
||||
height="256px"
|
||||
p={3}
|
||||
mb={4}
|
||||
>
|
||||
{mfaOption.value === 'otp' && (
|
||||
<>
|
||||
<Flex
|
||||
height="168px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
{fetchQrCodeAttempt.status === 'processing' && (
|
||||
<Indicator />
|
||||
)}
|
||||
{fetchQrCodeAttempt.status === 'success' && (
|
||||
<Image
|
||||
src={`data:image/png;base64,${qrCode}`}
|
||||
height="100%"
|
||||
style={{
|
||||
boxSizing: 'border-box',
|
||||
border: '8px solid white',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Text fontSize={1} textAlign="center" mt={2}>
|
||||
Scan the QR Code with any authenticator app and enter the
|
||||
generated code.{' '}
|
||||
<Text color="text.secondary">
|
||||
We recommend{' '}
|
||||
<Link href="https://authy.com/download/" target="_blank">
|
||||
Authy
|
||||
</Link>
|
||||
.
|
||||
<form>
|
||||
<DialogHeader style={{ flexDirection: 'column' }}>
|
||||
<DialogTitle>Add New Two-Factor Device</DialogTitle>
|
||||
</DialogHeader>
|
||||
{addDeviceAttempt.status === 'failed' && (
|
||||
<Danger mt={2} width="100%">
|
||||
{addDeviceAttempt.statusText}
|
||||
</Danger>
|
||||
)}
|
||||
{fetchQrCodeAttempt.status === 'failed' && (
|
||||
<Danger mt={2} width="100%">
|
||||
{fetchQrCodeAttempt.statusText}
|
||||
</Danger>
|
||||
)}
|
||||
<DialogContent>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
bg="primary.light"
|
||||
borderRadius={8}
|
||||
height="256px"
|
||||
p={3}
|
||||
mb={4}
|
||||
>
|
||||
{mfaOption.value === 'otp' && (
|
||||
<>
|
||||
<Flex
|
||||
height="168px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
{fetchQrCodeAttempt.status === 'processing' && (
|
||||
<Indicator />
|
||||
)}
|
||||
{fetchQrCodeAttempt.status === 'success' && (
|
||||
<Image
|
||||
src={`data:image/png;base64,${qrCode}`}
|
||||
height="100%"
|
||||
style={{
|
||||
boxSizing: 'border-box',
|
||||
border: '8px solid white',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Text fontSize={1} textAlign="center" mt={2}>
|
||||
Scan the QR Code with any authenticator app and enter the
|
||||
generated code.{' '}
|
||||
<Text color="text.secondary">
|
||||
We recommend{' '}
|
||||
<Link
|
||||
href="https://authy.com/download/"
|
||||
target="_blank"
|
||||
>
|
||||
Authy
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
{mfaOption.value === 'webauthn' && (
|
||||
<>
|
||||
<Image src={secKeyGraphic} height="168px" />
|
||||
<Text mt={3}>{hardwareInstructions}</Text>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex alignItems="center">
|
||||
<FieldSelect
|
||||
maxWidth="50%"
|
||||
width="100%"
|
||||
label="Two-factor type"
|
||||
data-testid="mfa-select"
|
||||
value={mfaOption}
|
||||
options={mfaOptions}
|
||||
onChange={(o: MfaOption) => {
|
||||
validator.reset();
|
||||
onSetMfaOption(o);
|
||||
}}
|
||||
mr={3}
|
||||
isDisabled={addDeviceAttempt.status === 'processing'}
|
||||
/>
|
||||
{mfaOption.value === 'otp' && (
|
||||
<FieldInput
|
||||
width="50%"
|
||||
label="Authenticator code"
|
||||
rule={requiredToken}
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
value={otpToken}
|
||||
onChange={e => setOtpToken(e.target.value)}
|
||||
placeholder="123 456"
|
||||
readonly={addDeviceAttempt.status === 'processing'}
|
||||
/>
|
||||
)}
|
||||
{mfaOption.value === 'webauthn' && isPasswordlessEnabled && (
|
||||
</>
|
||||
)}
|
||||
{mfaOption.value === 'webauthn' && (
|
||||
<>
|
||||
<Image src={secKeyGraphic} height="168px" />
|
||||
<Text mt={3}>{hardwareInstructions}</Text>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex alignItems="center">
|
||||
<FieldSelect
|
||||
width="50%"
|
||||
label="Allow Passwordless Login?"
|
||||
value={usageOption}
|
||||
options={deviceUsageOpts}
|
||||
onChange={(o: DeviceusageOpt) => setUsageOption(o)}
|
||||
maxWidth="50%"
|
||||
width="100%"
|
||||
label="Two-factor type"
|
||||
data-testid="mfa-select"
|
||||
value={mfaOption}
|
||||
options={mfaOptions}
|
||||
onChange={(o: MfaOption) => {
|
||||
validator.reset();
|
||||
onSetMfaOption(o);
|
||||
}}
|
||||
mr={3}
|
||||
isDisabled={addDeviceAttempt.status === 'processing'}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<FieldInput
|
||||
rule={requiredField('Device name is required')}
|
||||
label="Device name"
|
||||
placeholder="Name"
|
||||
width="100%"
|
||||
autoFocus
|
||||
value={deviceName}
|
||||
type="text"
|
||||
onChange={e => setDeviceName(e.target.value)}
|
||||
readonly={addDeviceAttempt.status === 'processing'}
|
||||
mb={1}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<ButtonPrimary
|
||||
size="large"
|
||||
width="45%"
|
||||
type="submit"
|
||||
onClick={e => validator.validate() && onSubmit(e)}
|
||||
disabled={addDeviceAttempt.status === 'processing'}
|
||||
mr={3}
|
||||
>
|
||||
Add device
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary size="large" width="30%" onClick={onClose}>
|
||||
Cancel
|
||||
</ButtonSecondary>
|
||||
</DialogFooter>
|
||||
{mfaOption.value === 'otp' && (
|
||||
<FieldInput
|
||||
width="50%"
|
||||
label="Authenticator code"
|
||||
rule={requiredToken}
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
value={otpToken}
|
||||
onChange={e => setOtpToken(e.target.value)}
|
||||
placeholder="123 456"
|
||||
readonly={addDeviceAttempt.status === 'processing'}
|
||||
/>
|
||||
)}
|
||||
{mfaOption.value === 'webauthn' && isPasswordlessEnabled && (
|
||||
<FieldSelect
|
||||
width="50%"
|
||||
label="Allow Passwordless Login?"
|
||||
value={usageOption}
|
||||
options={deviceUsageOpts}
|
||||
onChange={(o: DeviceusageOpt) => setUsageOption(o)}
|
||||
isDisabled={addDeviceAttempt.status === 'processing'}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<FieldInput
|
||||
rule={requiredField('Device name is required')}
|
||||
label="Device name"
|
||||
placeholder="Name"
|
||||
width="100%"
|
||||
autoFocus
|
||||
value={deviceName}
|
||||
type="text"
|
||||
onChange={e => setDeviceName(e.target.value)}
|
||||
readonly={addDeviceAttempt.status === 'processing'}
|
||||
mb={1}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<ButtonPrimary
|
||||
size="large"
|
||||
width="45%"
|
||||
type="submit"
|
||||
onClick={e => validator.validate() && onSubmit(e)}
|
||||
disabled={addDeviceAttempt.status === 'processing'}
|
||||
mr={3}
|
||||
>
|
||||
Add device
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary size="large" width="30%" onClick={onClose}>
|
||||
Cancel
|
||||
</ButtonSecondary>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
)}
|
||||
</Validation>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -97,6 +97,7 @@ export function ManageDevices({
|
|||
<ReAuthenticate
|
||||
onAuthenticated={setToken}
|
||||
onClose={hideReAuthenticate}
|
||||
actionText="registering a new device"
|
||||
/>
|
||||
)}
|
||||
{isAddDeviceVisible && (
|
||||
|
|
|
@ -44,4 +44,5 @@ const props: State = {
|
|||
preferredMfaType: 'webauthn',
|
||||
onClose: () => null,
|
||||
auth2faType: 'on',
|
||||
actionText: 'performing this action',
|
||||
};
|
||||
|
|
|
@ -44,6 +44,7 @@ export function ReAuthenticate({
|
|||
onClose,
|
||||
auth2faType,
|
||||
preferredMfaType,
|
||||
actionText,
|
||||
}: State) {
|
||||
const [otpToken, setOtpToken] = useState('');
|
||||
const mfaOptions = createMfaOptions({
|
||||
|
@ -69,68 +70,72 @@ export function ReAuthenticate({
|
|||
{({ validator }) => (
|
||||
<Dialog
|
||||
dialogCss={() => ({
|
||||
width: '400px',
|
||||
width: '416px',
|
||||
})}
|
||||
disableEscapeKeyDown={false}
|
||||
onClose={onClose}
|
||||
open={true}
|
||||
>
|
||||
<DialogHeader style={{ flexDirection: 'column' }}>
|
||||
<DialogTitle>Verify your identity</DialogTitle>
|
||||
<Text textAlign="center" color="text.secondary">
|
||||
You must verify your identity before peforming this action.
|
||||
</Text>
|
||||
</DialogHeader>
|
||||
{attempt.status === 'failed' && (
|
||||
<Danger mt={2} width="100%">
|
||||
{attempt.statusText}
|
||||
</Danger>
|
||||
)}
|
||||
<DialogContent>
|
||||
<Flex mt={2} alignItems="flex-end">
|
||||
<FieldSelect
|
||||
width="50%"
|
||||
label="Two-factor type"
|
||||
value={mfaOption}
|
||||
options={mfaOptions}
|
||||
onChange={(o: MfaOption) => {
|
||||
setMfaOption(o);
|
||||
clearAttempt();
|
||||
}}
|
||||
data-testid="mfa-select"
|
||||
<form>
|
||||
<DialogHeader style={{ flexDirection: 'column' }}>
|
||||
<DialogTitle>Verify your identity</DialogTitle>
|
||||
<Text textAlign="center" color="text.secondary">
|
||||
You must verify your identity with one of your existing
|
||||
two-factor devices before {actionText}.
|
||||
</Text>
|
||||
</DialogHeader>
|
||||
{attempt.status === 'failed' && (
|
||||
<Danger mt={2} width="100%">
|
||||
{attempt.statusText}
|
||||
</Danger>
|
||||
)}
|
||||
<DialogContent>
|
||||
<Flex mt={2} alignItems="flex-end">
|
||||
<FieldSelect
|
||||
width="50%"
|
||||
label="Two-factor type"
|
||||
value={mfaOption}
|
||||
options={mfaOptions}
|
||||
onChange={(o: MfaOption) => {
|
||||
setMfaOption(o);
|
||||
clearAttempt();
|
||||
}}
|
||||
data-testid="mfa-select"
|
||||
mr={3}
|
||||
mb={0}
|
||||
isDisabled={attempt.status === 'processing'}
|
||||
/>
|
||||
<Box width="50%">
|
||||
{mfaOption.value === 'otp' && (
|
||||
<FieldInput
|
||||
label="Authenticator code"
|
||||
rule={requiredToken}
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
value={otpToken}
|
||||
onChange={e => setOtpToken(e.target.value)}
|
||||
placeholder="123 456"
|
||||
readonly={attempt.status === 'processing'}
|
||||
mb={0}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<ButtonPrimary
|
||||
onClick={e => validator.validate() && onSubmit(e)}
|
||||
disabled={attempt.status === 'processing'}
|
||||
mr={3}
|
||||
mb={0}
|
||||
isDisabled={attempt.status === 'processing'}
|
||||
/>
|
||||
<Box width="50%">
|
||||
{mfaOption.value === 'otp' && (
|
||||
<FieldInput
|
||||
label="Authenticator code"
|
||||
rule={requiredToken}
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
value={otpToken}
|
||||
onChange={e => setOtpToken(e.target.value)}
|
||||
placeholder="123 456"
|
||||
readonly={attempt.status === 'processing'}
|
||||
mb={0}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<ButtonPrimary
|
||||
onClick={e => validator.validate() && onSubmit(e)}
|
||||
disabled={attempt.status === 'processing'}
|
||||
mr={3}
|
||||
mt={3}
|
||||
autoFocus
|
||||
>
|
||||
Continue
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary onClick={onClose}>Cancel</ButtonSecondary>
|
||||
</DialogFooter>
|
||||
mt={3}
|
||||
type="submit"
|
||||
autoFocus
|
||||
>
|
||||
Continue
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary onClick={onClose}>Cancel</ButtonSecondary>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
)}
|
||||
</Validation>
|
||||
|
|
|
@ -203,7 +203,7 @@ exports[`render failed state for re-authentication dialog 1`] = `
|
|||
position: relative;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100% - 96px);
|
||||
width: 400px;
|
||||
width: 416px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
|
@ -313,124 +313,129 @@ exports[`render failed state for re-authentication dialog 1`] = `
|
|||
class="c3"
|
||||
data-testid="dialogbox"
|
||||
>
|
||||
<div
|
||||
class="c4"
|
||||
style="flex-direction: column;"
|
||||
>
|
||||
<form>
|
||||
<div
|
||||
class="c5"
|
||||
color="text.primary"
|
||||
>
|
||||
Verify your identity
|
||||
</div>
|
||||
<div
|
||||
class="c6"
|
||||
color="text.secondary"
|
||||
>
|
||||
You must verify your identity before peforming this action.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c7"
|
||||
kind="danger"
|
||||
width="100%"
|
||||
>
|
||||
an error has occurred
|
||||
</div>
|
||||
<div
|
||||
class="c8"
|
||||
>
|
||||
<div
|
||||
class="c9"
|
||||
class="c4"
|
||||
style="flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
class="c10"
|
||||
data-testid="mfa-select"
|
||||
width="50%"
|
||||
class="c5"
|
||||
color="text.primary"
|
||||
>
|
||||
Verify your identity
|
||||
</div>
|
||||
<div
|
||||
class="c6"
|
||||
color="text.secondary"
|
||||
>
|
||||
You must verify your identity with one of your existing two-factor devices before
|
||||
performing this action
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c7"
|
||||
kind="danger"
|
||||
width="100%"
|
||||
>
|
||||
an error has occurred
|
||||
</div>
|
||||
<div
|
||||
class="c8"
|
||||
>
|
||||
<div
|
||||
class="c9"
|
||||
>
|
||||
<label
|
||||
class="c11"
|
||||
font-size="0"
|
||||
for="select"
|
||||
>
|
||||
Two-factor type
|
||||
</label>
|
||||
<div
|
||||
class="c12"
|
||||
class="c10"
|
||||
data-testid="mfa-select"
|
||||
width="50%"
|
||||
>
|
||||
<label
|
||||
class="c11"
|
||||
font-size="0"
|
||||
for="select"
|
||||
>
|
||||
Two-factor type
|
||||
</label>
|
||||
<div
|
||||
class="react-select-container css-2b097c-container"
|
||||
class="c12"
|
||||
>
|
||||
<div
|
||||
class="react-select__control css-yk16xz-control"
|
||||
class="react-select-container css-2b097c-container"
|
||||
>
|
||||
<div
|
||||
class="react-select__value-container react-select__value-container--has-value css-g1d714-ValueContainer"
|
||||
class="react-select__control css-yk16xz-control"
|
||||
>
|
||||
<div
|
||||
class="react-select__single-value css-1uccc91-singleValue"
|
||||
class="react-select__value-container react-select__value-container--has-value css-g1d714-ValueContainer"
|
||||
>
|
||||
Hardware Key
|
||||
</div>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
class="css-62g3xt-dummyInput"
|
||||
id="select"
|
||||
readonly=""
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<span
|
||||
class="react-select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-6q0nyr-Svg"
|
||||
focusable="false"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
<div
|
||||
class="react-select__single-value css-1uccc91-singleValue"
|
||||
>
|
||||
<path
|
||||
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||
/>
|
||||
</svg>
|
||||
Hardware Key
|
||||
</div>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
class="css-62g3xt-dummyInput"
|
||||
id="select"
|
||||
readonly=""
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<span
|
||||
class="react-select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-6q0nyr-Svg"
|
||||
focusable="false"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
>
|
||||
<path
|
||||
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c13"
|
||||
width="50%"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="c13"
|
||||
width="50%"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c14"
|
||||
>
|
||||
<button
|
||||
class="c15"
|
||||
kind="primary"
|
||||
<div
|
||||
class="c14"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
<button
|
||||
class="c16"
|
||||
kind="secondary"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="c15"
|
||||
kind="primary"
|
||||
type="submit"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
<button
|
||||
class="c16"
|
||||
kind="secondary"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -616,7 +621,7 @@ exports[`render re-authentication dialog 1`] = `
|
|||
position: relative;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100% - 96px);
|
||||
width: 400px;
|
||||
width: 416px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
|
@ -726,117 +731,122 @@ exports[`render re-authentication dialog 1`] = `
|
|||
class="c3"
|
||||
data-testid="dialogbox"
|
||||
>
|
||||
<div
|
||||
class="c4"
|
||||
style="flex-direction: column;"
|
||||
>
|
||||
<form>
|
||||
<div
|
||||
class="c5"
|
||||
color="text.primary"
|
||||
>
|
||||
Verify your identity
|
||||
</div>
|
||||
<div
|
||||
class="c6"
|
||||
color="text.secondary"
|
||||
>
|
||||
You must verify your identity before peforming this action.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c7"
|
||||
>
|
||||
<div
|
||||
class="c8"
|
||||
class="c4"
|
||||
style="flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
class="c9"
|
||||
data-testid="mfa-select"
|
||||
width="50%"
|
||||
class="c5"
|
||||
color="text.primary"
|
||||
>
|
||||
Verify your identity
|
||||
</div>
|
||||
<div
|
||||
class="c6"
|
||||
color="text.secondary"
|
||||
>
|
||||
You must verify your identity with one of your existing two-factor devices before
|
||||
performing this action
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c7"
|
||||
>
|
||||
<div
|
||||
class="c8"
|
||||
>
|
||||
<label
|
||||
class="c10"
|
||||
font-size="0"
|
||||
for="select"
|
||||
>
|
||||
Two-factor type
|
||||
</label>
|
||||
<div
|
||||
class="c11"
|
||||
class="c9"
|
||||
data-testid="mfa-select"
|
||||
width="50%"
|
||||
>
|
||||
<label
|
||||
class="c10"
|
||||
font-size="0"
|
||||
for="select"
|
||||
>
|
||||
Two-factor type
|
||||
</label>
|
||||
<div
|
||||
class="react-select-container css-2b097c-container"
|
||||
class="c11"
|
||||
>
|
||||
<div
|
||||
class="react-select__control css-yk16xz-control"
|
||||
class="react-select-container css-2b097c-container"
|
||||
>
|
||||
<div
|
||||
class="react-select__value-container react-select__value-container--has-value css-g1d714-ValueContainer"
|
||||
class="react-select__control css-yk16xz-control"
|
||||
>
|
||||
<div
|
||||
class="react-select__single-value css-1uccc91-singleValue"
|
||||
class="react-select__value-container react-select__value-container--has-value css-g1d714-ValueContainer"
|
||||
>
|
||||
Hardware Key
|
||||
</div>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
class="css-62g3xt-dummyInput"
|
||||
id="select"
|
||||
readonly=""
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<span
|
||||
class="react-select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-6q0nyr-Svg"
|
||||
focusable="false"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
<div
|
||||
class="react-select__single-value css-1uccc91-singleValue"
|
||||
>
|
||||
<path
|
||||
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||
/>
|
||||
</svg>
|
||||
Hardware Key
|
||||
</div>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
class="css-62g3xt-dummyInput"
|
||||
id="select"
|
||||
readonly=""
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<span
|
||||
class="react-select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-6q0nyr-Svg"
|
||||
focusable="false"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
>
|
||||
<path
|
||||
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c12"
|
||||
width="50%"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="c12"
|
||||
width="50%"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c13"
|
||||
>
|
||||
<button
|
||||
class="c14"
|
||||
kind="primary"
|
||||
<div
|
||||
class="c13"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
<button
|
||||
class="c15"
|
||||
kind="secondary"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="c14"
|
||||
kind="primary"
|
||||
type="submit"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
<button
|
||||
class="c15"
|
||||
kind="secondary"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -21,7 +21,11 @@ import useAttempt from 'shared/hooks/useAttemptNext';
|
|||
import cfg from 'teleport/config';
|
||||
import auth from 'teleport/services/auth';
|
||||
|
||||
export default function useReAuthenticate({ onAuthenticated, onClose }: Props) {
|
||||
export default function useReAuthenticate({
|
||||
onAuthenticated,
|
||||
onClose,
|
||||
actionText = defaultActionText,
|
||||
}: Props) {
|
||||
const { attempt, setAttempt, handleError } = useAttempt('');
|
||||
|
||||
function submitWithTotp(secondFactorToken: string) {
|
||||
|
@ -37,7 +41,20 @@ export default function useReAuthenticate({ onAuthenticated, onClose }: Props) {
|
|||
auth
|
||||
.createPrivilegeTokenWithWebauthn()
|
||||
.then(onAuthenticated)
|
||||
.catch(handleError);
|
||||
.catch((err: Error) => {
|
||||
// This catches a webauthn frontend error that occurs on Firefox and replaces it with a more helpful error message.
|
||||
if (
|
||||
err.message.includes('attempt was made to use an object that is not')
|
||||
) {
|
||||
setAttempt({
|
||||
status: 'failed',
|
||||
statusText:
|
||||
'The two-factor device you used is not registered on this account. You must verify using a device that has already been registered.',
|
||||
});
|
||||
} else {
|
||||
setAttempt({ status: 'failed', statusText: err.message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearAttempt() {
|
||||
|
@ -51,13 +68,26 @@ export default function useReAuthenticate({ onAuthenticated, onClose }: Props) {
|
|||
submitWithWebauthn,
|
||||
auth2faType: cfg.getAuth2faType(),
|
||||
preferredMfaType: cfg.getPreferredMfaType(),
|
||||
actionText,
|
||||
onClose,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultActionText = 'performing this action';
|
||||
|
||||
export type Props = {
|
||||
onAuthenticated: React.Dispatch<React.SetStateAction<string>>;
|
||||
onClose: () => void;
|
||||
/**
|
||||
* The text that will be appended to the text in the re-authentication dialog.
|
||||
*
|
||||
* Default value: "performing this action"
|
||||
*
|
||||
* Example: If `actionText` is set to "registering a new device" then the dialog will say
|
||||
* "You must verify your identity with one of your existing two-factor devices before registering a new device."
|
||||
*
|
||||
* */
|
||||
actionText?: string;
|
||||
};
|
||||
|
||||
export type State = ReturnType<typeof useReAuthenticate>;
|
||||
|
|
Loading…
Reference in a new issue