Skip to content
67 linesCodeBlameRaw
1import * as SecureStore from "expo-secure-store";
2
3import { HOSTED, type Server } from "./server";
4
5/**
6 * What the app keeps between launches, in the phone's keystore (Android
7 * Keystore, the iOS keychain): the server, and how it is signed in.
8 *
9 * Two ways to be signed in:
10 * - `oauth`: the app's own sign-in (lib/oauth.ts). An access token that
11 * lasts 30 days and a refresh token that works once and returns the
12 * next pair, as every OAuth client of g1t's gets.
13 * - `token`: a personal access token pasted in, for a g1t whose OAuth
14 * tokens do not yet open Chat (see the README) or for testing. One with
15 * "Use the website as you" turned on opens everything the app shows.
16 */
17export type Credentials =
18 | {
19 kind: "oauth";
20 accessToken: string;
21 refreshToken: string;
22 /** Epoch milliseconds. */
23 expiresAt: number;
24 /** The scopes the person granted, or `*` for full access. */
25 scope: string;
26 /**
27 * The client id it was issued to: a refresh must name the same one.
28 * Kept, because in Expo Go the id follows the computer's address,
29 * which changes between sessions (lib/oauth.ts).
30 */
31 clientId?: string;
32 }
33 | { kind: "token"; accessToken: string };
34
35const SERVER_KEY = "g1t.server";
36const CREDENTIALS_KEY = "g1t.credentials";
37
38async function readJson<T>(key: string): Promise<T | null> {
39 try {
40 const text = await SecureStore.getItemAsync(key);
41 return text ? (JSON.parse(text) as T) : null;
42 } catch {
43 // Unreadable (a restore to another phone, a keystore reset): start over.
44 return null;
45 }
46}
47
48export async function loadServer(): Promise<Server> {
49 const saved = await readJson<Server>(SERVER_KEY);
50 return saved?.site && saved?.api ? saved : HOSTED;
51}
52
53export async function saveServer(server: Server): Promise<void> {
54 await SecureStore.setItemAsync(SERVER_KEY, JSON.stringify(server));
55}
56
57export async function loadCredentials(): Promise<Credentials | null> {
58 return readJson<Credentials>(CREDENTIALS_KEY);
59}
60
61export async function saveCredentials(credentials: Credentials): Promise<void> {
62 await SecureStore.setItemAsync(CREDENTIALS_KEY, JSON.stringify(credentials));
63}
64
65export async function clearCredentials(): Promise<void> {
66 await SecureStore.deleteItemAsync(CREDENTIALS_KEY);
67}