From b3786827ef16f8168423b1871d33aae8d076fb99 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 15 Jun 2025 17:29:41 +0800 Subject: [PATCH] :sparkles: Login with apple --- android/app/src/main/AndroidManifest.xml | 16 +++ assets/i18n/en-US.json | 1 + assets/images/oidc/apple.svg | 3 + assets/images/oidc/google.svg | 104 ++++++++++++++++++ assets/images/oidc/microsoft.svg | 1 + ios/Podfile.lock | 6 + ios/Runner/Runner.entitlements | 8 ++ lib/screens/auth/login.dart | 79 ++++++++++++- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + macos/Runner/DebugProfile.entitlements | 8 +- macos/Runner/Release.entitlements | 8 +- pubspec.lock | 26 ++++- pubspec.yaml | 5 + web/index.html | 11 +- 14 files changed, 270 insertions(+), 8 deletions(-) create mode 100644 assets/images/oidc/apple.svg create mode 100644 assets/images/oidc/google.svg create mode 100644 assets/images/oidc/microsoft.svg diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 29bdf98..fd1d358 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,6 +42,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/oidc/google.svg b/assets/images/oidc/google.svg new file mode 100644 index 0000000..9d25a23 --- /dev/null +++ b/assets/images/oidc/google.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/oidc/microsoft.svg b/assets/images/oidc/microsoft.svg new file mode 100644 index 0000000..5334aa7 --- /dev/null +++ b/assets/images/oidc/microsoft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 4933b9c..acaa896 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -158,6 +158,8 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - sign_in_with_apple (0.0.1): + - Flutter - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS @@ -221,6 +223,7 @@ DEPENDENCIES: - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - record_ios (from `.symlinks/plugins/record_ios/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`) @@ -299,6 +302,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/record_ios/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sign_in_with_apple: + :path: ".symlinks/plugins/sign_in_with_apple/ios" sqflite_darwin: :path: ".symlinks/plugins/sqflite_darwin/darwin" sqlite3_flutter_libs: @@ -353,6 +358,7 @@ SPEC CHECKSUMS: SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SDWebImage: f29024626962457f3470184232766516dee8dfea shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqlite3: 1d85290c3321153511f6e900ede7a1608718bbd5 sqlite3_flutter_libs: e7fc8c9ea2200ff3271f08f127842131746b70e2 diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index e14e33c..791a15a 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -4,6 +4,14 @@ aps-environment development + com.apple.developer.applesignin + + Default + + com.apple.developer.associated-domains + + webcredentials:solian.app + com.apple.developer.device-information.user-assigned-device-name com.apple.developer.usernotifications.communication diff --git a/lib/screens/auth/login.dart b/lib/screens/auth/login.dart index 3d0169b..6e1478a 100644 --- a/lib/screens/auth/login.dart +++ b/lib/screens/auth/login.dart @@ -11,6 +11,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_otp_text_field/flutter_otp_text_field.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:gap/gap.dart'; import 'package:island/models/auth.dart'; @@ -23,6 +24,7 @@ import 'package:island/services/udid.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:material_symbols_icons/symbols.dart'; +import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -558,6 +560,43 @@ class _LoginLookupScreen extends HookConsumerWidget { } } + Future withApple() async { + final client = ref.watch(apiClientProvider); + try { + final credential = await SignInWithApple.getAppleIDCredential( + scopes: [AppleIDAuthorizationScopes.email], + ); + + if (context.mounted) showLoadingModal(context); + + final resp = await client.post( + '/auth/login/apple/mobile', + data: { + 'identity_token': credential.identityToken!, + 'authorization_code': credential.authorizationCode, + }, + ); + final token = resp.data['token']; + setToken(ref.watch(sharedPreferencesProvider), token); + ref.invalidate(tokenProvider); + if (!context.mounted) return; + + // Do post login tasks + final userNotifier = ref.read(userInfoProvider.notifier); + userNotifier.fetchUser().then((_) { + final apiClient = ref.read(apiClientProvider); + subscribePushNotification(apiClient); + final wsNotifier = ref.read(websocketStateProvider.notifier); + wsNotifier.connect(); + if (context.mounted) Navigator.pop(context, true); + }); + } catch (err) { + showErrorAlert(err); + } finally { + if (context.mounted) hideLoadingModal(context); + } + } + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -586,7 +625,45 @@ class _LoginLookupScreen extends HookConsumerWidget { onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), onSubmitted: isBusy.value ? null : (_) => performNewTicket(), ).padding(horizontal: 7), - const Gap(12), + Row( + spacing: 4, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text("loginOr").tr().fontSize(11).opacity(0.85), + const Gap(8), + Spacer(), + IconButton.filled( + onPressed: () async {}, + padding: EdgeInsets.zero, + icon: SvgPicture.asset( + 'assets/images/oidc/google.svg', + width: 16, + height: 16, + ), + tooltip: 'Google', + ), + IconButton.filled( + onPressed: () async {}, + padding: EdgeInsets.zero, + icon: SvgPicture.asset( + 'assets/images/oidc/microsoft.svg', + width: 16, + height: 16, + ), + tooltip: 'Microsoft', + ), + IconButton.filled( + onPressed: withApple, + padding: EdgeInsets.zero, + icon: SvgPicture.asset( + 'assets/images/oidc/apple.svg', + width: 16, + height: 16, + ), + tooltip: 'Apple Account', + ), + ], + ).padding(horizontal: 8, top: 8, bottom: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cf37ccd..8824bf0 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -27,6 +27,7 @@ import pasteboard import path_provider_foundation import record_macos import shared_preferences_foundation +import sign_in_with_apple import sqflite_darwin import sqlite3_flutter_libs import super_native_extensions @@ -57,6 +58,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index 157dc8e..07f0ebe 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -2,6 +2,12 @@ + com.apple.developer.applesignin + + Default + + com.apple.developer.device-information.user-assigned-device-name + com.apple.security.app-sandbox com.apple.security.cs.allow-jit @@ -18,7 +24,5 @@ com.apple.security.network.server - com.apple.developer.device-information.user-assigned-device-name - diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index e29630b..8d4672b 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -2,6 +2,12 @@ + com.apple.developer.applesignin + + Default + + com.apple.developer.device-information.user-assigned-device-name + com.apple.security.app-sandbox com.apple.security.device.audio-input @@ -16,7 +22,5 @@ com.apple.security.network.server - com.apple.developer.device-information.user-assigned-device-name - diff --git a/pubspec.lock b/pubspec.lock index 2667b91..ce9c89c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -892,7 +892,7 @@ packages: source: hosted version: "2.6.1" flutter_svg: - dependency: transitive + dependency: "direct main" description: name: flutter_svg sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 @@ -1901,6 +1901,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + sign_in_with_apple: + dependency: "direct main" + description: + name: sign_in_with_apple + sha256: "8bd875c8e8748272749eb6d25b896f768e7e9d60988446d543fe85a37a2392b8" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + sign_in_with_apple_platform_interface: + dependency: transitive + description: + name: sign_in_with_apple_platform_interface + sha256: "981bca52cf3bb9c3ad7ef44aace2d543e5c468bb713fd8dda4275ff76dfa6659" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + sign_in_with_apple_web: + dependency: transitive + description: + name: sign_in_with_apple_web + sha256: f316400827f52cafcf50d00e1a2e8a0abc534ca1264e856a81c5f06bd5b10fed + url: "https://pub.dev" + source: hosted + version: "3.0.0" simple_gesture_detector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0d9f8da..ccf7028 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -113,6 +113,8 @@ dependencies: timezone: ^0.10.1 flutter_timezone: ^4.1.1 fl_chart: ^1.0.0 + sign_in_with_apple: ^7.0.1 + flutter_svg: ^2.1.0 dev_dependencies: flutter_test: @@ -147,6 +149,9 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/i18n/ + - assets/images/ + - assets/images/oidc/ + - assets/icons/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images diff --git a/web/index.html b/web/index.html index 8c5a739..51e26be 100644 --- a/web/index.html +++ b/web/index.html @@ -1,4 +1,4 @@ - + + + + @@ -275,4 +283,3 @@ -