atproto explorer

new auth logic

handle.invalid 895e336f 4e4b7dfc

verified
+73 -28
+50 -23
src/components/account.tsx
··· 1 1 import { Client, CredentialManager } from "@atcute/client"; 2 2 import { Did } from "@atcute/lexicons"; 3 - import { deleteStoredSession, getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 3 + import { 4 + createAuthorizationUrl, 5 + deleteStoredSession, 6 + getSession, 7 + OAuthUserAgent, 8 + resolveFromIdentity, 9 + } from "@atcute/oauth-browser-client"; 4 10 import { A } from "@solidjs/router"; 5 11 import { createSignal, For, onMount, Show } from "solid-js"; 6 - import { createStore } from "solid-js/store"; 12 + import { createStore, produce } from "solid-js/store"; 7 13 import { resolveDidDoc } from "../utils/api.js"; 8 - import { agent, Login, retrieveSession, setAgent } from "./login.jsx"; 14 + import { agent, Login, retrieveSession, Sessions, setAgent } from "./login.jsx"; 9 15 import { Modal } from "./modal.jsx"; 10 16 11 17 const AccountManager = () => { 12 18 const [openManager, setOpenManager] = createSignal(false); 13 - const [sessions, setSessions] = createStore<Record<string, string | undefined>>(); 19 + const [sessions, setSessions] = createStore<Sessions>(); 14 20 const [avatars, setAvatars] = createStore<Record<Did, string>>(); 15 21 16 22 onMount(async () => { 17 - await retrieveSession(); 23 + try { 24 + await retrieveSession(); 25 + } catch {} 18 26 19 - const storedSessions = localStorage.getItem("atcute-oauth:sessions"); 20 - if (storedSessions) { 21 - const sessionDids = Object.keys(JSON.parse(storedSessions)) as Did[]; 22 - sessionDids.forEach((did) => setSessions(did, "")); 27 + const localSessions = localStorage.getItem("sessions"); 28 + if (localSessions) { 29 + const storedSessions: Sessions = JSON.parse(localSessions); 30 + const sessionDids = Object.keys(storedSessions) as Did[]; 23 31 sessionDids.forEach(async (did) => { 24 32 const doc = await resolveDidDoc(did); 25 33 doc.alsoKnownAs?.forEach((alias) => { 26 34 if (alias.startsWith("at://")) { 27 - setSessions(did, alias.replace("at://", "")); 35 + setSessions(did, { 36 + signedIn: storedSessions[did].signedIn, 37 + handle: alias.replace("at://", ""), 38 + }); 28 39 return; 29 40 } 30 41 }); 31 42 }); 32 43 sessionDids.forEach(async (did) => { 33 - try { 34 - await getSession(did, { allowStale: true }); 35 - const avatar = await getAvatar(did); 36 - if (avatar) setAvatars(did, avatar); 37 - } catch { 38 - deleteStoredSession(did); 39 - setSessions(did, undefined); 40 - } 44 + const avatar = await getAvatar(did); 45 + if (avatar) setAvatars(did, avatar); 41 46 }); 42 47 } 43 48 }); 44 49 45 50 const resumeSession = async (did: Did) => { 46 - localStorage.setItem("lastSignedIn", did); 47 - retrieveSession(); 51 + try { 52 + localStorage.setItem("lastSignedIn", did); 53 + await retrieveSession(); 54 + } catch { 55 + const resolved = await resolveFromIdentity(did); 56 + const authUrl = await createAuthorizationUrl({ 57 + scope: import.meta.env.VITE_OAUTH_SCOPE, 58 + ...resolved, 59 + }); 60 + 61 + await new Promise((resolve) => setTimeout(resolve, 250)); 62 + 63 + location.assign(authUrl); 64 + } 48 65 }; 49 66 50 67 const removeSession = async (did: Did) => { ··· 56 73 } catch { 57 74 deleteStoredSession(did); 58 75 } 59 - setSessions(did, undefined); 76 + setSessions( 77 + produce((accs) => { 78 + delete accs[did]; 79 + }), 80 + ); 81 + localStorage.setItem("sessions", JSON.stringify(sessions)); 60 82 if (currentSession === did) setAgent(undefined); 61 83 }; 62 84 ··· 93 115 class="size-6 rounded-full" 94 116 /> 95 117 </Show> 96 - <span class="truncate">{sessions[did]?.length ? sessions[did] : did}</span> 118 + <span class="truncate"> 119 + {sessions[did]?.handle ? sessions[did].handle : did} 120 + </span> 97 121 </span> 98 - <Show when={did === agent()?.sub}> 122 + <Show when={did === agent()?.sub && sessions[did].signedIn}> 99 123 <span class="iconify lucide--check shrink-0 text-green-500 dark:text-green-400"></span> 124 + </Show> 125 + <Show when={!sessions[did].signedIn}> 126 + <span class="iconify lucide--circle-alert shrink-0 text-red-500 dark:text-red-400"></span> 100 127 </Show> 101 128 </button> 102 129 <A
+23 -5
src/components/login.tsx
··· 1 + import { Client } from "@atcute/client"; 1 2 import { Did } from "@atcute/lexicons"; 2 3 import { isHandle } from "@atcute/lexicons/syntax"; 3 4 import { 4 5 configureOAuth, 5 6 createAuthorizationUrl, 6 - deleteStoredSession, 7 7 finalizeAuthorization, 8 8 getSession, 9 9 OAuthUserAgent, ··· 21 21 }); 22 22 23 23 export const [agent, setAgent] = createSignal<OAuthUserAgent | undefined>(); 24 + 25 + type Account = { 26 + signedIn: boolean; 27 + handle?: string; 28 + }; 29 + 30 + export type Sessions = Record<string, Account>; 24 31 25 32 const Login = () => { 26 33 const [notice, setNotice] = createSignal(""); ··· 100 107 const did = session.info.sub; 101 108 102 109 localStorage.setItem("lastSignedIn", did); 110 + 111 + const sessions = localStorage.getItem("sessions"); 112 + const newSessions: Sessions = sessions ? JSON.parse(sessions) : { [did]: {} }; 113 + newSessions[did] = { signedIn: true }; 114 + localStorage.setItem("sessions", JSON.stringify(newSessions)); 103 115 return session; 104 116 } else { 105 117 const lastSignedIn = localStorage.getItem("lastSignedIn"); 106 118 107 119 if (lastSignedIn) { 108 120 try { 109 - return await getSession(lastSignedIn as Did); 121 + const session = await getSession(lastSignedIn as Did); 122 + const rpc = new Client({ handler: new OAuthUserAgent(session) }); 123 + const res = await rpc.get("com.atproto.server.getSession"); 124 + if (!res.ok) throw res.data.error; 125 + return session; 110 126 } catch (err) { 111 - deleteStoredSession(lastSignedIn as Did); 112 - localStorage.removeItem("lastSignedIn"); 127 + const sessions = localStorage.getItem("sessions"); 128 + const newSessions: Sessions = sessions ? JSON.parse(sessions) : {}; 129 + newSessions[lastSignedIn].signedIn = false; 130 + localStorage.setItem("sessions", JSON.stringify(newSessions)); 113 131 throw err; 114 132 } 115 133 } 116 134 } 117 135 }; 118 136 119 - const session = await init().catch(() => {}); 137 + const session = await init(); 120 138 121 139 if (session) setAgent(new OAuthUserAgent(session)); 122 140 };