A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
98
fork

Configure Feed

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

Add prompt support for login and remove client-side OAuth

Support a "prompt" query param in /login and include it when
redirecting from the web client. Server uses a fallback handle when
prompt is present and forwards the prompt to the OAuth client. Remove
client-side identity resolver and oauth-browser-client usage and tidy
related dependencies in package.json and bun.lock.

+23 -106
+10 -5
apps/api/src/bsky/app.ts
··· 22 22 23 23 app.get("/login", async (c) => { 24 24 requestCounter.add(1, { method: "GET", route: "/login" }); 25 - const { handle, cli } = c.req.query(); 26 - if (typeof handle !== "string" || !isValidHandle(handle)) { 25 + const { handle, cli, prompt } = c.req.query(); 26 + if ((typeof handle !== "string" || !isValidHandle(handle)) && !prompt) { 27 27 c.status(400); 28 28 return c.text("Invalid handle"); 29 29 } 30 30 try { 31 - const url = await ctx.oauthClient.authorize(handle, { 32 - scope: "atproto transition:generic", 33 - }); 31 + const url = await ctx.oauthClient.authorize( 32 + prompt ? "tsiry.selfhosted.social" : handle, 33 + { 34 + scope: "atproto transition:generic", 35 + // @ts-ignore: allow custom prompt param 36 + prompt, 37 + }, 38 + ); 34 39 if (cli) { 35 40 ctx.kv.set(`cli:${handle}`, "1"); 36 41 }
-2
apps/web/package.json
··· 16 16 "format": "biome format src" 17 17 }, 18 18 "dependencies": { 19 - "@atcute/identity-resolver": "^1.2.2", 20 - "@atcute/oauth-browser-client": "^2.0.3", 21 19 "@emotion/react": "^11.14.0", 22 20 "@emotion/styled": "^11.14.0", 23 21 "@hookform/resolvers": "^4.0.0",
+12 -66
apps/web/src/layouts/Main.tsx
··· 18 18 import SpotifyLogin from "./SpotifyLogin"; 19 19 import { IconEye, IconEyeOff, IconLock } from "@tabler/icons-react"; 20 20 import { consola } from "consola"; 21 - import { 22 - CompositeDidDocumentResolver, 23 - CompositeHandleResolver, 24 - DohJsonHandleResolver, 25 - LocalActorResolver, 26 - PlcDidDocumentResolver, 27 - WebDidDocumentResolver, 28 - WellKnownHandleResolver, 29 - } from "@atcute/identity-resolver"; 30 - import { 31 - configureOAuth, 32 - // createAuthorizationUrl, 33 - } from "@atcute/oauth-browser-client"; 34 - 35 - const DOH_RESOLVER = "https://mozilla.cloudflare-dns.com/dns-query"; 36 - const PUBLIC_URL: string = 37 - import.meta.env.VITE_PUBLIC_URL || "http://localhost:8000"; 38 - const REDIRECT_URI = `${PUBLIC_URL}/oauth/callback`; 39 - const scope = "atproto transition:generic"; 40 21 41 22 const Container = styled.div` 42 23 display: flex; ··· 88 69 const [passwordLogin, setPasswordLogin] = useState(false); 89 70 90 71 useEffect(() => { 91 - const clientId = PUBLIC_URL.startsWith("http://localhost") 92 - ? `http://localhost` + 93 - `?redirect_uri=${encodeURIComponent(REDIRECT_URI)}` + 94 - `&scope=${encodeURIComponent(scope)}` 95 - : `${PUBLIC_URL}/oauth-client-metadata.json`; 96 - 97 - const handleResolver = new CompositeHandleResolver({ 98 - methods: { 99 - dns: new DohJsonHandleResolver({ dohUrl: DOH_RESOLVER }), 100 - http: new WellKnownHandleResolver(), 101 - }, 102 - }); 103 - 104 - configureOAuth({ 105 - metadata: { 106 - client_id: clientId, 107 - redirect_uri: REDIRECT_URI, 108 - }, 109 - identityResolver: new LocalActorResolver({ 110 - handleResolver: handleResolver, 111 - didDocumentResolver: new CompositeDidDocumentResolver({ 112 - methods: { 113 - plc: new PlcDidDocumentResolver(), 114 - web: new WebDidDocumentResolver(), 115 - }, 116 - }), 117 - }), 118 - }); 119 - }, []); 120 - 121 - useEffect(() => { 122 72 if (did && did !== "null") { 123 73 localStorage.setItem("did", did); 124 74 ··· 158 108 159 109 useProfile(token || localStorage.getItem("token")); 160 110 161 - const onLogin = async () => { 111 + const onLogin = async (prompt?: string) => { 162 112 if (!handle.trim()) { 163 113 return; 164 114 } ··· 205 155 } 206 156 207 157 if (API_URL.includes("localhost")) { 208 - window.location.href = `${API_URL}/login?handle=${handle}`; 158 + window.location.href = prompt 159 + ? `${API_URL}/login?handle=${handle}&prompt=${prompt}` 160 + : `${API_URL}/login?handle=${handle}`; 209 161 return; 210 162 } 211 163 212 - window.location.href = `https://rocksky.pages.dev/loading?handle=${handle}`; 164 + window.location.href = prompt 165 + ? `https://rocksky.pages.dev/loading?handle=${handle}&prompt=${prompt}` 166 + : `https://rocksky.pages.dev/loading?handle=${handle}`; 213 167 }; 214 168 215 - /*const onCreateAccount = async () => { 216 - const authUrl = await createAuthorizationUrl({ 217 - target: { type: "pds", serviceUrl: "https://selfhosted.social" }, 218 - // @ts-expect-error - new stuff 219 - prompt: "create", 220 - scope, 221 - }); 222 - window.location.assign(authUrl); 223 - };*/ 169 + const onCreateAccount = async () => { 170 + await onLogin("create_account"); 171 + }; 224 172 225 173 return ( 226 174 <Container ··· 362 310 )} 363 311 </div> 364 312 <Button 365 - onClick={onLogin} 313 + onClick={() => onLogin()} 366 314 overrides={{ 367 315 BaseButton: { 368 316 style: { ··· 385 333 </LabelMedium> 386 334 <div className="text-center text-[var(--color-text-muted)] "> 387 335 You can create one at{" "} 388 - {/* 389 - <span 336 + <span 390 337 onClick={onCreateAccount} 391 338 className="no-underline cursor-pointer !text-[var(--color-primary)]" 392 339 > 393 340 selfhosted.social 394 341 </span> 395 342 ,{" "} 396 - */} 397 343 <a 398 344 href="https://bsky.app" 399 345 className="no-underline cursor-pointer !text-[var(--color-primary)]"
+1 -33
bun.lock
··· 196 196 "name": "@rocksky/web", 197 197 "version": "0.0.0", 198 198 "dependencies": { 199 - "@atcute/identity-resolver": "^1.2.2", 200 - "@atcute/oauth-browser-client": "^2.0.3", 201 199 "@emotion/react": "^11.14.0", 202 200 "@emotion/styled": "^11.14.0", 203 201 "@hookform/resolvers": "^4.0.0", ··· 374 372 375 373 "@asteasolutions/zod-to-openapi": ["@asteasolutions/zod-to-openapi@7.3.4", "", { "dependencies": { "openapi3-ts": "^4.1.2" }, "peerDependencies": { "zod": "^3.20.2" } }, "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA=="], 376 374 377 - "@atcute/client": ["@atcute/client@4.2.1", "", { "dependencies": { "@atcute/identity": "^1.1.3", "@atcute/lexicons": "^1.2.6" } }, "sha512-ZBFM2pW075JtgGFu5g7HHZBecrClhlcNH8GVP9Zz1aViWR+cjjBsTpeE63rJs+FCOHFYlirUyo5L8SGZ4kMINw=="], 378 - 379 - "@atcute/identity": ["@atcute/identity@1.1.3", "", { "dependencies": { "@atcute/lexicons": "^1.2.4", "@badrap/valita": "^0.4.6" } }, "sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng=="], 380 - 381 - "@atcute/identity-resolver": ["@atcute/identity-resolver@1.2.2", "", { "dependencies": { "@atcute/lexicons": "^1.2.6", "@atcute/util-fetch": "^1.0.5", "@badrap/valita": "^0.4.6" }, "peerDependencies": { "@atcute/identity": "^1.0.0" } }, "sha512-eUh/UH4bFvuXS0X7epYCeJC/kj4rbBXfSRumLEH4smMVwNOgTo7cL/0Srty+P/qVPoZEyXdfEbS0PHJyzoXmHw=="], 382 - 383 - "@atcute/lexicons": ["@atcute/lexicons@1.2.6", "", { "dependencies": { "@atcute/uint8array": "^1.0.6", "@atcute/util-text": "^0.0.1", "@standard-schema/spec": "^1.1.0", "esm-env": "^1.2.2" } }, "sha512-s76UQd8D+XmHIzrjD9CJ9SOOeeLPHc+sMmcj7UFakAW/dDFXc579fcRdRfuUKvXBL5v1Gs2VgDdlh/IvvQZAwA=="], 384 - 385 - "@atcute/multibase": ["@atcute/multibase@1.1.6", "", { "dependencies": { "@atcute/uint8array": "^1.0.5" } }, "sha512-HBxuCgYLKPPxETV0Rot4VP9e24vKl8JdzGCZOVsDaOXJgbRZoRIF67Lp0H/OgnJeH/Xpva8Z5ReoTNJE5dn3kg=="], 386 - 387 - "@atcute/oauth-browser-client": ["@atcute/oauth-browser-client@2.0.3", "", { "dependencies": { "@atcute/client": "^4.1.1", "@atcute/identity-resolver": "^1.2.0", "@atcute/lexicons": "^1.2.5", "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.6", "nanoid": "^5.1.6" } }, "sha512-rzUjwhjE4LRRKdQnCFQag/zXRZMEAB1hhBoLfnoQuHwWbmDUCL7fzwC3jRhDPp3om8XaYNDj8a/iqRip0wRqoQ=="], 388 - 389 - "@atcute/uint8array": ["@atcute/uint8array@1.0.6", "", {}, "sha512-ucfRBQc7BFT8n9eCyGOzDHEMKF/nZwhS2pPao4Xtab1ML3HdFYcX2DM1tadCzas85QTGxHe5urnUAAcNKGRi9A=="], 390 - 391 - "@atcute/util-fetch": ["@atcute/util-fetch@1.0.5", "", { "dependencies": { "@badrap/valita": "^0.4.6" } }, "sha512-qjHj01BGxjSjIFdPiAjSARnodJIIyKxnCMMEcXMESo9TAyND6XZQqrie5fia+LlYWVXdpsTds8uFQwc9jdKTig=="], 392 - 393 - "@atcute/util-text": ["@atcute/util-text@0.0.1", "", { "dependencies": { "unicode-segmenter": "^0.14.4" } }, "sha512-t1KZqvn0AYy+h2KcJyHnKF9aEqfRfMUmyY8j1ELtAEIgqN9CxINAjxnoRCJIFUlvWzb+oY3uElQL/Vyk3yss0g=="], 394 - 395 375 "@atproto-labs/did-resolver": ["@atproto-labs/did-resolver@0.1.11", "", { "dependencies": { "@atproto-labs/fetch": "0.2.2", "@atproto-labs/pipe": "0.1.0", "@atproto-labs/simple-store": "0.1.2", "@atproto-labs/simple-store-memory": "0.1.2", "@atproto/did": "0.1.5", "zod": "^3.23.8" } }, "sha512-qXNzIX2GPQnxT1gl35nv/8ErDdc4Fj/+RlJE7oyE7JGkFAPUyuY03TvKJ79SmWFsWE8wyTXEpLuphr9Da1Vhkw=="], 396 376 397 377 "@atproto-labs/fetch": ["@atproto-labs/fetch@0.2.2", "", { "dependencies": { "@atproto-labs/pipe": "0.1.0" } }, "sha512-QyafkedbFeVaN20DYUpnY2hcArYxjdThPXbYMqOSoZhcvkrUqaw4xDND4wZB5TBD9cq2yqe9V6mcw9P4XQKQuQ=="], ··· 505 485 "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], 506 486 507 487 "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], 508 - 509 - "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 510 488 511 489 "@biomejs/biome": ["@biomejs/biome@2.2.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.5", "@biomejs/cli-darwin-x64": "2.2.5", "@biomejs/cli-linux-arm64": "2.2.5", "@biomejs/cli-linux-arm64-musl": "2.2.5", "@biomejs/cli-linux-x64": "2.2.5", "@biomejs/cli-linux-x64-musl": "2.2.5", "@biomejs/cli-win32-arm64": "2.2.5", "@biomejs/cli-win32-x64": "2.2.5" }, "bin": { "biome": "bin/biome" } }, "sha512-zcIi+163Rc3HtyHbEO7CjeHq8DjQRs40HsGbW6vx2WI0tg8mYQOPouhvHSyEnCBAorfYNnKdR64/IxO7xQ5faw=="], 512 490 ··· 1846 1824 1847 1825 "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], 1848 1826 1849 - "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 1850 - 1851 1827 "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], 1852 1828 1853 1829 "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], ··· 2304 2280 2305 2281 "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], 2306 2282 2307 - "nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], 2283 + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 2308 2284 2309 2285 "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], 2310 2286 ··· 2858 2834 2859 2835 "unenv": ["unenv@2.0.0-rc.14", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.1", "ohash": "^2.0.10", "pathe": "^2.0.3", "ufo": "^1.5.4" } }, "sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q=="], 2860 2836 2861 - "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 2862 - 2863 2837 "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], 2864 2838 2865 2839 "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], ··· 2965 2939 "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="], 2966 2940 2967 2941 "zx": ["zx@8.8.4", "", { "bin": { "zx": "build/cli.js" } }, "sha512-44GcD+ZlM/v1OQtbwnSxLPcoE1ZEUICmR+RSbJZLAqfIixNLuMjLyh0DcS75OyfJ/sWYAwCWDmDvJ4hdnANAPQ=="], 2968 - 2969 - "@atcute/lexicons/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], 2970 2942 2971 2943 "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="], 2972 2944 ··· 3284 3256 3285 3257 "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 3286 3258 3287 - "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 3288 - 3289 3259 "posthog-js/fflate": ["fflate@0.4.8", "", {}, "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="], 3290 3260 3291 3261 "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], ··· 3693 3663 "send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 3694 3664 3695 3665 "styled-components/@emotion/is-prop-valid/@emotion/memoize": ["@emotion/memoize@0.8.1", "", {}, "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="], 3696 - 3697 - "styled-components/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 3698 3666 3699 3667 "table/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], 3700 3668