Files
ChatApp/packages/shared/src/auth/device.ts
T
byGalax f7c60945d0 refactor(shared): strip cryptographic device provisioning (now telemetry-only)
devices rows no longer carry public_key for crypto purposes. The whole
per-device key API surface (provisionNewDevice, loadDevicePrivateKey,
saveDevicePrivateKey, forgetDevicePrivateKey, restoreDeviceFromServerRecord)
is removed; registerDevice now records {name, platform} only. SQL drops the
NOT NULL on devices.public_key so future telemetry rows can omit it.

Note: SQL not applied locally - push via pnpm prod:migrate when ready.
2026-05-15 23:19:26 +02:00

80 lines
2.3 KiB
TypeScript

import { fromBase64, toBase64 } from '../crypto/index';
import type { AppSupabaseClient } from '../supabase/client';
import type { DevicePlatform } from '../supabase/types';
// After the per-user encryption refactor, devices rows are pure telemetry:
// they record which physical/browser installs are signed into the account so
// the user can audit them, but they no longer carry cryptographic identity.
export interface RegisterDeviceParams {
name: string; // user-facing, e.g. "Dennis Laptop"
platform: DevicePlatform;
}
export interface DeviceRecord {
id: string;
name: string;
platform: DevicePlatform;
lastSeenAt: string;
}
export async function registerDevice(
client: AppSupabaseClient,
params: RegisterDeviceParams,
): Promise<DeviceRecord> {
const { data: session } = await client.auth.getUser();
if (!session.user) throw new Error('not authenticated');
const { data, error } = await client
.from('devices')
.insert({
user_id: session.user.id,
name: params.name,
platform: params.platform,
})
.select('id, name, platform, last_seen_at')
.single();
if (error) throw error;
return {
id: data.id,
name: data.name,
platform: data.platform,
lastSeenAt: data.last_seen_at,
};
}
export async function listOwnDevices(client: AppSupabaseClient): Promise<DeviceRecord[]> {
const { data: session } = await client.auth.getUser();
if (!session.user) throw new Error('not authenticated');
const { data, error } = await client
.from('devices')
.select('id, name, platform, last_seen_at')
.eq('user_id', session.user.id)
.order('last_seen_at', { ascending: false });
if (error) throw error;
return data.map((row) => ({
id: row.id,
name: row.name,
platform: row.platform,
lastSeenAt: row.last_seen_at,
}));
}
export async function touchDeviceLastSeen(
client: AppSupabaseClient,
deviceId: string,
): Promise<void> {
const { error } = await client
.from('devices')
.update({ last_seen_at: new Date().toISOString() })
.eq('id', deviceId);
if (error) throw error;
}
// Intentional re-exports so app layers only need @chat-app/shared/auth.
export type { SecretStore } from './secure-storage';
export { toBase64 as base64FromBytes, fromBase64 as bytesFromBase64 };