From e6a6e7968d9b1029866a77641a6d1d8e3d61dd33 Mon Sep 17 00:00:00 2001 From: Komyyy Date: Tue, 4 Mar 2025 20:10:19 +0900 Subject: [PATCH 1/2] packages: add sign_in_with_apple --- ios/Podfile.lock | 34 +++++++++++-------- ios/Runner/Runner.entitlements | 4 +++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ pubspec.lock | 24 +++++++++++++ pubspec.yaml | 1 + 5 files changed, 51 insertions(+), 14 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 27e8691603..074c95baf6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -117,6 +117,8 @@ PODS: - SDWebImage/Core (5.20.0) - share_plus (0.0.1): - Flutter + - sign_in_with_apple (0.0.1): + - Flutter - sqlite3 (3.47.2): - sqlite3/common (= 3.47.2) - sqlite3/common (3.47.2) @@ -157,6 +159,7 @@ DEPENDENCIES: - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `.symlinks/plugins/share_plus/ios`) + - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) @@ -202,6 +205,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/darwin" share_plus: :path: ".symlinks/plugins/share_plus/ios" + sign_in_with_apple: + :path: ".symlinks/plugins/sign_in_with_apple/ios" sqlite3_flutter_libs: :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" url_launcher_ios: @@ -212,14 +217,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: - app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc - device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 + app_settings: 3507c575c2b18a462c99948f61d5de21d4420999 + device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 - file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517 Firebase: 374a441a91ead896215703a674d58cdb3e9d772b - firebase_core: feb37e79f775c2bd08dd35e02d83678291317e10 - firebase_messaging: e2f0ba891b1509668c07f5099761518a5af8fe3c + firebase_core: 2337982fb78ee4d8d91e608b0a3d4f44346a93c8 + firebase_messaging: f3bddfa28c2cad70b3341bf461e987a24efd28d6 FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2 FirebaseInstallations: efc0946fc756e4d22d8113f7c761948120322e8c @@ -227,20 +232,21 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d - image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 - integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a + integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8 - share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418 sqlite3: 7559e33dae4c78538df563795af3a86fc887ee71 - sqlite3_flutter_libs: 58ae36c0dd086395d066b4fe4de9cdca83e717b3 + sqlite3_flutter_libs: 5235ce0546528db87927a3ef1baff8b7d5107f0e SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe - video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 - wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56 + url_launcher_ios: 694010445543906933d732453a59da0a173ae33d + video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b + wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49 PODFILE CHECKSUM: 7ed5116924b3be7e8fb75f7aada61e057028f5c7 diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 903def2af5..80b5221de7 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -4,5 +4,9 @@ aps-environment development + com.apple.developer.applesignin + + Default + diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 83889555f8..b02a56cab1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -12,6 +12,7 @@ import firebase_messaging import package_info_plus import path_provider_foundation import share_plus +import sign_in_with_apple import sqlite3_flutter_libs import url_launcher_macos import video_player_avfoundation @@ -25,6 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 4ee3317f8d..5c28e55755 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -955,6 +955,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + sign_in_with_apple: + dependency: "direct main" + description: + name: sign_in_with_apple + sha256: e84a62e17b7e463abf0a64ce826c2cd1f0b72dff07b7b275e32d5302d76fb4c5 + url: "https://pub.dev" + source: hosted + version: "6.1.4" + sign_in_with_apple_platform_interface: + dependency: transitive + description: + name: sign_in_with_apple_platform_interface + sha256: c2ef2ce6273fce0c61acd7e9ff5be7181e33d7aa2b66508b39418b786cca2119 + url: "https://pub.dev" + source: hosted + version: "1.1.0" + sign_in_with_apple_web: + dependency: transitive + description: + name: sign_in_with_apple_web + sha256: "2f7c38368f49e3f2043bca4b46a4a61aaae568c140a79aa0675dc59ad0ca49bc" + url: "https://pub.dev" + source: hosted + version: "2.1.1" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index c17bebe704..2e46d78a1f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,6 +56,7 @@ dependencies: path_provider: ^2.0.13 share_plus: ^10.1.3 share_plus_platform_interface: ^5.0.2 + sign_in_with_apple: ^6.1.4 sqlite3: ^2.4.0 sqlite3_flutter_libs: ^0.5.13 url_launcher: ^6.1.11 From a65697809293c93e76e3e66706c13541d6c626b2 Mon Sep 17 00:00:00 2001 From: Komyyy Date: Tue, 4 Mar 2025 20:10:29 +0900 Subject: [PATCH 2/2] login_page: apple auth --- lib/api/model/web_auth.dart | 2 ++ lib/widgets/login.dart | 43 +++++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/lib/api/model/web_auth.dart b/lib/api/model/web_auth.dart index 490c4b79db..53efde2bc5 100644 --- a/lib/api/model/web_auth.dart +++ b/lib/api/model/web_auth.dart @@ -75,6 +75,8 @@ String generateOtp() { return hex.encode(bytes); } +String generateRandomToken() => generateOtp(); + /// For tests, create an OTP-encrypted API key. @visibleForTesting String debugEncodeApiKey(String apiKey, String otp) { diff --git a/lib/widgets/login.dart b/lib/widgets/login.dart index 504289adc1..df86ae1a68 100644 --- a/lib/widgets/login.dart +++ b/lib/widgets/login.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:url_launcher/url_launcher.dart'; import '../api/exception.dart'; @@ -347,19 +348,6 @@ class _LoginPageState extends State { } catch (e) { assert(debugLog(e.toString())); - if (e is PlatformException - && defaultTargetPlatform == TargetPlatform.iOS - && e.message != null && e.message!.startsWith('Error while launching')) { - // Ignore; I've seen this on my iPhone even when auth succeeds. - // Specifically, Apple web auth…which on iOS should be replaced by - // Apple native auth; that's #462. - // Possibly related: - // https://github.com/flutter/flutter/issues/91660 - // but in that issue, people report authentication not succeeding. - // TODO(#462) remove this? - return; - } - if (!mounted) return; final zulipLocalizations = ZulipLocalizations.of(context); @@ -425,6 +413,33 @@ class _LoginPageState extends State { } } + Future _handleNativeAppleAuth() async { + final state = generateRandomToken(); + final credential = await SignInWithApple.getAppleIDCredential( + state: state, + scopes: [ + AppleIDAuthorizationScopes.fullName, + AppleIDAuthorizationScopes.email, + ], + ); + if (credential.state != state) throw Exception('`state` mismatch'); + + __otp = generateOtp(); + + final url = widget.serverSettings.realmUrl.resolve('/complete/apple/') + .replace(queryParameters: {'mobile_flow_otp': _otp!, 'native_flow': 'true', 'id_token': credential.identityToken}); + + await ZulipBinding.instance.launchUrl(url, mode: LaunchMode.inAppBrowserView); + } + + Future _handleExtAuth(ExternalAuthenticationMethod method) async { + if (method.name == 'apple' && defaultTargetPlatform == TargetPlatform.iOS) { + await _handleNativeAppleAuth(); + } else { + await _beginWebAuth(method); + } + } + @override Widget build(BuildContext context) { assert(!PerAccountStoreWidget.debugExistsOf(context)); @@ -447,7 +462,7 @@ class _LoginPageState extends State { ? Image.network(icon, width: 24, height: 24) : null, onPressed: !_inProgress - ? () => _beginWebAuth(method) + ? () => _handleExtAuth(method) : null, label: Text( zulipLocalizations.signInWithFoo(method.displayName)));