forked from
microcosm.blue/microcosm-rs
Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm
1{{#*inline "description"}}A little identity-verifying auth service for microcosm demos{{/inline}}
2
3{{#*inline "main"}}
4<div class="mini-content">
5 <div class="explain">
6 <p>This is a little identity-verifying service for microcosm demos.</p>
7 <p>Only <strong>read access to your public data</strong> is required to connect: connecting does not grant any ability to modify your account or data.</p>
8 </div>
9
10 {{#if did}}
11 <p id="error-message" class="hidden"></p>
12
13 <p id="prompt" class="detail">
14 Connected identity:
15 </p>
16
17 <div id="loader">
18 <span class="spinner"></span>
19 </div>
20
21 <div id="user-info">
22 <div id="handle-action" class="action">
23 <span id="handle-view" class="handle"></span>
24 <button id="revoke">disconnect</button>
25 </div>
26 </div>
27 <script>
28 const errorEl = document.getElementById('error-message');
29 const loaderEl = document.getElementById('loader');
30 const handleViewEl = document.getElementById('handle-view');
31 const revokeEl = document.getElementById('revoke'); // for known-did
32
33 function err(e, msg) {
34 loaderEl.classList.add('hidden');
35 errorEl.classList.remove('hidden');
36 errorEl.textContent = msg || e;
37 throw new Error(e);
38 }
39
40 // already-known user
41 ({{{json did}}}) && (async () => {
42
43 const handle = await lookUp({{{json fetch_key}}});
44
45 loaderEl.classList.add('hidden');
46 handleViewEl.textContent = `@${handle}`;
47 revokeEl.addEventListener('click', async () => {
48 try {
49 let res = await fetch('/disconnect', { method: 'POST', credentials: 'include' });
50 if (!res.ok) throw res;
51 } catch (e) {
52 err(e, 'failed to clear session, sorry');
53 }
54 window.location.replace(location.pathname);
55 window.location.reload(); // backup, in case there is no query?
56 });
57 })();
58
59 async function lookUp(fetch_key) {
60 let info;
61 try {
62 const resp = await fetch('/user-info', {
63 method: 'POST',
64 headers: {'Content-Type': 'application/json'},
65 body: JSON.stringify({ fetch_key }),
66 });
67 if (!resp.ok) throw resp;
68 info = await resp.json();
69 } catch (e) {
70 err(e, 'failed to resolve handle from DID')
71 }
72 return info.handle;
73 }
74 </script>
75 {{else}}
76
77 <p class="hello-connect-plz">Connect your handle</p>
78
79 {{#if is_auth_reload}}
80 {{#if no_cookie}}
81 <p id="prompt" class="detail no">
82 No identity connected. Your browser may be blocking access for connecting.
83 </p>
84 {{else}}
85 {{#if auth_failed}}
86 <p id="prompt" class="detail no">
87 No identity connected. Connecting failed or was denied.
88 </p>
89 {{else}}
90 <p id="prompt" class="detail no">
91 No identity connected.
92 </p>
93 {{/if}}
94 {{/if}}
95 {{/if}}
96
97 <div id="user-info">
98 <form id="form-action" action="/auth" target="_blank" method="GET" class="action {{#if did}}hidden{{/if}}">
99 <label>
100 @<input id="handle-input" class="handle" name="handle" placeholder="example.bsky.social" />
101 </label>
102 <button id="connect" type="submit">connect</button>
103 </form>
104 </div>
105 {{/if}}
106
107</div>
108<script>
109window.addEventListener('storage', e => {
110 console.log('eyyy got storage', e);
111 if (e.key !== 'who-am-i') return;
112 if (!e.newValue) return;
113 if (e.newValue.result === 'success') {
114 window.location = '/?auth_reload=1';
115 } else {
116 window.location = '/?auth_reload=1&auth_failed=1';
117 }
118});
119</script>
120{{/inline}}
121
122{{#> base-full}}{{/base-full}}