Something went wrong. Try again.
csr tangled client in solid-js forked from ptr.pet/untangled
Something went wrong. Try again.
4.5 kB · 186 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187import { OAuthUserAgent, configureOAuth, createAuthorizationUrl, deleteStoredSession, finalizeAuthorization, getSession, listStoredSessions,} from '@atcute/oauth-browser-client';import type { ActorIdentifier, Did } from '@atcute/lexicons/syntax';import type { Component, JSX } from 'solid-js';import { createContext, createSignal, onMount, useContext } from 'solid-js';
import { TANGLED_OAUTH_SCOPE, identityResolver } from './api';import { getErrorMessage } from './repo-utils';
const CURRENT_DID_KEY = 'untangled.currentDid';
configureOAuth({ metadata: { client_id: import.meta.env.VITE_OAUTH_CLIENT_ID, redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URI, }, identityResolver,});
export interface AuthContextValue { agent: () => OAuthUserAgent | null; currentDid: () => Did | null; error: () => string | null; isLoading: () => boolean; signIn: (identifier: string) => Promise<void>; completeSignIn: () => Promise<string>; signOut: () => Promise<void>; refresh: () => Promise<void>;}
const AuthContext = createContext<AuthContextValue>();
export const AuthProvider: Component<{ children: JSX.Element }> = (props) => { const [agent, setAgent] = createSignal<OAuthUserAgent | null>(null); const [currentDid, setCurrentDid] = createSignal<Did | null>(null); const [isLoading, setIsLoading] = createSignal(true); const [error, setError] = createSignal<string | null>(null);
const refresh = async () => { setIsLoading(true); setError(null);
const preferred = readStoredDid() ?? currentDid() ?? listStoredSessions()[0] ?? null;
if (!preferred) { setAgent(null); setCurrentDid(null); setIsLoading(false); return; }
try { const session = await getSession(preferred, { allowStale: true, }); const nextAgent = new OAuthUserAgent(session); setAgent(nextAgent); setCurrentDid(nextAgent.sub); localStorage.setItem(CURRENT_DID_KEY, nextAgent.sub); } catch (cause) { deleteStoredSession(preferred); localStorage.removeItem(CURRENT_DID_KEY); setAgent(null); setCurrentDid(null); setError(getErrorMessage(cause)); } finally { setIsLoading(false); } };
onMount(() => { void refresh(); });
const signIn = async (identifier: string) => { setError(null); const trimmed = identifier.trim(); if (!trimmed) { setError('Handle or DID required'); return; }
try { const url = await createAuthorizationUrl({ target: { type: 'account', identifier: trimmed as ActorIdentifier, }, scope: TANGLED_OAUTH_SCOPE, state: { returnTo: `${location.pathname}${location.search}${location.hash}`, }, });
window.location.assign(url.toString()); } catch (cause) { setError(getErrorMessage(cause)); throw cause; } };
const completeSignIn = async (): Promise<string> => { setIsLoading(true); setError(null);
try { const params = new URLSearchParams(location.hash.slice(1)); history.replaceState(null, '', location.pathname + location.search);
const { session, state } = await finalizeAuthorization(params); const nextAgent = new OAuthUserAgent(session);
setAgent(nextAgent); setCurrentDid(nextAgent.sub); localStorage.setItem(CURRENT_DID_KEY, nextAgent.sub); return typeof (state as { returnTo?: unknown } | null)?.returnTo === 'string' ? (state as { returnTo: string }).returnTo : '/'; } catch (cause) { setError(getErrorMessage(cause)); throw cause; } finally { setIsLoading(false); } };
const signOut = async () => { setError(null); const current = agent(); const did = currentDid();
try { if (current) { await current.signOut(); } else if (did) { deleteStoredSession(did); } } catch { if (did) { deleteStoredSession(did); } } finally { localStorage.removeItem(CURRENT_DID_KEY); setAgent(null); setCurrentDid(null); } };
const value: AuthContextValue = { agent, currentDid, error, isLoading, signIn, completeSignIn, signOut, refresh, };
return <AuthContext.Provider value={value}>{props.children}</AuthContext.Provider>;};
export const useAuth = (): AuthContextValue => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used inside AuthProvider'); }
return context;};
const readStoredDid = (): Did | null => { const value = localStorage.getItem(CURRENT_DID_KEY); if (!value?.startsWith('did:')) { return null; }
return value as Did;};