Skip to content

Commit a20501f

Browse files
FatumaAdshukertjr
andauthored
feat: add native auth support (supabase-community#67)
* build: add deps * unrelated - clean up * feat: add native google sign in 🎉 * build: update sdk, add crypto dep * chore: clean up * refactor: have it support both google and apple * minor fixes for social auth * refactor and style: make params named, add styles * feat: add basic example to example/lib/sign in * Delete text * refactor: combine supanative auth & supasocials auth * refactor: update example to the new api * refactor: move functions over, remove some * delete original native auth file * Update lib/src/components/supa_socials_auth.dart Co-authored-by: Tyler <[email protected]> * Update lib/src/components/supa_socials_auth.dart Co-authored-by: Tyler <[email protected]> * Update lib/src/components/supa_socials_auth.dart Co-authored-by: Tyler <[email protected]> * remove export of deleted file * Minor code cleanup * minor example cleanup * Add variable name to onSuccess parameter --------- Co-authored-by: Tyler <[email protected]>
1 parent 616d77b commit a20501f

File tree

5 files changed

+135
-6
lines changed

5 files changed

+135
-6
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*.txt
12
# Miscellaneous
23
*.class
34
*.log

example/lib/sign_in.dart

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class SignUp extends StatelessWidget {
5757
spacer,
5858
SupaSocialsAuth(
5959
colored: true,
60+
nativeGoogleAuthConfig: const NativeGoogleAuthConfig(
61+
webClientId: 'YOUR_WEB_CLIENT_ID',
62+
iosClientId: 'YOUR_IOS_CLIENT_ID',
63+
),
64+
enableNativeAppleAuth: false,
6065
socialProviders: OAuthProvider.values,
6166
onSuccess: (session) {
6267
Navigator.of(context).pushReplacementNamed('/home');

example/macos/Flutter/GeneratedPluginRegistrant.swift

+4
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ import FlutterMacOS
66
import Foundation
77

88
import app_links
9+
import google_sign_in_ios
910
import path_provider_foundation
1011
import shared_preferences_foundation
12+
import sign_in_with_apple
1113
import url_launcher_macos
1214

1315
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
1416
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
17+
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
1518
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
1619
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
20+
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
1721
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
1822
}

lib/src/components/supa_socials_auth.dart

+119-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import 'dart:async';
2+
import 'dart:convert';
3+
import 'dart:io';
24

5+
import 'package:crypto/crypto.dart';
6+
import 'package:flutter/foundation.dart';
37
import 'package:flutter/material.dart';
48
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
9+
import 'package:google_sign_in/google_sign_in.dart';
10+
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
511
import 'package:supabase_auth_ui/src/utils/constants.dart';
612
import 'package:supabase_flutter/supabase_flutter.dart';
713

@@ -20,7 +26,7 @@ extension on OAuthProvider {
2026
OAuthProvider.slack => FontAwesomeIcons.slack,
2127
OAuthProvider.spotify => FontAwesomeIcons.spotify,
2228
OAuthProvider.twitch => FontAwesomeIcons.twitch,
23-
OAuthProvider.twitter => FontAwesomeIcons.x,
29+
OAuthProvider.twitter => FontAwesomeIcons.xTwitter,
2430
_ => Icons.close,
2531
};
2632

@@ -58,8 +64,31 @@ enum SocialButtonVariant {
5864
iconAndText,
5965
}
6066

67+
class NativeGoogleAuthConfig {
68+
/// Web Client ID that you registered with Google Cloud.
69+
///
70+
/// Required to perform native Google Sign In on Android
71+
final String? webClientId;
72+
73+
/// iOS Client ID that you registered with Google Cloud.
74+
///
75+
/// Required to perform native Google Sign In on iOS
76+
final String? iosClientId;
77+
78+
const NativeGoogleAuthConfig({
79+
this.webClientId,
80+
this.iosClientId,
81+
});
82+
}
83+
6184
/// UI Component to create social login form
6285
class SupaSocialsAuth extends StatefulWidget {
86+
/// Defines native google provider to show in the form
87+
final NativeGoogleAuthConfig? nativeGoogleAuthConfig;
88+
89+
/// Whether to use native Apple sign in on iOS and macOS
90+
final bool enableNativeAppleAuth;
91+
6392
/// List of social providers to show in the form
6493
final List<OAuthProvider> socialProviders;
6594

@@ -77,7 +106,7 @@ class SupaSocialsAuth extends StatefulWidget {
77106
final String? redirectUrl;
78107

79108
/// Method to be called when the auth action is success
80-
final void Function(Session) onSuccess;
109+
final void Function(Session session) onSuccess;
81110

82111
/// Method to be called when the auth action threw an excepction
83112
final void Function(Object error)? onError;
@@ -93,6 +122,8 @@ class SupaSocialsAuth extends StatefulWidget {
93122

94123
const SupaSocialsAuth({
95124
Key? key,
125+
this.nativeGoogleAuthConfig,
126+
this.enableNativeAppleAuth = true,
96127
required this.socialProviders,
97128
this.colored = true,
98129
this.redirectUrl,
@@ -111,6 +142,63 @@ class SupaSocialsAuth extends StatefulWidget {
111142
class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
112143
late final StreamSubscription<AuthState> _gotrueSubscription;
113144

145+
/// Performs Google sign in on Android and iOS
146+
Future<AuthResponse> _nativeGoogleSignIn({
147+
required String? webClientId,
148+
required String? iosClientId,
149+
}) async {
150+
final GoogleSignIn googleSignIn = GoogleSignIn(
151+
clientId: iosClientId,
152+
serverClientId: webClientId,
153+
);
154+
155+
final googleUser = await googleSignIn.signIn();
156+
final googleAuth = await googleUser!.authentication;
157+
final accessToken = googleAuth.accessToken;
158+
final idToken = googleAuth.idToken;
159+
160+
if (accessToken == null) {
161+
throw const AuthException(
162+
'No Access Token found from Google sign in result.');
163+
}
164+
if (idToken == null) {
165+
throw const AuthException(
166+
'No ID Token found from Google sign in result.');
167+
}
168+
169+
return supabase.auth.signInWithIdToken(
170+
provider: OAuthProvider.google,
171+
idToken: idToken,
172+
accessToken: accessToken,
173+
);
174+
}
175+
176+
/// Performs Apple sign in on iOS or macOS
177+
Future<AuthResponse> _nativeAppleSignIn() async {
178+
final rawNonce = supabase.auth.generateRawNonce();
179+
final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();
180+
181+
final credential = await SignInWithApple.getAppleIDCredential(
182+
scopes: [
183+
AppleIDAuthorizationScopes.email,
184+
AppleIDAuthorizationScopes.fullName,
185+
],
186+
nonce: hashedNonce,
187+
);
188+
189+
final idToken = credential.identityToken;
190+
if (idToken == null) {
191+
throw const AuthException(
192+
'Could not find ID Token from generated Apple sign in credential.');
193+
}
194+
195+
return supabase.auth.signInWithIdToken(
196+
provider: OAuthProvider.apple,
197+
idToken: idToken,
198+
nonce: rawNonce,
199+
);
200+
}
201+
114202
@override
115203
void initState() {
116204
super.initState();
@@ -135,6 +223,8 @@ class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
135223
@override
136224
Widget build(BuildContext context) {
137225
final providers = widget.socialProviders;
226+
final googleAuthConfig = widget.nativeGoogleAuthConfig;
227+
final isNativeAppleAuthEnabled = widget.enableNativeAppleAuth;
138228
final coloredBg = widget.colored == true;
139229

140230
if (providers.isEmpty) {
@@ -208,12 +298,38 @@ class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
208298
);
209299
break;
210300
default:
211-
// Handle other cases or provide a default behavior.
212301
break;
213302
}
214303

215304
onAuthButtonPressed() async {
216305
try {
306+
// Check if native Google login should be performed
307+
if (socialProvider == OAuthProvider.google) {
308+
final webClientId = googleAuthConfig?.webClientId;
309+
final iosClientId = googleAuthConfig?.iosClientId;
310+
final shouldPerformNativeGoogleSignIn =
311+
(webClientId != null && !kIsWeb && Platform.isAndroid) ||
312+
(iosClientId != null && !kIsWeb && Platform.isIOS);
313+
if (shouldPerformNativeGoogleSignIn) {
314+
await _nativeGoogleSignIn(
315+
webClientId: webClientId,
316+
iosClientId: iosClientId,
317+
);
318+
return;
319+
}
320+
}
321+
322+
// Check if native Apple login should be performed
323+
if (socialProvider == OAuthProvider.apple) {
324+
final shouldPerformNativeAppleSignIn =
325+
(isNativeAppleAuthEnabled && !kIsWeb && Platform.isIOS) ||
326+
(isNativeAppleAuthEnabled && !kIsWeb && Platform.isMacOS);
327+
if (shouldPerformNativeAppleSignIn) {
328+
await _nativeAppleSignIn();
329+
return;
330+
}
331+
}
332+
217333
await supabase.auth.signInWithOAuth(
218334
socialProvider,
219335
redirectTo: widget.redirectUrl,

pubspec.yaml

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@ name: supabase_auth_ui
22
description: UI library to implement auth forms using Supabase and Flutter
33
version: 0.4.1
44
homepage: https://supabase.com
5-
repository: 'https://github.com/supabase-community/flutter-auth-ui'
5+
repository: "https://github.com/supabase-community/flutter-auth-ui"
66

77
environment:
8-
sdk: '>=3.0.0 <4.0.0'
9-
flutter: '>=3.0.0'
8+
sdk: ">=3.0.0 <4.0.0"
9+
flutter: ">=3.0.0"
1010

1111
dependencies:
1212
flutter:
1313
sdk: flutter
1414
supabase_flutter: ^2.0.1
1515
email_validator: ^2.0.1
1616
font_awesome_flutter: ^10.6.0
17+
google_sign_in: ^6.2.1
18+
sign_in_with_apple: ^5.0.0
19+
crypto: ^3.0.3
1720

1821
dev_dependencies:
1922
flutter_test:

0 commit comments

Comments
 (0)