fix: email verification improvements

- FRONTEND_URL now generates correct URL from BASE_DOMAIN and SPAWNER_SUBDOMAIN
- Fixed German umlaut in email button: 'bestaetigen' → 'bestätigen'
- Added 'verified=true' parameter to backend redirect for hybrid approach
- Frontend now checks 'verified' parameter and shows error if not set
- Removed unused token logic from verify-success page (backend handles verification)
- Added warning UI for unverified emails with resend link
This commit is contained in:
XPS\Micro 2026-01-31 11:57:52 +01:00
parent 63a396955e
commit 4b8cd3eb4a
4 changed files with 27 additions and 26 deletions

2
api.py
View File

@ -190,7 +190,7 @@ def api_verify_email():
db.session.commit() db.session.commit()
current_app.logger.info(f"User {user.username} hat Email verifiziert") current_app.logger.info(f"User {user.username} hat Email verifiziert")
return redirect(f"{frontend_url}/verify-success") return redirect(f"{frontend_url}/verify-success?verified=true")
@api_bp.route('/auth/resend-verification', methods=['POST']) @api_bp.route('/auth/resend-verification', methods=['POST'])

View File

@ -86,7 +86,10 @@ class Config:
SMTP_USE_TLS = os.getenv('SMTP_USE_TLS', 'true').lower() == 'true' SMTP_USE_TLS = os.getenv('SMTP_USE_TLS', 'true').lower() == 'true'
# Frontend-URL fuer Email-Links # Frontend-URL fuer Email-Links
FRONTEND_URL = os.getenv('FRONTEND_URL', f"http://localhost:3000") FRONTEND_URL = os.getenv(
'FRONTEND_URL',
f"{PREFERRED_URL_SCHEME}://{SPAWNER_SUBDOMAIN}.{BASE_DOMAIN}"
)
class DevelopmentConfig(Config): class DevelopmentConfig(Config):

View File

@ -58,7 +58,7 @@ def send_verification_email(user_email, username, token, base_url=None):
<p>Vielen Dank fuer deine Registrierung beim Container Spawner.</p> <p>Vielen Dank fuer deine Registrierung beim Container Spawner.</p>
<p>Bitte bestatige deine Email-Adresse, indem du auf den folgenden Button klickst:</p> <p>Bitte bestatige deine Email-Adresse, indem du auf den folgenden Button klickst:</p>
<p style="text-align: center;"> <p style="text-align: center;">
<a href="{verify_url}" class="button">Email bestaetigen</a> <a href="{verify_url}" class="button">Email bestätigen</a>
</p> </p>
<p>Oder kopiere diesen Link in deinen Browser:</p> <p>Oder kopiere diesen Link in deinen Browser:</p>
<p style="word-break: break-all; background: #eee; padding: 10px; border-radius: 3px;"> <p style="word-break: break-all; background: #eee; padding: 10px; border-radius: 3px;">

View File

@ -12,37 +12,35 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { CheckCircle2, Container, Loader2 } from "lucide-react"; import { CheckCircle2, Container, Loader2, AlertCircle, RefreshCw } from "lucide-react";
function VerifySuccessContent() { function VerifySuccessContent() {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const token = searchParams.get("token"); const verified = searchParams.get("verified") === "true";
const [isVerifying, setIsVerifying] = useState(!!token);
const [verified, setVerified] = useState(!token);
useEffect(() => { if (!verified) {
// Wenn ein Token in der URL ist, wurde der User vom Backend hierher redirected
// und die Verifizierung ist bereits erfolgt
if (token) {
// Kurze Verzoegerung fuer bessere UX
const timer = setTimeout(() => {
setIsVerifying(false);
setVerified(true);
}, 1000);
return () => clearTimeout(timer);
}
}, [token]);
if (isVerifying) {
return ( return (
<div className="flex min-h-screen items-center justify-center bg-muted/50 px-4"> <div className="flex min-h-screen items-center justify-center bg-muted/50 px-4">
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
<CardContent className="flex flex-col items-center justify-center py-12"> <CardHeader className="text-center">
<Loader2 className="h-12 w-12 animate-spin text-primary" /> <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-yellow-100">
<p className="mt-4 text-center text-muted-foreground"> <AlertCircle className="h-8 w-8 text-yellow-600" />
Email wird verifiziert... </div>
</p> <CardTitle className="text-2xl">Email nicht verifiziert</CardTitle>
<CardDescription>
Bitte pruefe dein Postfach oder fordere eine neue Email an.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-col gap-2">
<Button asChild variant="outline" className="w-full">
<Link href="/login">
<RefreshCw className="mr-2 h-4 w-4" />
Zum Login (neue Email anfordern)
</Link>
</Button>
</div>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>