Flutter Firebase check if user is signed in anonymously - firebase

I am giving Users the option to sign in via Email and Password or anonymously.
Creating and signing in the User with Email and Password works fine, but I am having problems with displaying different contents for anonymously signed in users.
This is how I create them:
final _auth = FirebaseAuth.instance;
// Create Anonymous User
Future signInAnonymously() {
// return _auth.signInAnonymously();
_auth.signInAnonymously();
}
If a user chooses to get into the app anonymously in the Auth Form, I trigger following function:
Future submitAnon() async {
await signInAnonymously();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(),
),
);
}
I am using a Future Builder / Stream to listen to Auth Changes in my main.dart:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
return FutureBuilder(
// Initialize FlutterFire:
future: _initialization,
builder: (context, appSnapshot) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'myapp',
home: appSnapshot.connectionState != ConnectionState.done
? SplashScreen()
: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (ctx, userSnapshot) {
if (userSnapshot.connectionState ==
ConnectionState.waiting) {
return SplashScreen();
}
if (userSnapshot.hasData) {
return HomeScreen();
}
return AuthScreen();
}),
);
});
}
}
What I tried was passing a "isAnon=true" boolean to HomeScreen in submitAnon() but once the user re-opens the app, he is getting into the app from main.dart, where all the users that signed-up via email also get in. Is there a good way to check inside the App if the User is authenticated anonymously to build my widgets depending on that? e.g. showing Authentication Options instead of actual content that is only directed to e-mail&passsword users?

I think it's too late but here's the simplest way:
FirebaseAuth.instance.currentUser!.isAnonymous // returns a bool

To check if a user signed in to firebase anonymously. Firebase User class returned has an isAnonymous() method that returns true if the user signs in anonymously.
In your code, you can get a firebase user from the streambuilder snapshot. and check if isAnonymous is true. Like below:
StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (ctx, userSnapshot)
{
User user = userSnapshot.data;
print('Is user anonymous: ${user.isAnonymous}');
},
);
should print
'Is user anonymous: true'
if the user signs in anonymously and userSnapshot is not null.

I would propose you use the SharedPreferences library in order to keep track of of whether the user is anonymous or not. You should simply be able to store the isAnon in Shared Preferences to track whether the user is anonymous or not and from there you could use the Provider library to retrieve the isAnon value throughout the app. With a combinations of these two things you should be able to create the behavior you are looking for.
Please let me know if I misunderstood your question or need a further explanation!

Related

Firebase Flutter serve page based on login

I have 2 types of user and based on user details from firestore, I want to serve the type of homepage. In this code, as soon as the user logs in, the listener here prints out the user ID based on the user that just logged in.
Widget build(BuildContext context){
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if(snapshot.hasData){
print('reached here');
User user = snapshot.data!;
final authUser = MyUser.getUser(user.uid);
print(authUser);
return StudentHomeScreen();
}
else{
return LoginScreen();
}
},
),
);
}
From here, how can I query the user details based on the User ID. I cannot place async await here and query the user details to return different home screens. Please help me solve this.

Flutter/Firebase problem with logout after email user verification

i'am building an app with flutter and firebase..(I'am new in flutter development). What I did is a system where the user can signup and signin. Once the user signup an email verification is sent to the user email account. I'll try to put all the step below
signup
redirect to email verification widget..(this check if user has verified the email with a Timer) if yes, navigator push new Page (HomePage).
the main logic is this one.
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "My App Name",
debugShowCheckedModeBanner: false,
home: AuthService().handleAuth(),
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity
),
);
}}
this is what the AuthService().handleAuth() does:
handleAuth() {
return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
print(snapshot.hasData);
if (snapshot.hasData && emailVerificationNeeded() == false) {
return HomePage();
}
return LoginPage();
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
The verifyPage check with a timer if user has verified the email
Future<void> checkEmailVerified() async {
user = auth.currentUser!;
await user.reload();
if (user.emailVerified) {
timer.cancel();
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => HomePage()));
}
}
Until here everything work fine! Now for a test purpose in the HomePage there is a button that fire the following action
FirebaseAuth.instance.signOut();
if I click in the button and I logout the user still remain in the home page instead of going back to the LoginPage. This problem happen only the first time when the user is redirected in the Verification page. On all the other case if I'm verified and I'm logged in, once I click on the Logout button the user is redirect back to the Login page.
Any ideas?
Thank you all
The StreamBuilder isn't reacting to changes because you've navigated to another route.
You can use FirebaseAuth.instance.userChanges instead of FirebaseAuth.instance.authChanges as authChanges only "notifies about changes to the user's sign-in state (such as sign-in or sign-out)." while userChanges listens to different states such as sign-in, sign-out, different user & token refresh.
And then you can call FirebaseAuth.instance.currentUser!.reload(); when you verify the user which removes the need to navigate to the HomePage manually.
So when you call FirebaseAuth.instance.signOut, it automatically signs you out.
Code Changes:
Update handleAuth to this:
handleAuth() {
return StreamBuilder(
stream: FirebaseAuth.instance.userChanges(),
...
);
}
Update checkEmailVerified to this:
Future<void> checkEmailVerified() async {
user = auth.currentUser!;
await user.reload();
if (user.emailVerified) {
timer.cancel();
}
}

how to signout user from application when his data is deleted from Authentication tab in firebase console?

