diff --git a/web/packages/teleport/src/Account/Account.test.tsx b/web/packages/teleport/src/Account/Account.test.tsx index fb3d4534d651..6fb23549a0e3 100644 --- a/web/packages/teleport/src/Account/Account.test.tsx +++ b/web/packages/teleport/src/Account/Account.test.tsx @@ -243,6 +243,9 @@ test('adding an MFA device', async () => { const user = userEvent.setup(); const ctx = createTeleportContext(); jest.spyOn(ctx.mfaService, 'fetchDevices').mockResolvedValue([testPasskey]); + jest + .spyOn(auth, 'getChallenge') + .mockResolvedValue({ webauthnPublicKey: null, totpChallenge: true }); jest .spyOn(auth, 'createNewWebAuthnDevice') .mockResolvedValueOnce(dummyCredential); @@ -322,6 +325,9 @@ test('removing an MFA method', async () => { const user = userEvent.setup(); const ctx = createTeleportContext(); jest.spyOn(ctx.mfaService, 'fetchDevices').mockResolvedValue([testMfaMethod]); + jest + .spyOn(auth, 'getChallenge') + .mockResolvedValue({ webauthnPublicKey: null, totpChallenge: false }); jest .spyOn(auth, 'createPrivilegeTokenWithWebauthn') .mockResolvedValueOnce('webauthn-privilege-token'); diff --git a/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx b/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx index d5e17e71be03..458357e83ed5 100644 --- a/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx +++ b/web/packages/teleport/src/Account/ChangePasswordWizard/ChangePasswordWizard.tsx @@ -22,7 +22,7 @@ import { ButtonPrimary, ButtonSecondary } from 'design/Button'; import Dialog from 'design/Dialog'; import Flex from 'design/Flex'; import { RadioGroup } from 'design/RadioGroup'; -import { StepComponentProps, StepSlider } from 'design/StepSlider'; +import { StepComponentProps, StepSlider, StepHeader } from 'design/StepSlider'; import React, { useState } from 'react'; import FieldInput from 'shared/components/FieldInput'; import Validation, { Validator } from 'shared/components/Validation'; @@ -36,8 +36,6 @@ import { Auth2faType } from 'shared/services'; import Box from 'design/Box'; -import { StepHeader } from 'design/StepSlider'; - import { ChangePasswordReq } from 'teleport/services/auth'; import auth, { MfaChallengeScope } from 'teleport/services/auth/auth'; import { MfaDevice } from 'teleport/services/mfa'; diff --git a/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts b/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts index 12bfaf6743f0..95929bf9d971 100644 --- a/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts +++ b/web/packages/teleport/src/Account/ManageDevices/useManageDevices.ts @@ -23,6 +23,7 @@ import Ctx from 'teleport/teleportContext'; import cfg from 'teleport/config'; import auth, { DeviceUsage } from 'teleport/services/auth'; import { MfaDevice } from 'teleport/services/mfa'; +import { MfaChallengeScope } from 'teleport/services/auth/auth'; export default function useManageDevices(ctx: Ctx) { const [devices, setDevices] = useState([]); @@ -45,9 +46,16 @@ export default function useManageDevices(ctx: Ctx) { ); } - function onAddDevice(usage: DeviceUsage) { + async function onAddDevice(usage: DeviceUsage) { setNewDeviceUsage(usage); - if (devices.length === 0) { + const response = await auth.getChallenge({ + scope: MfaChallengeScope.MANAGE_DEVICES, + }); + // If the user doesn't receieve any challenges from the backend, that means + // they have no valid devices to be challenged and should instead use a privilege token + // to add a new device. + // TODO (avatus): add SSO challenge here as well when we add SSO for MFA + if (!response.webauthnPublicKey?.challenge && !response.totpChallenge) { createRestrictedTokenAttempt.run(() => auth.createRestrictedPrivilegeToken().then(token => { setToken(token); diff --git a/web/packages/teleport/src/services/auth/auth.ts b/web/packages/teleport/src/services/auth/auth.ts index 5059ac5646e6..4dec9b2ade0f 100644 --- a/web/packages/teleport/src/services/auth/auth.ts +++ b/web/packages/teleport/src/services/auth/auth.ts @@ -37,8 +37,8 @@ import { ChangePasswordReq, CreateNewHardwareDeviceRequest, DeviceUsage, + CreateAuthenticateChallengeRequest, } from './types'; -import { CreateAuthenticateChallengeRequest } from './types'; const auth = { checkWebauthnSupport() { @@ -258,6 +258,21 @@ const auth = { return api.post(cfg.api.createPrivilegeTokenPath, { secondFactorToken }); }, + async getChallenge( + req: CreateAuthenticateChallengeRequest, + abortSignal?: AbortSignal + ) { + return api + .post( + cfg.api.mfaAuthnChallengePath, + { + challenge_scope: req.scope, + }, + abortSignal + ) + .then(makeMfaAuthenticateChallenge); + }, + async fetchWebAuthnChallenge( req: CreateAuthenticateChallengeRequest, abortSignal?: AbortSignal diff --git a/web/packages/teleport/src/services/auth/makeMfa.ts b/web/packages/teleport/src/services/auth/makeMfa.ts index 20957e7a773d..063796748391 100644 --- a/web/packages/teleport/src/services/auth/makeMfa.ts +++ b/web/packages/teleport/src/services/auth/makeMfa.ts @@ -70,6 +70,7 @@ export function makeMfaAuthenticateChallenge(json): MfaAuthenticateChallenge { } return { + totpChallenge: json.totp_challenge, webauthnPublicKey: webauthnPublicKey, }; } diff --git a/web/packages/teleport/src/services/auth/types.ts b/web/packages/teleport/src/services/auth/types.ts index 078464ed8a52..11057cd18564 100644 --- a/web/packages/teleport/src/services/auth/types.ts +++ b/web/packages/teleport/src/services/auth/types.ts @@ -33,6 +33,7 @@ export type AuthnChallengeRequest = { }; export type MfaAuthenticateChallenge = { + totpChallenge: boolean; webauthnPublicKey: PublicKeyCredentialRequestOptions; };