Flutter & Firebase - Apple Sign In not working - firebase

I am trying to set up with apple sign in with the following code. When tapping the button nothing happens.
I get the following error:
Tried calling: authorizationCode
flutter: NoSuchMethodError: The getter 'authorizationCode' was called on null.
Receiver: null
How would I fix this?
Future<bool> get appleSignInAvailable => AppleSignIn.isAvailable();
Future<User> appleSignIn() async {
try {
final AuthorizationResult appleResult =
await AppleSignIn.performRequests([
AppleIdRequest(requestedScopes: [Scope.email, Scope.fullName])
]);
if (appleResult.error != null) {
// handle errors from Apple
}
final AuthCredential credential =
OAuthProvider('apple.com').credential(
accessToken:
String.fromCharCodes(appleResult.credential.authorizationCode),
idToken: String.fromCharCodes(appleResult.credential.identityToken),
);
UserCredential result =
await Global.fbAuth.signInWithCredential(credential);
User user = result.user;
updateUserData(user);
return user;
} catch (error) {
print(error);
return null;
}
}

If you are using iOS 14 simulator, this may be due to the issue reported here. The workaround would be to use a real device for debugging or use the iOS 13 simulator
Also, see this thread for reference

I have faced same kind of issue in one of my projects.You have to add your sha key in firebase and facebook to resolve the issue.
Also you can try the below code,
import 'package:apple_sign_in/apple_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
class AuthService {
final _firebaseAuth = FirebaseAuth.instance;
Future<User> signInWithApple({List<Scope> scopes = const []}) async {
// 1. perform the sign-in request
final result = await AppleSignIn.performRequests(
[AppleIdRequest(requestedScopes: scopes)]);
// 2. check the result
switch (result.status) {
case AuthorizationStatus.authorized:
final appleIdCredential = result.credential;
final oAuthProvider = OAuthProvider('apple.com');
final credential = oAuthProvider.credential(
idToken: String.fromCharCodes(appleIdCredential.identityToken),
accessToken:
String.fromCharCodes(appleIdCredential.authorizationCode),
);
final authResult = await _firebaseAuth.signInWithCredential(credential);
final firebaseUser = authResult.user;
if (scopes.contains(Scope.fullName)) {
final displayName =
'${appleIdCredential.fullName.givenName} ${appleIdCredential.fullName.familyName}';
await firebaseUser.updateProfile(displayName: displayName);
}
return firebaseUser;
case AuthorizationStatus.error:
throw PlatformException(
code: 'ERROR_AUTHORIZATION_DENIED',
message: result.error.toString(),
);
case AuthorizationStatus.cancelled:
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
default:
throw UnimplementedError();
}
}
}

Related

The getter 'uid' not defined

i'm trying to create a food track app on android studio, it's my first time and i'm working with firebase_auth 3.3.12. my code in the aut.dart is:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:my_firstapp/models/user_model.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AuthService();
// create user object based on FirebaseUser.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
// auth change user stream
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_userFromUser);
}
Future<UserModel> getUser() async {
User user = await _auth.currentUser();
return _userFromUser(user);
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign up with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
// create a new user document in database
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch(e){
print(e.toString());
return null;
}
}
}
However i'm getting 2 errors:
-The getter 'uid' isn't defined for the type 'Stream';
-The expression "await _auth.currentUser()" doesn't evaluate to a function, so it can't be invoked.
How can i rewrite the code? thanks
The _auth.currentUser is not a function (it used to be, but changed about a year ago), but rather a property. It also isn't asynchronous, so you don't need await nor to return a Future.
So:
UserModel getUser() {
User user = _auth.currentUser;
return _userFromUser(user);
}
In this code, your argument is capitalised ('User') but in the code block you write 'user'.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
Furthermore, for _auth.currentUser(), you do not need to use await as it does not return a future.

Google Sign In current user return null Flutter

