finishSignIn

tcmts.atproto.finishSignIn · #6wfsmw4g · function · runs in the browser

uses location

If the page was opened coming back from signing in (its fragment holds the answer): finish it, keep who's signed in, tell the app's server (with server: #rg75eniw), and go back where it left. Who signed in, or undefined if the page wasn't coming back; throws what went wrong.

import {
  ACCOUNT_EVENT, accountOf, comingBack, configureBrowser, serviceDid, SIGN_IN_LXM, SIGNED_IN, SIGNING_IN, takeSignIn,
} from "#codebase/tcmts/atproto";
import type { Account, SignInOptions } from "#codebase/tcmts/atproto";
import { finalizeAuthorization, OAuthUserAgent } from "@atcute/oauth-browser-client";

export async function finishSignIn(): Promise<Account | undefined> {
  const params = new URLSearchParams(location.hash.slice(1));
  if (!params.has("state") || !(params.has("code") || params.has("error"))) return undefined;
  // (so it isn't used twice)
  history.replaceState(null, "", location.pathname + location.search);
  const asked = JSON.parse(sessionStorage.getItem(SIGNING_IN) ?? "null") as
    | (SignInOptions & { returnTo?: string })
    | null;
  sessionStorage.removeItem(SIGNING_IN);
  if (!asked) throw new Error("This sign-in wasn't started here: sign in again.");
  await configureBrowser(asked);
  const { session } = await finalizeAuthorization(params);
  const did = session.info.sub;
  let account: Account = { did };
  if (asked.server) {
    const agent = new OAuthUserAgent(session);
    const q = new URLSearchParams({
      aud: serviceDid(location.origin),
      lxm: SIGN_IN_LXM,
      exp: String(Math.floor(Date.now() / 1000) + 120),
    });
    const res = await agent.handle(`/xrpc/com.atproto.server.getServiceAuth?${q}`);
    const body = (await res.json().catch(() => ({}))) as { token?: string; message?: string };
    if (!res.ok || !body.token)
      throw new Error(`Your PDS wouldn't sign you in here: ${body.message ?? res.status}`);
    account = await takeSignIn(body.token);
  } else account = await accountOf(did);
  localStorage.setItem(
    SIGNED_IN,
    JSON.stringify({
      ...account,
      scope: asked.scope,
      ...(asked.client ? { client: asked.client } : {}),
      ...(asked.server ? { server: true } : {}),
    }),
  );
  window.dispatchEvent(new Event(ACCOUNT_EVENT));
  const here = location.pathname + location.search;
  const back = comingBack(asked.returnTo ?? null, here);
  // (told to the server, the page is made again with the sign-in; else it goes on as it is, unless it's going elsewhere)
  if (back !== here || asked.server) location.replace(back);
  return account;
}

uses

used by