Implement Google OAuth linking functionality. Update backend to handle linking existing accounts with Google, including state token management. Enhance frontend to support linking process, including new UI components for user input and feedback. Update mobile app to handle OAuth callbacks and integrate linking features. Refactor related services and controllers for improved error handling and user experience.
This commit is contained in:
@@ -1,6 +1,24 @@
|
||||
<template>
|
||||
<div class="oauth-callback">
|
||||
<div class="loading-container">
|
||||
<div v-if="pendingToken" class="link-container">
|
||||
<h2>Google-Konto verknüpfen</h2>
|
||||
<p>
|
||||
Für {{ googleEmail }} existiert bereits ein Account. Melden Sie sich mit dem bestehenden Account an,
|
||||
um ihn mit Google zu verknüpfen.
|
||||
</p>
|
||||
<form @submit.prevent="linkExistingAccount" class="link-form">
|
||||
<label>E-Mail-Adresse</label>
|
||||
<input v-model="email" type="email" required>
|
||||
<label>Passwort</label>
|
||||
<input v-model="password" type="password" required>
|
||||
<button class="btn btn-primary" :disabled="linking">
|
||||
{{ linking ? 'Wird verknüpft...' : 'Bestehenden Account verknüpfen' }}
|
||||
</button>
|
||||
<button type="button" class="btn" @click="router.push('/login')">Abbrechen</button>
|
||||
</form>
|
||||
<p v-if="errorMessage" class="error">{{ errorMessage }}</p>
|
||||
</div>
|
||||
<div v-else class="loading-container">
|
||||
<h2>{{ status }}</h2>
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
@@ -11,16 +29,60 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '../stores/authStore'
|
||||
import { API_BASE_URL } from '@/config/api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const API_URL = API_BASE_URL
|
||||
|
||||
const status = ref('Authentifizierung läuft...')
|
||||
const pendingToken = ref('')
|
||||
const googleEmail = ref('')
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const linking = ref(false)
|
||||
const errorMessage = ref('')
|
||||
|
||||
async function finishLogin(token) {
|
||||
authStore.saveToken(token)
|
||||
await authStore.fetchCurrentUser()
|
||||
status.value = 'Login erfolgreich! Sie werden weitergeleitet...'
|
||||
setTimeout(() => {
|
||||
router.push('/')
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
async function linkExistingAccount() {
|
||||
try {
|
||||
linking.value = true
|
||||
errorMessage.value = ''
|
||||
const response = await fetch(`${API_URL}/auth/oauth/link-existing`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
pendingToken: pendingToken.value,
|
||||
email: email.value,
|
||||
password: password.value
|
||||
})
|
||||
})
|
||||
const result = await response.json()
|
||||
if (!response.ok || !result.success || !result.token) {
|
||||
throw new Error(result.error || 'Verknüpfung fehlgeschlagen')
|
||||
}
|
||||
pendingToken.value = ''
|
||||
await finishLogin(result.token)
|
||||
} catch (error) {
|
||||
errorMessage.value = error.message || 'Verknüpfung fehlgeschlagen'
|
||||
} finally {
|
||||
linking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const token = route.query.token
|
||||
const error = route.query.error
|
||||
const pending = route.query.pending
|
||||
|
||||
if (error) {
|
||||
status.value = 'OAuth-Login fehlgeschlagen'
|
||||
@@ -30,19 +92,16 @@ onMounted(async () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
pendingToken.value = pending
|
||||
googleEmail.value = route.query.email || ''
|
||||
email.value = route.query.email || ''
|
||||
return
|
||||
}
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
// Token speichern
|
||||
authStore.saveToken(token)
|
||||
|
||||
// Benutzer-Daten laden
|
||||
await authStore.fetchCurrentUser()
|
||||
|
||||
status.value = 'Login erfolgreich! Sie werden weitergeleitet...'
|
||||
|
||||
setTimeout(() => {
|
||||
router.push('/')
|
||||
}, 1000)
|
||||
await finishLogin(token)
|
||||
} catch (err) {
|
||||
status.value = 'Fehler beim Login'
|
||||
setTimeout(() => {
|
||||
@@ -71,6 +130,34 @@ onMounted(async () => {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link-container {
|
||||
background: #fafafa;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
padding: 24px;
|
||||
width: min(460px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
.link-container h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.link-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.link-form input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.loading-container h2 {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
@@ -94,4 +181,3 @@ onMounted(async () => {
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,13 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Google-Anmeldung</label>
|
||||
<button type="button" class="btn" @click="linkGoogle" :disabled="loading">
|
||||
Mit Google-Konto verknüpfen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary" :disabled="loading">
|
||||
{{ loading ? 'Wird gespeichert...' : 'Einstellungen speichern' }}
|
||||
@@ -216,6 +223,29 @@ async function saveProfile() {
|
||||
}
|
||||
}
|
||||
|
||||
async function linkGoogle() {
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await fetch(`${API_URL}/auth/google/link-url`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...authStore.getAuthHeaders(),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ platform: 'web' })
|
||||
})
|
||||
const result = await response.json()
|
||||
if (!response.ok || !result.url) {
|
||||
throw new Error(result.error || 'Google-Verknüpfung konnte nicht gestartet werden')
|
||||
}
|
||||
window.location.href = result.url
|
||||
} catch (error) {
|
||||
await alert(`Fehler: ${error.message}`, 'Fehler')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Initiales Laden
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
@@ -325,4 +355,3 @@ onMounted(async () => {
|
||||
box-shadow: 0 4px 8px rgba(76, 175, 80, 0.4);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user