replies timeline only, appview-less bluesky client
at main 2.8 kB view raw
1import { 2 configureOAuth, 3 defaultIdentityResolver, 4 createAuthorizationUrl, 5 finalizeAuthorization, 6 OAuthUserAgent, 7 getSession, 8 deleteStoredSession 9} from '@atcute/oauth-browser-client'; 10 11import { 12 CompositeDidDocumentResolver, 13 PlcDidDocumentResolver, 14 WebDidDocumentResolver, 15 XrpcHandleResolver 16} from '@atcute/identity-resolver'; 17import { slingshotUrl } from './client'; 18import type { ActorIdentifier } from '@atcute/lexicons'; 19import { err, ok, type Result } from '$lib/result'; 20import type { AtprotoDid } from '@atcute/lexicons/syntax'; 21import { clientId, redirectUri } from '$lib/oauth'; 22 23configureOAuth({ 24 metadata: { 25 client_id: clientId, 26 redirect_uri: redirectUri 27 }, 28 identityResolver: defaultIdentityResolver({ 29 handleResolver: new XrpcHandleResolver({ serviceUrl: slingshotUrl.href }), 30 31 didDocumentResolver: new CompositeDidDocumentResolver({ 32 methods: { 33 plc: new PlcDidDocumentResolver(), 34 web: new WebDidDocumentResolver() 35 } 36 }) 37 }) 38}); 39 40export const sessions = { 41 get: async (did: AtprotoDid) => { 42 const session = await getSession(did, { allowStale: true }); 43 return new OAuthUserAgent(session); 44 }, 45 remove: async (did: AtprotoDid) => { 46 try { 47 const agent = await sessions.get(did); 48 await agent.signOut(); 49 } catch { 50 deleteStoredSession(did); 51 } 52 } 53}; 54 55export const flow = { 56 start: async (identifier: ActorIdentifier): Promise<Result<null, string>> => { 57 try { 58 const authUrl = await createAuthorizationUrl({ 59 target: { type: 'account', identifier }, 60 scope: 'atproto transition:generic' 61 }); 62 // recommended to wait for the browser to persist local storage before proceeding 63 await new Promise((resolve) => setTimeout(resolve, 200)); 64 // redirect the user to sign in and authorize the app 65 window.location.assign(authUrl); 66 // if this is on an async function, ideally the function should never ever resolve. 67 // the only way it should resolve at this point is if the user aborted the authorization 68 // by returning back to this page (thanks to back-forward page caching) 69 await new Promise((_resolve, reject) => { 70 const listener = () => { 71 reject(new Error(`user aborted the login request`)); 72 }; 73 window.addEventListener('pageshow', listener, { once: true }); 74 }); 75 return ok(null); 76 } catch (error) { 77 return err(`login error: ${error}`); 78 } 79 }, 80 finalize: async (url: URL): Promise<Result<OAuthUserAgent | null, string>> => { 81 try { 82 // createAuthorizationUrl asks server to put the params in the hash 83 const params = new URLSearchParams(url.hash.slice(1)); 84 if (!params.has('code')) return ok(null); 85 const { session } = await finalizeAuthorization(params); 86 return ok(new OAuthUserAgent(session)); 87 } catch (error) { 88 return err(`login error: ${error}`); 89 } 90 } 91};