Flutter: Phone Auth with firebase not working in iOS - ios

I create an app with flutter using phone authentication by firebase, everything went fine with android, but in iOS I did all things in documentation, still get this error :
the phone auth credential was created with an empty verification id.
my code :
verifyPhoneNumner(String phoneNumber) async {
await auth.verifyPhoneNumber(
timeout: const Duration(seconds: 10),
phoneNumber: completeNumber(phoneNumber),
verificationCompleted: (AuthCredential authCredential) {},
verificationFailed: (authException) {},
codeSent: (String id, [int? forceResent]) {},
codeAutoRetrievalTimeout: (id) {
verificationId = id; // save id in provider to use it in veryfy otp
});
}
verifyOtp(String otp) async {
var credential = await auth.signInWithCredential(
PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: otp)); // otp is textform
if (credential.user != null) {
return true;
} else {
return false;
}
}

Related

Flutter Apple SignIn not returning the required credentials

the_apple_sign_in
I am using this package for sign in with apple. I am successfully complete auth process. But i cant receive credential info (like a display name, email etc.). I already tried remove app from Pass and Secure settings.
My signInMethod
Future signInWithApple({List<Scope> scopes = const [], required context}) async {
showLoading(context);
final result = await TheAppleSignIn.performRequests([AppleIdRequest(requestedScopes: scopes)]).then((result) {
hideLoading(context);
return 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 userCredential = await _auth.signInWithCredential(credential);
final firebaseUser = userCredential.user!;
if (scopes.contains(Scope.fullName)) {
final fullName = appleIdCredential.fullName;
if (fullName != null && fullName.givenName != null && fullName.familyName != null) {
final displayName = '${fullName.givenName} ${fullName.familyName}';
await firebaseUser.updateDisplayName(displayName);
}
}
debugPrint(Scope.fullName.toString());
debugPrint(Scope.email.toString());
debugPrint(firebaseUser.displayName);
debugPrint(firebaseUser.email);
debugPrint(result.credential!.email);
debugPrint(result.credential!.user);
debugPrint(result.credential!.fullName.toString());
debugPrint('${appleIdCredential.email}CREDENITAL EMAIL');
debugPrint('Apple User UID => ${userCredential.user!.uid}');
debugPrint('Apple User Name => ${userCredential.user!.displayName}');
debugPrint('Apple User E-Mail => ${userCredential.user!.email}');
debugPrint('Apple User Number => ${userCredential.user!.phoneNumber}');
debugPrint('Apple User Photo URL => ${userCredential.user!.photoURL}');
return firebaseUser;
case AuthorizationStatus.error:
return null;
case AuthorizationStatus.cancelled:
return null;
default:
return null;
}
}

flutter :NoSuchMethodError: The getter 'id' was called on null. Receiver: null Tried calling: id

When I register a new user or even log in, the user's data is not stored in currentUser, so when I want to use it to save the post data in a new collection with name posts, I get this error.NoSuchMethodError: The getter 'id' was called on null. Receiver: null Tried calling: id .
alsol i get this Exception:
throw new NoSuchMethodError.withInvocation(this, invocation);
my register code :
Future<void> registerWithEmailAndPassword(
String email, String password, username) async {
print("registering now");
try {
final authResult = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
var _uid = authResult.user.uid;
// DocumentSnapshot doc = await usersRef.doc(user.id).get();
print("this is userid: $_uid");
createUserInFirestore(authResult, username);
return _userFromFirebase(authResult.user);
} catch (e) {
print(e.toString());
return null;
}
}
Future<void> logout() async {
await FirebaseAuth.instance.signOut();
}
Future<void>createUserInFirestore(authResult, username) async {
var User user= FirebaseAuth.instance.currentUser;
DocumentSnapshot doc = await usersRef.doc(user.uid).get();
if (!doc.exists) {
collection
usersRef.doc(user.uid).set({
"userid": user.uid,
"username": username,
"photoUrl": user.photoURL,
"email": user.email,
"displayName": user.displayName,
"bio": "",
"timestamp": timestamp
});
doc = await usersRef.doc(user.uid).get();
}
currentUser = Users.fromDocument(doc);
}
and her for post
createPostInFirestore(
{String mediaUrl, String location, String description}) {
postsRef
.doc(widget.currentUser.id)
.collection("userPosts")
.doc(postId)
.set({
"postId": postId,
"ownerId": widget.currentUser.id,
"username": widget.currentUser.username,
"mediaUrl": mediaUrl,
"description": description,
"location": location,
"timestamp": timestamp,
"likes": {},
});
}
handleSubmit() async {
setState(() {
isUploading = true;
});
await compressImage();
String mediaUrl = await uploadImage(file);
createPostInFirestore(
mediaUrl: mediaUrl,
location: locationController.text,
description: captionController.text,
);
setState(() {
file = null;
isUploading = false;
postId = Uuid().v4();
});
}
and I use this model to save currentUser datat
import 'package:cloud_firestore/cloud_firestore.dart';
class Users {
final String id;
final String username;
final String email;
final String photoUrl;
final String displayName;
final String bio;
Users({
this.id,
this.username,
this.email,
this.photoUrl,
this.displayName,
this.bio,
});
factory Users.fromDocument(DocumentSnapshot doc) {
return Users(
id: doc['userid'],
email: doc['email'],
username: doc['username'],
//photoUrl: doc['photoUrl'],
//displayName: doc['displayName'],
bio: doc['bio'],
);
}
}
Please try to refer to your current user like this:
//you were missing 'this'
this.widget.currentUser.id
And please provide more information. Are the two functions in the same class or widget? If yes, is it stateful and have you called
setState((){});
Maybe you must wait for the registration to complete, so you are trying to access the currentUser even though he has not been set yet?
Your error suggests that your currentUser variable is null, so you cannot use it. I suggest checking whether the currentUser is null before you perform any code that involves POSTING

iOS device not receiving push notification from Firebase Cloud Function

I've created an Ionic chat app with firebase cloud functions. The push notifications are working with Android but not ios.
async getIosToken(token: string, userId: string): Promise<void> {
if (!FCM.hasPermission()) {
FCM.requestPushPermission()
.then(async (hasPerm) => {
if (hasPerm) {
const iosToken = await FCM.getAPNSToken();
if (iosToken === token) {
return;
} else {
this.saveToken(iosToken, userId);
}
}
});
} else {
const iosToken = await FCM.getAPNSToken();
if (iosToken === token) {
return;
} else {
this.saveToken(iosToken, userId);
}
}
}
saveToken(token: string, userId: string): void {
this.userSvc.saveTokenToFirestore(token, userId)
.then(() => {
this.storageSvc.setDeviceToken(token);
});
}
The iOS token is being saved to firebase...although it never prompted the user for request permissions.
I console logged the firebase cloud function and I can see the APNs token.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
exports.newChatNotification = functions.firestore
.document(`chat/{id}/messages/{doc}`)
.onWrite(async event => {
const message = event.after.data();
let data: any;
let device: any;
const db = admin.firestore();
console.log('message', message);
console.log('db', db);
if (message) { data = message; }
const receivingUserId = data ? data.receivingUserId : '';
const content = data ? data.content : '';
const sendingUserId = data ? data.sendingUserId : '';
console.log('payload', receivingUserId, sendingUserId);
const payload = {
notification: {
title: 'New message',
body: `${content}`,
click_action: 'FCM_PLUGIN_ACTIVITY'
},
data: {
page: 'tabs/travel-buddies',
}
};
console.log('payload2', payload);
const devicesRef = (await db.collection('devices').doc(`${receivingUserId}`).get()).data();
if (devicesRef) { device = devicesRef; }
const token = device ? device.token : '';
console.log('devicesRef', devicesRef, token);
return admin.messaging().sendToDevice(token, payload);
});
Here's the firebase cloud function console
I'm not sure how to troubleshoot why ios is not receiving a push notification because there aren't any errors and I'm getting the APNs token.
I've also tried updating the build system per Google searches online:
Any help would be greatly appreciated.

I want to merge the cognito user (mail/password) with google account user in'cognito'

I'm trying to link a user within 'Pre Signup Trigger' using adminLinkProviderForUser.
I can merge by setting the following in the parameter of adminLinkProviderForUser.
const params = {
DestinationUser: {
ProviderAttributeValue: username,
ProviderName: 'Cognito'
},
SourceUser: {
ProviderAttributeName: 'Cognito_Subject',
ProviderAttributeValue: providerUserId,
ProviderName: 'Google'
},
UserPoolId: userPoolId
}
But, I get an error when I try to merge the Cognito user (email/password) by first creating a Google account as follows.
const params = {
DestinationUser: {
ProviderAttributeValue: username,
ProviderName: 'Google'
},
SourceUser: {
ProviderAttributeName: 'Cognito_Subject',
ProviderAttributeValue: providerUserId,
ProviderName: 'Cognito'
},
UserPoolId: userPoolId
}
If I created a Google account first, is there a way to merge the cognito user (email / password) created later?
All the code currently written in Lambda. (I'm having trouble with the FIXME part.)
'use strict';
const AWS = require('aws-sdk');
const cognitoIdp = new AWS.CognitoIdentityServiceProvider();
const getUserByEmail = async (userPoolId, email) => {
const params = {
UserPoolId: userPoolId,
Filter: `email = "${email}"`
}
return cognitoIdp.listUsers(params).promise()
}
const linkProviderToUser = async (username, baseProviderName, userPoolId, providerUserName, providerName, providerUserId) => {
const params = {
DestinationUser: {
ProviderAttributeValue: username,
ProviderName: baseProviderName
},
SourceUser: {
ProviderAttributeName: providerUserName,
ProviderAttributeValue: providerUserId,
ProviderName: providerName
},
UserPoolId: userPoolId
}
const result = await (new Promise((resolve, reject) => {
cognitoIdp.adminLinkProviderForUser(params, (err, data) => {
if (err) {
reject(err)
return
}
resolve(data)
})
}))
return result
}
exports.handler = async (event, context, callback) => {
console.log(event)
if (event.triggerSource == 'PreSignUp_ExternalProvider') {
const userRs = await getUserByEmail(event.userPoolId, event.request.userAttributes.email)
if (userRs && userRs.Users.length > 0) {
let [ providerName, providerUserId ] = event.userName.split('_') // event userName example: "Facebook_12324325436"
providerName = providerName === 'google' ? 'Google' : providerName
await linkProviderToUser(userRs.Users[0].Username, 'Cognito' ,event.userPoolId, 'Cognito_Subject', providerName, providerUserId)
} else {
console.log('Users Not Found. This process skip.')
}
}
if (event.triggerSource == 'PreSignUp_SignUp') {
const userRs = await getUserByEmail(event.userPoolId, event.request.userAttributes.email)
if (userRs && userRs.Users.length > 0) {
// FIXME: This will be executed if the Cognito user is created later.I want to set parameters appropriately and merge with Google account users.
} else {
console.log('Users Not Found. This process skip.')
}
}
return callback(null, event)
}
According to the AWS documentation for the AdminLinkProviderForUser action, you cannot link a native Cognito email/password user to any existing user. Only federated users from external IdPs can be linked.
Definition for the SourceUser parameter:
An external IdP account for a user who doesn't exist yet in the user
pool. This user must be a federated user (for example, a SAML or
Facebook user), not another native user.

Dart future with non async function

I am creating a function for firebase phone auth using Dart. There are two functions getCredential and then signIn. When using a try/catch block I am unsure of how this should be coded. Should the non-async function getCredential be outside of the try/catch block or inside?
Should it be coded as:
// Sign in with phone
Future signInWithPhoneNumber(String verificationId, String smsCode) async {
AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
try {
AuthResult result = await _auth.signInWithCredential(credential);
FirebaseUser user = result.user;
return user;
} catch (e) {
print(e.toString());
return null;
}
}
Or should it be coded like this?
// Sign in with phone
Future signInWithPhoneNumber(String verificationId, String smsCode) async {
try {
AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
AuthResult result = await _auth.signInWithCredential(credential);
FirebaseUser user = result.user;
return user;
} catch (e) {
print(e.toString());
return null;
}
}
If coded as the second option does the try/catch only work with the async function or both. For example, if the getCredential function generated an error would it be caught in the catch block?
Yes the catch will handle anything that throws in your try block, it's not async specific. To confirm this you could write a function that gets called at the beginning of the try for example:
// this is a function that throws
void doSomething(String param) {
if (param == null) {
throw FormatException('param can not be null');
}
}
Future signInWithPhoneNumber(String verificationId, String smsCode) async {
try {
doSomething(null); // this will be caught
AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
AuthResult result = await _auth.signInWithCredential(credential);
FirebaseUser user = result.user;
return user;
} catch (e) {
print(e.toString()); // this prints 'FormatException: param can not be null'
return null;
}
}
So async is not related to whether your function will be caught or not so it's better to use the second option.

Resources