'use client'; import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { useRouter, useSearchParams } from 'next/navigation'; import { Loader2, ShieldCheck } from 'lucide-react'; import { BrandLogo } from '@/components/id/brand-logo'; import { useAuth } from '@/components/id/auth-provider'; import { Button } from '@/components/ui/button'; import { approveOAuthAuthorization, fetchAuthSession } from '@/lib/api'; function buildOAuthQuery(searchParams: URLSearchParams) { const params = new URLSearchParams(); searchParams.forEach((value, key) => { if (key !== 'userId') params.set(key, value); }); return params; } function OAuthAuthorizeContent() { const searchParams = useSearchParams(); const router = useRouter(); const { user, token, isPinLocked, isLoading } = useAuth(); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [sessionChecked, setSessionChecked] = useState(false); const oauthQuery = useMemo(() => buildOAuthQuery(searchParams), [searchParams]); const clientId = searchParams.get('client_id') ?? searchParams.get('clientId'); const redirectUri = searchParams.get('redirect_uri') ?? searchParams.get('redirectUri'); const scope = searchParams.get('scope') ?? 'openid profile'; const state = searchParams.get('state'); const returnUrl = useMemo(() => `/auth/oauth/authorize?${oauthQuery.toString()}`, [oauthQuery]); useEffect(() => { if (!searchParams.has('userId')) return; router.replace(returnUrl); }, [returnUrl, router, searchParams]); useEffect(() => { if (isLoading) return; if (!clientId || !redirectUri) return; if (isPinLocked) return; if (user && token) return; router.replace(`/auth/login?redirect=${encodeURIComponent(returnUrl)}`); }, [clientId, isLoading, isPinLocked, redirectUri, returnUrl, router, token, user]); useEffect(() => { if (isLoading || isPinLocked || !token || !user) { setSessionChecked(false); return; } let cancelled = false; void fetchAuthSession(token) .then(() => { if (!cancelled) setSessionChecked(true); }) .catch(() => { if (!cancelled) { setSessionChecked(false); router.replace(`/auth/login?redirect=${encodeURIComponent(returnUrl)}`); } }); return () => { cancelled = true; }; }, [isLoading, isPinLocked, returnUrl, router, token, user]); const approve = useCallback(async () => { if (!token || !user || isPinLocked) return; setSubmitting(true); setError(null); try { const data = await approveOAuthAuthorization(oauthQuery, token); if (!data.redirectUrl) { throw new Error('Сервер не вернул redirect URL'); } window.location.href = data.redirectUrl; } catch (err) { setError(err instanceof Error ? err.message : 'Ошибка OAuth авторизации'); } finally { setSubmitting(false); } }, [isPinLocked, oauthQuery, token, user]); const cancel = useCallback(() => { if (!redirectUri) { router.push('/'); return; } try { const url = new URL(redirectUri); url.searchParams.set('error', 'access_denied'); url.searchParams.set('error_description', 'Пользователь отклонил запрос'); if (state) url.searchParams.set('state', state); window.location.href = url.toString(); } catch { router.push('/'); } }, [redirectUri, router, state]); const authReady = Boolean(user && token && !isPinLocked && sessionChecked); if (isLoading || (clientId && redirectUri && !authReady && !isPinLocked)) { return (
); } if (isPinLocked) { return (

Подтвердите PIN-код, чтобы продолжить авторизацию приложения.

); } if (!clientId || !redirectUri) { return (

Некорректный OAuth запрос

Отсутствуют обязательные параметры client_id и redirect_uri.

На главную
); } return (

Разрешить доступ?

Приложение {clientId} запрашивает доступ к вашему аккаунту Lendry ID.

Пользователь: {user?.displayName}

Scopes: {scope}

Redirect URI: {redirectUri}

{error ?

{error}

: null}
); } export default function OAuthAuthorizePage() { return ( } > ); }