feat(auth): Issue-691

This commit is contained in:
mso7nueh
2025-09-30 16:11:30 +03:00
parent 27376ca54d
commit b65fca8116
3 changed files with 47 additions and 74 deletions

View File

@@ -389,63 +389,46 @@ class _SocialButton extends StatelessWidget {
}
void _onVkPressed(BuildContext context) async {
print('[VK] VK button pressed');
try {
final accessToken = await SocialAuthFacade.signInWithVk(context: context);
print('[VK] Got access token: ${accessToken != null ? 'success' : 'null'}');
if (accessToken == null) {
print('[VK] No access token received');
return;
}
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'vk', accessToken: accessToken);
print('[VK] Authentication result: $isAuthenticated');
if (isAuthenticated) {
if (AppRouter.navigatorKey.currentContext != null) {
// Reuse success flow
// ignore: use_build_context_synchronously
await _reconnectAfterAuth();
}
}
} catch (e) {
print('[VK] Error during VK auth: $e');
}
}
void _onOkPressed() async {
print('[OK] OK button pressed');
try {
final accessToken = await SocialAuthFacade.signInWithOk();
print('[OK] Got access token: ${accessToken != null ? 'success' : 'null'}');
if (accessToken == null) {
print('[OK] No access token received');
return;
}
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'ok', accessToken: accessToken);
print('[OK] Authentication result: $isAuthenticated');
if (isAuthenticated) {
await _reconnectAfterAuth();
}
} catch (e) {
print('[OK] Error during OK auth: $e');
}
}
void _onMailRuPressed() async {
print('[MailRu] MailRu button pressed');
try {
final accessToken = await SocialAuthFacade.signInWithMailRu();
print('[MailRu] Got access token: ${accessToken != null ? 'success' : 'null'}');
if (accessToken == null) {
print('[MailRu] No access token received');
return;
}
final isAuthenticated = await sl<AuthCubit>().getTokenBySocial(provider: 'mailru', accessToken: accessToken);
print('[MailRu] Authentication result: $isAuthenticated');
if (isAuthenticated) {
await _reconnectAfterAuth();
}
} catch (e) {
print('[MailRu] Error during MailRu auth: $e');
}
}

View File

