Skip to content

feat: add native auth support #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e281aa9
build: add deps
FatumaA Jan 3, 2024
ba545fb
unrelated - clean up
FatumaA Jan 3, 2024
a676252
feat: add native google sign in 🎉
FatumaA Jan 4, 2024
1581138
build: update sdk, add crypto dep
FatumaA Jan 5, 2024
874ca0c
chore: clean up
FatumaA Jan 5, 2024
d66eb3b
refactor: have it support both google and apple
FatumaA Jan 8, 2024
05c5f5d
minor fixes for social auth
FatumaA Jan 8, 2024
4da15fa
refactor and style: make params named, add styles
FatumaA Jan 8, 2024
f4f9d61
feat: add basic example to example/lib/sign in
FatumaA Jan 8, 2024
b6163e7
Merge branch 'main' into feat-add-native-auth-support
dshukertjr Jan 9, 2024
7cbe2ab
Delete text
FatumaA Jan 10, 2024
4468897
refactor: combine supanative auth & supasocials auth
FatumaA Jan 19, 2024
dcb5335
refactor: update example to the new api
FatumaA Jan 19, 2024
ed58987
refactor: move functions over, remove some
FatumaA Jan 20, 2024
2af251b
delete original native auth file
FatumaA Jan 22, 2024
83e87d8
Update lib/src/components/supa_socials_auth.dart
FatumaA Jan 22, 2024
e7ed677
Update lib/src/components/supa_socials_auth.dart
FatumaA Jan 22, 2024
79cd120
Update lib/src/components/supa_socials_auth.dart
FatumaA Jan 22, 2024
092cc2a
remove export of deleted file
FatumaA Jan 22, 2024
5aa390e
Merge branch 'feat-add-native-auth-support' of https://github.com/Fat…
FatumaA Jan 22, 2024
dbf806e
Minor code cleanup
dshukertjr Jan 23, 2024
07cc5ef
minor example cleanup
dshukertjr Jan 23, 2024
b2897dc
Add variable name to onSuccess parameter
dshukertjr Jan 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.txt
# Miscellaneous
*.class
*.log
Expand Down
5 changes: 5 additions & 0 deletions example/lib/sign_in.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ class SignUp extends StatelessWidget {
spacer,
SupaSocialsAuth(
colored: true,
nativeGoogleAuthConfig: const NativeGoogleAuthConfig(
webClientId: 'YOUR_WEB_CLIENT_ID',
iosClientId: 'YOUR_IOS_CLIENT_ID',
),
enableNativeAppleAuth: false,
socialProviders: OAuthProvider.values,
onSuccess: (session) {
Navigator.of(context).pushReplacementNamed('/home');
Expand Down
4 changes: 4 additions & 0 deletions example/macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import FlutterMacOS
import Foundation

import app_links
import google_sign_in_ios
import path_provider_foundation
import shared_preferences_foundation
import sign_in_with_apple
import url_launcher_macos

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}
122 changes: 119 additions & 3 deletions lib/src/components/supa_socials_auth.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:crypto/crypto.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:supabase_auth_ui/src/utils/constants.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

Expand All @@ -20,7 +26,7 @@ extension on OAuthProvider {
OAuthProvider.slack => FontAwesomeIcons.slack,
OAuthProvider.spotify => FontAwesomeIcons.spotify,
OAuthProvider.twitch => FontAwesomeIcons.twitch,
OAuthProvider.twitter => FontAwesomeIcons.x,
OAuthProvider.twitter => FontAwesomeIcons.xTwitter,
_ => Icons.close,
};

Expand Down Expand Up @@ -58,8 +64,31 @@ enum SocialButtonVariant {
iconAndText,
}

class NativeGoogleAuthConfig {
/// Web Client ID that you registered with Google Cloud.
///
/// Required to perform native Google Sign In on Android
final String? webClientId;

/// iOS Client ID that you registered with Google Cloud.
///
/// Required to perform native Google Sign In on iOS
final String? iosClientId;

const NativeGoogleAuthConfig({
this.webClientId,
this.iosClientId,
});
}

/// UI Component to create social login form
class SupaSocialsAuth extends StatefulWidget {
/// Defines native google provider to show in the form
final NativeGoogleAuthConfig? nativeGoogleAuthConfig;

/// Whether to use native Apple sign in on iOS and macOS
final bool enableNativeAppleAuth;

/// List of social providers to show in the form
final List<OAuthProvider> socialProviders;

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

/// Method to be called when the auth action is success
final void Function(Session) onSuccess;
final void Function(Session session) onSuccess;

/// Method to be called when the auth action threw an excepction
final void Function(Object error)? onError;
Expand All @@ -87,6 +116,8 @@ class SupaSocialsAuth extends StatefulWidget {

const SupaSocialsAuth({
Key? key,
this.nativeGoogleAuthConfig,
this.enableNativeAppleAuth = true,
required this.socialProviders,
this.colored = true,
this.redirectUrl,
Expand All @@ -103,6 +134,63 @@ class SupaSocialsAuth extends StatefulWidget {
class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
late final StreamSubscription<AuthState> _gotrueSubscription;

/// Performs Google sign in on Android and iOS
Future<AuthResponse> _nativeGoogleSignIn({
required String? webClientId,
required String? iosClientId,
}) async {
final GoogleSignIn googleSignIn = GoogleSignIn(
clientId: iosClientId,
serverClientId: webClientId,
);

final googleUser = await googleSignIn.signIn();
final googleAuth = await googleUser!.authentication;
final accessToken = googleAuth.accessToken;
final idToken = googleAuth.idToken;

if (accessToken == null) {
throw const AuthException(
'No Access Token found from Google sign in result.');
}
if (idToken == null) {
throw const AuthException(
'No ID Token found from Google sign in result.');
}

return supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: idToken,
accessToken: accessToken,
);
}

/// Performs Apple sign in on iOS or macOS
Future<AuthResponse> _nativeAppleSignIn() async {
final rawNonce = supabase.auth.generateRawNonce();
final hashedNonce = sha256.convert(utf8.encode(rawNonce)).toString();

final credential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
nonce: hashedNonce,
);

final idToken = credential.identityToken;
if (idToken == null) {
throw const AuthException(
'Could not find ID Token from generated Apple sign in credential.');
}

return supabase.auth.signInWithIdToken(
provider: OAuthProvider.apple,
idToken: idToken,
nonce: rawNonce,
);
}

@override
void initState() {
super.initState();
Expand All @@ -127,6 +215,8 @@ class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
@override
Widget build(BuildContext context) {
final providers = widget.socialProviders;
final googleAuthConfig = widget.nativeGoogleAuthConfig;
final isNativeAppleAuthEnabled = widget.enableNativeAppleAuth;
final coloredBg = widget.colored == true;

if (providers.isEmpty) {
Expand Down Expand Up @@ -200,12 +290,38 @@ class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
);
break;
default:
// Handle other cases or provide a default behavior.
break;
}

onAuthButtonPressed() async {
try {
// Check if native Google login should be performed
if (socialProvider == OAuthProvider.google) {
final webClientId = googleAuthConfig?.webClientId;
final iosClientId = googleAuthConfig?.iosClientId;
final shouldPerformNativeGoogleSignIn =
(webClientId != null && !kIsWeb && Platform.isAndroid) ||
(iosClientId != null && !kIsWeb && Platform.isIOS);
if (shouldPerformNativeGoogleSignIn) {
await _nativeGoogleSignIn(
webClientId: webClientId,
iosClientId: iosClientId,
);
return;
}
}

// Check if native Apple login should be performed
if (socialProvider == OAuthProvider.apple) {
final shouldPerformNativeAppleSignIn =
(isNativeAppleAuthEnabled && !kIsWeb && Platform.isIOS) ||
(isNativeAppleAuthEnabled && !kIsWeb && Platform.isMacOS);
if (shouldPerformNativeAppleSignIn) {
await _nativeAppleSignIn();
return;
}
}

await supabase.auth.signInWithOAuth(
socialProvider,
redirectTo: widget.redirectUrl,
Expand Down
9 changes: 6 additions & 3 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ name: supabase_auth_ui
description: UI library to implement auth forms using Supabase and Flutter
version: 0.4.0+1
homepage: https://supabase.com
repository: 'https://github.com/supabase-community/flutter-auth-ui'
repository: "https://github.com/supabase-community/flutter-auth-ui"

environment:
sdk: '>=3.0.0 <4.0.0'
flutter: '>=3.0.0'
sdk: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"

dependencies:
flutter:
sdk: flutter
supabase_flutter: ^2.0.1
email_validator: ^2.0.1
font_awesome_flutter: ^10.6.0
google_sign_in: ^6.2.1
sign_in_with_apple: ^5.0.0
crypto: ^3.0.3

dev_dependencies:
flutter_test:
Expand Down