diff --git a/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.story.tsx b/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.story.tsx index ba8678459ac13..7841bdf4ff680 100644 --- a/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.story.tsx +++ b/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.story.tsx @@ -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', + }, ]; diff --git a/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.test.tsx b/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.test.tsx index 3e809cae0a538..10e4781b2071d 100644 --- a/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.test.tsx +++ b/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.test.tsx @@ -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 { @@ -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( + + ); + 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', () => { diff --git a/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.tsx b/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.tsx index dcc3af1da3ac4..ed1b94964ea8b 100644 --- a/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.tsx +++ b/web/packages/teleport/src/Account/ManageDevices/AuthDeviceList/AuthDeviceList.tsx @@ -72,9 +72,14 @@ export function AuthDeviceList({ { altKey: 'remove-btn', headerText: 'Actions', - render: device => ( - onRemove(device)} /> - ), + render: device => { + return ( + onRemove(device)} + /> + ); + }, }, ]} data={devices} @@ -93,12 +98,18 @@ export function AuthDeviceList({ interface RemoveCellProps { onRemove?: () => void; + isSsoDevice?: boolean; } -function RemoveCell({ onRemove }: RemoveCellProps) { +function RemoveCell({ onRemove, isSsoDevice }: RemoveCellProps) { return ( - - + + diff --git a/web/packages/teleport/src/services/mfa/makeMfaDevice.ts b/web/packages/teleport/src/services/mfa/makeMfaDevice.ts index dec83f762e4e0..2547f4d720c28 100644 --- a/web/packages/teleport/src/services/mfa/makeMfaDevice.ts +++ b/web/packages/teleport/src/services/mfa/makeMfaDevice.ts @@ -16,7 +16,17 @@ * along with this program. If not, see . */ -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; @@ -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 { diff --git a/web/packages/teleport/src/services/mfa/types.ts b/web/packages/teleport/src/services/mfa/types.ts index 401bee906d3b5..6d1752c4082a1 100644 --- a/web/packages/teleport/src/services/mfa/types.ts +++ b/web/packages/teleport/src/services/mfa/types.ts @@ -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 =