Update your ATProto bio with what you're currently listening to

Implement ATProto OAuth

modamo-gh fdbdeda8 21dec05c

Changed files
+438 -6
app
api
auth
atproto
callback
client-metadata.json
login
lib
+39
app/api/auth/atproto/callback/route.ts
··· 1 + import { atprotoClient } from "@/lib/atproto-auth"; 2 + import { Agent } from "@atproto/api"; 3 + import { NextRequest, NextResponse } from "next/server"; 4 + 5 + const APP_URL = process.env.NEXT_PUBLIC_APP_URL; 6 + 7 + if (!APP_URL) { 8 + throw new Error( 9 + "NEXT_PUBLIC_APP_URL is not set. This is required for OAuth configuration." 10 + ); 11 + } 12 + 13 + export async function GET(request: NextRequest) { 14 + try { 15 + const params = request.nextUrl.searchParams; 16 + const { session, state } = await atprotoClient.callback(params); 17 + 18 + console.log(`User authenticated as: ${session.did}`); 19 + 20 + const agent = new Agent(session); 21 + 22 + await agent.getProfile({ actor: session.did }); 23 + 24 + const redirectURL = new URL("/", APP_URL); 25 + 26 + redirectURL.searchParams.set("atproto_did", session.did); 27 + redirectURL.searchParams.set("auth_status", "success"); 28 + 29 + return NextResponse.redirect(redirectURL); 30 + } catch (error) { 31 + console.error("ATProto Callback Error:", error); 32 + 33 + const redirectURL = new URL("/", APP_URL); 34 + 35 + redirectURL.searchParams.set("auth_error", "failed"); 36 + 37 + return NextResponse.redirect(redirectURL); 38 + } 39 + }
+8
app/api/auth/atproto/client-metadata.json/route.ts
··· 1 + import { atprotoClient } from "@/lib/atproto-auth"; 2 + import { NextResponse } from "next/server"; 3 + 4 + export async function GET() { 5 + return NextResponse.json(atprotoClient.clientMetadata, { 6 + headers: { "Content-Type": "application/json" } 7 + }); 8 + }
+27
app/api/auth/atproto/login/route.ts
··· 1 + import { atprotoClient } from "@/lib/atproto-auth"; 2 + import { NextRequest, NextResponse } from "next/server"; 3 + 4 + export async function POST(request: NextRequest) { 5 + try { 6 + const { atProtoIdentifier } = await request.json(); 7 + 8 + if (!atProtoIdentifier) { 9 + return NextResponse.json( 10 + { error: "Missing AT Protocol identifier." }, 11 + { status: 400 } 12 + ); 13 + } 14 + 15 + const state = crypto.randomUUID(); 16 + const url = await atprotoClient.authorize(atProtoIdentifier, { state }); 17 + 18 + return NextResponse.json({ url: url.toString() }); 19 + } catch (error) { 20 + console.error("ATProto Login Error:", error); 21 + 22 + return NextResponse.json( 23 + { error: "Failed to initiate AT Protocol login." }, 24 + { status: 500 } 25 + ); 26 + } 27 + }
+13 -6
app/page.tsx
··· 16 16 const loginToATProto = async () => { 17 17 setIsLoggingIn(true); 18 18 19 + console.log(atProtoIdentifier) 20 + 19 21 try { 20 - const response = await fetch("/api/auth/atproto", { 22 + const response = await fetch("/api/auth/atproto/login", { 21 23 body: JSON.stringify({ 22 - hostingProvide: hostingProvider || "https://bsky.social", 23 - identifier: atProtoIdentifier, 24 - password 24 + atProtoIdentifier 25 25 }), 26 26 headers: { "Content-Type": "application/json" }, 27 27 method: "POST" 28 28 }); 29 29 const data = await response.json(); 30 30 31 - console.log(data); 31 + if (response.ok && data.url) { 32 + window.location.href = data.url; 33 + } else { 34 + console.error("Login initiation failed:", data.error); 35 + 36 + setIsLoggingIn(false); 37 + } 32 38 } catch (error) { 33 - } finally { 39 + console.error("Error initiating AT Protocol OAuth:", error); 40 + 34 41 setIsLoggingIn(false); 35 42 } 36 43 };
+54
lib/atproto-auth.ts
··· 1 + import { 2 + NodeOAuthClient, 3 + NodeSavedSession, 4 + NodeSavedState 5 + } from "@atproto/oauth-client-node"; 6 + 7 + const APP_URL = process.env.NEXT_PUBLIC_APP_URL; 8 + 9 + if (!APP_URL) { 10 + throw new Error( 11 + "NEXT_PUBLIC_APP_URL is not set. This is required for OAuth configuration." 12 + ); 13 + } 14 + 15 + const sessionStore = new Map<string, NodeSavedSession>(); 16 + const stateStore = new Map<string, NodeSavedState>(); 17 + 18 + export const atprotoClient = new NodeOAuthClient({ 19 + clientMetadata: { 20 + application_type: "web", 21 + client_id: `${APP_URL}/api/auth/atproto/client-metadata.json`, 22 + client_name: "scrobbleToBio", 23 + dpop_bound_access_tokens: true, 24 + grant_types: ["authorization_code", "refresh_token"], 25 + redirect_uris: [`${APP_URL}/api/auth/atproto/callback`], 26 + response_types: ["code"], 27 + scope: "atproto transition:generic", 28 + token_endpoint_auth_method: "none" 29 + }, 30 + handleResolver: 31 + "https://bsky.social/xrpc/com.atproto.identity.resolveHandle", 32 + sessionStore: { 33 + async del(sub: string): Promise<void> { 34 + sessionStore.delete(sub); 35 + }, 36 + async get(sub: string): Promise<NodeSavedSession | undefined> { 37 + return sessionStore.get(sub); 38 + }, 39 + async set(sub: string, session: NodeSavedSession): Promise<void> { 40 + sessionStore.set(sub, session); 41 + } 42 + }, 43 + stateStore: { 44 + async del(key: string): Promise<void> { 45 + stateStore.delete(key); 46 + }, 47 + async get(key: string): Promise<NodeSavedState | undefined> { 48 + return stateStore.get(key); 49 + }, 50 + async set(key: string, internalState: NodeSavedState): Promise<void> { 51 + stateStore.set(key, internalState); 52 + } 53 + } 54 + });
+296
package-lock.json
··· 9 9 "version": "0.1.0", 10 10 "dependencies": { 11 11 "@atproto/api": "^0.17.7", 12 + "@atproto/oauth-client-node": "^0.3.10", 12 13 "lucide-react": "^0.548.0", 13 14 "next": "16.0.1", 14 15 "react": "19.2.0", ··· 38 39 "url": "https://github.com/sponsors/sindresorhus" 39 40 } 40 41 }, 42 + "node_modules/@atproto-labs/did-resolver": { 43 + "version": "0.2.2", 44 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.2.tgz", 45 + "integrity": "sha512-ca2B7xR43tVoQ8XxBvha58DXwIH8cIyKQl6lpOKGkPUrJuFoO4iCLlDiSDi2Ueh+yE1rMDPP/qveHdajgDX3WQ==", 46 + "license": "MIT", 47 + "dependencies": { 48 + "@atproto-labs/fetch": "0.2.3", 49 + "@atproto-labs/pipe": "0.1.1", 50 + "@atproto-labs/simple-store": "0.3.0", 51 + "@atproto-labs/simple-store-memory": "0.1.4", 52 + "@atproto/did": "0.2.1", 53 + "zod": "^3.23.8" 54 + } 55 + }, 56 + "node_modules/@atproto-labs/did-resolver/node_modules/zod": { 57 + "version": "3.25.76", 58 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 59 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 60 + "license": "MIT", 61 + "funding": { 62 + "url": "https://github.com/sponsors/colinhacks" 63 + } 64 + }, 65 + "node_modules/@atproto-labs/fetch": { 66 + "version": "0.2.3", 67 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz", 68 + "integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==", 69 + "license": "MIT", 70 + "dependencies": { 71 + "@atproto-labs/pipe": "0.1.1" 72 + } 73 + }, 74 + "node_modules/@atproto-labs/fetch-node": { 75 + "version": "0.2.0", 76 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch-node/-/fetch-node-0.2.0.tgz", 77 + "integrity": "sha512-Krq09nH/aeoiU2s9xdHA0FjTEFWG9B5FFenipv1iRixCcPc7V3DhTNDawxG9gI8Ny0k4dBVS9WTRN/IDzBx86Q==", 78 + "license": "MIT", 79 + "dependencies": { 80 + "@atproto-labs/fetch": "0.2.3", 81 + "@atproto-labs/pipe": "0.1.1", 82 + "ipaddr.js": "^2.1.0", 83 + "undici": "^6.14.1" 84 + }, 85 + "engines": { 86 + "node": ">=18.7.0" 87 + } 88 + }, 89 + "node_modules/@atproto-labs/handle-resolver": { 90 + "version": "0.3.2", 91 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.3.2.tgz", 92 + "integrity": "sha512-KIerCzh3qb+zZoqWbIvTlvBY0XPq0r56kwViaJY/LTe/3oPO2JaqlYKS/F4dByWBhHK6YoUOJ0sWrh6PMJl40A==", 93 + "license": "MIT", 94 + "dependencies": { 95 + "@atproto-labs/simple-store": "0.3.0", 96 + "@atproto-labs/simple-store-memory": "0.1.4", 97 + "@atproto/did": "0.2.1", 98 + "zod": "^3.23.8" 99 + } 100 + }, 101 + "node_modules/@atproto-labs/handle-resolver-node": { 102 + "version": "0.1.21", 103 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver-node/-/handle-resolver-node-0.1.21.tgz", 104 + "integrity": "sha512-fuJy5Px5pGF3lJX/ATdurbT8tbmaFWtf+PPxAQDFy7ot2no3t+iaAgymhyxYymrssOuWs6BwOP8tyF3VrfdwtQ==", 105 + "license": "MIT", 106 + "dependencies": { 107 + "@atproto-labs/fetch-node": "0.2.0", 108 + "@atproto-labs/handle-resolver": "0.3.2", 109 + "@atproto/did": "0.2.1" 110 + }, 111 + "engines": { 112 + "node": ">=18.7.0" 113 + } 114 + }, 115 + "node_modules/@atproto-labs/handle-resolver/node_modules/zod": { 116 + "version": "3.25.76", 117 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 118 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 119 + "license": "MIT", 120 + "funding": { 121 + "url": "https://github.com/sponsors/colinhacks" 122 + } 123 + }, 124 + "node_modules/@atproto-labs/identity-resolver": { 125 + "version": "0.3.2", 126 + "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.3.2.tgz", 127 + "integrity": "sha512-MYxO9pe0WsFyi5HFdKAwqIqHfiF2kBPoVhAIuH/4PYHzGr799ED47xLhNMxR3ZUYrJm5+TQzWXypGZ0Btw1Ffw==", 128 + "license": "MIT", 129 + "dependencies": { 130 + "@atproto-labs/did-resolver": "0.2.2", 131 + "@atproto-labs/handle-resolver": "0.3.2" 132 + } 133 + }, 134 + "node_modules/@atproto-labs/pipe": { 135 + "version": "0.1.1", 136 + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz", 137 + "integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==", 138 + "license": "MIT" 139 + }, 140 + "node_modules/@atproto-labs/simple-store": { 141 + "version": "0.3.0", 142 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz", 143 + "integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==", 144 + "license": "MIT" 145 + }, 146 + "node_modules/@atproto-labs/simple-store-memory": { 147 + "version": "0.1.4", 148 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz", 149 + "integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==", 150 + "license": "MIT", 151 + "dependencies": { 152 + "@atproto-labs/simple-store": "0.3.0", 153 + "lru-cache": "^10.2.0" 154 + } 155 + }, 156 + "node_modules/@atproto-labs/simple-store-memory/node_modules/lru-cache": { 157 + "version": "10.4.3", 158 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 159 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 160 + "license": "ISC" 161 + }, 41 162 "node_modules/@atproto/api": { 42 163 "version": "0.17.7", 43 164 "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.17.7.tgz", ··· 84 205 "url": "https://github.com/sponsors/colinhacks" 85 206 } 86 207 }, 208 + "node_modules/@atproto/did": { 209 + "version": "0.2.1", 210 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.1.tgz", 211 + "integrity": "sha512-1i5BTU2GnBaaeYWhxUOnuEKFVq9euT5+dQPFabHpa927BlJ54PmLGyBBaOI7/NbLmN5HWwBa18SBkMpg3jGZRA==", 212 + "license": "MIT", 213 + "dependencies": { 214 + "zod": "^3.23.8" 215 + } 216 + }, 217 + "node_modules/@atproto/did/node_modules/zod": { 218 + "version": "3.25.76", 219 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 220 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 221 + "license": "MIT", 222 + "funding": { 223 + "url": "https://github.com/sponsors/colinhacks" 224 + } 225 + }, 226 + "node_modules/@atproto/jwk": { 227 + "version": "0.6.0", 228 + "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.6.0.tgz", 229 + "integrity": "sha512-bDoJPvt7TrQVi/rBfBrSSpGykhtIriKxeYCYQTiPRKFfyRhbgpElF0wPXADjIswnbzZdOwbY63az4E/CFVT3Tw==", 230 + "license": "MIT", 231 + "dependencies": { 232 + "multiformats": "^9.9.0", 233 + "zod": "^3.23.8" 234 + } 235 + }, 236 + "node_modules/@atproto/jwk-jose": { 237 + "version": "0.1.11", 238 + "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.11.tgz", 239 + "integrity": "sha512-i4Fnr2sTBYmMmHXl7NJh8GrCH+tDQEVWrcDMDnV5DjJfkgT17wIqvojIw9SNbSL4Uf0OtfEv6AgG0A+mgh8b5Q==", 240 + "license": "MIT", 241 + "dependencies": { 242 + "@atproto/jwk": "0.6.0", 243 + "jose": "^5.2.0" 244 + } 245 + }, 246 + "node_modules/@atproto/jwk-webcrypto": { 247 + "version": "0.2.0", 248 + "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.2.0.tgz", 249 + "integrity": "sha512-UmgRrrEAkWvxwhlwe30UmDOdTEFidlIzBC7C3cCbeJMcBN1x8B3KH+crXrsTqfWQBG58mXgt8wgSK3Kxs2LhFg==", 250 + "license": "MIT", 251 + "dependencies": { 252 + "@atproto/jwk": "0.6.0", 253 + "@atproto/jwk-jose": "0.1.11", 254 + "zod": "^3.23.8" 255 + } 256 + }, 257 + "node_modules/@atproto/jwk-webcrypto/node_modules/zod": { 258 + "version": "3.25.76", 259 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 260 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 261 + "license": "MIT", 262 + "funding": { 263 + "url": "https://github.com/sponsors/colinhacks" 264 + } 265 + }, 266 + "node_modules/@atproto/jwk/node_modules/zod": { 267 + "version": "3.25.76", 268 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 269 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 270 + "license": "MIT", 271 + "funding": { 272 + "url": "https://github.com/sponsors/colinhacks" 273 + } 274 + }, 87 275 "node_modules/@atproto/lexicon": { 88 276 "version": "0.5.1", 89 277 "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", ··· 106 294 "url": "https://github.com/sponsors/colinhacks" 107 295 } 108 296 }, 297 + "node_modules/@atproto/oauth-client": { 298 + "version": "0.5.8", 299 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.5.8.tgz", 300 + "integrity": "sha512-7YEym6d97+Dd73qGdkQTXi5La8xvCQxwRUDzzlR/NVAARa9a4YP7MCmqBJVeP2anT0By+DSAPyPDLTsxcjIcCg==", 301 + "license": "MIT", 302 + "dependencies": { 303 + "@atproto-labs/did-resolver": "0.2.2", 304 + "@atproto-labs/fetch": "0.2.3", 305 + "@atproto-labs/handle-resolver": "0.3.2", 306 + "@atproto-labs/identity-resolver": "0.3.2", 307 + "@atproto-labs/simple-store": "0.3.0", 308 + "@atproto-labs/simple-store-memory": "0.1.4", 309 + "@atproto/did": "0.2.1", 310 + "@atproto/jwk": "0.6.0", 311 + "@atproto/oauth-types": "0.5.0", 312 + "@atproto/xrpc": "0.7.5", 313 + "core-js": "^3", 314 + "multiformats": "^9.9.0", 315 + "zod": "^3.23.8" 316 + } 317 + }, 318 + "node_modules/@atproto/oauth-client-node": { 319 + "version": "0.3.10", 320 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client-node/-/oauth-client-node-0.3.10.tgz", 321 + "integrity": "sha512-6khKlJqu1Ed5rt3rzcTD5hymB6JUjKdOHWYXwiphw4inkAIo6GxLCighI4eGOqZorYk2j8ueeTNB6KsgH0kcRw==", 322 + "license": "MIT", 323 + "dependencies": { 324 + "@atproto-labs/did-resolver": "0.2.2", 325 + "@atproto-labs/handle-resolver-node": "0.1.21", 326 + "@atproto-labs/simple-store": "0.3.0", 327 + "@atproto/did": "0.2.1", 328 + "@atproto/jwk": "0.6.0", 329 + "@atproto/jwk-jose": "0.1.11", 330 + "@atproto/jwk-webcrypto": "0.2.0", 331 + "@atproto/oauth-client": "0.5.8", 332 + "@atproto/oauth-types": "0.5.0" 333 + }, 334 + "engines": { 335 + "node": ">=18.7.0" 336 + } 337 + }, 338 + "node_modules/@atproto/oauth-client/node_modules/zod": { 339 + "version": "3.25.76", 340 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 341 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 342 + "license": "MIT", 343 + "funding": { 344 + "url": "https://github.com/sponsors/colinhacks" 345 + } 346 + }, 347 + "node_modules/@atproto/oauth-types": { 348 + "version": "0.5.0", 349 + "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.5.0.tgz", 350 + "integrity": "sha512-33xz7HcXhbl+XRqbIMVu3GE02iK1nKe2oMWENASsfZEYbCz2b9ZOarOFuwi7g4LKqpGowGp0iRKsQHFcq4SDaQ==", 351 + "license": "MIT", 352 + "dependencies": { 353 + "@atproto/did": "0.2.1", 354 + "@atproto/jwk": "0.6.0", 355 + "zod": "^3.23.8" 356 + } 357 + }, 358 + "node_modules/@atproto/oauth-types/node_modules/zod": { 359 + "version": "3.25.76", 360 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 361 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 362 + "license": "MIT", 363 + "funding": { 364 + "url": "https://github.com/sponsors/colinhacks" 365 + } 366 + }, 109 367 "node_modules/@atproto/syntax": { 110 368 "version": "0.4.1", 111 369 "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", ··· 2732 2990 "dev": true, 2733 2991 "license": "MIT" 2734 2992 }, 2993 + "node_modules/core-js": { 2994 + "version": "3.46.0", 2995 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz", 2996 + "integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==", 2997 + "hasInstallScript": true, 2998 + "license": "MIT", 2999 + "funding": { 3000 + "type": "opencollective", 3001 + "url": "https://opencollective.com/core-js" 3002 + } 3003 + }, 2735 3004 "node_modules/cross-spawn": { 2736 3005 "version": "7.0.6", 2737 3006 "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", ··· 4070 4339 "node": ">= 0.4" 4071 4340 } 4072 4341 }, 4342 + "node_modules/ipaddr.js": { 4343 + "version": "2.2.0", 4344 + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", 4345 + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", 4346 + "license": "MIT", 4347 + "engines": { 4348 + "node": ">= 10" 4349 + } 4350 + }, 4073 4351 "node_modules/is-array-buffer": { 4074 4352 "version": "3.0.5", 4075 4353 "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", ··· 4531 4809 "license": "MIT", 4532 4810 "bin": { 4533 4811 "jiti": "lib/jiti-cli.mjs" 4812 + } 4813 + }, 4814 + "node_modules/jose": { 4815 + "version": "5.10.0", 4816 + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", 4817 + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", 4818 + "license": "MIT", 4819 + "funding": { 4820 + "url": "https://github.com/sponsors/panva" 4534 4821 } 4535 4822 }, 4536 4823 "node_modules/js-tokens": { ··· 6455 6742 }, 6456 6743 "funding": { 6457 6744 "url": "https://github.com/sponsors/ljharb" 6745 + } 6746 + }, 6747 + "node_modules/undici": { 6748 + "version": "6.22.0", 6749 + "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", 6750 + "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", 6751 + "license": "MIT", 6752 + "engines": { 6753 + "node": ">=18.17" 6458 6754 } 6459 6755 }, 6460 6756 "node_modules/undici-types": {
+1
package.json
··· 10 10 }, 11 11 "dependencies": { 12 12 "@atproto/api": "^0.17.7", 13 + "@atproto/oauth-client-node": "^0.3.10", 13 14 "lucide-react": "^0.548.0", 14 15 "next": "16.0.1", 15 16 "react": "19.2.0",