Skip to content

Commit

Permalink
Hide delete button for SSO MFA devices in account settings
Browse files Browse the repository at this point in the history
An SSO device cannot be deleted and will fail if you try to delete. This
will hide the button to prevent any confusion
  • Loading branch information
avatus committed Oct 11, 2024
1 parent 32971a2 commit 853972d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,13 @@ const devices: MfaDevice[] = [
type: 'webauthn',
usage: 'passwordless',
},
{
id: '5',
description: 'sso provider',
name: 'okta',
registeredDate: new Date(1612493852000),
lastUsedDate: new Date(1614481052000),
type: 'sso',
usage: 'mfa',
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ const devices: MfaDevice[] = [
},
];

const ssoDevice: MfaDevice[] = [
{
id: '1',
description: 'SSO Provider',
name: 'okta',
registeredDate: new Date(1628799417000),
lastUsedDate: new Date(1628799417000),
type: 'sso',
usage: 'mfa',
},
];

function getTableCellContents() {
const [header, ...rows] = screen.getAllByRole('row');
return {
Expand Down Expand Up @@ -75,6 +87,32 @@ test('renders devices', () => {
['Hardware Key', 'yubikey', '2021-06-15', '2021-06-18', ''],
],
});

const buttons = screen.queryAllByTitle('Delete');
expect(buttons).toHaveLength(2);
// all buttons should be enabled
buttons.forEach(button => {
expect(button).toBeEnabled();
});
});

test('delete button is disabled for sso devices', () => {
render(
<AuthDeviceList
header="Header"
deviceTypeColumnName="Passkey Type"
devices={ssoDevice}
/>
);
expect(screen.getByText('Header')).toBeInTheDocument();
expect(getTableCellContents()).toEqual({
header: ['Passkey Type', 'Nickname', 'Added', 'Last Used', 'Actions'],
rows: [['SSO Provider', 'okta', '2021-08-12', '2021-08-12', '']],
});

const button = screen.getByTitle('SSO device cannot be deleted');
expect(button).toBeInTheDocument();
expect(button).toBeDisabled();
});

test('renders no devices', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,14 @@ export function AuthDeviceList({
{
altKey: 'remove-btn',
headerText: 'Actions',
render: device => (
<RemoveCell onRemove={() => onRemove(device)} />
),
render: device => {
return (
<RemoveCell
isSsoDevice={device.type === 'sso'}
onRemove={() => onRemove(device)}
/>
);
},
},
]}
data={devices}
Expand All @@ -93,12 +98,18 @@ export function AuthDeviceList({

interface RemoveCellProps {
onRemove?: () => void;
isSsoDevice?: boolean;
}

function RemoveCell({ onRemove }: RemoveCellProps) {
function RemoveCell({ onRemove, isSsoDevice }: RemoveCellProps) {
return (
<Cell>
<ButtonWarningBorder title="Delete" p={2} onClick={onRemove}>
<Cell data-testid="delete-device">
<ButtonWarningBorder
disabled={isSsoDevice}
title={isSsoDevice ? 'SSO device cannot be deleted' : 'Delete'}
p={2}
onClick={onRemove}
>
<Icon.Trash size="small" />
</ButtonWarningBorder>
</Cell>
Expand Down
16 changes: 14 additions & 2 deletions web/packages/teleport/src/services/mfa/makeMfaDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { MfaDevice } from './types';
import { DeviceType, MfaDevice } from './types';

function getType(deviceTypeFromJsonResponse: string): DeviceType {
if (deviceTypeFromJsonResponse === 'TOTP') {
return 'totp';
}
if (deviceTypeFromJsonResponse === 'SSO') {
return 'sso';
}
return 'webauthn';
}

export default function makeMfaDevice(json): MfaDevice {
const { id, name, lastUsed, addedAt, residentKey } = json;
Expand All @@ -26,11 +36,13 @@ export default function makeMfaDevice(json): MfaDevice {
description = 'Authenticator App';
} else if (json.type === 'U2F' || json.type === 'WebAuthn') {
description = 'Hardware Key';
} else if (json.type === 'SSO') {
description = 'SSO Provider';
} else {
description = 'unknown device';
}

const type = json.type === 'TOTP' ? 'totp' : 'webauthn';
const type = getType(json.type);
const usage = residentKey ? 'passwordless' : 'mfa';

return {
Expand Down
2 changes: 1 addition & 1 deletion web/packages/teleport/src/services/mfa/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export type SaveNewHardwareDeviceRequest = {
credential: Credential;
};

export type DeviceType = 'totp' | 'webauthn';
export type DeviceType = 'totp' | 'webauthn' | 'sso';

// MfaAuthnResponse is a response to a MFA device challenge.
export type MfaAuthnResponse =
Expand Down

0 comments on commit 853972d

Please sign in to comment.