atprotoSession
uses process
A sign-in for scope, as who (a handle or DID) or, without one, the one used last: kept from before (its tokens
refreshed if they've expired), or a new one, kept for next time: with the app password in
TCM_ATPROTO_APP_PASSWORD if it's set (passwordSignIn: no browser, and never again, as it signs in again by
itself), else in the browser. A kept app password's sign-in covers every scope.
import { APP_PASSWORD_ENV, keepSession, passwordSignIn, readSessions, refreshSession, Refused, signIn, } from "#codebase/tcmts/atproto"; import type { OAuthSession } from "#codebase/tcmts/atproto"; export async function atprotoSession(o: { who?: string; scope: string; why: string; tell?: (line: string) => void; }): Promise<OAuthSession> { const kept = readSessions(); const who = o.who?.trim().replace(/^@/, "").toLowerCase(); const s = who ? Object.values(kept.sessions).find((x) => x.did === who || x.handle?.toLowerCase() === who) : kept.last ? kept.sessions[kept.last] : undefined; const password = process.env[APP_PASSWORD_ENV]?.trim(); const covers = (x: OAuthSession) => x.appPassword !== undefined ? !password || x.appPassword === password : !password && o.scope.split(" ").every((p) => x.scope.split(" ").includes(p)); if (s && covers(s) && (s.expiresAt - 60_000 > Date.now() || (await refreshSession(s)))) { if (kept.last !== s.did) keepSession(s.did, s); return s; } const as = who ?? s?.handle ?? s?.did; if (!as) throw new Refused(`${o.why}: sign in with --as <handle> (once: it's kept for next time)`); const fresh = password ? await passwordSignIn(as, password) : await signIn(as, o.scope, o.tell); keepSession(fresh.did, fresh); return fresh; }
uses
- tcmts.atproto.APP_PASSWORD_ENV
- tcmts.atproto.OAuthSession
- tcmts.atproto.Refused
- tcmts.atproto.keepSession
- tcmts.atproto.passwordSignIn
- tcmts.atproto.readSessions
- tcmts.atproto.refreshSession
- tcmts.atproto.signIn