Implement OAuth login functionality and enhance documentation
- Added support for Google OAuth login, including new API endpoints for OAuth providers and callbacks. - Updated user authentication logic to handle OAuth-only users. - Enhanced README.md and deployment documentation with OAuth setup instructions. - Modified frontend components to include OAuth login options and improved error handling. - Updated configuration files to include new environment variables for OAuth integration.
This commit is contained in:
@@ -53,11 +53,33 @@ export function useMapApi() {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user, pass }),
|
||||
})
|
||||
if (res.status === 401) throw new Error('Unauthorized')
|
||||
if (res.status === 401) {
|
||||
const data = (await res.json().catch(() => ({}))) as { error?: string }
|
||||
throw new Error(data.error || 'Unauthorized')
|
||||
}
|
||||
if (!res.ok) throw new Error(`API ${res.status}`)
|
||||
return res.json() as Promise<MeResponse>
|
||||
}
|
||||
|
||||
/** OAuth login URL for redirect (full page navigation). */
|
||||
function oauthLoginUrl(provider: string, redirect?: string): string {
|
||||
const url = new URL(`${apiBase}/oauth/${provider}/login`)
|
||||
if (redirect) url.searchParams.set('redirect', redirect)
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
/** List of configured OAuth providers. */
|
||||
async function oauthProviders(): Promise<string[]> {
|
||||
try {
|
||||
const res = await fetch(`${apiBase}/oauth/providers`, { credentials: 'include' })
|
||||
if (!res.ok) return []
|
||||
const data = await res.json()
|
||||
return Array.isArray(data) ? data : []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await fetch(`${apiBase}/logout`, { method: 'POST', credentials: 'include' })
|
||||
}
|
||||
@@ -183,6 +205,8 @@ export function useMapApi() {
|
||||
login,
|
||||
logout,
|
||||
me,
|
||||
oauthLoginUrl,
|
||||
oauthProviders,
|
||||
setupRequired,
|
||||
meTokens,
|
||||
mePassword,
|
||||
|
||||
@@ -4,6 +4,18 @@
|
||||
<div class="card-body">
|
||||
<h1 class="card-title justify-center text-2xl">HnH Map</h1>
|
||||
<p class="text-center text-base-content/70 text-sm">Log in to continue</p>
|
||||
<div v-if="(oauthProviders ?? []).length" class="flex flex-col gap-2">
|
||||
<a
|
||||
v-for="p in (oauthProviders ?? [])"
|
||||
:key="p"
|
||||
:href="api.oauthLoginUrl(p, redirect || undefined)"
|
||||
class="btn btn-outline gap-2"
|
||||
>
|
||||
<span v-if="p === 'google'">Login with Google</span>
|
||||
<span v-else>Login with {{ p }}</span>
|
||||
</a>
|
||||
<div class="divider text-sm">or</div>
|
||||
</div>
|
||||
<form @submit.prevent="submit" class="flex flex-col gap-4">
|
||||
<div class="form-control">
|
||||
<label class="label" for="user">User</label>
|
||||
@@ -40,15 +52,23 @@ const user = ref('')
|
||||
const pass = ref('')
|
||||
const error = ref('')
|
||||
const loading = ref(false)
|
||||
const oauthProviders = ref<string[]>([])
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const api = useMapApi()
|
||||
|
||||
const redirect = computed(() => (route.query.redirect as string) || '')
|
||||
|
||||
onMounted(async () => {
|
||||
oauthProviders.value = await api.oauthProviders()
|
||||
})
|
||||
|
||||
async function submit() {
|
||||
error.value = ''
|
||||
loading.value = true
|
||||
try {
|
||||
await api.login(user.value, pass.value)
|
||||
await router.push('/profile')
|
||||
await router.push(redirect.value || '/profile')
|
||||
} catch (e: unknown) {
|
||||
error.value = e instanceof Error ? e.message : 'Login failed'
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user