Flutter Problem in FireAuth Email Verification - flutter

I tried to make email verification and the problem is that the app registered the user without sending the email
this is my Function code
Future<String> registerUsingEmailAndPassword(String email, String password) async {
UserCredential userCredential = await firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
User user = userCredential.user;
if(!user.emailVerified){
await user.sendEmailVerification();
print(user.email);
}
return user.uid;
}
and this is my log
W/System ( 3599): Ignoring header X-Firebase-Locale because its value was null.
W/System ( 3599): Ignoring header X-Firebase-Locale because its value was null.
D/FirebaseAuth( 3599): Notifying id token listeners about user ( Q8d7R7mE9dOv5lAbkZH2UCiwmKA2 ).
D/FirebaseAuth( 3599): Notifying auth state listeners about user ( Q8d7R7mE9dOv5lAbkZH2UCiwmKA2 ).
W/System ( 3599): Ignoring header X-Firebase-Locale because its value was null.
I/flutter ( 3599): wasee.2132254#gmail.com
EDIT
when I check my email I found the messages, so, wanna do something like waiting screen that waiting for the user verification and then resumes the registration process
any idea?

I think you could check if Firebase User user.isEmailVerified is true

Related

I am facing issue in storing uid in database

This is error
error getting token java.util.concurrent.ExecutionException:
com.google.firebase.internal.api.FirebaseNoSignedInUserException:
Please sign in before trying to get a token. W/NetworkRequest(10820):
no auth token for request W/NetworkRequest(10820): No App Check token
for request. E/flutter (10820):
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled
Exception: Null check operator used on a null value E/flutter (10820):
#0 _signupState.build. (package:ecommerce/Auth/signup.dart:161:28) E/flutter (10820):
E/flutter (10820):
I think error is in uid because when use id it signup successfully. If my code is wrong then please tell how I can store.
This is my code
final uid =auth.currentUser!.uid ;
auth.createUserWithEmailAndPassword(email: emailcontroller.text.toString(), password: passcontroller.text.toString()).then((signin){
ref.doc(uid).set({
"id":uid,
"name":namecontroller.text.toString(),
"email":emailcontroller.text.toString(),
"password":passcontroller.text.toString(),
"phone":phonecontroller.text.toString(),
"imageurl":url1.toString()
})
You are using uid before signUp in App. Try this code:
var uid;
await auth.createUserWithEmailAndPassword(email: emailcontroller.text.toString(), password: passcontroller.text.toString()).then((signin){
uid =auth.currentUser!.uid ;
ref.doc(uid).set({
"id":uid,
"name":namecontroller.text.toString(),
"email":emailcontroller.text.toString(),
"password":passcontroller.text.toString(),
"phone":phonecontroller.text.toString(),
"imageurl":url1.toString()
})

Unhandled Exception: NoSuchMethodError: Class 'FirebaseAuthException' has no instance getter '_message'

Help me pls.
I have this error.
10Q
Unhandled Exception: NoSuchMethodError: Class 'FirebaseAuthException' has no instance getter '_message'.
E/flutter ( 5700): Receiver: Instance of 'FirebaseAuthException'
E/flutter ( 5700): Tried calling: _message
await _auth
.signInWithEmailAndPassword(
email: _emailTextEditingController.text.trim(),
password: _passwordTextEditingController.text.trim(),
)
.then((authUser) {
setState(() {
firebaseUser = authUser.user;
});
}).catchError((error) {
showDialog(
context: context,
builder: (c) {
return ErrorAlertDialog(
message: error._message == '[firebase_auth/user-not-found] There is no user record corresponding to this identifier. The user may have been deleted.'
? 'Email or password incorrect' : 'Error',
);
});
});
error._message == '[firebase_auth/user-not-found] There is no user record corresponding to this identifier. The user may have been deleted.'
? 'Email or password incorrect' : 'Error',
You are being told that there is no getter named _message for FirebaseAuthException. If you go to the code for that class, or the documentation (here) and look at the methods you have available to you,
_message is not one.
There is one there (getErrorCode) that you should be able to compare with much easier.
I think that the _message was called on null, I am not sure about this.

Flutter: Http Status Error 403 while downloading the file

In my Flutter application, I have to download multiple files and have to store it in application storage.
For that I am using Dio and added below plugin dependencies in pubspec.yaml file :
dio: any
path_provider: any
In main.dart, I have below variables :
String uri =
'https://i.picsum.photos/id/698/200/200.jpg?hmac=EElVlYPe8BAq1Btf4bWUxP9NoQP01_e8LTUzpbdKbgY';
bool downloading = false;
where uri is the URL for an Image online.
Inside initState() method, called the downloadFile() method, which is as below :
Future<void> downloadFile() async{
Dio dio= Dio();
try{
var dir= await getApplicationDocumentsDirectory();
await dio.download(uri, "${dir.path}/myimg.jpg", onReceiveProgress: (rec,total){
setState(() {
downloading=true;
});
});
}catch(e)
{
print("Error >> "+e.toString());
}
setState(() {
downloading=false;
print("Download Completes");
});
}
But, I am getting below error :
HTTP Status Error 403
MSG_WINDOW_FOCUS_CHANGED 1 1
D/InputMethodManager(15750): startInputInner - Id : 0
I/InputMethodManager(15750): startInputInner - mService.startInputOrWindowGainedFocus
D/InputMethodManager(15750): startInputInner - Id : 0
I/flutter (15750): DioError [DioErrorType.response]: Http status error [403]
I/flutter (15750): Source stack:
I/flutter (15750): #0 DioMixin.fetch (package:dio/src/dio_mixin.dart:488:35)
I/flutter (15750): #1 DioMixin.request (package:dio/src/dio_mixin.dart:483:12)
I/flutter (15750): #2 DioForNative.download (package:dio/src/entry/dio_for_native.dart:84:24)
I/flutter (15750): #3 _MyHomePageState.downloadFile (package:downloadfiles/main.dart:82:17)
I/flutter (15750):
I/flutter (15750): Download Completes
I am getting this error while running the app in android device, I have manually given File storage permission from settings for the application.
Also updated AndroidManifest.xml file as below :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/>
Still am getting above error. What might causing the issue?
The HTTP 403 Forbidden response status code indicates that the server understands the request but refuses to authorize it.
This status is similar to 401, but for the 403 Forbidden status code, re-authenticating makes no difference. The access is permanently forbidden and tied to the application logic, such as insufficient rights to a resource.
String uri =
'https://i.picsum.photos/id/698/200/200.jpg?hmac=EElVlYPe8BAq1Btf4bWUxP9NoQP01_e8LTUzpbdKbgY';
This URL Asks for authorization in headers.
So use Authorization via headers for calling API.
Like this,
Future<void> downloadFile() async {
Dio dio = Dio();
try {
var dir = await getApplicationDocumentsDirectory();
await dio.download(uri,
options: Options(
headers: {"Authorization": "YOUR_AUTH_KEY HERE"},
),
"${dir.path}/myimg.jpg",
onReceiveProgress: (rec, total) {
setState(() {
downloading = true;
});
});
} catch (e) {
print("Error >> " + e.toString());
}
setState(() {
downloading = false;
print("Download Completes");
});
}

Flutter Send user to another screen if response is 401 or 403

I have functions witch I use to get data from the api, and the user can be logged in, but the admin can change the password of the employee, so when the user comes back to the app the besic auth token that is saved in local storage isnt the same as the one that the admin changed to since the admin changed the password, so all i need is that if there is status code such as 401 or 403 to send the user back to the log in screen. And if the response code is 500 to show like an image to come back later.
Future<Response> fetchWorkingLocationData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/v1/projects';
basicAuth = 'Basic ' +
base64Encode(
utf8.encode('${phoneNumberController.text}:${passwordController.text}'),
);
response2 = await http.get(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString()
},
);
return response2;
}
You can write custom logic based on the statusCode
So something like:
if (response2.statusCode == HttpStatus.unauthorized) {
Navigator.pushNamed(
context,
'login',
);
}
If you don't have context for the Navigator, check out https://medium.com/flutter-community/navigate-without-context-in-flutter-with-a-navigation-service-e6d76e880c1c

How to handle the authentication token and and GraphQL client with Riverpod?

We are using GraphQL to connect our backend with our mobil application. We are using Riverpod to, among other things, handle global state, dependency injection, etc.
We are using a provider to handle the GraphQL cliente without any authentication header at first while the user is not authentication (this will handle unauthenticated request like login or registering), after the user is authenticated, a token provider who provides the token is updated and it must update the client provider who is user for every repository on the application:
final StateProvider<String> tokenProvider =
StateProvider<String>((_) => '', name: "Token Provider");
final graphQLClientProvider = Provider<GraphQLClient>(
(ref) {
final String token = ref.watch(tokenProvider).state;
final Link _link = HttpLink(
'http://192.168.1.36:1337/graphql',
defaultHeaders: {
if (token != '') 'Authorization': 'Bearer $token',
});
return GraphQLClient(
cache: GraphQLCache(),
link: _link,
);
},
name: "GraphQL Client Provider",
);
There is too problems here:
After updating the token, the client is changed, and from this, the function who updates the token does not finish in a proper way:
[ +141 ms] E/flutter ( 8361): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Bad state: Tried to use AuthNotifier after `dispose` was called.
[ ] E/flutter ( 8361):
[ ] E/flutter ( 8361): Consider checking `mounted`.
[ ] E/flutter ( 8361):
[ ] E/flutter ( 8361): #0 StateNotifier._debugIsMounted.<anonymous closure> (package:state_notifier/state_notifier.dart:128:9)
[ ] E/flutter ( 8361): #1 StateNotifier._debugIsMounted (package:state_notifier/state_notifier.dart:135:6)
[ ] E/flutter ( 8361): #2 StateNotifier.state= (package:state_notifier/state_notifier.dart:155:12)
[ ] E/flutter ( 8361): #3 AuthNotifier.signIn (package:thesis_cancer/features/auth/application/auth.notifier.dart:84:7)
[ ] E/flutter ( 8361): <asynchronous suspension>
[ ] E/flutter ( 8361): #4 _LoginCardState._submit (package:flutter_login/src/widgets/auth_card.dart:503:15)
[ ] E/flutter ( 8361): <asynchronous suspension>
[ ] E/flutter ( 8361):
It means when the token is updated, the authentication notifier who receives the repository with depends on the provided client is updated/changes (following the chain):
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>(
(ref) => AuthNotifier(
authRepository: ref.watch(authRepositoryProvider),
dataStore: ref.watch(dataStoreRepositoryProvider),
profileRepository: ref.watch(profileRepositoryProvider),
tokenController: ref.watch(tokenProvider.notifier),
userController: ref.watch(userEntityProvider.notifier),
),
name: "Authentication Notifier Provider",
);
The notification function who breaks at the conditional(if):
Future<String?> signIn({
required String username,
required String password,
}) async {
try {
final Map<String, dynamic> rawUser = await authRepository.signIn(
identifier: username, password: password) as Map<String, dynamic>;
final User sessionUser = User.fromJson(rawUser);
if (sessionUser.confirmed != false) {
tokenController.state = sessionUser.token!; <-- Bug begins here
}
await dataStore.writeUserProfile(sessionUser);
userController.state = sessionUser;
state = const AuthState.loggedIn();
} on LogInFailureByBadRequest {
return "E-posta veya şifre geçersiz.";
} on LogInFailure catch (error) {
return error.toString();
}
}
While the client appears to be changed when the token is updated, the client itself does not reflect the change (it does not include the authentication header with the token) and thus breaks the GraphQL client:
Following the comments on the issue we opened, we added the ProviderReference to our notifier (following this question) but it does not work:
AuthNotifier(
this._ref, {
required this.authRepository,
required this.profileRepository,
required this.dataStore,
required this.tokenController,
required this.userController,
}) : super(const AuthState.loading());
final ProviderReference _ref;
final ProfileRepository profileRepository;
final AuthRepository authRepository;
final DataStoreRepository dataStore;
final StateController<User?> userController;
final StateController<String> tokenController;
At this point, we don't understand what's the problem here.
UPDATE
We removed this changes both in the StateNotifier and its provider.
Getting the client at repository by read does not avoid the error:
What's the proper way to handle the authorization token and the GraphQL client with Riverpod?
Thank you.
The problem is the authRepositoryProvider:
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
In this fragment, the authRepositoryProvider is using a GraphQLClient without token. When the token is updated, the graphQLClientProvider state is well updated, but the authRepositoryProvider isn't because you are using ref.read avoiding the rebuild of the authRepositoryProvider state. This implies that the authRepositoryProvider knows a disposed (and unmounted) state of graphQLClientProvider and that's why the error message.
There are 2 solutions:
Using ref.watch:
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.watch(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
Passing the ref.read as parameter:
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read),
name: 'Auth Repository Provider',
);
class AuthRepository {
AuthRepository(this.read)
final Reader read;
GraphQLClient get client => read(graphQLClientProvider);
}
Be care about using read and watch, the first one does not react to the changes but could be used anywhere, while the second one reacts to a provider's state update but only can be used in another provider constructor or in a widget that provides access to a watch or ref.watch depending on the version of Riverpod.
In addition, there is another problem in your code:
final User sessionUser = User.fromJson(rawUser);
if (sessionUser.confirmed != false) {
// In this moment, the token is updated,
// then the QrahpQLClient is updated,
// then the AuthNotifier is recreated,
// i.e the current is disposed
// and now becomes the previous
tokenController.state = sessionUser.token!; <-- Bug begins here
}
await dataStore.writeUserProfile(sessionUser);
userController.state = sessionUser;
// In this moment you are updating the state of a disposed notifier
state = const AuthState.loggedIn();
To provide a solution it is necessary to define which elements will make rebuild the AuthNotifer.state instead of recreating the notifier.
You must consider access to another notifiers bases on the ref.read and manually create the subscriptions in the StateController constructor.
In the future, when riverpod 1.0 will be released, this issue will be easy with ref.listen but now you need to rewrite your code. You can take inspiration in the bloc-to-bloc communication gide: https://bloclibrary.dev/#/architecture?id=bloc-to-bloc-communication
i think the error is in your AuthNotifier:
if (sessionUser.confirmed != false) {
tokenController.state = sessionUser.token!; <-- you are updating a dependencie of your actual AuthNotifier object
}
await dataStore.writeUserProfile(sessionUser); <-- during the await a new object is created and the actual instance is disposed so this line will throw