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, tcmSessions } from "#codebase/tcmts/cli/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 s.fetch("/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( { ATPROTO_DIRECTORY: net.url, ATPROTO_ALLOW_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(tcmSessions().read().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(tcmSessions().read().sessions[alice.did]!.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 await ask("atproto repo:codes.tcm.release"); const kept = tcmSessions().read().sessions[alice.did]!; assert.equal(kept.appPassword, "a-pass"); // its tokens gone: it signs in again with the password, by itself tcmSessions().keep(alice.did, { ...kept, accessToken: "expired", refreshToken: "gone" }); assert.equal(await write(await ask("atproto repo:codes.tcm.release"), "two"), 200); assert.equal(tcmSessions().read().sessions[alice.did]!.accessToken, `acc-${alice.did}`); assert.equal(net.oauth.signIns, 0); }, ); })