32ffe782ea
Flatten widget layouts and use system colors so banner and expanded regions render on vibrant lock screens; migrate auth sessions per account to prevent double sign-in; scope app lock PIN to accounts; default clock description to "Clock In"; add architecture docs and deferred form validation on auth screens. Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
import * as SecureStore from "expo-secure-store";
|
|
|
|
import { authStoragePrefix, buildAccountId } from "@/lib/accounts";
|
|
import { normalizeSecureStoreKey } from "@/lib/secure-store-keys";
|
|
|
|
export const GUEST_AUTH_STORAGE_PREFIX = "beenvoice:guest";
|
|
|
|
const CHUNK_MARKER = "\u0001ba-chunks:";
|
|
const AUTH_STORAGE_SUFFIXES = ["_cookie", "_session_data", "_last_login_method"] as const;
|
|
|
|
function storageKeyForPrefix(prefix: string, suffix: (typeof AUTH_STORAGE_SUFFIXES)[number]) {
|
|
return normalizeSecureStoreKey(`${prefix}${suffix}`);
|
|
}
|
|
|
|
async function copySecureStoreEntry(fromKey: string, toKey: string): Promise<void> {
|
|
const value = await SecureStore.getItemAsync(fromKey);
|
|
if (value == null) return;
|
|
|
|
await SecureStore.setItemAsync(toKey, value);
|
|
|
|
if (!value.startsWith(CHUNK_MARKER)) return;
|
|
|
|
const count = Number(value.slice(CHUNK_MARKER.length));
|
|
if (!Number.isInteger(count) || count < 1) return;
|
|
|
|
for (let i = 0; i < count; i += 1) {
|
|
const chunk = await SecureStore.getItemAsync(`${fromKey}.${i}`);
|
|
if (chunk != null) {
|
|
await SecureStore.setItemAsync(`${toKey}.${i}`, chunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function migrateAuthStorage(fromPrefix: string, toPrefix: string): Promise<void> {
|
|
if (fromPrefix === toPrefix) return;
|
|
|
|
await Promise.all(
|
|
AUTH_STORAGE_SUFFIXES.map((suffix) =>
|
|
copySecureStoreEntry(storageKeyForPrefix(fromPrefix, suffix), storageKeyForPrefix(toPrefix, suffix)),
|
|
),
|
|
);
|
|
}
|
|
|
|
export async function finalizeAuthenticatedAccount(input: {
|
|
apiUrl: string;
|
|
userId: string;
|
|
email: string;
|
|
name: string;
|
|
activeAccountId: string | null;
|
|
registerAccount: (input: {
|
|
instanceUrl: string;
|
|
userId: string;
|
|
email: string;
|
|
name: string;
|
|
}) => Promise<unknown>;
|
|
}): Promise<void> {
|
|
const accountId = buildAccountId(input.apiUrl, input.userId);
|
|
const targetPrefix = authStoragePrefix(accountId);
|
|
const sourcePrefix = input.activeAccountId
|
|
? authStoragePrefix(input.activeAccountId)
|
|
: GUEST_AUTH_STORAGE_PREFIX;
|
|
|
|
await migrateAuthStorage(sourcePrefix, targetPrefix);
|
|
|
|
await input.registerAccount({
|
|
instanceUrl: input.apiUrl,
|
|
userId: input.userId,
|
|
email: input.email,
|
|
name: input.name,
|
|
});
|
|
}
|