Skip to content

Commit 6215eb3

Browse files
committed
working atproto auth w/o nuxt-auth-utils
1 parent 560f04d commit 6215eb3

9 files changed

Lines changed: 532 additions & 0 deletions

File tree

app/components/AppHeader.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ withDefaults(
5454
<span class="hidden sm:inline">github</span>
5555
</a>
5656
</li>
57+
58+
<li>
59+
<AuthButton />
60+
</li>
5761
</ul>
5862
</nav>
5963
</header>

app/components/AuthButton.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script setup lang="ts">
2+
const showModal = ref(false)
3+
</script>
4+
5+
<template>
6+
<div class="relative">
7+
<button
8+
type="button"
9+
class="relative flex items-center justify-center w-8 h-8 rounded-md transition-colors duration-200 hover:bg-bg-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
10+
:aria-label="ariaLabel"
11+
@click="showModal = true"
12+
>
13+
Login
14+
</button>
15+
16+
<AuthModal v-model:open="showModal" />
17+
</div>
18+
</template>

app/components/AuthModal.vue

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<script setup lang="ts">
2+
const open = defineModel<boolean>('open', { default: false })
3+
4+
const handleInput = ref('')
5+
6+
async function handleLogin() {
7+
if (handleInput.value) {
8+
await navigateTo(
9+
{
10+
path: '/api/auth/atproto',
11+
query: { handle: handleInput.value },
12+
},
13+
{ external: true },
14+
)
15+
}
16+
}
17+
</script>
18+
19+
<template>
20+
<Teleport to="body">
21+
<Transition
22+
enter-active-class="transition-opacity duration-200"
23+
leave-active-class="transition-opacity duration-200"
24+
enter-from-class="opacity-0"
25+
leave-to-class="opacity-0"
26+
>
27+
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center p-4">
28+
<!-- Backdrop -->
29+
<button
30+
type="button"
31+
class="absolute inset-0 bg-black/60 cursor-default"
32+
aria-label="Close modal"
33+
@click="open = false"
34+
/>
35+
36+
<!-- Modal -->
37+
<div
38+
class="relative w-full bg-bg border border-border rounded-lg shadow-xl max-h-[90vh] overflow-y-auto overscroll-contain"
39+
role="dialog"
40+
aria-modal="true"
41+
aria-labelledby="auth-modal-title"
42+
>
43+
<div class="p-6">
44+
<div class="flex items-center justify-between mb-6">
45+
<h2 id="auth-modal-title" class="font-mono text-lg font-medium">Account Login</h2>
46+
<button
47+
type="button"
48+
class="text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
49+
aria-label="Close"
50+
@click="open = false"
51+
>
52+
<span class="i-carbon-close block w-5 h-5" aria-hidden="true" />
53+
</button>
54+
</div>
55+
56+
<!-- Disconnected state -->
57+
<form class="space-y-4" @submit.prevent="handleLogin">
58+
<p class="text-sm text-fg-muted">Login with your Atmosphere account</p>
59+
60+
<div class="space-y-3">
61+
<div>
62+
<label
63+
for="handle-input"
64+
class="block text-xs text-fg-subtle uppercase tracking-wider mb-1.5"
65+
>
66+
Internet Handle
67+
</label>
68+
<input
69+
id="handle-input"
70+
v-model="handleInput"
71+
type="text"
72+
name="handle"
73+
placeholder="alice.bsky.social"
74+
autocomplete="off"
75+
spellcheck="false"
76+
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 focus:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
77+
/>
78+
</div>
79+
80+
<details class="text-sm">
81+
<summary
82+
class="text-fg-subtle cursor-pointer hover:text-fg-muted transition-colors duration-200"
83+
>
84+
What is an Atmosphere account?
85+
</summary>
86+
<div class="mt-3">
87+
<p>TODO</p>
88+
</div>
89+
</details>
90+
</div>
91+
92+
<button
93+
type="submit"
94+
:disabled="!handleInput.trim()"
95+
class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
96+
>
97+
Login
98+
</button>
99+
</form>
100+
</div>
101+
</div>
102+
</div>
103+
</Transition>
104+
</Teleport>
105+
</template>

nuxt.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export default defineNuxtConfig({
1818
],
1919

2020
devtools: { enabled: true },
21+
devServer: {
22+
host: '127.0.0.1',
23+
},
2124

2225
app: {
2326
head: {

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
"test:unit": "vitest --project unit"
2727
},
2828
"dependencies": {
29+
"@atproto/api": "^0.18.17",
30+
"@atproto/oauth-client-node": "^0.3.15",
2931
"@iconify-json/simple-icons": "^1.2.67",
3032
"@iconify-json/vscode-icons": "^1.2.40",
3133
"@nuxt/a11y": "1.0.0-alpha.1",

0 commit comments

Comments
 (0)