import { generatePKCE } from "@openauthjs/openauth/pkce" import { Global } from "../global" import path from "path" import fs from "fs/promises" export namespace AuthAnthropic { const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e" const filepath = path.join(Global.Path.data, "auth", "anthropic.json") export async function authorize() { const pkce = await generatePKCE() const url = new URL("https://claude.ai/oauth/authorize", import.meta.url) url.searchParams.set("code", "true") url.searchParams.set("client_id", "9d1c250a-e61b-44d9-88ed-5944d1962f5e") url.searchParams.set("response_type", "code") url.searchParams.set( "redirect_uri", "https://console.anthropic.com/oauth/code/callback", ) url.searchParams.set( "scope", "org:create_api_key user:profile user:inference", ) url.searchParams.set("code_challenge", pkce.challenge) url.searchParams.set("code_challenge_method", "S256") url.searchParams.set("state", pkce.verifier) return { url: url.toString(), verifier: pkce.verifier, } } export async function exchange(code: string, verifier: string) { const splits = code.split("#") const result = await fetch("https://console.anthropic.com/v1/oauth/token", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ code: splits[0], state: splits[1], grant_type: "authorization_code", client_id: "9d1c250a-e61b-44d9-88ed-5944d1962f5e", redirect_uri: "https://console.anthropic.com/oauth/code/callback", code_verifier: verifier, }), }) if (!result.ok) throw new ExchangeFailed() const file = Bun.file(filepath) await Bun.write(file, result) await fs.chmod(file.name!, 0o600) } export async function access() { const file = Bun.file(filepath) const result = await file.json().catch(() => ({})) if (!result) return const refresh = result.refresh_token const response = await fetch( "https://console.anthropic.com/v1/oauth/token", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ grant_type: "refresh_token", refresh_token: refresh, client_id: CLIENT_ID, }), }, ) if (!response.ok) return const json = await response.json() await Bun.write(file, JSON.stringify(json)) return json.access_token as string } export class ExchangeFailed extends Error { constructor() { super("Exchange failed") } } }