Перейти к основному содержимому

Auth

Аутентификация через Supabase Auth с поддержкой email/password и OAuth.

Структура

src/features/auth/
├── context/
│ └── session-provider.tsx # React Context для сессии
├── services/
│ └── supabase.ts # Инициализация Supabase клиента
├── hooks/
│ └── use-session.ts # Hook для получения сессии
├── types.ts
└── index.ts

SessionProvider

Оборачивает приложение и предоставляет текущую сессию:

// src/features/auth/context/session-provider.tsx
export function SessionProvider({ children }: PropsWithChildren) {
const [session, setSession] = useState<Session | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
// Получить текущую сессию при старте
supabase.auth.getSession().then(({ data: { session } }) => {
setSession(session);
setIsLoading(false);
});

// Подписаться на изменения auth state
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, session) => {
setSession(session);
}
);

return () => subscription.unsubscribe();
}, []);

return (
<SessionContext.Provider value={{ session, isLoading }}>
{children}
</SessionContext.Provider>
);
}

useSession Hook

// Использование в компонентах
import { useSession } from '@/features/auth';

function ProfileScreen() {
const { session, isLoading } = useSession();

if (isLoading) return <LoadingSpinner />;
if (!session) return <Redirect href="/login" />;

return <Text>Привет, {session.user.email}</Text>;
}

Supabase Client

// src/features/auth/services/supabase.ts
import { createClient } from '@supabase/supabase-js';
import AsyncStorage from '@react-native-async-storage/async-storage';

export const supabase = createClient(
process.env.EXPO_PUBLIC_SUPABASE_URL!,
process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
{
auth: {
storage: AsyncStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
},
}
);

Auth Flow

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│ Login │────▶│ Supabase │────▶│ Session │
│ Screen │ │ Auth API │ │ Provider │
└─────────────┘ └──────────────┘ └──────────────┘

┌───────────────────────────┘

┌───────────────┐
│ Root Layout │
│ redirects to │
│ (app)/ │
└───────────────┘

Экраны

ЭкранПутьОписание
Login(auth)/loginEmail + password вход
Register(auth)/registerРегистрация
Forgot Password(auth)/forgot-passwordСброс пароля
Callback(auth)/callbackOAuth redirect

Примеры

Sign In

const handleSignIn = async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});

if (error) {
Alert.alert('Ошибка', error.message);
}
// SessionProvider автоматически обновит сессию
};

Sign Up

const handleSignUp = async (email: string, password: string) => {
const { error } = await supabase.auth.signUp({
email,
password,
});

if (error) {
Alert.alert('Ошибка', error.message);
}
};

Sign Out

const handleSignOut = async () => {
await supabase.auth.signOut();
// SessionProvider обнулит сессию, Root Layout перенаправит на login
};

Password Reset

const handleResetPassword = async (email: string) => {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: 'edu-mobile://reset-password',
});

if (error) {
Alert.alert('Ошибка', error.message);
}
};

Protected Routes

Root layout автоматически перенаправляет неавторизованных:

// app/_layout.tsx
export default function RootLayout() {
const { session, isLoading } = useSession();

if (isLoading) return <SplashScreen />;

return (
<Stack>
{session ? (
<Stack.Screen name="(app)" />
) : (
<Stack.Screen name="(auth)" />
)}
</Stack>
);
}