diff --git a/apps/rebreak-native/app.config.ts b/apps/rebreak-native/app.config.ts
index bae81f6..94adcbc 100644
--- a/apps/rebreak-native/app.config.ts
+++ b/apps/rebreak-native/app.config.ts
@@ -21,6 +21,10 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
supportsTablet: true,
bundleIdentifier: "org.rebreak.app",
buildNumber: "10",
+ // Apple Sign-In Entitlement — Pflicht für expo-apple-authentication nativen
+ // signInAsync()-Flow. Ohne flag generiert Expo's prebuild den
+ // com.apple.developer.applesignin-Entitlement nicht in die .entitlements.
+ usesAppleSignIn: true,
config: {
usesNonExemptEncryption: false,
},
diff --git a/apps/rebreak-native/app/(auth)/signin.tsx b/apps/rebreak-native/app/(auth)/signin.tsx
index 254f729..1e92674 100644
--- a/apps/rebreak-native/app/(auth)/signin.tsx
+++ b/apps/rebreak-native/app/(auth)/signin.tsx
@@ -5,6 +5,7 @@ import {
TextInput,
TouchableOpacity,
ActivityIndicator,
+ Platform,
} from 'react-native';
import { useRouter } from 'expo-router';
import { SafeAreaView } from 'react-native-safe-area-context';
@@ -268,20 +269,22 @@ export default function SignInScreen() {
{t('auth.googleSignin')}
- onOAuth('apple')}
- disabled={isLoading}
- activeOpacity={0.8}
- className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 disabled:opacity-40"
- style={{ paddingVertical: 14 }}
- >
- {oauthLoading === 'apple' ? (
-
- ) : (
-
- )}
- {t('auth.appleSignin')}
-
+ {Platform.OS === 'ios' ? (
+ onOAuth('apple')}
+ disabled={isLoading}
+ activeOpacity={0.8}
+ className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 disabled:opacity-40"
+ style={{ paddingVertical: 14 }}
+ >
+ {oauthLoading === 'apple' ? (
+
+ ) : (
+
+ )}
+ {t('auth.appleSignin')}
+
+ ) : null}
{/* Divider */}
diff --git a/apps/rebreak-native/app/(auth)/signup.tsx b/apps/rebreak-native/app/(auth)/signup.tsx
index 45d5428..c6feef2 100644
--- a/apps/rebreak-native/app/(auth)/signup.tsx
+++ b/apps/rebreak-native/app/(auth)/signup.tsx
@@ -6,6 +6,7 @@ import {
TouchableOpacity,
Image,
ActivityIndicator,
+ Platform,
} from 'react-native';
import { useRouter } from 'expo-router';
import { SafeAreaView } from 'react-native-safe-area-context';
@@ -132,20 +133,22 @@ export default function SignUpScreen() {
{t('auth.googleSignup')}
- onOAuth('apple')}
- disabled={isLoading}
- activeOpacity={0.8}
- className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 disabled:opacity-40"
- style={{ paddingVertical: 14 }}
- >
- {oauthLoading === 'apple' ? (
-
- ) : (
-
- )}
- {t('auth.appleSignup')}
-
+ {Platform.OS === 'ios' ? (
+ onOAuth('apple')}
+ disabled={isLoading}
+ activeOpacity={0.8}
+ className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 disabled:opacity-40"
+ style={{ paddingVertical: 14 }}
+ >
+ {oauthLoading === 'apple' ? (
+
+ ) : (
+
+ )}
+ {t('auth.appleSignup')}
+
+ ) : null}
{/* Divider */}
diff --git a/apps/rebreak-native/stores/auth.ts b/apps/rebreak-native/stores/auth.ts
index 319047e..22b7de0 100644
--- a/apps/rebreak-native/stores/auth.ts
+++ b/apps/rebreak-native/stores/auth.ts
@@ -1,7 +1,9 @@
import { create } from 'zustand';
+import { Platform } from 'react-native';
import type { Session, User } from '@supabase/supabase-js';
import * as WebBrowser from 'expo-web-browser';
import * as Linking from 'expo-linking';
+import * as AppleAuthentication from 'expo-apple-authentication';
import { supabase } from '../lib/supabase';
import { apiFetch } from '../lib/api';
@@ -129,15 +131,44 @@ export const useAuthStore = create((set) => ({
},
signInWithOAuth: async (provider) => {
- const redirectUri = Linking.createURL('auth/callback');
-
+ // Apple Sign-In native flow (iOS only) — KEIN Supabase-Apple-OAuth-Provider-
+ // Config nötig (Bundle-ID = Apple-Client-ID). expo-apple-authentication
+ // liefert identityToken → supabase.auth.signInWithIdToken verifiziert direkt
+ // gegen Apple's public keys. App-Store-Submit-Pflicht weil Google-Sign-In
+ // auch da ist (Apple Guideline 4.8).
if (provider === 'apple') {
- // TODO: configure Apple Sign-In
- // Requires expo-apple-authentication to be installed + Apple Developer entitlement.
- // Apple Client ID = Bundle ID (org.rebreak.app) for native flow.
- // For now we fall through to the Supabase OAuth web flow as a temporary path.
+ if (Platform.OS !== 'ios') {
+ return { error: 'Apple Sign-In ist nur auf iOS verfügbar.' };
+ }
+ const available = await AppleAuthentication.isAvailableAsync();
+ if (!available) {
+ return { error: 'Apple Sign-In auf diesem Gerät nicht verfügbar.' };
+ }
+ try {
+ const credential = await AppleAuthentication.signInAsync({
+ requestedScopes: [
+ AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
+ AppleAuthentication.AppleAuthenticationScope.EMAIL,
+ ],
+ });
+ if (!credential.identityToken) {
+ return { error: 'Apple identityToken fehlt.' };
+ }
+ const { data, error } = await supabase.auth.signInWithIdToken({
+ provider: 'apple',
+ token: credential.identityToken,
+ });
+ if (error) return { error: error.message };
+ set({ session: data.session, user: data.user ?? null });
+ return {};
+ } catch (e: any) {
+ // User-Cancel ist kein Error — leere Antwort.
+ if (e?.code === 'ERR_REQUEST_CANCELED') return {};
+ return { error: e?.message ?? 'Apple Sign-In fehlgeschlagen.' };
+ }
}
+ const redirectUri = Linking.createURL('auth/callback');
const { data, error } = await supabase.auth.signInWithOAuth({
provider,
options: {