feat(mobile): auth callback screen consumes magic-link tokens

This commit is contained in:
byGalax
2026-05-13 23:58:52 +02:00
parent 2a5f836b86
commit 3313dd84f3
+77
View File
@@ -0,0 +1,77 @@
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import { supabase } from '../../lib/supabase';
import { colors } from '../../theme/colors';
// Supabase magic-link emails redirect to netralax://auth/callback with
// the tokens in either the URL fragment (#access_token=...&refresh_token=...)
// or the query string depending on the provider. Expo Router parses the
// query string into useLocalSearchParams. The hash portion would require
// expo-linking; we accept both shapes for safety.
export default function AuthCallback() {
const params = useLocalSearchParams<{
access_token?: string;
refresh_token?: string;
error?: string;
error_description?: string;
}>();
const router = useRouter();
const [status, setStatus] = useState<'working' | 'error'>('working');
const [message, setMessage] = useState<string>('');
useEffect(() => {
void (async () => {
if (params.error) {
setStatus('error');
setMessage(params.error_description ?? params.error);
return;
}
if (!params.access_token || !params.refresh_token) {
setStatus('error');
setMessage('Magic-Link-URL enthielt keine Tokens.');
return;
}
const { error } = await supabase.auth.setSession({
access_token: params.access_token,
refresh_token: params.refresh_token,
});
if (error) {
setStatus('error');
setMessage(error.message);
return;
}
router.replace('/(app)/chats');
})();
}, [params, router]);
return (
<View style={styles.container}>
{status === 'working' ? (
<>
<ActivityIndicator color={colors.accent} size="large" />
<Text style={styles.text}>Du wirst angemeldet</Text>
</>
) : (
<>
<Text style={styles.errorTitle}>Anmeldung fehlgeschlagen</Text>
<Text style={styles.text}>{message}</Text>
</>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.bg,
padding: 24,
gap: 16,
},
text: { color: colors.textMuted, fontSize: 14, textAlign: 'center' },
errorTitle: { color: colors.danger, fontSize: 18, fontWeight: '600' },
});