forked from
pds.ls/pdsls
this repo has no description
1import { Did } from "@atcute/lexicons";
2import { isHandle } from "@atcute/lexicons/syntax";
3import {
4 configureOAuth,
5 createAuthorizationUrl,
6 deleteStoredSession,
7 finalizeAuthorization,
8 getSession,
9 OAuthUserAgent,
10 resolveFromIdentity,
11 resolveFromService,
12 type Session,
13} from "@atcute/oauth-browser-client";
14import { createSignal } from "solid-js";
15import { TextInput } from "./text-input";
16
17configureOAuth({
18 metadata: {
19 client_id: import.meta.env.VITE_OAUTH_CLIENT_ID,
20 redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URL,
21 },
22});
23
24export const [agent, setAgent] = createSignal<OAuthUserAgent | undefined>();
25
26const Login = () => {
27 const [notice, setNotice] = createSignal("");
28 const [loginInput, setLoginInput] = createSignal("");
29
30 const login = async (handle: string) => {
31 try {
32 if (!handle) return;
33 let resolved;
34 if (!isHandle(handle)) {
35 setNotice(`Resolving your service...`);
36 resolved = await resolveFromService(handle);
37 } else {
38 setNotice(`Resolving your identity...`);
39 resolved = await resolveFromIdentity(handle);
40 }
41
42 setNotice(`Contacting your data server...`);
43 const authUrl = await createAuthorizationUrl({
44 scope: import.meta.env.VITE_OAUTH_SCOPE,
45 ...resolved,
46 });
47
48 setNotice(`Redirecting...`);
49 await new Promise((resolve) => setTimeout(resolve, 250));
50
51 location.assign(authUrl);
52 } catch (e) {
53 console.error(e);
54 setNotice(`${e}`);
55 }
56 };
57
58 return (
59 <form class="flex flex-col gap-y-2" onsubmit={(e) => e.preventDefault()}>
60 <div class="flex items-center gap-2">
61 <label for="handle" class="flex items-center">
62 <span class="iconify lucide--user-round-plus text-lg"></span>
63 </label>
64 <TextInput
65 id="handle"
66 placeholder="user.bsky.social"
67 onInput={(e) => setLoginInput(e.currentTarget.value)}
68 class="grow"
69 />
70 <button onclick={() => login(loginInput())} class="iconify lucide--log-in text-lg"></button>
71 </div>
72 <div>{notice()}</div>
73 </form>
74 );
75};
76
77const retrieveSession = async () => {
78 const init = async (): Promise<Session | undefined> => {
79 const params = new URLSearchParams(location.hash.slice(1));
80
81 if (params.has("state") && (params.has("code") || params.has("error"))) {
82 history.replaceState(null, "", location.pathname + location.search);
83
84 const session = await finalizeAuthorization(params);
85 const did = session.info.sub;
86
87 localStorage.setItem("lastSignedIn", did);
88 return session;
89 } else {
90 const lastSignedIn = localStorage.getItem("lastSignedIn");
91
92 if (lastSignedIn) {
93 try {
94 return await getSession(lastSignedIn as Did);
95 } catch (err) {
96 deleteStoredSession(lastSignedIn as Did);
97 localStorage.removeItem("lastSignedIn");
98 throw err;
99 }
100 }
101 }
102 };
103
104 const session = await init().catch(() => {});
105
106 if (session) setAgent(new OAuthUserAgent(session));
107};
108
109export { Login, retrieveSession };