passwordSignIn

tcmts.atproto.passwordSignIn · #ci2cf3zc · term · runs on the server

uses tcmts.atproto.jwtExpiry, which uses Buffer

Sign in as who (a handle or DID) with an app password (com.atproto.server.createSession at their PDS): no browser. The password is kept with the sign-in, so when its tokens can't be refreshed it signs in again by itself. An app password may write any record, so the sign-in covers every scope.

import { jwtExpiry, Refused, resolveIdentity } from "#codebase/tcmts/atproto";
import type { OAuthSession } from "#codebase/tcmts/atproto";

export async function passwordSignIn(who: string, password: string): Promise<OAuthSession> {
  const id = await resolveIdentity(who);
  const res = await fetch(`${id.pds}/xrpc/com.atproto.server.createSession`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ identifier: id.did, password }),
  });
  if (!res.ok) {
    const e = (await res.json().catch(() => ({}))) as { error?: string; message?: string };
    throw new Refused(
      `signing in as ${id.handle ?? id.did} with an app password: ${e.message ?? e.error ?? res.status}`,
    );
  }
  const t = (await res.json()) as { did: string; accessJwt: string; refreshJwt: string };
  if (t.did !== id.did) throw new Error(`signed in as ${t.did}, not ${id.did}`);
  return {
    did: id.did,
    ...(id.handle ? { handle: id.handle } : {}),
    pds: id.pds,
    issuer: id.pds,
    tokenEndpoint: `${id.pds}/xrpc/com.atproto.server.refreshSession`,
    clientId: "",
    accessToken: t.accessJwt,
    refreshToken: t.refreshJwt,
    expiresAt: jwtExpiry(t.accessJwt),
    scope: "app-password",
    appPassword: password,
  };
}

uses

used by