1---
2import Auth from "../../layouts/Auth.astro";
3---
4
5<Auth
6 title="Complete Authentication"
7 message="Trying to complete the OAuth flow"
8>
9 <section class="relay" aria-live="polite">
10 <p id="relay-connecting">Connecting to your local Alchemy process…</p>
11 <div id="relay-manual" hidden>
12 <code id="relay-code"></code>
13 <div class="relay__actions">
14 <button id="relay-copy" type="button">Copy code</button>
15 <a id="relay-local" href="#">Try local callback anyway</a>
16 </div>
17 </div>
18 </section>
19</Auth>
20
21<script>
22 const params = new URLSearchParams(window.location.search);
23 const code = params.get("code");
24 const state = params.get("state");
25 const error = params.get("error");
26 const localCallback = new URL("http://localhost:9976/auth/callback");
27 localCallback.search = params.toString();
28
29 const connecting = document.querySelector<HTMLElement>("#relay-connecting")!;
30 const manual = document.querySelector<HTMLElement>("#relay-manual")!;
31 const codeBox = document.querySelector<HTMLElement>("#relay-code")!;
32 const copyButton = document.querySelector<HTMLButtonElement>("#relay-copy")!;
33 const localLink = document.querySelector<HTMLAnchorElement>("#relay-local")!;
34
35 localLink.href = localCallback.toString();
36
37 const showManual = () => {
38 connecting.textContent = error
39 ? "Cloudflare did not authorize the request."
40 : "Couldn't connect to your local Alchemy process.";
41 if (!code || !state) return;
42 codeBox.textContent = `${code}#${state}`;
43 manual.hidden = false;
44 };
45
46 if (error || !code || !state) {
47 showManual();
48 } else {
49 const controller = new AbortController();
50 const timeout = window.setTimeout(() => controller.abort(), 10000);
51
52 fetch("http://localhost:9976/auth/ping", {
53 mode: "cors",
54 cache: "no-store",
55 signal: controller.signal,
56 })
57 .then(async (response) => {
58 if (!response.ok) throw new Error("Local callback unavailable");
59 window.location.replace(localCallback);
60 })
61 .catch(showManual)
62 .finally(() => window.clearTimeout(timeout));
63 }
64
65 copyButton.addEventListener("click", async () => {
66 await navigator.clipboard.writeText(codeBox.textContent ?? "");
67 copyButton.textContent = "Copied";
68 });
69</script>
70
71<style>
72 .relay {
73 width: 100%;
74 color: var(--alc-fg-2);
75 }
76 .relay p {
77 line-height: 1.6;
78 }
79 .relay code {
80 display: block;
81 overflow-wrap: anywhere;
82 padding: 1rem;
83 border: 1px solid var(--alc-hairline-2);
84 border-radius: 0.5rem;
85 background: var(--alc-bg-elev-1);
86 color: var(--alc-fg-1);
87 text-align: left;
88 user-select: all;
89 }
90 .relay__actions {
91 display: flex;
92 justify-content: center;
93 gap: 0.75rem;
94 margin-top: 1rem;
95 }
96 .relay__actions button,
97 .relay__actions a {
98 padding: 0.625rem 1rem;
99 border: 1px solid var(--alc-hairline-2);
100 border-radius: 0.5rem;
101 background: var(--alc-bg-elev-1);
102 color: var(--alc-fg-1);
103 font: inherit;
104 text-decoration: none;
105 cursor: pointer;
106 }
107</style>