keptSession

tcmts.atproto.keptSession · #jdvgfoef · function · runs anywhere

A kept sign-in as a Session: its requests DPoP-bound (or an app password's bearer token), its tokens refreshed once if the PDS says they've expired, and kept again in store; signing out forgets it there.

import { dpopFetch, refreshSession, sessionOver } from "#codebase/tcmts/atproto";
import type { OAuthSession, Session, SessionStore } from "#codebase/tcmts/atproto";

export function keptSession(s: OAuthSession, store?: SessionStore): Session {
  const send = async (p: string, init: RequestInit = {}): Promise<Response> => {
    const go = () =>
      s.dpopKey
        ? dpopFetch(s.dpopKey, `${s.pds}${p}`, init, s.accessToken)
        : fetch(`${s.pds}${p}`, {
            ...init,
            headers: {
              ...(init.headers as Record<string, string> | undefined),
              authorization: `Bearer ${s.accessToken}`,
            },
          });
    const res = await go();
    // (an app password's token that's expired is a 400 ExpiredToken)
    const expired =
      res.status === 401 ||
      (!s.dpopKey &&
        res.status === 400 &&
        (
          (await res
            .clone()
            .json()
            .catch(() => ({}))) as { error?: string }
        ).error === "ExpiredToken");
    if (!expired || !(await refreshSession(s))) return res;
    store?.keep(s.did, s);
    return go();
  };
  return sessionOver({
    did: s.did,
    ...(s.handle ? { handle: s.handle } : {}),
    scope: s.scope,
    send,
    signOut: async () => store?.keep(s.did, undefined),
  });
}

uses

used by