Compare commits
10 Commits
f278c015b5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
345f3c93aa | ||
|
|
d67aa41838 | ||
|
|
6051d741b5 | ||
|
|
f591c2b0a0 | ||
|
|
5d3dcfb4bd | ||
|
|
6167df084f | ||
|
|
3576ccf976 | ||
|
|
eaa151dbe9 | ||
|
|
a40108ea04 | ||
|
|
b2e10b85f2 |
@@ -2,7 +2,9 @@
|
|||||||
"client_id": "https://coffee.apoena.dev/client-metadata.json",
|
"client_id": "https://coffee.apoena.dev/client-metadata.json",
|
||||||
"client_name": "Coffee Map",
|
"client_name": "Coffee Map",
|
||||||
"client_uri": "https://coffee.apoena.dev",
|
"client_uri": "https://coffee.apoena.dev",
|
||||||
"redirect_uris": ["https://coffee.apoena.dev/oauth/callback"],
|
"redirect_uris": [
|
||||||
|
"https://coffee.apoena.dev/oauth/callback"
|
||||||
|
],
|
||||||
"grant_types": ["authorization_code", "refresh_token"],
|
"grant_types": ["authorization_code", "refresh_token"],
|
||||||
"response_types": ["code"],
|
"response_types": ["code"],
|
||||||
"scope": "atproto transition:generic",
|
"scope": "atproto transition:generic",
|
||||||
|
|||||||
9
public/icons/icon.svg
Normal file
9
public/icons/icon.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#fdf6ec" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect width="24" height="24" rx="4" fill="#6f4e37"/>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M3 14c.83 .642 2.077 1.017 3.5 1c1.423 .017 2.67 -.358 3.5 -1c.83 -.642 2.077 -1.017 3.5 -1c1.423 -.017 2.67 .358 3.5 1"/>
|
||||||
|
<path d="M8 3a2.4 2.4 0 0 0 -1 2a2.4 2.4 0 0 0 1 2"/>
|
||||||
|
<path d="M12 3a2.4 2.4 0 0 0 -1 2a2.4 2.4 0 0 0 1 2"/>
|
||||||
|
<path d="M3 10h14v5a6 6 0 0 1 -6 6h-2a6 6 0 0 1 -6 -6v-5"/>
|
||||||
|
<path d="M16.746 16.726a3 3 0 1 0 .252 -5.555"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 630 B |
@@ -1,9 +1,12 @@
|
|||||||
import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
|
import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
|
||||||
import { Agent } from '@atproto/api'
|
import { Agent } from '@atproto/api'
|
||||||
|
|
||||||
// The client_id must equal the public URL of client-metadata.json.
|
// client_id must always point to the publicly accessible metadata file
|
||||||
// Update VITE_APP_URL in your environment or set it here directly.
|
// so the PDS can fetch it — even in local dev.
|
||||||
const APP_URL = import.meta.env.VITE_APP_URL ?? 'https://coffee.apoena.dev'
|
const PROD_URL = 'https://coffee.apoena.dev'
|
||||||
|
|
||||||
|
// OAuth only works on the deployed domain (web apps can't use loopback).
|
||||||
|
const ORIGIN = PROD_URL
|
||||||
|
|
||||||
let _client: BrowserOAuthClient | null = null
|
let _client: BrowserOAuthClient | null = null
|
||||||
|
|
||||||
@@ -11,10 +14,10 @@ export function getOAuthClient(): BrowserOAuthClient {
|
|||||||
if (!_client) {
|
if (!_client) {
|
||||||
_client = new BrowserOAuthClient({
|
_client = new BrowserOAuthClient({
|
||||||
clientMetadata: {
|
clientMetadata: {
|
||||||
client_id: `${APP_URL}/client-metadata.json`,
|
client_id: `${PROD_URL}/client-metadata.json`,
|
||||||
client_name: 'Coffee Map',
|
client_name: 'Coffee Map',
|
||||||
client_uri: APP_URL,
|
client_uri: PROD_URL,
|
||||||
redirect_uris: [`${APP_URL}/oauth/callback`],
|
redirect_uris: [`${ORIGIN}/oauth/callback`],
|
||||||
grant_types: ['authorization_code', 'refresh_token'],
|
grant_types: ['authorization_code', 'refresh_token'],
|
||||||
response_types: ['code'],
|
response_types: ['code'],
|
||||||
scope: 'atproto transition:generic',
|
scope: 'atproto transition:generic',
|
||||||
@@ -22,7 +25,6 @@ export function getOAuthClient(): BrowserOAuthClient {
|
|||||||
token_endpoint_auth_method: 'none',
|
token_endpoint_auth_method: 'none',
|
||||||
application_type: 'web',
|
application_type: 'web',
|
||||||
},
|
},
|
||||||
// Use the public ATProto resolver — for full privacy use your own PDS
|
|
||||||
handleResolver: 'https://bsky.social',
|
handleResolver: 'https://bsky.social',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e instanceof Error ? e.message : String(e)
|
error.value = e instanceof Error ? e.message : String(e)
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="error" class="text-red-600 text-sm">{{ error }}</p>
|
<p v-if="error || auth.error" class="text-red-600 text-sm">{{ error || auth.error }}</p>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -40,14 +40,24 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const handle = ref('')
|
const route = useRoute()
|
||||||
|
const handle = ref((route.query.handle as string) ?? '')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
|
||||||
|
const PROD_URL = 'https://coffee.apoena.dev'
|
||||||
|
const isLocalDev = window.location.origin !== PROD_URL
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
|
// OAuth state is scoped to the initiating origin — must start from production
|
||||||
|
if (isLocalDev) {
|
||||||
|
window.location.href = `${PROD_URL}/login?handle=${encodeURIComponent(handle.value.trim())}`
|
||||||
|
return
|
||||||
|
}
|
||||||
loading.value = true
|
loading.value = true
|
||||||
error.value = ''
|
error.value = ''
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="min-h-screen bg-coffee-50 flex items-center justify-center">
|
<div class="min-h-screen bg-coffee-50 flex items-center justify-center">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-4xl mb-4">☕</div>
|
<div class="text-4xl mb-4">☕</div>
|
||||||
<p v-if="error" class="text-red-600">{{ error }}</p>
|
<p v-if="error || auth.error" class="text-red-600 max-w-sm px-4">{{ error || auth.error }}</p>
|
||||||
<p v-else class="text-coffee-600">Signing you in…</p>
|
<p v-else class="text-coffee-600">Signing you in…</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,7 +26,7 @@ onMounted(async () => {
|
|||||||
await shops.fetchAll()
|
await shops.fetchAll()
|
||||||
router.replace('/')
|
router.replace('/')
|
||||||
} else {
|
} else {
|
||||||
error.value = 'Authentication failed. Please try again.'
|
error.value = auth.error ?? 'Authentication failed — check console for details.'
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e instanceof Error ? e.message : 'Authentication error'
|
error.value = e instanceof Error ? e.message : 'Authentication error'
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import UnoCSS from 'unocss/vite'
|
|||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
server: {
|
||||||
|
host: '127.0.0.1',
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
@@ -15,7 +18,7 @@ export default defineConfig({
|
|||||||
vue(),
|
vue(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
includeAssets: ['icons/*.png', 'icons/*.svg'],
|
includeAssets: ['icons/*.svg'],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'Coffee Map',
|
name: 'Coffee Map',
|
||||||
short_name: 'Coffee',
|
short_name: 'Coffee',
|
||||||
@@ -27,20 +30,10 @@ export default defineConfig({
|
|||||||
start_url: '/',
|
start_url: '/',
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
src: 'icons/icon-192.png',
|
src: 'icons/icon.svg',
|
||||||
sizes: '192x192',
|
sizes: 'any',
|
||||||
type: 'image/png',
|
type: 'image/svg+xml',
|
||||||
},
|
purpose: 'any maskable',
|
||||||
{
|
|
||||||
src: 'icons/icon-512.png',
|
|
||||||
sizes: '512x512',
|
|
||||||
type: 'image/png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: 'icons/icon-512.png',
|
|
||||||
sizes: '512x512',
|
|
||||||
type: 'image/png',
|
|
||||||
purpose: 'maskable',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user