A web app to better navigate saved skeets
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement ATProto Auth

modamo-gh 4a9244e0 9a77572b

+661 -64
+47
app/api/auth/atproto/callback/route.ts
··· 1 + import { atprotoClient } from "@/lib/atprotoAuth"; 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 } = await atprotoClient.callback(params); 17 + 18 + const did = session.sub 19 + 20 + const agent = new Agent(session); 21 + const profile = await agent.getProfile({ actor: session.did }); 22 + const redirectURL = new URL("/", APP_URL); 23 + 24 + redirectURL.searchParams.set("atproto_did", session.did); 25 + redirectURL.searchParams.set("auth_status", "success"); 26 + 27 + const response = NextResponse.redirect(redirectURL); 28 + 29 + response.cookies.set("atproto_did", did, { 30 + httpOnly: true, 31 + maxAge: 60 * 60 * 24 * 30, 32 + path: "/", 33 + sameSite: "lax", 34 + secure: false 35 + }); 36 + 37 + return response; 38 + } catch (error) { 39 + console.error("ATProto Callback Error:", error); 40 + 41 + const redirectURL = new URL("/", APP_URL); 42 + 43 + redirectURL.searchParams.set("auth_error", "failed"); 44 + 45 + return NextResponse.redirect(redirectURL); 46 + } 47 + }
+8
app/api/auth/atproto/client-metadata.json/route.ts
··· 1 + import { atprotoClient } from "@/lib/atprotoAuth"; 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 + }
+26
app/api/auth/atproto/login/route.ts
··· 1 + import { atprotoClient } from "@/lib/atprotoAuth"; 2 + import { NextRequest, NextResponse } from "next/server"; 3 + 4 + export async function POST(request: NextRequest) { 5 + const { atProtoIdentifier } = await request.json(); 6 + 7 + try { 8 + const url = await atprotoClient.authorize(atProtoIdentifier, { 9 + state: crypto.randomUUID() 10 + }); 11 + 12 + return NextResponse.json({ 13 + url: url.toString() 14 + }); 15 + } catch (error) { 16 + const message = 17 + error instanceof Error 18 + ? error.message 19 + : "An unknown error occurred during the process."; 20 + 21 + return NextResponse.json( 22 + { error: message, success: false }, 23 + { status: 401 } 24 + ); 25 + } 26 + }
+59 -64
app/page.tsx
··· 1 - import Image from "next/image"; 1 + "use client"; 2 + 3 + import { AtSign } from "lucide-react"; 4 + import { useState } from "react"; 5 + 6 + const Home = () => { 7 + const [atProtoIdentifier, setAtProtoIdentifier] = useState(""); 8 + 9 + const loginToATProto = async () => { 10 + console.log(atProtoIdentifier); 11 + 12 + try { 13 + const response = await fetch("/api/auth/atproto/login", { 14 + body: JSON.stringify({ 15 + atProtoIdentifier 16 + }), 17 + headers: { "Content-Type": "application/json" }, 18 + method: "POST" 19 + }); 20 + const data = await response.json(); 2 21 3 - export default function Home() { 4 - return ( 5 - <div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black"> 6 - <main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start"> 7 - <Image 8 - className="dark:invert" 9 - src="/next.svg" 10 - alt="Next.js logo" 11 - width={100} 12 - height={20} 13 - priority 14 - /> 15 - <div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left"> 16 - <h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50"> 17 - To get started, edit the page.tsx file. 18 - </h1> 19 - <p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400"> 20 - Looking for a starting point or more instructions? Head over to{" "} 21 - <a 22 - href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" 23 - className="font-medium text-zinc-950 dark:text-zinc-50" 24 - > 25 - Templates 26 - </a>{" "} 27 - or the{" "} 28 - <a 29 - href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" 30 - className="font-medium text-zinc-950 dark:text-zinc-50" 31 - > 32 - Learning 33 - </a>{" "} 34 - center. 35 - </p> 36 - </div> 37 - <div className="flex flex-col gap-4 text-base font-medium sm:flex-row"> 38 - <a 39 - className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]" 40 - href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" 41 - target="_blank" 42 - rel="noopener noreferrer" 43 - > 44 - <Image 45 - className="dark:invert" 46 - src="/vercel.svg" 47 - alt="Vercel logomark" 48 - width={16} 49 - height={16} 50 - /> 51 - Deploy Now 52 - </a> 53 - <a 54 - className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]" 55 - href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" 56 - target="_blank" 57 - rel="noopener noreferrer" 58 - > 59 - Documentation 60 - </a> 61 - </div> 62 - </main> 63 - </div> 64 - ); 65 - } 22 + if (response.ok && data.url) { 23 + window.location.href = data.url; 24 + } else { 25 + console.error("Login initiation failed:", data.error); 26 + } 27 + } catch (error) { 28 + console.error("Error initiating AT Protocol OAuth:", error); 29 + } 30 + }; 31 + 32 + return ( 33 + <div className="bg-indigo-900 flex h-screen items-center justify-center w-screen"> 34 + <div className=" flex flex-col gap-2 h-screen items-center justify-center w-full"> 35 + <div className="border flex gap-2 h-12 items-center p-2 rounded-lg w-3/4 md:w-9/16 lg:w-3/8"> 36 + <AtSign /> 37 + <input 38 + className="flex-1 outline-none" 39 + onChange={(e) => setAtProtoIdentifier(e.target.value)} 40 + onKeyDown={(event) => { 41 + if (event.key === "Enter") { 42 + loginToATProto(); 43 + } 44 + }} 45 + placeholder="Enter your ATProto handle or DID" 46 + value={atProtoIdentifier} 47 + /> 48 + </div> 49 + <button 50 + className="bg-white/90 active:bg-white hover:cursor-pointer h-12 px-2 rounded-lg text-xl text-indigo-900 w-1/4 lg:w-1/8" 51 + onClick={loginToATProto} 52 + > 53 + Log In 54 + </button> 55 + </div> 56 + </div> 57 + ); 58 + }; 59 + 60 + export default Home;
+54
lib/atprotoAuth.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: "ebb&follow", 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 + });
+464
package-lock.json
··· 8 8 "name": "angel", 9 9 "version": "0.1.0", 10 10 "dependencies": { 11 + "@atproto/api": "^0.18.3", 12 + "@atproto/oauth-client-node": "^0.3.11", 13 + "lucide-react": "^0.555.0", 11 14 "next": "16.0.6", 12 15 "react": "19.2.0", 13 16 "react-dom": "19.2.0" ··· 36 39 "url": "https://github.com/sponsors/sindresorhus" 37 40 } 38 41 }, 42 + "node_modules/@atproto-labs/did-resolver": { 43 + "version": "0.2.3", 44 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.3.tgz", 45 + "integrity": "sha512-NAAVMthPD98eEYprpU7+MccDxFzwa3OhecweitL0/4ntjjcmqy1ytTl+ynOcTEC69H/ENjlpo36BB+b2obDcvA==", 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.2", 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.3", 91 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.3.3.tgz", 92 + "integrity": "sha512-tBPRiDNWigk39TEQgupnez2st/midvtSuwg+UMEhnEyYItH7imXgZWcHuDkO4HmT9Lxz7br5zLUNaqIfFuTf5w==", 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.2", 98 + "zod": "^3.23.8" 99 + } 100 + }, 101 + "node_modules/@atproto-labs/handle-resolver-node": { 102 + "version": "0.1.22", 103 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver-node/-/handle-resolver-node-0.1.22.tgz", 104 + "integrity": "sha512-Qu2Jc8CqwY8PeToWh1DBeExC6acHHamFXVsrhxB1+7jDRpRDNXhXfQLkblIkdL4y+F7c3KnE0u9VAxLWN9tFBw==", 105 + "license": "MIT", 106 + "dependencies": { 107 + "@atproto-labs/fetch-node": "0.2.0", 108 + "@atproto-labs/handle-resolver": "0.3.3", 109 + "@atproto/did": "0.2.2" 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.3", 126 + "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.3.3.tgz", 127 + "integrity": "sha512-UfijnA+1JB97vKXRv1zqOacgP6BRGVr6zS7sZCt+i84TxhydPfFxbku1iH+BUUEv86VMys/C/anunFu3Hmg6wg==", 128 + "license": "MIT", 129 + "dependencies": { 130 + "@atproto-labs/did-resolver": "0.2.3", 131 + "@atproto-labs/handle-resolver": "0.3.3" 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 + }, 162 + "node_modules/@atproto/api": { 163 + "version": "0.18.3", 164 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.18.3.tgz", 165 + "integrity": "sha512-CBqyZfkcKYsr348KP4CKb9plMlZ5A96HwA/DnYscPBl6fvMZkAezAjniZX+xUILASHQJg5c+NaNw9xP8ZuyyDQ==", 166 + "license": "MIT", 167 + "dependencies": { 168 + "@atproto/common-web": "^0.4.5", 169 + "@atproto/lexicon": "^0.5.2", 170 + "@atproto/syntax": "^0.4.1", 171 + "@atproto/xrpc": "^0.7.6", 172 + "await-lock": "^2.2.2", 173 + "multiformats": "^9.9.0", 174 + "tlds": "^1.234.0", 175 + "zod": "^3.23.8" 176 + } 177 + }, 178 + "node_modules/@atproto/api/node_modules/zod": { 179 + "version": "3.25.76", 180 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 181 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 182 + "license": "MIT", 183 + "funding": { 184 + "url": "https://github.com/sponsors/colinhacks" 185 + } 186 + }, 187 + "node_modules/@atproto/common-web": { 188 + "version": "0.4.5", 189 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.5.tgz", 190 + "integrity": "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA==", 191 + "license": "MIT", 192 + "dependencies": { 193 + "@atproto/lex-data": "0.0.1", 194 + "@atproto/lex-json": "0.0.1", 195 + "zod": "^3.23.8" 196 + } 197 + }, 198 + "node_modules/@atproto/common-web/node_modules/zod": { 199 + "version": "3.25.76", 200 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 201 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 202 + "license": "MIT", 203 + "funding": { 204 + "url": "https://github.com/sponsors/colinhacks" 205 + } 206 + }, 207 + "node_modules/@atproto/did": { 208 + "version": "0.2.2", 209 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.2.tgz", 210 + "integrity": "sha512-IfOcEIpGp3owcaWA/e8VSIjdi/ocz5JbT3Ghg9jgFgnxLlwE8ndrihiR4xbRdcdPLza9YQjrPQhfRYdCu7s4Yw==", 211 + "license": "MIT", 212 + "dependencies": { 213 + "zod": "^3.23.8" 214 + } 215 + }, 216 + "node_modules/@atproto/did/node_modules/zod": { 217 + "version": "3.25.76", 218 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 219 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 220 + "license": "MIT", 221 + "funding": { 222 + "url": "https://github.com/sponsors/colinhacks" 223 + } 224 + }, 225 + "node_modules/@atproto/jwk": { 226 + "version": "0.6.0", 227 + "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.6.0.tgz", 228 + "integrity": "sha512-bDoJPvt7TrQVi/rBfBrSSpGykhtIriKxeYCYQTiPRKFfyRhbgpElF0wPXADjIswnbzZdOwbY63az4E/CFVT3Tw==", 229 + "license": "MIT", 230 + "dependencies": { 231 + "multiformats": "^9.9.0", 232 + "zod": "^3.23.8" 233 + } 234 + }, 235 + "node_modules/@atproto/jwk-jose": { 236 + "version": "0.1.11", 237 + "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.11.tgz", 238 + "integrity": "sha512-i4Fnr2sTBYmMmHXl7NJh8GrCH+tDQEVWrcDMDnV5DjJfkgT17wIqvojIw9SNbSL4Uf0OtfEv6AgG0A+mgh8b5Q==", 239 + "license": "MIT", 240 + "dependencies": { 241 + "@atproto/jwk": "0.6.0", 242 + "jose": "^5.2.0" 243 + } 244 + }, 245 + "node_modules/@atproto/jwk-webcrypto": { 246 + "version": "0.2.0", 247 + "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.2.0.tgz", 248 + "integrity": "sha512-UmgRrrEAkWvxwhlwe30UmDOdTEFidlIzBC7C3cCbeJMcBN1x8B3KH+crXrsTqfWQBG58mXgt8wgSK3Kxs2LhFg==", 249 + "license": "MIT", 250 + "dependencies": { 251 + "@atproto/jwk": "0.6.0", 252 + "@atproto/jwk-jose": "0.1.11", 253 + "zod": "^3.23.8" 254 + } 255 + }, 256 + "node_modules/@atproto/jwk-webcrypto/node_modules/zod": { 257 + "version": "3.25.76", 258 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 259 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 260 + "license": "MIT", 261 + "funding": { 262 + "url": "https://github.com/sponsors/colinhacks" 263 + } 264 + }, 265 + "node_modules/@atproto/jwk/node_modules/zod": { 266 + "version": "3.25.76", 267 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 268 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 269 + "license": "MIT", 270 + "funding": { 271 + "url": "https://github.com/sponsors/colinhacks" 272 + } 273 + }, 274 + "node_modules/@atproto/lex-data": { 275 + "version": "0.0.1", 276 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.1.tgz", 277 + "integrity": "sha512-DrS/8cQcQs3s5t9ELAFNtyDZ8/PdiCx47ALtFEP2GnX2uCBHZRkqWG7xmu6ehjc787nsFzZBvlnz3T/gov5fGA==", 278 + "license": "MIT", 279 + "dependencies": { 280 + "@atproto/syntax": "0.4.1", 281 + "multiformats": "^9.9.0", 282 + "tslib": "^2.8.1", 283 + "uint8arrays": "3.0.0", 284 + "unicode-segmenter": "^0.14.0" 285 + } 286 + }, 287 + "node_modules/@atproto/lex-json": { 288 + "version": "0.0.1", 289 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.1.tgz", 290 + "integrity": "sha512-ivcF7+pDRuD/P97IEKQ/9TruunXj0w58Khvwk3M6psaI5eZT6LRsRZ4cWcKaXiFX4SHnjy+x43g0f7pPtIsERg==", 291 + "license": "MIT", 292 + "dependencies": { 293 + "@atproto/lex-data": "0.0.1", 294 + "tslib": "^2.8.1" 295 + } 296 + }, 297 + "node_modules/@atproto/lexicon": { 298 + "version": "0.5.2", 299 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.2.tgz", 300 + "integrity": "sha512-lRmJgMA8f5j7VB5Iu5cp188ald5FuI4FlmZ7nn6EBrk1dgOstWVrI5Ft6K3z2vjyLZRG6nzknlsw+tDP63p7bQ==", 301 + "license": "MIT", 302 + "dependencies": { 303 + "@atproto/common-web": "^0.4.4", 304 + "@atproto/syntax": "^0.4.1", 305 + "iso-datestring-validator": "^2.2.2", 306 + "multiformats": "^9.9.0", 307 + "zod": "^3.23.8" 308 + } 309 + }, 310 + "node_modules/@atproto/lexicon/node_modules/zod": { 311 + "version": "3.25.76", 312 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 313 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 314 + "license": "MIT", 315 + "funding": { 316 + "url": "https://github.com/sponsors/colinhacks" 317 + } 318 + }, 319 + "node_modules/@atproto/oauth-client": { 320 + "version": "0.5.9", 321 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.5.9.tgz", 322 + "integrity": "sha512-23Z77A9bQFJYWAn5qJRwfzYeLEzcAx77G6HK8mlbIOYuQbmv6YMmQBm4BntAntJmEukOG69IrdnGIHKvsC4wdg==", 323 + "license": "MIT", 324 + "dependencies": { 325 + "@atproto-labs/did-resolver": "0.2.3", 326 + "@atproto-labs/fetch": "0.2.3", 327 + "@atproto-labs/handle-resolver": "0.3.3", 328 + "@atproto-labs/identity-resolver": "0.3.3", 329 + "@atproto-labs/simple-store": "0.3.0", 330 + "@atproto-labs/simple-store-memory": "0.1.4", 331 + "@atproto/did": "0.2.2", 332 + "@atproto/jwk": "0.6.0", 333 + "@atproto/oauth-types": "0.5.1", 334 + "@atproto/xrpc": "0.7.6", 335 + "core-js": "^3", 336 + "multiformats": "^9.9.0", 337 + "zod": "^3.23.8" 338 + } 339 + }, 340 + "node_modules/@atproto/oauth-client-node": { 341 + "version": "0.3.11", 342 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client-node/-/oauth-client-node-0.3.11.tgz", 343 + "integrity": "sha512-qcA3rr4gO9+THrwDWxAp0N249fiDhHpU1paOjq7eQUvpP0yAx0zaMjBnXCIY+ghU0ahX9dO+fS9/+TGNqZR6DA==", 344 + "license": "MIT", 345 + "dependencies": { 346 + "@atproto-labs/did-resolver": "0.2.3", 347 + "@atproto-labs/handle-resolver-node": "0.1.22", 348 + "@atproto-labs/simple-store": "0.3.0", 349 + "@atproto/did": "0.2.2", 350 + "@atproto/jwk": "0.6.0", 351 + "@atproto/jwk-jose": "0.1.11", 352 + "@atproto/jwk-webcrypto": "0.2.0", 353 + "@atproto/oauth-client": "0.5.9", 354 + "@atproto/oauth-types": "0.5.1" 355 + }, 356 + "engines": { 357 + "node": ">=18.7.0" 358 + } 359 + }, 360 + "node_modules/@atproto/oauth-client/node_modules/zod": { 361 + "version": "3.25.76", 362 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 363 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 364 + "license": "MIT", 365 + "funding": { 366 + "url": "https://github.com/sponsors/colinhacks" 367 + } 368 + }, 369 + "node_modules/@atproto/oauth-types": { 370 + "version": "0.5.1", 371 + "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.5.1.tgz", 372 + "integrity": "sha512-x651IN8Ul0LOB7fZXXggcBbc66/7vsTvh0Zqg6vplSbVjozJR6lzKo8i42QKrstXO8+2kLy49qoIHXbTHHOI1Q==", 373 + "license": "MIT", 374 + "dependencies": { 375 + "@atproto/did": "0.2.2", 376 + "@atproto/jwk": "0.6.0", 377 + "zod": "^3.23.8" 378 + } 379 + }, 380 + "node_modules/@atproto/oauth-types/node_modules/zod": { 381 + "version": "3.25.76", 382 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 383 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 384 + "license": "MIT", 385 + "funding": { 386 + "url": "https://github.com/sponsors/colinhacks" 387 + } 388 + }, 389 + "node_modules/@atproto/syntax": { 390 + "version": "0.4.1", 391 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 392 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 393 + "license": "MIT" 394 + }, 395 + "node_modules/@atproto/xrpc": { 396 + "version": "0.7.6", 397 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.6.tgz", 398 + "integrity": "sha512-RvCf4j0JnKYWuz3QzsYCntJi3VuiAAybQsMIUw2wLWcHhchO9F7UaBZINLL2z0qc/cYWPv5NSwcVydMseoCZLA==", 399 + "license": "MIT", 400 + "dependencies": { 401 + "@atproto/lexicon": "^0.5.2", 402 + "zod": "^3.23.8" 403 + } 404 + }, 405 + "node_modules/@atproto/xrpc/node_modules/zod": { 406 + "version": "3.25.76", 407 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 408 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 409 + "license": "MIT", 410 + "funding": { 411 + "url": "https://github.com/sponsors/colinhacks" 412 + } 413 + }, 39 414 "node_modules/@babel/code-frame": { 40 415 "version": "7.27.1", 41 416 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", ··· 2380 2755 "url": "https://github.com/sponsors/ljharb" 2381 2756 } 2382 2757 }, 2758 + "node_modules/await-lock": { 2759 + "version": "2.2.2", 2760 + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", 2761 + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", 2762 + "license": "MIT" 2763 + }, 2383 2764 "node_modules/axe-core": { 2384 2765 "version": "4.11.0", 2385 2766 "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", ··· 2611 2992 "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", 2612 2993 "dev": true, 2613 2994 "license": "MIT" 2995 + }, 2996 + "node_modules/core-js": { 2997 + "version": "3.47.0", 2998 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", 2999 + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", 3000 + "hasInstallScript": true, 3001 + "license": "MIT", 3002 + "funding": { 3003 + "type": "opencollective", 3004 + "url": "https://opencollective.com/core-js" 3005 + } 2614 3006 }, 2615 3007 "node_modules/cross-spawn": { 2616 3008 "version": "7.0.6", ··· 3951 4343 "node": ">= 0.4" 3952 4344 } 3953 4345 }, 4346 + "node_modules/ipaddr.js": { 4347 + "version": "2.3.0", 4348 + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", 4349 + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", 4350 + "license": "MIT", 4351 + "engines": { 4352 + "node": ">= 10" 4353 + } 4354 + }, 3954 4355 "node_modules/is-array-buffer": { 3955 4356 "version": "3.0.5", 3956 4357 "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", ··· 4380 4781 "dev": true, 4381 4782 "license": "ISC" 4382 4783 }, 4784 + "node_modules/iso-datestring-validator": { 4785 + "version": "2.2.2", 4786 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 4787 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 4788 + "license": "MIT" 4789 + }, 4383 4790 "node_modules/iterator.prototype": { 4384 4791 "version": "1.1.5", 4385 4792 "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", ··· 4406 4813 "license": "MIT", 4407 4814 "bin": { 4408 4815 "jiti": "lib/jiti-cli.mjs" 4816 + } 4817 + }, 4818 + "node_modules/jose": { 4819 + "version": "5.10.0", 4820 + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", 4821 + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", 4822 + "license": "MIT", 4823 + "funding": { 4824 + "url": "https://github.com/sponsors/panva" 4409 4825 } 4410 4826 }, 4411 4827 "node_modules/js-tokens": { ··· 4842 5258 "yallist": "^3.0.2" 4843 5259 } 4844 5260 }, 5261 + "node_modules/lucide-react": { 5262 + "version": "0.555.0", 5263 + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.555.0.tgz", 5264 + "integrity": "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA==", 5265 + "license": "ISC", 5266 + "peerDependencies": { 5267 + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" 5268 + } 5269 + }, 4845 5270 "node_modules/magic-string": { 4846 5271 "version": "0.30.21", 4847 5272 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", ··· 4915 5340 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 4916 5341 "dev": true, 4917 5342 "license": "MIT" 5343 + }, 5344 + "node_modules/multiformats": { 5345 + "version": "9.9.0", 5346 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 5347 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 5348 + "license": "(Apache-2.0 AND MIT)" 4918 5349 }, 4919 5350 "node_modules/nanoid": { 4920 5351 "version": "3.3.11", ··· 6095 6526 "url": "https://github.com/sponsors/jonschlinkert" 6096 6527 } 6097 6528 }, 6529 + "node_modules/tlds": { 6530 + "version": "1.261.0", 6531 + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz", 6532 + "integrity": "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==", 6533 + "license": "MIT", 6534 + "bin": { 6535 + "tlds": "bin.js" 6536 + } 6537 + }, 6098 6538 "node_modules/to-regex-range": { 6099 6539 "version": "5.0.1", 6100 6540 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", ··· 6282 6722 "typescript": ">=4.8.4 <6.0.0" 6283 6723 } 6284 6724 }, 6725 + "node_modules/uint8arrays": { 6726 + "version": "3.0.0", 6727 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 6728 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 6729 + "license": "MIT", 6730 + "dependencies": { 6731 + "multiformats": "^9.4.2" 6732 + } 6733 + }, 6285 6734 "node_modules/unbox-primitive": { 6286 6735 "version": "1.1.0", 6287 6736 "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", ··· 6301 6750 "url": "https://github.com/sponsors/ljharb" 6302 6751 } 6303 6752 }, 6753 + "node_modules/undici": { 6754 + "version": "6.22.0", 6755 + "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", 6756 + "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", 6757 + "license": "MIT", 6758 + "engines": { 6759 + "node": ">=18.17" 6760 + } 6761 + }, 6304 6762 "node_modules/undici-types": { 6305 6763 "version": "6.21.0", 6306 6764 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 6307 6765 "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 6308 6766 "dev": true, 6767 + "license": "MIT" 6768 + }, 6769 + "node_modules/unicode-segmenter": { 6770 + "version": "0.14.0", 6771 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.0.tgz", 6772 + "integrity": "sha512-AH4lhPCJANUnSLEKnM4byboctePJzltF4xj8b+NbNiYeAkAXGh7px2K/4NANFp7dnr6+zB3e6HLu8Jj8SKyvYg==", 6309 6773 "license": "MIT" 6310 6774 }, 6311 6775 "node_modules/unrs-resolver": {
+3
package.json
··· 9 9 "lint": "eslint" 10 10 }, 11 11 "dependencies": { 12 + "@atproto/api": "^0.18.3", 13 + "@atproto/oauth-client-node": "^0.3.11", 14 + "lucide-react": "^0.555.0", 12 15 "next": "16.0.6", 13 16 "react": "19.2.0", 14 17 "react-dom": "19.2.0"