158 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { create } from 'zustand';
import type { Session, User } from '@supabase/supabase-js';
import * as WebBrowser from 'expo-web-browser';
import * as Linking from 'expo-linking';
import { supabase } from '../lib/supabase';
WebBrowser.maybeCompleteAuthSession();
type AuthState = {
user: User | null;
session: Session | null;
loading: boolean;
init: () => Promise<void>;
signInWithPassword: (email: string, password: string) => Promise<{ error?: string }>;
signUp: (
email: string,
password: string,
metadata: { username: string; firstName?: string; lastName?: string; avatarId: string; avatarUrl: string }
) => Promise<{ error?: string }>;
signOut: () => Promise<void>;
signInWithOAuth: (provider: 'google' | 'apple') => Promise<{ error?: string }>;
resetPasswordForEmail: (email: string) => Promise<{ error?: string }>;
verifyOtp: (email: string, token: string) => Promise<{ error?: string }>;
resendConfirmation: (email: string) => Promise<{ error?: string }>;
};
export const useAuthStore = create<AuthState>((set) => ({
user: null,
session: null,
loading: true,
init: async () => {
const { data } = await supabase.auth.getSession();
set({
session: data.session,
user: data.session?.user ?? null,
loading: false,
});
supabase.auth.onAuthStateChange((_event, session) => {
set({ session, user: session?.user ?? null });
});
},
signInWithPassword: async (email, password) => {
const { data, error } = await supabase.auth.signInWithPassword({ email, password });
if (error) return { error: error.message };
set({ session: data.session, user: data.user });
return {};
},
signUp: async (email, password, metadata) => {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
username: metadata.username,
first_name: metadata.firstName ?? '',
last_name: metadata.lastName ?? '',
avatar_id: metadata.avatarId,
avatar_url: metadata.avatarUrl,
},
// Deep-link redirect for email confirmation — scheme registered in app.config.ts
emailRedirectTo: 'rebreak://auth/confirm',
},
});
if (error) return { error: error.message };
set({ session: data.session, user: data.user ?? null });
return {};
},
signOut: async () => {
await supabase.auth.signOut();
set({ session: null, user: null });
},
signInWithOAuth: async (provider) => {
const redirectUri = Linking.createURL('auth/callback');
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.
}
const { data, error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: redirectUri,
skipBrowserRedirect: true,
},
});
if (error) return { error: error.message };
if (!data.url) return { error: 'Kein OAuth-URL erhalten' };
const result = await WebBrowser.openAuthSessionAsync(data.url, redirectUri);
if (result.type !== 'success') {
return result.type === 'cancel' ? {} : { error: 'OAuth fehlgeschlagen' };
}
// Extract tokens from the deep-link URL fragment
const url = result.url;
const params = new URLSearchParams(url.split('#')[1] ?? url.split('?')[1] ?? '');
const accessToken = params.get('access_token');
const refreshToken = params.get('refresh_token');
if (!accessToken || !refreshToken) {
// Session may already be set via onAuthStateChange — check
const { data: sessionData } = await supabase.auth.getSession();
if (sessionData.session) {
set({ session: sessionData.session, user: sessionData.session.user });
return {};
}
return { error: 'Session konnte nicht gelesen werden' };
}
const { data: sessionData, error: sessionError } = await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken,
});
if (sessionError) return { error: sessionError.message };
set({ session: sessionData.session, user: sessionData.session?.user ?? null });
return {};
},
resetPasswordForEmail: async (email) => {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: 'rebreak://auth/reset-password',
});
if (error) return { error: error.message };
return {};
},
verifyOtp: async (email, token) => {
const { data, error } = await supabase.auth.verifyOtp({
email,
token,
type: 'signup',
});
if (error) return { error: error.message };
if (!data.session) return { error: 'Bestätigung fehlgeschlagen bitte erneut versuchen.' };
set({ session: data.session, user: data.user ?? null });
return {};
},
resendConfirmation: async (email) => {
const { error } = await supabase.auth.resend({ type: 'signup', email });
if (error) return { error: error.message };
return {};
},
}));