Skip to content

Commit

Permalink
Fix regenerating recovery codes webauthn scope (#40633)
Browse files Browse the repository at this point in the history
* Always provide MANAGE_DEVICES webauthn scope for creating privileged tokens.

* Make challenge scope exclusive to onMfaResponse flow.

* Fix UI lint.
  • Loading branch information
Joerger authored Apr 22, 2024
1 parent 44550e8 commit 6b69472
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 27 deletions.
3 changes: 0 additions & 3 deletions web/packages/teleport/src/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ import {
import ReAuthenticate from 'teleport/components/ReAuthenticate';
import { RemoveDialog } from 'teleport/components/MfaDeviceList';

import { MfaChallengeScope } from 'teleport/services/auth/auth';

import cfg from 'teleport/config';

import { DeviceUsage } from 'teleport/services/auth';
Expand Down Expand Up @@ -232,7 +230,6 @@ export function Account({
onAuthenticated={setToken}
onClose={hideReAuthenticate}
actionText="registering a new device"
challengeScope={MfaChallengeScope.MANAGE_DEVICES}
/>
)}
{EnterpriseComponent && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import Box from 'design/Box';

import { DialogHeader } from 'teleport/Account/DialogHeader';
import useReAuthenticate from 'teleport/components/ReAuthenticate/useReAuthenticate';
import auth, { MfaChallengeScope } from 'teleport/services/auth/auth';
import auth from 'teleport/services/auth/auth';
import { DeviceUsage } from 'teleport/services/auth';
import useTeleport from 'teleport/useTeleport';

Expand Down Expand Up @@ -131,15 +131,13 @@ export function ReauthenticateStep({
onClose,
onAuthenticated: onAuthenticatedProp,
}: AddAuthDeviceWizardStepProps) {
const challengeScope = MfaChallengeScope.MANAGE_DEVICES;
const onAuthenticated = (privilegeToken: string) => {
onAuthenticatedProp(privilegeToken);
next();
};
const { attempt, clearAttempt, submitWithTotp, submitWithWebauthn } =
useReAuthenticate({
onAuthenticated,
challengeScope,
});
const mfaOptions = createMfaOptions({
auth2faType,
Expand All @@ -160,7 +158,7 @@ export function ReauthenticateStep({
e.preventDefault();
if (!validator.validate()) return;
if (mfaOption === 'webauthn') {
submitWithWebauthn(challengeScope);
submitWithWebauthn();
}
if (mfaOption === 'otp') {
submitWithTotp(authCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import React from 'react';

import { MfaChallengeScope } from 'teleport/services/auth/auth';

import { State } from './useReAuthenticate';
import { ReAuthenticate } from './ReAuthenticate';

Expand Down Expand Up @@ -49,5 +47,4 @@ const props: State = {
onClose: () => null,
auth2faType: 'on',
actionText: 'performing this action',
challengeScope: MfaChallengeScope.UNSPECIFIED,
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export function ReAuthenticate({
auth2faType,
preferredMfaType,
actionText,
challengeScope,
}: State) {
const [otpToken, setOtpToken] = useState('');
const mfaOptions = createMfaOptions({
Expand All @@ -61,7 +60,7 @@ export function ReAuthenticate({
e.preventDefault();

if (mfaOption?.value === 'webauthn') {
submitWithWebauthn(challengeScope);
submitWithWebauthn();
}
if (mfaOption?.value === 'otp') {
submitWithTotp(otpToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type { MfaAuthnResponse } from 'teleport/services/mfa';
// token, and after successfully obtaining the token, the function
// `onAuthenticated` will be called with this token.
export default function useReAuthenticate(props: Props) {
const { onClose, actionText = defaultActionText, challengeScope } = props;
const { onClose, actionText = defaultActionText } = props;

// Note that attempt state "success" is not used or required.
// After the user submits, the control is passed back
Expand All @@ -53,12 +53,12 @@ export default function useReAuthenticate(props: Props) {
.catch(handleError);
}

function submitWithWebauthn(scope: MfaChallengeScope) {
function submitWithWebauthn() {
setAttempt({ status: 'processing' });

if ('onMfaResponse' in props) {
auth
.getWebauthnResponse(scope)
.getWebauthnResponse(props.challengeScope)
.then(webauthnResponse =>
props.onMfaResponse({ webauthn_response: webauthnResponse })
)
Expand All @@ -67,7 +67,7 @@ export default function useReAuthenticate(props: Props) {
}

auth
.createPrivilegeTokenWithWebauthn(scope)
.createPrivilegeTokenWithWebauthn()
.then(props.onAuthenticated)
.catch((err: Error) => {
// This catches a webauthn frontend error that occurs on Firefox and replaces it with a more helpful error message.
Expand Down Expand Up @@ -97,7 +97,6 @@ export default function useReAuthenticate(props: Props) {
auth2faType: cfg.getAuth2faType(),
preferredMfaType: cfg.getPreferredMfaType(),
actionText,
challengeScope,
onClose,
};
}
Expand All @@ -116,17 +115,17 @@ type BaseProps = {
*
* */
actionText?: string;
/**
* The MFA challenge scope of the action to perform, as defined in webauthn.proto.
*/
challengeScope: MfaChallengeScope;
};

// MfaResponseProps defines a function
// that accepts a MFA response. No
// authentication has been done at this point.
type MfaResponseProps = BaseProps & {
onMfaResponse(res: MfaAuthnResponse): void;
/**
* The MFA challenge scope of the action to perform, as defined in webauthn.proto.
*/
challengeScope: MfaChallengeScope;
onAuthenticated?: never;
};

Expand All @@ -137,6 +136,8 @@ type MfaResponseProps = BaseProps & {
type DefaultProps = BaseProps & {
onAuthenticated(privilegeTokenId: string): void;
onMfaResponse?: never;
// TODO(Joerger): change type to 'never' once it is no longer expected in /e
challengeScope?: MfaChallengeScope;
};

export type Props = MfaResponseProps | DefaultProps;
Expand Down
15 changes: 9 additions & 6 deletions web/packages/teleport/src/services/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,15 @@ const auth = {
);
},

createPrivilegeTokenWithWebauthn(scope: MfaChallengeScope) {
return auth.fetchWebAuthnChallenge({ scope }).then(res =>
api.post(cfg.api.createPrivilegeTokenPath, {
webauthnAssertionResponse: makeWebauthnAssertionResponse(res),
})
);
createPrivilegeTokenWithWebauthn() {
// Creating privilege tokens always expects the MANAGE_DEVICES webauthn scope.
return auth
.fetchWebAuthnChallenge({ scope: MfaChallengeScope.MANAGE_DEVICES })
.then(res =>
api.post(cfg.api.createPrivilegeTokenPath, {
webauthnAssertionResponse: makeWebauthnAssertionResponse(res),
})
);
},

createRestrictedPrivilegeToken() {
Expand Down

0 comments on commit 6b69472

Please sign in to comment.