I want to integrate my app with Calendar API from Google. And in order to use it, I have to have an AuthClient (which is obtained from _googleSignIn.authenticatedClient();). The problem is, my GoogleSignIn().currentUser always return null and I don't know why. I already use Firebase Auth and Google Sign In.
This is my signInWithGoogle method:
Future signInWithGoogle() async {
try {
await GoogleSignIn().disconnect();
await FirebaseAuth.instance.signOut();
} catch (e) {
print(e.toString());
}
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await GoogleSignIn(scopes: [CalendarApi.calendarScope]).signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth =
await googleUser!.authentication;
// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
UserCredential result =
await FirebaseAuth.instance.signInWithCredential(credential);
User user = result.user!;
// note: this line always return null and I don't know why
print('current user auth ${GoogleSignIn().currentUser.toString()}');
return _userFromFirebaseUser(user);
}
Did I do something wrong in my code? Any help will be appreciated, thank you!
I also had the issue of GoogleSignIn().currentUser always being null but managed to (finally!) fix it by only initialising GoogleSignIn() once.
For those who want more details: I did this by creating a class called AuthManager that handles everything authentication-related, and making GoogleSignIn one of the parameters required to initialise it (since I'm using Firebase, this was the other parameter):
class AuthManager {
final FirebaseAuth _auth;
final GoogleSignIn _googleSignIn;
AuthManager(this._auth, this._googleSignIn);
Future signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
// etc....
}
GoogleSignInAccount? get googleAccount {
return _googleSignIn.currentUser;
}
}
And I initiaised by AuthManager class ONCE at the top of my app in a Provider, meaning that I can access it anywhere in my app.
In main.dart:
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
// To use AuthManager throughout app without initialising it each time
Provider<AuthManager>(
create: (_) => AuthManager(
FirebaseAuth.instance,
GoogleSignIn(scopes:
// Put whatever scopes you need here
),
),
),
// etc...
(Note: I used MultiProvider as I had other things I wanted to put, but if you only have one, you can obviously just go straight to Provider).
Now I can successfully get the current google user by getting googleAccount through my AuthManager class.

Firebase Authentication in Flutter Check if user is a new user

I have implemented the following login method and I am trying to use the isNewUser function to push a new screen:
Future<void> googleLogin() async {
try {
final googleUser = await GoogleSignIn().signIn();
if (googleUser == null) return;
final googleAuth = await googleUser.authentication;
final authCredential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
UserCredential userCredential =
await FirebaseAuth.instance.signInWithCredential(authCredential);
if (userCredential.additionalUserInfo!.isNewUser) {
return const SignUpNewUser();
}
} on FirebaseAuthException catch (e) {
AlertDialog(
title: const Text("Error"),
content: Text('Failed to sign in with Google: ${e.message}'),
);
}
}
I get the following error:
A value of type 'SignUpNewUser' can't be returned from the method 'googleLogin' because it has a return type of 'Future<void>'.
I'm pretty sure that I placed it in the correct spot to implement the function, but I have no idea how to do it in a Future.
The problem is in the return type, you need change the type from void to dynamic.
Future<dynamic> googleLogin() async {...}
you can return a widget directly but that doesn't makes sense so you need to use Navigator in order to push to a new screen.
Add context as parameter in the method googleLogin()
Use this Navigator.push(context,MaterialPageRoute(builder: (context) =>your_new_screen()),);
in the condition userCredential.additionalUserInfo!.isNewUser
In the above replace your_new_screen() with the widget you have returned before ie. SignUpNewUser()

Flutter firebase auth web not persistent

