anthropic.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { generatePKCE } from "@openauthjs/openauth/pkce"
  2. import { Global } from "../global"
  3. import path from "path"
  4. export namespace AuthAnthropic {
  5. const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
  6. export async function authorize() {
  7. const pkce = await generatePKCE()
  8. const url = new URL("https://claude.ai/oauth/authorize", import.meta.url)
  9. url.searchParams.set("code", "true")
  10. url.searchParams.set("client_id", "9d1c250a-e61b-44d9-88ed-5944d1962f5e")
  11. url.searchParams.set("response_type", "code")
  12. url.searchParams.set(
  13. "redirect_uri",
  14. "https://console.anthropic.com/oauth/code/callback",
  15. )
  16. url.searchParams.set(
  17. "scope",
  18. "org:create_api_key user:profile user:inference",
  19. )
  20. url.searchParams.set("code_challenge", pkce.challenge)
  21. url.searchParams.set("code_challenge_method", "S256")
  22. url.searchParams.set("state", pkce.verifier)
  23. return {
  24. url: url.toString(),
  25. verifier: pkce.verifier,
  26. }
  27. }
  28. export async function exchange(code: string, verifier: string) {
  29. const splits = code.split("#")
  30. const result = await fetch("https://console.anthropic.com/v1/oauth/token", {
  31. method: "POST",
  32. headers: {
  33. "Content-Type": "application/json",
  34. },
  35. body: JSON.stringify({
  36. code: splits[0],
  37. state: splits[1],
  38. grant_type: "authorization_code",
  39. client_id: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
  40. redirect_uri: "https://console.anthropic.com/oauth/code/callback",
  41. code_verifier: verifier,
  42. }),
  43. })
  44. if (!result.ok) throw new ExchangeFailed()
  45. await Bun.write(path.join(Global.Path.data, "anthropic.json"), result)
  46. }
  47. export async function access() {
  48. const file = Bun.file(path.join(Global.Path.data, "anthropic.json"))
  49. if (!(await file.exists())) return
  50. const result = await file.json()
  51. const refresh = result.refresh_token
  52. const now = Date.now()
  53. const response = await fetch(
  54. "https://console.anthropic.com/v1/oauth/token",
  55. {
  56. method: "POST",
  57. headers: {
  58. "Content-Type": "application/json",
  59. },
  60. body: JSON.stringify({
  61. grant_type: "refresh_token",
  62. refresh_token: refresh,
  63. client_id: CLIENT_ID,
  64. }),
  65. },
  66. )
  67. if (!response.ok) return
  68. const json = await response.json()
  69. await Bun.write(file, JSON.stringify(json))
  70. return json.access_token as string
  71. }
  72. export class ExchangeFailed extends Error {
  73. constructor() {
  74. super("Exchange failed")
  75. }
  76. }
  77. }