ghostguild-org/app/pages/oidc/login.vue
Jennie Robinson Faber 8a529a8e7c Add OIDC provider for Outline wiki SSO
Add oidc-provider with MongoDB adapter so ghostguild.org can act as
the identity provider for the self-hosted Outline wiki. Members
authenticate via the existing magic-link flow, with automatic SSO
when an active session exists. Includes interaction routes, well-known
discovery endpoint, and login page.
2026-03-01 15:46:01 +00:00

96 lines
3 KiB
Vue

<script setup lang="ts">
definePageMeta({
layout: false,
});
const route = useRoute();
const uid = route.query.uid as string;
const email = ref("");
const sent = ref(false);
const loading = ref(false);
const error = ref("");
async function sendMagicLink() {
if (!email.value || !uid) return;
loading.value = true;
error.value = "";
try {
await $fetch("/oidc/interaction/login", {
method: "POST",
body: { email: email.value, uid },
});
sent.value = true;
} catch (e: any) {
error.value = e?.data?.statusMessage || "Something went wrong. Please try again.";
} finally {
loading.value = false;
}
}
</script>
<template>
<div class="min-h-screen flex items-center justify-center bg-gray-50 px-4">
<div class="w-full max-w-sm">
<div class="bg-white rounded-lg shadow-md p-8">
<div class="text-center mb-6">
<h1 class="text-2xl font-bold text-gray-900">Ghost Guild Wiki</h1>
<p class="mt-2 text-sm text-gray-600">
Sign in with your Ghost Guild account
</p>
</div>
<template v-if="!sent">
<form @submit.prevent="sendMagicLink" class="space-y-4">
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">
Email address
</label>
<input
id="email"
v-model="email"
type="email"
required
placeholder="you@example.com"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
:disabled="loading"
/>
</div>
<p v-if="error" class="text-sm text-red-600">{{ error }}</p>
<button
type="submit"
:disabled="loading || !email"
class="w-full py-2 px-4 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
>
{{ loading ? "Sending..." : "Send magic link" }}
</button>
</form>
<p class="mt-4 text-xs text-center text-gray-500">
We'll send a sign-in link to your email.
</p>
</template>
<template v-else>
<div class="text-center space-y-3">
<div class="text-4xl">✉️</div>
<h2 class="text-lg font-semibold text-gray-900">Check your email</h2>
<p class="text-sm text-gray-600">
We sent a sign-in link to <strong>{{ email }}</strong>.
Click the link in the email to continue.
</p>
<button
@click="sent = false; email = '';"
class="mt-4 text-sm text-blue-600 hover:text-blue-800"
>
Use a different email
</button>
</div>
</template>
</div>
</div>
</div>
</template>