feat(web): /auth/reset + /auth/verify als Fallback-Pages
Some checks are pending
CI / validate (push) Waiting to run

Wenn der Reset-Link aus der Email auf einem Gerät ohne installierte
Cardecky-iOS-App geöffnet wird (Desktop, Android, iOS-ohne-App),
fängt Apple den Universal-Link nicht ab — der Browser landet auf
cardecky.mana.how/auth/reset. Heute = 404.

Diese minimalen Brücken redirecten den Token an die jeweilige
auth.mana.how-Surface, damit der Reset/Verify-Flow trotzdem durchläuft:

  cardecky.mana.how/auth/reset?token=X
    → auth.mana.how/reset-password?token=X (Web-Reset-Formular)

  cardecky.mana.how/auth/verify?token=X
    → auth.mana.how/api/auth/verify-email?token=X (Better-Auth-Endpoint)

iOS mit installierter App: Universal-Link greift, Browser-Page wird
nie gerendert.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-13 21:18:44 +02:00
parent 8c7c8c9c98
commit eb39faddb3
2 changed files with 128 additions and 0 deletions

View file

@ -0,0 +1,69 @@
<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/state';
import { env as publicEnv } from '$env/dynamic/public';
/**
* Web-Fallback für die Reset-Password-Mail von mana-auth.
*
* Auf iOS-Geräten mit installierter Cardecky-App öffnet Apple den
* Universal Link direkt in der App, diese Page wird nie gerendert.
*
* Für alle anderen User (kein iOS, App nicht installiert, Desktop)
* leiten wir zum Web-Auth-Portal weiter, das den gleichen Token
* akzeptiert und ein Web-Reset-Formular zeigt.
*/
let phase = $state<'redirecting' | 'no-token' | 'error'>('redirecting');
function authWebUrl(): string {
return publicEnv.PUBLIC_AUTH_WEB_URL ?? 'https://auth.mana.how';
}
onMount(() => {
const token = page.url.searchParams.get('token');
if (!token) {
phase = 'no-token';
return;
}
const target = new URL(`${authWebUrl()}/reset-password`);
target.searchParams.set('token', token);
window.location.replace(target.toString());
});
</script>
<svelte:head>
<title>Passwort zurücksetzen — Cardecky</title>
</svelte:head>
<main class="container">
{#if phase === 'redirecting'}
<p>Weiterleitung zum Reset-Formular…</p>
{:else if phase === 'no-token'}
<h1>Link unvollständig</h1>
<p>
Der Reset-Link enthält keinen Token. Bitte klicke den Link aus deiner
Bestätigungs-Mail erneut oder fordere einen neuen an.
</p>
<p><a href={authWebUrl() + '/forgot-password'}>Neuen Reset-Link anfordern</a></p>
{:else}
<h1>Fehler</h1>
<p>Etwas ist schiefgelaufen. Bitte versuche es später erneut.</p>
{/if}
</main>
<style>
.container {
max-width: 480px;
margin: 64px auto;
padding: 24px;
text-align: center;
font-family: system-ui, -apple-system, sans-serif;
}
h1 {
font-size: 1.5rem;
margin-bottom: 16px;
}
a {
color: var(--primary, #2563eb);
}
</style>

View file

@ -0,0 +1,59 @@
<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/state';
import { env as publicEnv } from '$env/dynamic/public';
/**
* Web-Fallback für die Verify-Email-Mail von mana-auth.
*
* Auf iOS-Geräten mit installierter Cardecky-App öffnet Apple den
* Universal Link direkt in der App. Für alle anderen User leiten
* wir zum Better-Auth-Verify-Endpoint auf auth.mana.how weiter,
* der den Token konsumiert und auf das Auth-Portal redirected.
*/
let phase = $state<'redirecting' | 'no-token'>('redirecting');
function authWebUrl(): string {
return publicEnv.PUBLIC_AUTH_WEB_URL ?? 'https://auth.mana.how';
}
onMount(() => {
const token = page.url.searchParams.get('token');
if (!token) {
phase = 'no-token';
return;
}
// Better Auths Verify-Endpoint nimmt den Token aus dem Query
// und setzt nach Erfolg die Email-Verified-Flag in der DB.
const target = new URL(`${authWebUrl()}/api/auth/verify-email`);
target.searchParams.set('token', token);
window.location.replace(target.toString());
});
</script>
<svelte:head>
<title>Email bestätigen — Cardecky</title>
</svelte:head>
<main class="container">
{#if phase === 'redirecting'}
<p>Email wird bestätigt…</p>
{:else}
<h1>Link unvollständig</h1>
<p>Der Verify-Link enthält keinen Token. Bitte klicke den Link aus der Mail erneut.</p>
{/if}
</main>
<style>
.container {
max-width: 480px;
margin: 64px auto;
padding: 24px;
text-align: center;
font-family: system-ui, -apple-system, sans-serif;
}
h1 {
font-size: 1.5rem;
margin-bottom: 16px;
}
</style>