testAppPasswordsSignInWithoutABrowser
With an app password in TCM_ATPROTO_APP_PASSWORD, a sign-in needs no browser and covers any scope; it's kept with
the password, so later sign-ins, for any scope and with no password given, need nothing, and when its tokens are
gone it signs in again by itself. A wrong password is refused, and nothing kept.
import { APP_PASSWORD_ENV, atprotoSession, pdsFetch, readSessions } from "#codebase/tcmts/atproto"; import { startFakeAtproto } from "#codebase/tcmts/cli/test/e2e/examples"; import { tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness"; import { withEnv } from "#codebase/tcmts/registry/test"; import assert from "node:assert/strict"; export const testAppPasswordsSignInWithoutABrowser = () => withChildren(async (keep) => { const alice = { did: "did:plc:alice000000000000000000", handle: "alice.test", password: "a-pass", }; const net = await startFakeAtproto(keep, [alice]); const write = async (s: Awaited<ReturnType<typeof atprotoSession>>, rkey: string) => ( await pdsFetch(s, "/xrpc/com.atproto.repo.createRecord", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ repo: s.did, collection: "codes.tcm.codebase", rkey, record: { $type: "codes.tcm.codebase", name: rkey }, }), }) ).status; await withEnv( { TCM_ATPROTO_DIRECTORY: net.url, TCM_ATPROTO_LOCAL: "1", XDG_CONFIG_HOME: tempDir("app password sign-ins"), BROWSER: "false", }, async () => { const ask = (scope: string) => atprotoSession({ who: "alice.test", scope, why: "testing", tell: () => {} }); // a wrong password: refused, nothing kept await withEnv({ [APP_PASSWORD_ENV]: "wrong" }, async () => { await assert.rejects(ask("atproto repo:codes.tcm.codebase"), /app password: Invalid/); }); assert.deepEqual(readSessions().sessions, {}); // the right one: signed in, no browser, and it writes const s = await withEnv({ [APP_PASSWORD_ENV]: "a-pass" }, () => ask("atproto repo:codes.tcm.codebase"), ); assert.equal(s.did, alice.did); assert.equal(s.dpopKey, undefined); assert.equal(net.oauth.signIns, 0); assert.equal(await write(s, "one"), 200); assert.ok(net.records.get(`at://${alice.did}/codes.tcm.codebase/one`)); // kept: asked again, for another scope and with no password given, it's the same sign-in const again = await ask("atproto repo:codes.tcm.release"); assert.equal(again.appPassword, "a-pass"); // its tokens gone: it signs in again with the password, by itself again.accessToken = "expired"; again.refreshToken = "gone"; assert.equal(await write(again, "two"), 200); assert.equal(readSessions().sessions[alice.did]!.accessToken, `acc-${alice.did}`); assert.equal(net.oauth.signIns, 0); }, ); })