I have the following auth provider, and I can sign the user in and the app state changes. But when I do a sign in I notice that nothing is stored in the local storage and when I refresh the page the authStateChanges gives null for the user. I also just tried tho get the current user as a Future which also returns null. What am I doing wrong?
const String oneTimePasswordFunctionName = "requestOneTimePassword";
const String signInWithOneTimePasswordFunctionName = "signInWithOneTimePassword";
class FirebaseAuthProvider extends AuthProvider {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final FirebaseFunctions _firebaseFunctions = FirebaseFunctions.instance;
Stream<AuthUser> get user {
return _firebaseAuth.authStateChanges().asyncMap((firebaseUser) {
//firebaseUser is always null on refresh
return firebaseUser == null ? AuthUser.empty : firebaseUser.toUser();
});
}
Future<void> requestOneTimePassword({required String email}) async {
final data = {"email": email};
final cloudFunction = _firebaseFunctions.httpsCallable(oneTimePasswordFunctionName);
await cloudFunction.call(data);
}
Future<void> signInWithOneTimePassword({required String email, required String oneTimePassword}) async {
final data = {"email": email, "oneTimePassword": oneTimePassword};
final cloudFunction = _firebaseFunctions.httpsCallable(signInWithOneTimePasswordFunctionName);
final result = await cloudFunction.call(data);
final jwt = result.data["data"]["jwt"] as String;
if (jwt.isEmpty) return;
await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);
await _firebaseAuth.signInWithCustomToken(jwt);
}
Future<void> signOut() async {
try {
await _firebaseAuth.signOut();
} on Exception {
throw SignOutException();
}
}
}
extension on User {
FutureOr<AuthUser> toUser() async {
final tokenResult = await this.getIdTokenResult(true);
final claims = tokenResult.claims?.map<String, String>((key, value) => MapEntry(key, value.toString()));
return AuthUser(
id: uid,
email: Email.dirty(email ?? ''),
name: displayName ?? '',
photoURL: Uri.parse(photoURL ?? ''),
claims: claims ?? const {},
);
}
}
I really need it to work with the authStateChanges stream, because I want it also to work when the user does a sign out.
I am using the firebase auth emulator. This is my main
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FirebaseAuth.instance.useAuthEmulator("localhost", 9099);
FirebaseFirestore.instance.settings = Settings(
host: "localhost:8080",
sslEnabled: false,
persistenceEnabled: false,
);
FirebaseFunctions.instance.useFunctionsEmulator("localhost", 5001);
Future<dynamic> Function() initializeFirebase = () async {
if (Firebase.apps.length == 0) await Firebase.initializeApp();
};
await Future.wait([
initializeFirebase(),
EasyLocalization.ensureInitialized(),
]);
runApp(_buildAppWithDependencies());
}
I am testing in the latest version of google chrome
Versions
flutter: 2.5.3 (stable)
firebase_core: ^1.10.0
firebase_auth: ^3.2.0

How to Sign In with Twitter using Firebase_Auth with Flutter