@@ -29,51 +29,35 @@ class SocialAuthFacade {
static Future<VkAuthResult?> signInWithVkAndFetchProfile({required BuildContext context}) async {
final clientIdStr = EnvConfig.vkClientId.trim();
print('[VK] VK_CLIENT_ID from EnvConfig: $clientIdStr');
if (clientIdStr.isEmpty) {
print('[VK] VK_CLIENT_ID is not set. Configure it in .env');
return null;
}
try {
print('[VK] Creating VkIDController with clientId: $clientIdStr');
dynamic controller = VkIDController(clID: int.parse(clientIdStr));
final authUri = controller.generateAuthorizeLinkWithCodeVerifier();
print('[VK] Generated auth URI: $authUri');
// Extract URL from MapEntry
String authUrl;
if (authUri is MapEntry) {
authUrl = authUri.value.toString();
} else {
authUrl = authUri.toString();
}
print('[VK] Extracted auth URL: $authUrl');
final redirectScheme = 'vk$clientIdStr://';
print('[VK] Redirect scheme: $redirectScheme');
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUrl, redirectSchemePrefix: redirectScheme);
print('[VK] WebView returned params: $redirectParams');
if (redirectParams == null) {
print('[VK] No redirect params received');
return null;
}
final code = redirectParams['code'];
final deviceId = redirectParams['device_id'];
print('[VK] Code: $code, DeviceId: $deviceId');
if (code == null || deviceId == null) {
print('[VK] Missing code or deviceId');
return null;
}
// Instead of exchanging the code on-device (the sdk method is not available),
// return the authorization code so the backend can exchange it securely.
print('[VK] Returning authorization code to backend for exchange');
return VkAuthResult(accessToken: code);
} catch (e) {
print('[VK] Error in signInWithVkAndFetchProfile: $e');
return null;
}
}
@@ -94,20 +78,47 @@ class SocialAuthFacade {
final clientId = EnvConfig.yandexClientId.trim();
final redirectUri = EnvConfig.yandexRedirectUri.trim();
print('[Yandex] Client ID: $clientId');
print('[Yandex] Redirect URI: $redirectUri');
if (clientId.isEmpty || redirectUri.isEmpty) {
print('[Yandex] Missing YANDEX_CLIENT_ID or YANDEX_REDIRECT_URI in .env');
return null;
}
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 (_) {
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;
}
}
}
@@ -117,26 +128,16 @@ class SocialAuthFacade {
return null;
}
try {
print('[VK] Provider flow start: $provider, clientId: $clientIdStr');
dynamic controller = VkIDController(clID: int.parse(clientIdStr));
// Try different approaches for provider parameter
dynamic authUri;
try {
// First try with provider as string
authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: provider);
print('[VK] [$provider] Generated auth URI with string provider: $authUri');
} catch (e) {
print('[VK] [$provider] Failed with string provider, trying null: $e');
try {
// Try with null provider
authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: null);
print('[VK] [$provider] Generated auth URI with null provider: $authUri');
} catch (e2) {
print('[VK] [$provider] Failed with null provider, trying without provider: $e2');
// Try without provider parameter
authUri = controller.generateAuthorizeLinkWithCodeVerifier();
print('[VK] [$provider] Generated auth URI without provider: $authUri');
}
}
@@ -147,61 +148,53 @@ class SocialAuthFacade {
} else {
authUrl = authUri.toString();
}
print('[VK] [$provider] Extracted auth URL: $authUrl');
final redirectScheme = 'vk$clientIdStr://';
print('[VK] [$provider] Redirect scheme: $redirectScheme');
final redirectParams = await _presentOAuthWebView(context: context, initialUrl: authUrl, redirectSchemePrefix: redirectScheme);
print('[VK] [$provider] WebView returned params: $redirectParams');
if (redirectParams == null) return null;
final code = redirectParams['code'];
final deviceId = redirectParams['device_id'];
print('[VK] [$provider] Code: $code, DeviceId: $deviceId');
if (code == null || deviceId == null) return null;
// Return the authorization code to be exchanged by the backend for provider flow
return code;
} catch (e) {
print('[VK] [$provider] Error in provider flow: $e');
return null;
}
}
// Token extraction is no longer needed because we return the authorization code
// to the backend for exchange.
static Future<Map<String, String>?> _presentOAuthWebView({
required BuildContext context,
required String initialUrl,
required String redirectSchemePrefix,
}) async {
print('[VK] WebView: Loading URL: $initialUrl');
print('[VK] WebView: Redirect scheme: $redirectSchemePrefix');
final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (request) {
final url = request.url;
print('[VK] WebView: Navigation to: $url');
if (url.startsWith(redirectSchemePrefix)) {
print('[VK] WebView: Redirect detected, parsing params');
final uri = Uri.parse(url);
final params = <String, String>{}..addAll(uri.queryParameters);
print('[VK] WebView: Parsed params: $params');
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) {
print('[VK] WebView: Page started loading: $url');
},
onPageFinished: (url) {
print('[VK] WebView: Page finished loading: $url');
},
onWebResourceError: (error) {
print('[VK] WebView: Error: ${error.description}');
},
),
);
@@ -209,7 +202,6 @@ class SocialAuthFacade {
try {
await controller.loadRequest(Uri.parse(initialUrl));
} catch (e) {
print('[VK] WebView: Failed to load URL: $e');
return null;
}

View File

@@ -31,8 +31,6 @@ Future<void> main() async {
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await dotenv.load(fileName: ".env");
print('[ENV] Loaded .env file');
print('[ENV] VK_CLIENT_ID: ${dotenv.maybeGet('VK_CLIENT_ID')}');
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await FirebaseMessaging.instance.requestPermission();