Something went wrong. Try again.
Identities for entities did.bot
agent llm did
Something went wrong. Try again.
3.1 kB · 77 lines
TypeScript
at commit 18ba4fe0
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778// Signing in: a public OAuth client with DPoP and PKCE, its keys and tokens in// the library's own IndexedDB store.
import { AtprotoDohHandleResolver, BrowserOAuthClient, buildAtprotoLoopbackClientMetadata, isLoopbackHost, type HandleResolver, type OAuthSession,} from "@atproto/oauth-client-browser";import { isValidDid } from "@atproto/syntax";import { clientMetadata, SCOPE } from "./oauth-config.ts";
export type { OAuthSession };
// Who sees what at sign-in:// - A handle is resolved over DNS-over-HTTPS at Cloudflare (1.1.1.1), which// sees the name `_atproto.<handle>` and the browser's address. The handle's// own domain is asked for /.well-known/atproto-did at the same time.// - A DID skips both. The OAuth library would otherwise look up the handle in// the DID document to verify it, so it is given a resolver that never// answers; the page shows the handle the operator's own PDS reports instead.// - Every sign-in then resolves the DID document: at plc.directory for// did:plc, or at the DID's own domain for did:web.const DOH_ENDPOINT = "https://cloudflare-dns.com/dns-query";
const handles = new AtprotoDohHandleResolver({ dohEndpoint: DOH_ENDPOINT });
const unverifiedHandles: HandleResolver = { resolve: async () => null };
// Served from a loopback address, the page signs in as the atproto loopback// client: the same scope, redirected back to wherever the page is.// See https://atproto.com/specs/oauth#localhost-client-development.function metadataForThisOrigin() { if (!isLoopbackHost(location.hostname)) return clientMetadata; const port = location.port ? `:${location.port}` : ""; return buildAtprotoLoopbackClientMetadata({ scope: SCOPE, redirect_uris: [`http://127.0.0.1${port}/`], });}
export const client = new BrowserOAuthClient({ clientMetadata: metadataForThisOrigin(), handleResolver: unverifiedHandles,});
/** * Restores a stored session, or completes a sign-in the URL is returning * from. `state` is what that sign-in was started with, if it was this one. */export async function resume(): Promise<{ session?: OAuthSession; state?: string | null }> { return (await client.init()) ?? {};}
/** * Leaves the page for the operator's authorization server. The library keeps * `state` in its own store, keyed by the request, and hands it back to * resume() when the operator returns: here, the path to go on from. */export async function signIn(identity: string, state?: string): Promise<void> { const input = identity.trim().replace(/^@/, ""); const did = isValidDid(input) ? input : await handles.resolve(input.toLowerCase()); if (!did) throw new Error(`${input} does not resolve to a DID`); await client.signIn(did, { scope: SCOPE, state });}
/** Revokes the session's tokens at its authorization server and forgets it. */export async function signOut(session: OAuthSession): Promise<void> { await session.signOut();}
/** The PDS the session's tokens are for. */export async function pdsUrl(session: OAuthSession): Promise<string> { return (await session.getTokenInfo()).aud;}