OAuth.svelte
1<script lang="ts">
2 import {
3 BrowserOAuthClient,
4 OAuthSession
5 } from "@atproto/oauth-client-browser";
6
7 import { Agent } from "@atproto/api";
8 import { onMount } from "svelte";
9
10 let client = $state<BrowserOAuthClient | null>(null);
11 let agent = $state<Agent | null>(null);
12 let loading = $state(true);
13
14 let loggedIn = $state(false);
15 let did = $state("");
16 let displayName = $state("");
17 let description = $state("");
18
19 async function login() {
20 if (!client) {
21 return; // ???
22 }
23
24 await client.signIn("hexaheximal.com", {
25 state: "",
26 prompt: "login",
27 ui_locales: "en",
28 signal: new AbortController().signal
29 });
30 }
31
32 onMount(async () => {
33 client = new BrowserOAuthClient({
34 clientMetadata: {
35 client_id: "https://hexaheximal.com/test.json",
36 client_name: "test",
37 client_uri: "https://hexaheximal.com",
38 redirect_uris: ["https://hexaheximal.com/bluesky-oauth-test"],
39 scope: "atproto transition:generic",
40 grant_types: ["authorization_code", "refresh_token"],
41 response_types: ["code"],
42 token_endpoint_auth_method: "none",
43 application_type: "web",
44 dpop_bound_access_tokens: true
45 },
46 handleResolver: "https://bsky.social"
47 });
48
49 (window as any).client = client;
50
51 const result: undefined | { session: OAuthSession; state?: string } =
52 await client.init();
53
54 if (!result) {
55 loading = false;
56 return; // Not logged in.
57 }
58
59 console.debug(result);
60
61 loggedIn = true;
62 did = result.session.sub;
63
64 agent = new Agent(await client.restore(result.session.sub));
65
66 const profile = await agent.com.atproto.repo.getRecord({
67 repo: result.session.sub,
68 collection: "app.bsky.actor.profile",
69 rkey: "self"
70 });
71
72 console.log(profile);
73
74 // TODO: proper types
75
76 displayName = profile.data.value.displayName as string;
77 description = profile.data.value.description as string;
78
79 loading = false;
80 });
81</script>
82
83{#if !loading}
84 <div class="p-4">
85 <h1 class="text-4xl font-bold">Hello, World!</h1>
86
87 {#if loggedIn}
88 <p class="mt-4">Logged in as: {did}</p>
89 <p class="mt-4">Display name: <b>{displayName}</b></p>
90 <p class="mt-4">Description: <b>{description}</b></p>
91 {:else}
92 <button
93 class="mt-4 p-4 pl-16 pr-16 bg-blue-800 rounded-xl hover:opacity-25 transition cursor-pointer"
94 onclick={login}>Log in</button
95 >
96 {/if}
97 </div>
98{/if}