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; revokedAt: string | null; } export async function registerDevice( client: AppSupabaseClient, params: RegisterDeviceParams, ): Promise { 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, revoked_at') .single(); if (error) throw error; return { id: data.id, name: data.name, platform: data.platform, lastSeenAt: data.last_seen_at, revokedAt: data.revoked_at, }; } export async function listOwnDevices(client: AppSupabaseClient): Promise { 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, revoked_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, revokedAt: row.revoked_at, })); } export async function touchDeviceLastSeen( client: AppSupabaseClient, deviceId: string, ): Promise { const { error } = await client .from('devices') .update({ last_seen_at: new Date().toISOString() }) .eq('id', deviceId); if (error) throw error; } export async function revokeDevice( client: AppSupabaseClient, deviceId: string, ): Promise { const { error } = await client.rpc('revoke_device', { p_device_id: deviceId }); if (error) throw new Error(error.message); } // 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 };