// Incremental OAuth permissions. First sign-in grants only BASE scope (membership). The first // time a signed-in member likes, reposts, follows, RSVPs, posts or subscribes, we explain once // and re-consent for FULL scope, which covers every such action from then on. // // The granted scope is read from the live session (getTokenInfo), so we prompt only when the // permission is genuinely missing. handleActionError still catches the rare case where a write // fails on scope despite this check (e.g. token info unavailable), so nothing is silently lost. import { OAUTH_SCOPE_FULL, STEP_UP_MARKER } from './oauth-scope'; /** Whether the current session can already write activity records (likes, RSVPs, etc.). */ export async function hasWriteScope(): Promise { const atn = (window as any).atn; const session = atn?.session; if (!session?.getTokenInfo) return false; try { const info = await session.getTokenInfo(); return typeof info?.scope === 'string' && info.scope.split(' ').includes(STEP_UP_MARKER); } catch { // Token info unavailable — treat as not granted so the caller prompts. A stale "yes" would // let the write proceed and fail; a "no" costs at most one extra consent. return false; } } /** * Ensure the session can write activity records. Returns true when the permission is already * granted (proceed with the action). When it is missing, explains once and redirects to the * account's consent screen for FULL scope, then returns false so the caller aborts this attempt * (the member re-clicks after returning). Also returns false if the member declines the prompt. */ export async function ensureWriteScope(action: string): Promise { if (await hasWriteScope()) return true; const atn = (window as any).atn; const client = (window as any).atnClient; if (!atn?.did || !client?.signInRedirect) return false; const ok = window.confirm( `To ${action.toLowerCase()}, your account needs to give this site permission to write on ` + `your behalf. You grant it once, and it then covers likes, reposts, follows, RSVPs and ` + `posts. Continue to your account to allow it?`, ); if (!ok) return false; try { // Re-consent for the full scope. Redirects away; on return the session carries FULL scope. await client.signInRedirect(atn.did, { scope: OAUTH_SCOPE_FULL }); } catch { /* redirect failed to start — leave the member where they are; the write is simply not done */ } return false; }