import type { Did } from "@atcute/lexicons/syntax";
import {
OAuthUserAgent,
deleteStoredSession,
getSession,
type Session
} from "@atcute/oauth-browser-client";
import {
forgetOrgDid,
loadOrgControllers,
loadOrgDids,
orgDidsControlledBy
} from "#lib/auth/accounts.ts";
// org sessions are minted from a controller's session and are useless to the
// user once that controller signs out, so they get revoked alongside it
export interface OrgSessionDependencies {
loadOrgDids: () => Did[];
loadOrgControllers: () => Record<string, Did>;
getSession: (sub: Did, options?: { allowStale?: boolean }) => Promise<Session>;
createAgent: (session: Session) => Pick<OAuthUserAgent, "signOut">;
deleteStoredSession: (sub: Did) => void;
forgetOrgDid: (did: Did) => void;
}
const defaultDependencies: OrgSessionDependencies = {
loadOrgDids,
loadOrgControllers,
getSession,
createAgent: (session) => new OAuthUserAgent(session),
deleteStoredSession,
forgetOrgDid
};
const revokeOne = async (orgDid: Did, deps: OrgSessionDependencies): Promise<void> => {
try {
// allowStale skips a refresh, the token is about to be revoked anyway
const session = await deps.getSession(orgDid, { allowStale: true });
await deps.createAgent(session).signOut();
} catch {
// nothing to revoke, or the pds refused, either way drop the local copy
deps.deleteStoredSession(orgDid);
}
deps.forgetOrgDid(orgDid);
};
const revoke = async (orgDids: Did[], deps: OrgSessionDependencies): Promise<void> => {
await Promise.allSettled(orgDids.map((orgDid) => revokeOne(orgDid, deps)));
};
export const revokeOrgSessionsFor = (
controllerDid: Did,
deps: OrgSessionDependencies = defaultDependencies
): Promise<void> => revoke(orgDidsControlledBy(deps.loadOrgControllers(), controllerDid), deps);
export const revokeAllOrgSessions = (
deps: OrgSessionDependencies = defaultDependencies
): Promise<void> => revoke(deps.loadOrgDids(), deps);