Files
eatvio_app/lib/features/auth/sdk/social_auth_facade.dart
2025-09-30 16:11:30 +03:00

226 lines
7.1 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_login_yandex/flutter_login_yandex.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/env_config.dart';
import 'package:vk_id/vk_id.dart';
import 'package:webview_flutter/webview_flutter.dart';
class VkAuthResult {
VkAuthResult({required this.accessToken, this.refreshToken, this.userId, this.firstName, this.lastName, this.email, this.phone, this.gender, this.birthDate});
final String accessToken;
final String? refreshToken;
final String? userId;
final String? firstName;
final String? lastName;
final String? email;
final String? phone;
final String? gender;
final String? birthDate;
}
class SocialAuthFacade {
static Future<String?> signInWithVk({required BuildContext context}) async {
final result = await signInWithVkAndFetchProfile(context: context);
return result?.accessToken;
}
static Future<VkAuthResult?> signInWithVkAndFetchProfile({required BuildContext context}) async {
final clientIdStr = EnvConfig.vkClientId.trim();
if (clientIdStr.isEmpty) {
return null;
}
try {
dynamic controller = VkIDController(clID: int.parse(clientIdStr));
final authUri = controller.generateAuthorizeLinkWithCodeVerifier();
String authUrl;
if (authUri is MapEntry) {
authUrl = authUri.value.toString();
} else {
authUrl = authUri.toString();
}
final redirectScheme = 'vk$clientIdStr://';
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUrl, redirectSchemePrefix: redirectScheme);
if (redirectParams == null) {
return null;
}
final code = redirectParams['code'];
final deviceId = redirectParams['device_id'];
if (code == null || deviceId == null) {
return null;
}
return VkAuthResult(accessToken: code);
} catch (e) {
return null;
}
}
static Future<String?> signInWithOk() async {
final context = AppRouter.navigatorKey.currentContext;
if (context == null) return null;
return _signInWithVkIdProvider(context: context, provider: 'ok');
}
static Future<String?> signInWithMailRu() async {
final context = AppRouter.navigatorKey.currentContext;
if (context == null) return null;
return _signInWithVkIdProvider(context: context, provider: 'mail');
}
static Future<String?> signInWithYandex() async {
final clientId = EnvConfig.yandexClientId.trim();
final redirectUri = EnvConfig.yandexRedirectUri.trim();
if (clientId.isEmpty || redirectUri.isEmpty) {
return null;
}
try {
final context = AppRouter.navigatorKey.currentContext;
if (context == null) return null;
final authorizeUrl = Uri.https('oauth.yandex.ru', '/authorize', {
'response_type': 'token',
'client_id': clientId,
'redirect_uri': redirectUri,
'force_confirm': 'yes',
}).toString();
final redirectSchemePrefix = redirectUri;
final params = await _presentOAuthWebView(context: context, initialUrl: authorizeUrl, redirectSchemePrefix: redirectSchemePrefix);
if (params == null) return null;
final token = params['access_token'];
if (token != null && token.isNotEmpty) {
return token;
}
final code = params['code'];
if (code != null && code.isNotEmpty) {
return code;
}
return null;
} catch (e) {
try {
final plugin = FlutterLoginYandex();
final response = await plugin.signIn();
final token = (response is Map) ? response['token'] as String? : null;
return (token != null && token.isNotEmpty) ? token : null;
} catch (_) {
return null;
}
}
}
static Future<String?> _signInWithVkIdProvider({required BuildContext context, required String provider}) async {
final clientIdStr = EnvConfig.vkClientId.trim();
if (clientIdStr.isEmpty) {
return null;
}
try {
dynamic controller = VkIDController(clID: int.parse(clientIdStr));
dynamic authUri;
try {
authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: provider);
} catch (e) {
try {
authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: null);
} catch (e2) {
authUri = controller.generateAuthorizeLinkWithCodeVerifier();
}
}
// Extract URL from MapEntry
String authUrl;
if (authUri is MapEntry) {
authUrl = authUri.value.toString();
} else {
authUrl = authUri.toString();
}
final redirectScheme = 'vk$clientIdStr://';
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUrl, redirectSchemePrefix: redirectScheme);
if (redirectParams == null) return null;
final code = redirectParams['code'];
final deviceId = redirectParams['device_id'];
if (code == null || deviceId == null) return null;
return code;
} catch (e) {
return null;
}
}
static Future<Map<String, String>?> _presentOAuthWebView({
required BuildContext context,
required String initialUrl,
required String redirectSchemePrefix,
}) async {
final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (request) {
final url = request.url;
if (url.startsWith(redirectSchemePrefix)) {
final uri = Uri.parse(url);
final params = <String, String>{}..addAll(uri.queryParameters);
if (uri.fragment.isNotEmpty) {
final fragPairs = uri.fragment.split('&');
for (final pair in fragPairs) {
final eq = pair.indexOf('=');
if (eq > 0 && eq < pair.length - 1) {
final key = Uri.decodeComponent(pair.substring(0, eq));
final value = Uri.decodeComponent(pair.substring(eq + 1));
params[key] = value;
}
}
}
Navigator.of(context, rootNavigator: true).pop(params);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
onPageStarted: (url) {
},
onPageFinished: (url) {
},
onWebResourceError: (error) {
},
),
);
try {
await controller.loadRequest(Uri.parse(initialUrl));
} catch (e) {
return null;
}
return showDialog<Map<String, String>>(
context: context,
barrierDismissible: false,
builder: (_) {
const borderRadius = BorderRadius.all(Radius.circular(16));
return Dialog(
insetPadding: const EdgeInsets.all(16),
shape: const RoundedRectangleBorder(borderRadius: borderRadius),
elevation: 0,
child: ClipRRect(
borderRadius: borderRadius,
child: SizedBox(height: 560, child: WebViewWidget(controller: controller)),
),
);
},
);
}
}