How should one program a Sign In with Twitter feature using firebase_auth and Flutter?
I see a few examples using flutter_twitter_login or flutter_twitter, however they use a now Deprecated API and folks complain about Apple Store Rejection.
Firebase Auth offers a TwitterAuthProvider, but the following code remains incomplete:
final AuthCredential credential = TwitterAuthProvider.getCredential(
authToken: twitterAccessToken,
authTokenSecret: twitterAccessTokenSecret,
);
final AuthResult result = await auth.signInWithCredential(credential);
I was able to solve this using 3 resources:
The Flutter Facebook Sign In (with Firebase) in 2020 article
The Log in with Twitter guide
The Dart OAuth1 library
Ultimately, I was able to completely remove the flutter_twitter package, yet still support Sign in with Twitter.
Similar to the CustomWebView outlined in the Facebook solution, I created a TwitterLoginScreen like:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:oauth1/oauth1.dart';
/// Twitter Login Screen.
/// See [Log in with Twitter](https://developer.twitter.com/en/docs/basics/authentication/guides/log-in-with-twitter).
class TwitterLoginScreen extends StatefulWidget {
final twitterPlatform = Platform(
'https://api.twitter.com/oauth/request_token', // temporary credentials request
'https://api.twitter.com/oauth/authorize', // resource owner authorization
'https://api.twitter.com/oauth/access_token', // token credentials request
SignatureMethods.hmacSha1, // signature method
);
final ClientCredentials clientCredentials;
final String oauthCallbackHandler;
TwitterLoginScreen({
#required final String consumerKey,
#required final String consumerSecret,
#required this.oauthCallbackHandler,
}) : clientCredentials = ClientCredentials(consumerKey, consumerSecret);
#override
_TwitterLoginScreenState createState() => _TwitterLoginScreenState();
}
class _TwitterLoginScreenState extends State<TwitterLoginScreen> {
final flutterWebviewPlugin = FlutterWebviewPlugin();
Authorization _oauth;
#override
void initState() {
super.initState();
// Initialize Twitter OAuth
_oauth = Authorization(widget.clientCredentials, widget.twitterPlatform);
flutterWebviewPlugin.onUrlChanged.listen((url) {
// Look for Step 2 callback so that we can move to Step 3.
if (url.startsWith(widget.oauthCallbackHandler)) {
final queryParameters = Uri.parse(url).queryParameters;
final oauthToken = queryParameters['oauth_token'];
final oauthVerifier = queryParameters['oauth_verifier'];
if (null != oauthToken && null != oauthVerifier) {
_twitterLogInFinish(oauthToken, oauthVerifier);
}
}
});
_twitterLogInStart();
}
#override
void dispose() {
flutterWebviewPlugin.dispose();
super.dispose();
}
Future<void> _twitterLogInStart() async {
assert(null != _oauth);
// Step 1 - Request Token
final requestTokenResponse =
await _oauth.requestTemporaryCredentials(widget.oauthCallbackHandler);
// Step 2 - Redirect to Authorization Page
final authorizationPage = _oauth.getResourceOwnerAuthorizationURI(
requestTokenResponse.credentials.token);
flutterWebviewPlugin.launch(authorizationPage);
}
Future<void> _twitterLogInFinish(
String oauthToken, String oauthVerifier) async {
// Step 3 - Request Access Token
final tokenCredentialsResponse = await _oauth.requestTokenCredentials(
Credentials(oauthToken, ''), oauthVerifier);
final result = TwitterAuthProvider.getCredential(
authToken: tokenCredentialsResponse.credentials.token,
authTokenSecret: tokenCredentialsResponse.credentials.tokenSecret,
);
Navigator.pop(context, result);
}
#override
Widget build(BuildContext context) {
return WebviewScaffold(
appBar: AppBar(title: Text("Twitter Login")),
url: "https://twitter.com",
);
}
}
Then, the AuthCredential result from this screen can be passed to FirebaseAuth.signInWithCredential.
To sign in with Twitter do the following:
Future<FirebaseUser> loginWithTwitter() async {
var twitterLogin = new TwitterLogin(
consumerKey: 'key',
consumerSecret: 'secretkey',
);
final TwitterLoginResult result = await twitterLogin.authorize();
switch (result.status) {
case TwitterLoginStatus.loggedIn:
var session=result.session;
final AuthCredential credential= TwitterAuthProvider.getCredential(
authToken: session.token,
authTokenSecret: session.secret
);
FirebaseUser firebaseUser=(await firebaseAuth.signInWithCredential(credential)).user;
print("twitter sign in"+firebaseUser.toString());
break;
case TwitterLoginStatus.cancelledByUser:
break;
case TwitterLoginStatus.error:
break;
}
Use twitterlogin and pass the consumer key and consumer secret key, then use the method getCredential() and signInWithCredential to log in.
They have shared a common sample in the home page itself, only the 'sign in provider' changes, rest is same for all (google, fb and twitter). the result has a user property which will return the user details, check with the below code
final AuthCredential credential = TwitterAuthProvider.getCredential(
authToken: twitterAccessToken,
authTokenSecret: twitterAccessTokenSecret,
);
final AuthResult result = await auth.signInWithCredential(credential);
final FirebaseUser user = result.user;
print("signed in " + user.displayName);
This worked for me. (Referred from https://firebase.flutter.dev/docs/auth/social#twitter)
import 'package:twitter_login/twitter_login.dart';
Future<UserCredential> signInWithTwitter() async {
// Create a TwitterLogin instance
final twitterLogin = new TwitterLogin(
apiKey: '<your consumer key>',
apiSecretKey:' <your consumer secret>',
redirectURI: '<your_scheme>://'
);
// Trigger the sign-in flow
final authResult = await twitterLogin.login();
// Create a credential from the access token
final twitterAuthCredential = TwitterAuthProvider.credential(
accessToken: authResult.authToken!,
secret: authResult.authTokenSecret!,
);
// Once signed in, return the UserCredential
return await FirebaseAuth.instance.signInWithCredential(twitterAuthCredential);
}
This wasn't working initially for me.
Things I had to change in order for this to work.
Request for Elevated Permissions in Twitter Developer Portal.
Create a custom scheme (callback URL) and configure the ios/android files accordingly. As given in (https://pub.dev/packages/twitter_login).
Also configure this callback URL in the Twitter dev portal.

Resources