the whole purpose of this is that i want to signout the current user using app, immediately when i open firebase console and delete his account from Authentication tab.
i want the signout process to be done smoothly without any errors.
what i've tried so far:
in my main function():
runApp(MyApp());
and this is myApp class:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthService>(
create: (_) => AuthService(),
),
StreamProvider(
create: (context) => context.read<AuthService>().onAuthStateChanged,
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => HomeController(),
'/signUp': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signUp,
),
'/signIn': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signIn,
),
'/addGig': (BuildContext context) => Home(passedSelectedIndex: 1),
},
home: HomeController(),
));
}
}
and this is the HomeController():
class _HomeControllerState extends State<HomeController> {
AuthService authService = locator.get<AuthService>();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final firebaseUserUid = context.watch<String>();
if (firebaseUserUid != null) {
return HomePage();
return MaterialApp(
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => HomeController(),
'/signUp': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signUp,
),
'/signIn': (BuildContext context) => SignUpView(
authFormType: AuthFormType.signIn,
),
'/addGig': (BuildContext context) => Home(passedSelectedIndex: 1),
},
theme: fyreworkTheme(),
builder: EasyLoading.init(),
home: Home(passedSelectedIndex: 0),
);
} else {
return StartPage();
}
}
}
in the build function of MyApp class, the provider is listening to this stream from the AuthService class:
Stream<String> get onAuthStateChanged => _firebaseAuth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
so far so good...when i start or restart the App...every thing works as intended...no probs.
what i wanted to achieve is that if i open the firebase console / Authentication tab and i know the identifier of a specific user i want to delete his account and i delete it.
i thought that would signout that user from the app and navigates him to StartPage()..as the whole app is listening to onAuthStateChanged stream from the Provider.
but that didn't achieve what i was trying to do.
how can i sign out a specific user from the App after i delete his data from Authentication tab in firebase console ?
i hope i've described the problem and the desired goal well....
any help would be much appreciated.
Whenever the users sends any request to the firebase, revalidate the user to ensure that the user exists in the database or not. You may only if the user exists in the tables or not (not check validity of the tokens). Incase it does not redirect the user to login page. If it does then the user must have authenticated successfully before. This is a very simple solution.
For a complex one, you could use triggers in firebase and send a push notification to the app on deletion of the record of the user. Such that whenever the app receives the push notification that tells it to reauthenticate, the app should assume that the authentication data has been deleted or moved and would redirect the user to the login screen.
As an alternative approach, you could use firebase functions to trigger on deleteUser to delete the data server-side... this has the benefit of smaller-client, plus reduces the risk of some 'stuck' conditions should either the data or user delete fail.
exports.onAuthUserDelete = functions.auth.user().onDelete(onAuthUserDelete);
async function onAuthUserDelete(user: admin.auth.UserRecord) {
await admin.firestore().doc(`/users/${user.uid}`).delete();
functions.logger.info(`User firestore record deleted: uid=${user.uid}, displayName=${user.displayName}`);
}

How can you manually trigger onAuthStateChanged for Firebase Authentication in Flutter?

I have a an app built around a StreamBuilder listening for FirebaseAuth.instance.onAuthStateChanged
return new StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { ...
I'm trying to reload the user and use onAuthStateChanged to pick up when an email is verified. User.reload() is insufficient with the StreamBuilder. It's a similar issue to this onAuthStateChanged doesn't get called when email is verified in flutter
I thought a solution would be to force a logout and login but I'm at a loss on how to do that - e.g. step 2, login again to trigger onAuthStateChanged.
_auth.signOut(); //sign out and immediate sign in
final FirebaseUser user = await _auth.signIn----( //which signin function?
Have you tried refreshing auth token?
https://firebase.google.com/docs/auth/admin/manage-sessions
Use _auth.userChanges() stream instead of _auth.onAuthStateChanges()
return StreamBuilder(
stream: _auth.userChanges(),
builder: (ctx, userSnapshot) {
User? user = _auth.currentUser;
if (userSnapshot.hasData && user != null && user.emailVerified) {
// load screen on successful login
}
// fallback to auth screen
},
);
and on successful verification call refreshToken in your verification method
Future<void> verifyEmail() async {
user = _auth.currentUser;
await user!.reload();
if (user!.emailVerified) {
setState(() {
_isEmailBeingVerified = false;
});
user!.refreshToken;
}
}

How to implement a proper log out method in my flutter app?

FirebaseAuth firebaseAuth;
_signOut() async {
await firebaseAuth.signOut();}
onTap: () {
Navigator.of(context).pop();
_signOut();
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) => new LoginPage()));
},
This is the way I implemented it in my code, but I have a problem when I want to register a new user after logging out from the current user. After I complete the registration form that I created, in my firebase it doesn't create a new user, but it just updates the former user info, like Name, Surname etc. To create a new user I need to restart the app.I think the problem is with the sign out procedure, I am not sure.
You should have a root page that would take the following.
#override
Widget build(BuildContext context) {
switch (_authStatus){
case AuthStatus.notSignedIn:
return new LoginPage(auth: widget.auth, onSignedIn:_signedIn ,);
case AuthStatus.signedIn:
return new NifesHome(
auth: widget.auth,
onSignedOut: _signedOut,
);
}
}
what this does is if the user is signed out automatically he is taken to the root page which confirms the status of the user and redirects him to the login page.

Resources