Skip to content
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

feat: add native auth support #67

Merged
Merged
Show file tree
Hide file tree
Changes from 10 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
13 changes: 13 additions & 0 deletions example/lib/sign_in.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ class SignUp extends StatelessWidget {
label: const Text('Sign in with Phone'),
),
spacer,
SupaNativeAuth(
authType: AuthType(
google: GoogleAuthType(
webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
iosClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com',
),
apple: true,
),
onSuccess: (response) {
Navigator.of(context).pushReplacementNamed('/home');
},
),
spacer,
SupaSocialsAuth(
colored: true,
socialProviders: OAuthProvider.values,
Expand Down
205 changes: 205 additions & 0 deletions lib/src/components/supa_native_auth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import 'dart:async';
FatumaA marked this conversation as resolved.
Show resolved Hide resolved

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:crypto/crypto.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';

class AuthType {
GoogleAuthType? google;
bool apple;

AuthType({this.google, required this.apple});
}

class GoogleAuthType {
/// Web Client ID and iOS Client ID that you registered with Google Cloud.
/// Needed for Sign in with Google
String webClientId;
String iosClientId;

GoogleAuthType({required this.webClientId, required this.iosClientId});
}

class SupaNativeAuth extends StatefulWidget {
/// Defines native auth providers to show in the form
final AuthType authType;

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

/// Method to be called when the auth action threw an excepction
final void Function(Object error)? onError;

/// Whether to show a SnackBar after a successful sign in
final bool showSuccessSnackBar;

const SupaNativeAuth({
super.key,
required this.authType,
required this.onSuccess,
this.onError,
this.showSuccessSnackBar = true,
});

@override
State<SupaNativeAuth> createState() => _SupaNativeAuthState();
}

class _SupaNativeAuthState extends State<SupaNativeAuth> {
/// Performs Google sign in on Android and iOS
Future<AuthResponse> _googleSignIn(webClientId, 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 'No Access Token found.';
}
if (idToken == null) {
throw 'No ID Token found.';
}

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

/// Performs Apple sign in on iOS or macOS
Future<AuthResponse> _appleSignIn() 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 credential.');
}

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

Widget _nativeAuthBtn(
{required Widget icon,
required String label,
required Color bgColor,
required Color textColor,
required Future Function() signInMethod}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
child: ElevatedButton.icon(
onPressed: () async {
try {
await signInMethod();
} on AuthException catch (error) {
if (widget.onError == null && context.mounted) {
context.showErrorSnackBar(error.message);
} else {
widget.onError?.call(error);
}
} catch (error) {
if (widget.onError == null && context.mounted) {
context
.showErrorSnackBar('Unexpected error has occurred: $error');
} else {
widget.onError?.call(error);
}
}
},
icon: icon,
label: Text(label),
style: ElevatedButton.styleFrom(
backgroundColor: bgColor,
foregroundColor: textColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
),
);
}

late final StreamSubscription<AuthState> _gotrueSubscription;

@override
void initState() {
super.initState();
_gotrueSubscription =
Supabase.instance.client.auth.onAuthStateChange.listen((data) {
final session = data.session;
if (session != null && mounted) {
widget.onSuccess.call(session);
if (widget.showSuccessSnackBar) {
context.showSnackBar('Successfully signed in!');
}
}
});
}

@override
void dispose() {
super.dispose();
_gotrueSubscription.cancel();
}

@override
Widget build(BuildContext context) {
final provider = widget.authType;

return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (provider.google != null)
_nativeAuthBtn(
icon: Image.asset(
'assets/logos/google_light.png',
package: 'supabase_auth_ui',
width: 36,
height: 36,
),
label: 'Sign in with Google',
signInMethod: () => _googleSignIn(
provider.google!.webClientId,
provider.google!.iosClientId,
),
bgColor: const Color.fromRGBO(242, 242, 242, 1),
textColor: Colors.black,
),
if (provider.apple)
_nativeAuthBtn(
icon: const Icon(
FontAwesomeIcons.apple,
),
label: 'Sign in with Apple',
signInMethod: _appleSignIn,
bgColor: Colors.black,
textColor: Colors.white,
),
],
);
}
}
3 changes: 1 addition & 2 deletions lib/src/components/supa_socials_auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension on OAuthProvider {
OAuthProvider.slack => FontAwesomeIcons.slack,
OAuthProvider.spotify => FontAwesomeIcons.spotify,
OAuthProvider.twitch => FontAwesomeIcons.twitch,
OAuthProvider.twitter => FontAwesomeIcons.x,
OAuthProvider.twitter => FontAwesomeIcons.xTwitter,
FatumaA marked this conversation as resolved.
Show resolved Hide resolved
_ => Icons.close,
};

Expand Down Expand Up @@ -200,7 +200,6 @@ class _SupaSocialsAuthState extends State<SupaSocialsAuth> {
);
break;
default:
// Handle other cases or provide a default behavior.
break;
}

Expand Down
1 change: 1 addition & 0 deletions lib/supabase_auth_ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export 'src/components/supa_reset_password.dart';
export 'src/components/supa_socials_auth.dart';
export 'src/components/supa_phone_auth.dart';
export 'src/components/supa_verify_phone.dart';
export 'src/components/supa_native_auth.dart';
dshukertjr marked this conversation as resolved.
Show resolved Hide resolved
export 'src/utils/supa_auth_action.dart';
export 'package:supabase_flutter/supabase_flutter.dart';
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
7 changes: 7 additions & 0 deletions text
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
url: 'https://xlvhpbcxavkjwhvvumjr.supabase.co',
anonKey:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzOTIyNDEzNSwiZXhwIjoxOTU0ODAwMTM1fQ.HZ4d4SdD40txeDY71d010S914KgrKYJNAzkrRU0C07w',
FatumaA marked this conversation as resolved.
Show resolved Hide resolved
webClientId:
'586179364408-p0cloviile92cndu00sshbm3qjc0vugt.apps.googleusercontent.com',
iosClientId:
'586179364408-9c4rhej5pdal0vggiteuqjcbsfuf8an8.apps.googleusercontent.com',
Loading