in my mobile app i have 4 methods of login:
The problem is for example when I create my account using Google Sign In with email@example.com, then i log out and try to login using my Facebook account which is created with the same e-mail there's error. Summing up every single login function works independently.
I wonder if it's possible to handle this with cloud function?
Here's Flutter code:
///GOOGLE
static Future<bool> signWithGoogle() async {
// return false;
final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email'] );
GoogleSignInAccount? account = await _googleSignIn.signIn();
googleSignInAccount = account;
if (account == null) return false;
GoogleSignInAuthentication authentication = await account.authentication;
String? email = _googleSignIn.currentUser!.email;
final bool firstLogin = await isFirstLogin(email);
ParseResponse response;
if (firstLogin){
response = await ParseUser.loginWith(
'google',
google(
authentication.accessToken!,
_googleSignIn.currentUser!.id,
authentication.idToken!
),
username: _googleSignIn.currentUser!.displayName,
email: email,
);
}else {
response = await ParseUser.loginWith(
'google',
google(
authentication.accessToken!,
_googleSignIn.currentUser!.id,
authentication.idToken!
),
);
}
if (response.success){
return _setDeviceId(response.result);
}else
_handleError(response);
return false;
}
static Future<GoogleSignInAccount?> googleForDataOnly() async {
final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email'] );
return await _googleSignIn.signIn();
}
///FACEBOOK
static Future<bool> signWithFacebook() async {
final LoginResult result = await FacebookAuth.instance.login(permissions: [
'email',
'public_profile',
'user_location'
]);
if (result.status == LoginStatus.cancelled) return false;
if (result.status == LoginStatus.success) {
if (result.accessToken == null) {
Fluttertoast.showToast(msg: Translate.unknownError());
return false;
}
final userData = await FacebookAuth.instance.getUserData();
String? email = userData['email'];
if (email == null || email.isEmpty){
Fluttertoast.showToast(msg: Translate.emailPermissionRequired());
return false;
}
final bool firstLogin = await isFirstLogin(email);
ParseResponse response;
if (firstLogin){
response = await ParseUser.loginWith(
'facebook',
facebook(result.accessToken!.token,
result.accessToken!.userId,
result.accessToken!.expires
),
email: email,
username: userData['name']
);
}else {
response = await ParseUser.loginWith(
'facebook',
facebook(result.accessToken!.token,
result.accessToken!.userId,
result.accessToken!.expires
),
);
}
if (response.success) return _setDeviceId(response.result);
else _handleError(response);
}
return false;
}
static Future<bool> facebookDataOnly() async {
final LoginResult result = await FacebookAuth.instance.login(permissions: [
'email',
'public_profile',
'user_location'
]);
if (result.status == LoginStatus.cancelled) return false;
if (result.status == LoginStatus.success) {
if (result.accessToken == null) {
Fluttertoast.showToast(msg: Translate.unknownError());
return false;
}
return true;
}
return false;
}
///APPLE
static Future<bool> signWithApple() async {
final AuthorizationCredentialAppleID credential = await SignInWithApple.getAppleIDCredential(
scopes: [AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName],
);
final String? email = credential.email;
final String? name = credential.givenName;
final String? famName = credential.familyName;
String? userName;
if (name != null && famName != null){
userName = '$name $famName';
}
if (email == null) {
Fluttertoast.showToast(msg: Translate.invalidEmail());
return false;
}
final bool firstLogin = await isFirstLogin(email);
print(firstLogin);
ParseResponse response;
if (firstLogin){
response = await ParseUser.loginWith(
'apple',
apple(
credential.identityToken!,
credential.userIdentifier!
),
email: email,
username: userName
);
}else {
response = await ParseUser.loginWith(
'apple',
apple(
credential.identityToken!,
credential.userIdentifier!
),
);
}
if (response.success){
return _setDeviceId(response.result);
}else {
_handleError(response);
}
_sendLoginInfo(credential, response.success);
return false;
}
What you're missing is a functionality called linking users, I didn't find built-in function in Flutter SDK so I'm attaching docs from REST API.
Parse allows you to link your users with services like Twitter and Facebook, enabling your users to sign up or log into your application using their existing identities. This is accomplished through the sign-up and update REST endpoints by providing authentication data for the service you wish to link to a user in the
authData
field. Once your user is associated with a service, theauthData
for the service will be stored with the user and is retrievable by logging in.
You're supposed to populate authData
attribute of user with login provider data in order to link it to the user. This can be done with a help of a cloud code or a simple http request from client. Let's say that you will integrate this in your flutter app the request for linking facebook login provider should be made of:
# Use PUT http method
curl -X PUT \
# Add App ID to header
-H "X-Parse-Application-Id: ${APPLICATION_ID}" \
# Add Client Key to header
-H "X-Parse-Client-Key: ${CLIENT_KEY}" \
# Add Session Token to header of the user you're linking the login provider to
-H "X-Parse-Session-Token: r:samplei3l83eerhnln0ecxgy5" \
# Add Contet-Type to header
-H "Content-Type: application/json" \
-d '{
"authData": {
"facebook": {
"id": "123456789",
"access_token": "SaMpLeAAibS7Q55FSzcERWIEmzn6rosftAr7pmDME10008bWgyZAmv7mziwfacNOhWkgxDaBf8a2a2FCc9Hbk9wAsqLYZBLR995wxBvSGNoTrEaL",
"expiration_date": "2022-01-01T12:23:45.678Z"
}
}
}' \
# Call endpoint for the user you're linking the login provider to
https://YOUR.PARSE-SERVER.HERE/parse/users/{user_object_id}