I'm creating a app where I should be able of managing users access. The admin should have permissions of creating, deleting and editing users accounts.
I'm using firebase for creating users account.
Right now individually users can creating, editing and delete their accounts, but the problem is that the admin should do that, and not just the users.
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
import 'package:google_sign_in/google_sign_in.dart';
class UserLoader {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSIgnIn = new GoogleSignIn();
static final UserLoader _singleton = new UserLoader._internal();
FirebaseUser user;
factory UserLoader() {
return _singleton;
}
UserLoader._internal();
Future<FirebaseUser> signInWithEmailAndPassword(email, password) async {
if (user != null) return user;
_signInAnonymously().then((value) {
if (value != null) {
user = value;
}
}).catchError((e) {
return null;
});
if (user == null) {
FirebaseUser user = await _auth.signInWithEmailAndPassword(
email: email, password: password).catchError(
(onError)
{
print(onError);
});
return user;
} else {
return null;
}
}
Future<FirebaseUser> signInWithGoogle() async {
if (user != null) return user;
_signInAnonymously().then((value) {
if (value != null) {
user = value;
}
}).catchError((e) {
print(e.toString());
});
if (user == null) {
GoogleSignInAccount googleSignInAccount = await googleSIgnIn.signIn();
GoogleSignInAuthentication gSA = await googleSignInAccount.authentication;
FirebaseUser user = await _auth.signInWithGoogle(
idToken: gSA.idToken, accessToken: gSA.accessToken);
return user;
} else {
return null;
}
}
Future<FirebaseUser> _signInAnonymously() async {
if (user != null) return user;
user = await _auth.signInAnonymously();
return user;
}
Future signOut() async {
await _auth.signOut();
await googleSIgnIn.signOut();
user = null;
}
Future changePassword(email) async{
await _auth.sendPasswordResetEmail(email: email);
}
Future createNewUser(email){
_auth.createUserWithEmailAndPassword(email: email, password: "new_pass");
}
Future deleteUser(FirebaseUser firebaseUser){
firebaseUser.delete();
}
}
I think that Firebase Admin should do the trick but I'm not sure.
The Firebase Admin SDK is only available for use in trusted environments, such as your development machine, a server you control, or Cloud Functions for Firebase. It is (intentionally) not available for use in client-side apps, such as those deployed on Android or iOS, neither when you build those with native code, nor when you build them through Flutter.
The only option is to implement the functionality you want with the Admin SDK in a trusted environment, and expose an end-point to your Flutter app. If you end up doing this, make sure to secure access to the end-point so that only the admin users of your app can access it. For an example of how to secure access with Cloud Functions, see this sample in the functions-samples repo.
There isn't an SDK for even dart, so if your server-side application is written in Dart then you'll have to depend on another that is written in node.js, Python, C#, Go or Java as these are the only SDKs that are provided.
If you insist on doing this on your client-side app, then FCM HTTP v1 API is your best shot. You can obtain an access token with Google API Auth to access the Google APIs
Doc: Firebase Documentation
Download the admin SDK:
https://pub.dev/packages/firebase_admin
Use:
import 'package:firebase_admin/firebase_admin.dart';
main() async {
var app = FirebaseAdmin.instance.initializeApp(AppOptions(
credential: ServiceAccountCredential('service-account.json'),
));
String customerUserToken = await app.auth().createCustomToken("userId");
}
Related
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.
I successfully integrated facebook login in android and ios, I also getting facebook access token on login but getting null in token id. Below is the code
Future<UserCredential> signInWithFacebook() async {
// Trigger the sign-in flow
final LoginResult loginResult = await FacebookAuth.instance.login();
// Create a credential from the access token
final OAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(loginResult.accessToken!.token);
print(facebookAuthCredential.idToken); //Here getting null
// Once signed in, return the UserCredential
return FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
}
You won't get an idToken at this point with Facebook authentication flow, only accessToken. Use the following code skeleton to manage Facebook sign-in and evaluate the results at specific lines with breakpoints:
Future<UserCredential> signInWithFacebook() async {
final LoginResult loginResult = await FacebookAuth.instance.login();
if (loginResult.status == LoginStatus.success) {
final AccessToken accessToken = loginResult.accessToken!;
final OAuthCredential credential =
FacebookAuthProvider.credential(accessToken.token);
try {
return await FirebaseAuth.instance.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
// manage Firebase authentication exceptions
} catch (e) {
// manage other exceptions
}
} else {
// login was not successful, for example user cancelled the process
}
}
Then you can call this function with await, and once the future is completed, you can access user data:
final userCredential = await signInWithFacebook();
if (userCredential != null) {
// here you will have your Firebase user in:
// userCredential.user
final idToken = userCredential.user!.getIdToken();
}
In my flutter app, am trying to check if a user is logged in or not before authenticating the user in firebase, so if he is not then do not authenticate
Future<String> loginUserWithGoogle() async {
String returnValue = "error";
GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: [
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);
UserData _user = UserData();
try {
GoogleSignInAccount _googleUser = await _googleSignIn.signIn();
GoogleSignInAuthentication _googleAuth = await _googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(idToken: _googleAuth.idToken, accessToken: _googleAuth.accessToken);
UserCredential _authResult = await _auth.signInWithCredential(credential);
if (_authResult.additionalUserInfo.isNewUser) {
String userGoogleName = _authResult.user.displayName;
List userSplitName = userGoogleName.split(" ");
String userGoogleFirstName = userSplitName.first;
String userGoogleLastName = userSplitName.last;
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('googleUid', _authResult.user.uid);
await prefs.setString('googleEmail', _authResult.user.email);
await prefs.setString('googleFirstName', userGoogleFirstName);
await prefs.setString('googleLastName', userGoogleLastName);
await prefs.setString('googleUserType', "user");
returnValue = "new";
} else {
_currentUser = await RealDatabase().getUserData(_authResult.user.uid);
if (_currentUser != null) {
returnValue = "success";
}
}
} on PlatformException catch (e) {
returnValue = e.message;
} catch (e) {
print(e);
}
return returnValue;
}
}
Here what I want to check is that if it is a new user then save his google data in sharedpreference and take him to another page where he can complete some other registration and then sign him in. but what this code does is that if it is a new user it will authenticate, save the info in sharedpeference and then take him to the page and if maybe that user decided to go back to the previous page (since i use Navigator.push(context)) and still click the google sign in button again then it will take him to the home screen without him completing the other registration I want him to do because it already authenticated him first. So please is there a way to do this without first authenticating the user.
You can use stream provider to control if user logged in or not.
Here is an example how to use stream provider in your project;
https://flutterbyexample.com/lesson/stream-provider.
I have created an app with Google login with firestore support and recently I have updated all the dependencies to a newer version. Here There are some problem with the old code I don't know what's the problem.
Future<String> getCurrentUser() async {
var user = await _auth.currentUser();
if (user == null) {
return null;
} else {
return user.uid;
}
}
It shows error near _auth.currentUser(); and the error is The expression doesn't evaluate to a function, so it can't be invoked. I have searched for it but nothing worked. This is the First problem.
The Second problem arises with rxdart package. Due to new packages Observable is not supported and Searched it for internet and tried using Stream But they are not working.
The Source Code is here for rxdart problem.
class AuthService {
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _db = FirebaseFirestore.instance;
Stream<User> user;
Stream<Map<String, dynamic>> profile;
String uuid;
AuthService() {
Stream<User> user = Observable(_auth.authStateChanges);
profile = user.switchMap((User u) {
if (u != null) {
return _db
.collection('users')
.doc(u.uid)
.snapshots()
.map((snap) => snap.data);
} else {
return Observable.just({});
}
});
}
}
It shows error near return Observable.just({}); and next problem arises with this line Stream<User> user = Observable(_auth.authStateChanges);
Help me sort this out and Help me understand my issues.
currentUser is no longer a method, and no longer asynchronous. So the correct invocation now is:
var user = _auth.currentUser;
I recommend keeping the migration guide handy while upgrading your code to this latest version, as there are quite a few of these changes.
In newer versions of the Firebase Auth SDK for Flutter, if you want a stream of auth state events, you should set up an auth state stream using authStateChanges, as shown in the documentation.
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
is it possible to delete firebase account in authentication on flutter? if yes, how to do that? I have been search but not found the way.
Firestore.instance.collection("users").document(uid).delete().then((_){
// delete account on authentication after user data on database is deleted
});
Using flutter, if you want to delete firebase accounts together with the associated firestore user collection document, the following method works fine. (documents in user collection named by the firebase uid).
Database Class
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection =
Firestore.instance.collection('users');
Future deleteuser() {
return userCollection.document(uid).delete();
}
}
Use Firebase version 0.15.0 or above otherwise, Firebase reauthenticateWithCredential() method throw an error like { noSuchMethod: was called on null }.
Authentication Class
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future deleteUser(String email, String password) async {
try {
FirebaseUser user = await _auth.currentUser();
AuthCredential credentials =
EmailAuthProvider.getCredential(email: email, password: password);
print(user);
AuthResult result = await user.reauthenticateWithCredential(credentials);
await DatabaseService(uid: result.user.uid).deleteuser(); // called from database class
await result.user.delete();
return true;
} catch (e) {
print(e.toString());
return null;
}
}
}
Then use the following code inside the clickable event of a flutter widget tree to achieve the goal;
onTap: () async {
await AuthService().deleteUser(email, password);
}
Code for deleting user:
FirebaseUser user = await FirebaseAuth.instance.currentUser();
user.delete();
To delete a user account, call delete() on the user object.
For more on this, see the reference documentation for FirebaseUser.delete().
User user = FirebaseAuth.instance.currentUser;
user.delete();
From this you can delete user