Skip to content
This repository has been archived by the owner on Nov 26, 2024. It is now read-only.

Commit

Permalink
feat: add device verification popup, no longer accept all
Browse files Browse the repository at this point in the history
  • Loading branch information
marekvospel committed Jan 20, 2024
1 parent 73fe4c9 commit f4b0ceb
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 85 deletions.
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export default antfu({
svelte: true,
jsonc: true,

ignores: ['**/node_modules', '**/dist', '**/build', '**/.svelte-kit'],
ignores: ['**/node_modules', '**/dist', '**/build', '**/.svelte-kit', '!services/frontend/src/lib'],
})
8 changes: 4 additions & 4 deletions services/frontend/src/lib/popup/PopupManager.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<script lang="ts">
<script lang='ts'>
import DeviceVerificationPopup from './popups/DeviceVerificationPopup.svelte'
import { currentPopup } from './index'
import DeviceVerificationPopup from './popups/DeviceVerificationPopup.svelte';
</script>

{#if $currentPopup}
<div class="fixed left-[50%] top-[50%] -translate-[50%] bg-surface0 px-8 py-4 rounded flex flex-col items-center">
<div class='fixed left-[50%] top-[50%] -translate-[50%] bg-surface0 px-8 py-4 rounded-lg flex flex-col items-center'>
{#if $currentPopup.type === 'device-verification'}
<DeviceVerificationPopup {...$currentPopup} />
{/if}
</div>
{/if}
{/if}
12 changes: 9 additions & 3 deletions services/frontend/src/lib/popup/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { writable } from "svelte/store"
import type { ShowSasCallbacks } from 'matrix-js-sdk/lib/crypto-api'
import { writable } from 'svelte/store'

export type Popup = DeviceVerificationPopup

Expand All @@ -8,8 +9,13 @@ export function createPopup(popup: Popup) {
currentPopup.set(popup)
}

export function clearPopup() {
currentPopup.set(null)
}

// Popups

export interface DeviceVerificationPopup {
type: "device-verification"
}
type: 'device-verification'
sas: ShowSasCallbacks
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
<script lang="ts">
<script lang='ts'>
import type { ShowSasCallbacks } from 'matrix-js-sdk/lib/crypto-api'
import { clearPopup } from '..'
export const type = 'device-verification'
export let sas: ShowSasCallbacks
</script>

<h4>Device verification</h4>
<h4>Device verification</h4>

<div class='flex flex-row gap-2 items-center justify-center flex-wrap'>
{#each sas.sas.emoji ?? [] as emoji}
<div class='flex flex-col gap-1'>
<p>{emoji[0]}</p>
<p>{emoji[1]}</p>
</div>
{/each}
<div class='flex flex-row justify-between w-[90%]'>
<button on:click={() => { sas.mismatch(); clearPopup() }}>They don't match</button>
<button type='button' on:click={() => { sas.confirm(); clearPopup() }}>They match</button>
</div>
</div>
92 changes: 26 additions & 66 deletions services/frontend/src/lib/views/pages/SettingsPage.svelte
Original file line number Diff line number Diff line change
@@ -1,66 +1,28 @@
<script lang="ts">
import { VerificationRequestEvent } from 'matrix-js-sdk/lib/crypto-api';
import { client, storageKeys } from '../../../matrix'
<script lang='ts'>
import { VerificationRequestEvent } from 'matrix-js-sdk/lib/crypto-api'
import { client } from '../../../matrix'
import { startVerification } from '../../../matrix/verification'
let securityKey = ''
let backupSettingUp = false
async function setupBackup() {
if (backupSettingUp) return
backupSettingUp = true
const privateKey = client.keyBackupKeyFromRecoveryKey(securityKey)
const backupInfo = await client.getKeyBackupVersion();
const defaultKey = client.getAccountData('m.secret_storage.default_key')?.getContent()?.key
const isCorrect = await client.checkSecretStorageKey(privateKey, client.getAccountData(`m.secret_storage.key.${defaultKey}`)?.getContent()!);
if (isCorrect) {
storageKeys.set(defaultKey, privateKey)
}
if (!await client.getCrypto()?.isCrossSigningReady()) {
await client.getCrypto()?.bootstrapCrossSigning({
setupNewCrossSigning: true,
})
}
await client.checkOwnCrossSigningTrust()
await client.restoreKeyBackupWithSecretStorage(backupInfo!, undefined, undefined)
console.log(await client.getCrypto()?.checkKeyBackupAndEnable())
securityKey = ''
backupSettingUp = false
}
async function verifyDevice(device: string) {
const req = await client.requestVerification(client.getUserId() ?? '', [device])
req.on(VerificationRequestEvent.Change, async () => {
const verifier = await req.startVerification('m.sas.v1')
// @ts-expect-error: sad but necessary for missing import
verifier.on('show_sas', async (sas) => {
await sas.confirm()
})
await verifier.verify()
await startVerification(client, req)
})
await req.accept()
}
</script>


<div class="px-4 flex flex-col gap-4">
<h1 class="text-4xl font-bold">Settings</h1>
<div class="flex flex-col gap-2">
<h2 class="text-2xl font-bold">Security</h2>
<h3 class="text-xl font-semibold">Cross-signing</h3>
<div class='px-4 flex flex-col gap-4'>
<h1 class='text-4xl font-bold'>Settings</h1>
<div class='flex flex-col gap-2'>
<h2 class='text-2xl font-bold'>Security</h2>
<h3 class='text-xl font-semibold'>Cross-signing</h3>
<div>
{#await client.getCrypto()?.isCrossSigningReady() }
{#await client.getCrypto()?.isCrossSigningReady()}
Loading...
{:then sign}
{:then sign}
{#if sign}
<p>Cross-signing is <strong>ready</strong></p>
{:else}
Expand All @@ -69,9 +31,9 @@
{/await}
</div>

<h3 class="text-xl font-semibold">Secure backup</h3>
<h3 class='text-xl font-semibold'>Secure backup</h3>
<div>
{#await Promise.all([client.getCrypto()?.getActiveSessionBackupVersion()]) }
{#await Promise.all([client.getCrypto()?.getActiveSessionBackupVersion()])}
Loading...
{:then [version]}
{#if version === null}
Expand All @@ -86,22 +48,22 @@
{/await}
</div>

<h3 class="text-xl font-semibold">Devices</h3>
<h3 class='text-xl font-semibold'>Devices</h3>

<div class="flex flex-col gap-2">
<div class='flex flex-col gap-2'>

{#await client.getDevices()}
Loading...
{:then devices}
{#each devices.devices ?? [] as device (device['device_id'])}
<div class="flex flex-row items-center gap-4 bg-surface1 rounded px-4 py-2">
<span class="text-2xl { client.checkDeviceTrust(client.getUserId() ?? '', device['device_id']).isVerified() ? 'text-green i-bxs:shield' : 'text-red i-bx:shield'}" />
{:then devices}
{#each devices.devices ?? [] as device (device.device_id)}
<div class='flex flex-row items-center gap-4 bg-surface1 rounded px-4 py-2'>
<span class="text-2xl {client.checkDeviceTrust(client.getUserId() ?? '', device.device_id).isVerified() ? 'text-green i-bxs:shield' : 'text-red i-bx:shield'}" />
<div>
<h4 class="font-bold">{ device['display_name'] }</h4>
<p>{device['device_id']}</p>
<h4 class='font-bold'>{device.display_name}</h4>
<p>{device.device_id}</p>
</div>
{#if !client.checkDeviceTrust(client.getUserId() ?? '', device['device_id']).isVerified()}
<button on:click={() => verifyDevice(device['device_id'])} class="bg-teal px-2 py-1 rounded-sm">Verify device</button>
{#if !client.checkDeviceTrust(client.getUserId() ?? '', device.device_id).isVerified()}
<button on:click={() => verifyDevice(device.device_id)} class='bg-teal px-2 py-1 rounded-sm'>Verify device</button>
{/if}
</div>
{/each}
Expand All @@ -110,6 +72,4 @@
</div>
</div>



</div>
</div>
10 changes: 2 additions & 8 deletions services/frontend/src/matrix/listeners/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ClientEvent, CryptoEvent, HttpApiEvent, type MatrixClient } from 'matrix-js-sdk'
import { stateStore } from '../../stores/matrixState.store'
import { matrixLogout } from '..'
import { startVerification } from '../verification'

export function registerListeners(client: MatrixClient) {
// Sync state with state store, which is used to show loading spinners
Expand All @@ -14,14 +15,7 @@ export function registerListeners(client: MatrixClient) {
client.on(CryptoEvent.VerificationRequestReceived, async (request) => {
await request.accept()

const verifier = await request.startVerification('m.sas.v1')

// @ts-expect-error: sad but necessary for missing import
verifier.on('show_sas', async (sas: any) => {
await sas.confirm()
})

await verifier.verify()
await startVerification(client, request)
})

// Log out handler, if logged out from the homeserver, clean old tokens and data
Expand Down
15 changes: 15 additions & 0 deletions services/frontend/src/matrix/verification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { MatrixClient } from 'matrix-js-sdk'
import { type VerificationRequest, VerifierEvent } from 'matrix-js-sdk/lib/crypto-api'
import { createPopup } from '$lib/popup'

export async function startVerification(_client: MatrixClient, req: VerificationRequest) {
const verifier = await req.startVerification('m.sas.v1')
verifier.on(VerifierEvent.ShowSas, async (sas) => {
createPopup({
type: 'device-verification',
sas,
})
})

await verifier.verify()
}
2 changes: 1 addition & 1 deletion services/frontend/src/routes/login/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
user,
},
password,
initial_device_display_name: 'Libretrix',
initial_device_display_name: `${window.location.host} | Libretrix`,
})
const baseUrl = result?.well_known?.['m.homeserver']?.base_url ?? client.baseUrl
Expand Down

0 comments on commit f4b0ceb

Please sign in to comment.