Improved error handling for Android and iOS. (#775)
Updated FireAuth docs.
Updated deprecated API "fetchProvidersForEmail" to "fetchSignInMethodsForEmail".
Added "unlinkCredential".
Updated changelog and brought wrapper inline with (#915).
diff --git a/packages/firebase_auth/CHANGELOG.md b/packages/firebase_auth/CHANGELOG.md
index 104de53..d0aa9da 100644
--- a/packages/firebase_auth/CHANGELOG.md
+++ b/packages/firebase_auth/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.6.7
+
+* `FirebaseAuth` and `FirebaseUser` are now fully documented.
+* `PlatformExceptions` now report error codes as stated in docs.
+* Credentials can now be unlinked from Accounts with new methods on `FirebaseUser`.
+
+## 0.6.6
+
+* Users can now reauthenticate in response to operations that require a recent sign-in.
+
## 0.6.5
* Fixing async method `verifyPhoneNumber`, that would never return even in a successful call.
diff --git a/packages/firebase_auth/android/build.gradle b/packages/firebase_auth/android/build.gradle
index 21495d4..7f96901 100755
--- a/packages/firebase_auth/android/build.gradle
+++ b/packages/firebase_auth/android/build.gradle
@@ -32,6 +32,6 @@
disable 'InvalidPackage'
}
dependencies {
- api 'com.google.firebase:firebase-auth:16.0.4'
+ api 'com.google.firebase:firebase-auth:16.0.5'
}
}
diff --git a/packages/firebase_auth/android/gradle/wrapper/gradle-wrapper.properties b/packages/firebase_auth/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..019065d
--- /dev/null
+++ b/packages/firebase_auth/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
diff --git a/packages/firebase_auth/android/src/main/java/io/flutter/plugins/firebaseauth/FirebaseAuthPlugin.java b/packages/firebase_auth/android/src/main/java/io/flutter/plugins/firebaseauth/FirebaseAuthPlugin.java
index a3a68de..4bf1ce2 100755
--- a/packages/firebase_auth/android/src/main/java/io/flutter/plugins/firebaseauth/FirebaseAuthPlugin.java
+++ b/packages/firebase_auth/android/src/main/java/io/flutter/plugins/firebaseauth/FirebaseAuthPlugin.java
@@ -6,14 +6,34 @@
import android.net.Uri;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.SparseArray;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApiNotAvailableException;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseException;
+import com.google.firebase.FirebaseNetworkException;
import com.google.firebase.FirebaseTooManyRequestsException;
-import com.google.firebase.auth.*;
+import com.google.firebase.auth.AuthCredential;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.EmailAuthProvider;
+import com.google.firebase.auth.FacebookAuthProvider;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseAuth.AuthStateListener;
+import com.google.firebase.auth.FirebaseAuthException;
+import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.auth.GetTokenResult;
+import com.google.firebase.auth.GithubAuthProvider;
+import com.google.firebase.auth.GoogleAuthProvider;
+import com.google.firebase.auth.PhoneAuthCredential;
+import com.google.firebase.auth.PhoneAuthProvider;
+import com.google.firebase.auth.PhoneAuthProvider.ForceResendingToken;
+import com.google.firebase.auth.SignInMethodQueryResult;
+import com.google.firebase.auth.TwitterAuthProvider;
+import com.google.firebase.auth.UserInfo;
+import com.google.firebase.auth.UserProfileChangeRequest;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
@@ -23,23 +43,20 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/** Flutter plugin for Firebase Auth. */
public class FirebaseAuthPlugin implements MethodCallHandler {
private final PluginRegistry.Registrar registrar;
- private final SparseArray<FirebaseAuth.AuthStateListener> authStateListeners =
- new SparseArray<>();
- private final SparseArray<PhoneAuthProvider.ForceResendingToken> forceResendingTokens =
- new SparseArray<>();
+ private final SparseArray<AuthStateListener> authStateListeners = new SparseArray<>();
+ private final SparseArray<ForceResendingToken> forceResendingTokens = new SparseArray<>();
private final MethodChannel channel;
// Handles are ints used as indexes into the sparse array of active observers
private int nextHandle = 0;
- private static final String ERROR_REASON_EXCEPTION = "exception";
-
public static void registerWith(PluginRegistry.Registrar registrar) {
MethodChannel channel =
new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_auth");
@@ -53,7 +70,7 @@
}
private FirebaseAuth getAuth(MethodCall call) {
- Map<String, Object> arguments = (Map<String, Object>) call.arguments;
+ Map<String, Object> arguments = call.arguments();
String appName = (String) arguments.get("app");
FirebaseApp app = FirebaseApp.getInstance(appName);
return FirebaseAuth.getInstance(app);
@@ -71,8 +88,8 @@
case "createUserWithEmailAndPassword":
handleCreateUserWithEmailAndPassword(call, result, getAuth(call));
break;
- case "fetchProvidersForEmail":
- handleFetchProvidersForEmail(call, result, getAuth(call));
+ case "fetchSignInMethodsForEmail":
+ handleFetchSignInMethodsForEmail(call, result, getAuth(call));
break;
case "sendPasswordResetEmail":
handleSendPasswordResetEmail(call, result, getAuth(call));
@@ -140,6 +157,9 @@
case "linkWithGithubCredential":
handleLinkWithGithubCredential(call, result, getAuth(call));
break;
+ case "unlinkCredential":
+ handleUnlinkCredential(call, result, getAuth(call));
+ break;
case "updateEmail":
handleUpdateEmail(call, result, getAuth(call));
break;
@@ -172,8 +192,7 @@
private void handleSignInWithPhoneNumber(
MethodCall call, Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ Map<String, String> arguments = call.arguments();
String verificationId = arguments.get("verificationId");
String smsCode = arguments.get("smsCode");
@@ -186,10 +205,10 @@
private void handleVerifyPhoneNumber(
MethodCall call, Result result, final FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- final int handle = call.argument("handle");
- String phoneNumber = call.argument("phoneNumber");
- int timeout = call.argument("timeout");
+ Map<String, Object> arguments = call.arguments();
+ final int handle = (int) arguments.get("handle");
+ String phoneNumber = (String) arguments.get("phoneNumber");
+ int timeout = (int) arguments.get("timeout");
PhoneAuthProvider.OnVerificationStateChangedCallbacks verificationCallbacks =
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
@@ -238,7 +257,7 @@
};
if (call.argument("forceResendingToken") != null) {
- int forceResendingTokenKey = call.argument("forceResendingToken");
+ int forceResendingTokenKey = (int) arguments.get("forceResendingToken");
PhoneAuthProvider.ForceResendingToken forceResendingToken =
forceResendingTokens.get(forceResendingTokenKey);
PhoneAuthProvider.getInstance()
@@ -263,9 +282,7 @@
}
private Map<String, Object> getVerifyPhoneNumberExceptionMap(FirebaseException e) {
- Map<String, Object> exceptionMap = new HashMap<>();
String errorCode = "verifyPhoneNumberError";
-
if (e instanceof FirebaseAuthInvalidCredentialsException) {
errorCode = "invalidCredential";
} else if (e instanceof FirebaseAuthException) {
@@ -275,6 +292,8 @@
} else if (e instanceof FirebaseApiNotAvailableException) {
errorCode = "apiNotAvailable";
}
+
+ Map<String, Object> exceptionMap = new HashMap<>();
exceptionMap.put("code", errorCode);
exceptionMap.put("message", e.getMessage());
return exceptionMap;
@@ -282,8 +301,7 @@
private void handleLinkWithEmailAndPassword(
MethodCall call, Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ Map<String, String> arguments = call.arguments();
String email = arguments.get("email");
String password = arguments.get("password");
@@ -294,7 +312,8 @@
.addOnCompleteListener(new SignInCompleteListener(result));
}
- private void handleCurrentUser(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ private void handleCurrentUser(
+ @SuppressWarnings("unused") MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user == null) {
result.success(null);
@@ -305,14 +324,13 @@
}
private void handleSignInAnonymously(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ @SuppressWarnings("unused") MethodCall call, Result result, FirebaseAuth firebaseAuth) {
firebaseAuth.signInAnonymously().addOnCompleteListener(new SignInCompleteListener(result));
}
private void handleCreateUserWithEmailAndPassword(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String email = arguments.get("email");
String password = arguments.get("password");
@@ -321,21 +339,19 @@
.addOnCompleteListener(new SignInCompleteListener(result));
}
- private void handleFetchProvidersForEmail(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ private void handleFetchSignInMethodsForEmail(
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String email = arguments.get("email");
firebaseAuth
- .fetchProvidersForEmail(email)
- .addOnCompleteListener(new ProvidersCompleteListener(result));
+ .fetchSignInMethodsForEmail(email)
+ .addOnCompleteListener(new GetSignInMethodsCompleteListener(result));
}
private void handleSendPasswordResetEmail(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String email = arguments.get("email");
firebaseAuth
@@ -344,24 +360,30 @@
}
private void handleSendEmailVerification(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ @SuppressWarnings("unused") MethodCall call, Result result, FirebaseAuth firebaseAuth) {
firebaseAuth
.getCurrentUser()
.sendEmailVerification()
.addOnCompleteListener(new TaskVoidCompleteListener(result));
}
- private void handleReload(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ private void handleReload(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
firebaseAuth
.getCurrentUser()
.reload()
.addOnCompleteListener(new TaskVoidCompleteListener(result));
}
+ private void handleDelete(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ firebaseAuth
+ .getCurrentUser()
+ .delete()
+ .addOnCompleteListener(new TaskVoidCompleteListener(result));
+ }
+
private void handleSignInWithEmailAndPassword(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String email = arguments.get("email");
String password = arguments.get("password");
@@ -370,19 +392,11 @@
.addOnCompleteListener(new SignInCompleteListener(result));
}
- private void handleDelete(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- firebaseAuth
- .getCurrentUser()
- .delete()
- .addOnCompleteListener(new TaskVoidCompleteListener(result));
- }
-
- private void handleSignInWithGoogle(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ private void handleSignInWithGoogle(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String idToken = arguments.get("idToken");
String accessToken = arguments.get("accessToken");
+
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, accessToken);
firebaseAuth
.signInWithCredential(credential)
@@ -391,8 +405,7 @@
private void handleReauthenticateWithEmailAndPassword(
MethodCall call, Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ Map<String, String> arguments = call.arguments();
String email = arguments.get("email");
String password = arguments.get("password");
@@ -405,10 +418,10 @@
private void handleReauthenticateWithGoogleCredential(
MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ Map<String, String> arguments = call.arguments();
String idToken = arguments.get("idToken");
String accessToken = arguments.get("accessToken");
+
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, accessToken);
firebaseAuth
.getCurrentUser()
@@ -418,9 +431,9 @@
private void handleReauthenticateWithFacebookCredential(
MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ Map<String, String> arguments = call.arguments();
String accessToken = arguments.get("accessToken");
+
AuthCredential credential = FacebookAuthProvider.getCredential(accessToken);
firebaseAuth
.getCurrentUser()
@@ -430,10 +443,10 @@
private void handleReauthenticateWithTwitterCredential(
MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ Map<String, String> arguments = call.arguments();
String authToken = arguments.get("authToken");
String authTokenSecret = arguments.get("authTokenSecret");
+
AuthCredential credential = TwitterAuthProvider.getCredential(authToken, authTokenSecret);
firebaseAuth
.getCurrentUser()
@@ -444,6 +457,7 @@
private void handleReauthenticateWithGithubCredential(
MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
String token = call.argument("token");
+
AuthCredential credential = GithubAuthProvider.getCredential(token);
firebaseAuth
.getCurrentUser()
@@ -452,11 +466,11 @@
}
private void handleLinkWithGoogleCredential(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String idToken = arguments.get("idToken");
String accessToken = arguments.get("accessToken");
+
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, accessToken);
firebaseAuth
.getCurrentUser()
@@ -465,10 +479,10 @@
}
private void handleLinkWithFacebookCredential(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String accessToken = arguments.get("accessToken");
+
AuthCredential credential = FacebookAuthProvider.getCredential(accessToken);
firebaseAuth
.getCurrentUser()
@@ -476,12 +490,31 @@
.addOnCompleteListener(new SignInCompleteListener(result));
}
+ private void handleSignInWithFacebook(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
+ String accessToken = arguments.get("accessToken");
+
+ AuthCredential credential = FacebookAuthProvider.getCredential(accessToken);
+ firebaseAuth
+ .signInWithCredential(credential)
+ .addOnCompleteListener(new SignInCompleteListener(result));
+ }
+
+ private void handleSignInWithTwitter(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ String authToken = call.argument("authToken");
+ String authTokenSecret = call.argument("authTokenSecret");
+
+ AuthCredential credential = TwitterAuthProvider.getCredential(authToken, authTokenSecret);
+ firebaseAuth
+ .signInWithCredential(credential)
+ .addOnCompleteListener(new SignInCompleteListener(result));
+ }
+
private void handleLinkWithTwitterCredential(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
- String authToken = arguments.get("authToken");
- String authTokenSecret = arguments.get("authTokenSecret");
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ String authToken = call.argument("authToken");
+ String authTokenSecret = call.argument("authTokenSecret");
+
AuthCredential credential = TwitterAuthProvider.getCredential(authToken, authTokenSecret);
firebaseAuth
.getCurrentUser()
@@ -489,9 +522,19 @@
.addOnCompleteListener(new SignInCompleteListener(result));
}
+ private void handleSignInWithGithub(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ String token = call.argument("token");
+
+ AuthCredential credential = GithubAuthProvider.getCredential(token);
+ firebaseAuth
+ .signInWithCredential(credential)
+ .addOnCompleteListener(new SignInCompleteListener(result));
+ }
+
private void handleLinkWithGithubCredential(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
String token = call.argument("token");
+
AuthCredential credential = GithubAuthProvider.getCredential(token);
firebaseAuth
.getCurrentUser()
@@ -499,33 +542,13 @@
.addOnCompleteListener(new SignInCompleteListener(result));
}
- private void handleSignInWithFacebook(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
- String accessToken = arguments.get("accessToken");
- AuthCredential credential = FacebookAuthProvider.getCredential(accessToken);
- firebaseAuth
- .signInWithCredential(credential)
- .addOnCompleteListener(new SignInCompleteListener(result));
- }
+ private void handleUnlinkCredential(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
+ final String provider = arguments.get("provider");
- private void handleSignInWithTwitter(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- String authToken = call.argument("authToken");
- String authTokenSecret = call.argument("authTokenSecret");
- AuthCredential credential = TwitterAuthProvider.getCredential(authToken, authTokenSecret);
firebaseAuth
- .signInWithCredential(credential)
- .addOnCompleteListener(new SignInCompleteListener(result));
- }
-
- private void handleSignInWithGithub(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- String token = call.argument("token");
- AuthCredential credential = GithubAuthProvider.getCredential(token);
- firebaseAuth
- .signInWithCredential(credential)
+ .getCurrentUser()
+ .unlink(provider)
.addOnCompleteListener(new SignInCompleteListener(result));
}
@@ -533,6 +556,7 @@
MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
Map<String, String> arguments = call.arguments();
String token = arguments.get("token");
+
firebaseAuth
.signInWithCustomToken(token)
.addOnCompleteListener(new SignInCompleteListener(result));
@@ -544,9 +568,9 @@
}
private void handleGetToken(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, Boolean> arguments = (Map<String, Boolean>) call.arguments;
+ Map<String, Boolean> arguments = call.arguments();
boolean refresh = arguments.get("refresh");
+
firebaseAuth
.getCurrentUser()
.getIdToken(refresh)
@@ -557,35 +581,34 @@
String idToken = task.getResult().getToken();
result.success(idToken);
} else {
- result.error(ERROR_REASON_EXCEPTION, task.getException().getMessage(), null);
+ reportException(result, task.getException());
}
}
});
}
- private void handleUpdateEmail(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ private void handleUpdateEmail(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
+ final String email = arguments.get("email");
+
firebaseAuth
.getCurrentUser()
- .updateEmail(arguments.get("email"))
+ .updateEmail(email)
.addOnCompleteListener(new TaskVoidCompleteListener(result));
}
- private void handleUpdatePassword(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ private void handleUpdatePassword(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
+ final String password = arguments.get("password");
+
firebaseAuth
.getCurrentUser()
- .updatePassword(arguments.get("password"))
+ .updatePassword(password)
.addOnCompleteListener(new TaskVoidCompleteListener(result));
}
- private void handleUpdateProfile(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ private void handleUpdateProfile(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
UserProfileChangeRequest.Builder builder = new UserProfileChangeRequest.Builder();
if (arguments.containsKey("displayName")) {
@@ -602,7 +625,7 @@
}
private void handleStartListeningAuthState(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ @SuppressWarnings("unused") MethodCall call, Result result, FirebaseAuth firebaseAuth) {
final int handle = nextHandle++;
FirebaseAuth.AuthStateListener listener =
new FirebaseAuth.AuthStateListener() {
@@ -612,7 +635,6 @@
Map<String, Object> userMap = mapFromUser(user);
Map<String, Object> map = new HashMap<>();
map.put("id", handle);
-
if (userMap != null) {
map.put("user", userMap);
}
@@ -625,7 +647,7 @@
}
private void handleStopListeningAuthState(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
+ MethodCall call, Result result, FirebaseAuth firebaseAuth) {
Map<String, Integer> arguments = call.arguments();
Integer id = arguments.get("id");
@@ -635,17 +657,16 @@
authStateListeners.remove(id);
result.success(null);
} else {
- result.error(
- ERROR_REASON_EXCEPTION,
- String.format("Listener with identifier '%d' not found.", id),
- null);
+ reportException(
+ result,
+ new FirebaseAuthException(
+ "ERROR_LISTENER_NOT_FOUND",
+ String.format(Locale.US, "Listener with identifier '%d' not found.", id)));
}
}
- private void handleSetLanguageCode(
- MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
- @SuppressWarnings("unchecked")
- Map<String, String> arguments = (Map<String, String>) call.arguments;
+ private void handleSetLanguageCode(MethodCall call, Result result, FirebaseAuth firebaseAuth) {
+ Map<String, String> arguments = call.arguments();
String language = arguments.get("language");
firebaseAuth.setLanguageCode(language);
@@ -661,9 +682,8 @@
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
- if (!task.isSuccessful()) {
- Exception e = task.getException();
- result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null);
+ if (!task.isSuccessful() || task.getResult() == null) {
+ reportException(result, task.getException());
} else {
FirebaseUser user = task.getResult().getUser();
Map<String, Object> userMap = Collections.unmodifiableMap(mapFromUser(user));
@@ -682,28 +702,27 @@
@Override
public void onComplete(@NonNull Task<Void> task) {
if (!task.isSuccessful()) {
- Exception e = task.getException();
- result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null);
+ reportException(result, task.getException());
} else {
result.success(null);
}
}
}
- private class ProvidersCompleteListener implements OnCompleteListener<ProviderQueryResult> {
+ private class GetSignInMethodsCompleteListener
+ implements OnCompleteListener<SignInMethodQueryResult> {
private final Result result;
- ProvidersCompleteListener(Result result) {
+ GetSignInMethodsCompleteListener(Result result) {
this.result = result;
}
@Override
- public void onComplete(@NonNull Task<ProviderQueryResult> task) {
- if (!task.isSuccessful()) {
- Exception e = task.getException();
- result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null);
+ public void onComplete(@NonNull Task<SignInMethodQueryResult> task) {
+ if (!task.isSuccessful() || task.getResult() == null) {
+ reportException(result, task.getException());
} else {
- List<String> providers = task.getResult().getProviders();
+ List<String> providers = task.getResult().getSignInMethods();
result.success(providers);
}
}
@@ -749,4 +768,23 @@
return null;
}
}
+
+ private void reportException(Result result, @Nullable Exception exception) {
+ if (exception != null) {
+ if (exception instanceof FirebaseAuthException) {
+ final FirebaseAuthException authException = (FirebaseAuthException) exception;
+ result.error(authException.getErrorCode(), exception.getMessage(), null);
+ } else if (exception instanceof FirebaseApiNotAvailableException) {
+ result.error("ERROR_API_NOT_AVAILABLE", exception.getMessage(), null);
+ } else if (exception instanceof FirebaseTooManyRequestsException) {
+ result.error("ERROR_TOO_MANY_REQUESTS", exception.getMessage(), null);
+ } else if (exception instanceof FirebaseNetworkException) {
+ result.error("ERROR_NETWORK_REQUEST_FAILED", exception.getMessage(), null);
+ } else {
+ result.error(exception.getClass().getSimpleName(), exception.getMessage(), null);
+ }
+ } else {
+ result.error("ERROR_UNKNOWN", "An unknown error occurred.", null);
+ }
+ }
}
diff --git a/packages/firebase_auth/ios/Classes/FirebaseAuthPlugin.m b/packages/firebase_auth/ios/Classes/FirebaseAuthPlugin.m
index ebac80f..5bbeaf3 100644
--- a/packages/firebase_auth/ios/Classes/FirebaseAuthPlugin.m
+++ b/packages/firebase_auth/ios/Classes/FirebaseAuthPlugin.m
@@ -6,15 +6,17 @@
#import "Firebase/Firebase.h"
-@interface NSError (FlutterError)
-@property(readonly, nonatomic) FlutterError *flutterError;
+@interface NSError (FIRAuthErrorCode)
+@property(readonly, nonatomic) NSString *firAuthErrorCode;
@end
-@implementation NSError (FlutterError)
-- (FlutterError *)flutterError {
- return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %d", (int)self.code]
- message:self.domain
- details:self.localizedDescription];
+@implementation NSError (FIRAuthErrorCode)
+- (NSString *)firAuthErrorCode {
+ NSString *code = [self userInfo][FIRAuthErrorNameKey];
+ if (code != nil) {
+ return code;
+ }
+ return [NSString stringWithFormat:@"ERROR_%d", (int)self.code];
}
@end
@@ -73,8 +75,8 @@
}];
} else if ([@"signInAnonymously" isEqualToString:call.method]) {
[[self getAuth:call.arguments]
- signInAnonymouslyWithCompletion:^(FIRAuthDataResult *dataResult, NSError *error) {
- [self sendResult:result forUser:dataResult.user error:error];
+ signInAnonymouslyWithCompletion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [self sendResult:result forUser:authResult.user error:error];
}];
} else if ([@"signInWithGoogle" isEqualToString:call.method]) {
NSString *idToken = call.arguments[@"idToken"];
@@ -114,35 +116,35 @@
[[self getAuth:call.arguments]
createUserWithEmail:email
password:password
- completion:^(FIRAuthDataResult *dataResult, NSError *error) {
- [self sendResult:result forUser:dataResult.user error:error];
+ completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [self sendResult:result forUser:authResult.user error:error];
}];
- } else if ([@"fetchProvidersForEmail" isEqualToString:call.method]) {
+ } else if ([@"fetchSignInMethodsForEmail" isEqualToString:call.method]) {
NSString *email = call.arguments[@"email"];
[[self getAuth:call.arguments]
fetchProvidersForEmail:email
completion:^(NSArray<NSString *> *providers, NSError *error) {
- [self sendResult:result forProviders:providers error:error];
+ [self sendResult:result forObject:providers error:error];
}];
} else if ([@"sendEmailVerification" isEqualToString:call.method]) {
[[self getAuth:call.arguments].currentUser
sendEmailVerificationWithCompletion:^(NSError *_Nullable error) {
- [self sendResult:result forProviders:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"reload" isEqualToString:call.method]) {
[[self getAuth:call.arguments].currentUser reloadWithCompletion:^(NSError *_Nullable error) {
- [self sendResult:result forProviders:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"delete" isEqualToString:call.method]) {
[[self getAuth:call.arguments].currentUser deleteWithCompletion:^(NSError *_Nullable error) {
- [self sendResult:result forProviders:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"sendPasswordResetEmail" isEqualToString:call.method]) {
NSString *email = call.arguments[@"email"];
[[self getAuth:call.arguments] sendPasswordResetWithEmail:email
completion:^(NSError *error) {
[self sendResult:result
- forUser:nil
+ forObject:nil
error:error];
}];
} else if ([@"signInWithEmailAndPassword" isEqualToString:call.method]) {
@@ -151,23 +153,23 @@
[[self getAuth:call.arguments]
signInWithEmail:email
password:password
- completion:^(FIRAuthDataResult *dataResult, NSError *error) {
- [self sendResult:result forUser:dataResult.user error:error];
+ completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [self sendResult:result forUser:authResult.user error:error];
}];
} else if ([@"signOut" isEqualToString:call.method]) {
NSError *signOutError;
BOOL status = [[self getAuth:call.arguments] signOut:&signOutError];
if (!status) {
NSLog(@"Error signing out: %@", signOutError);
- [self sendResult:result forUser:nil error:signOutError];
+ [self sendResult:result forObject:nil error:signOutError];
} else {
- [self sendResult:result forUser:nil error:nil];
+ [self sendResult:result forObject:nil error:nil];
}
} else if ([@"getIdToken" isEqualToString:call.method]) {
[[self getAuth:call.arguments].currentUser
getIDTokenForcingRefresh:YES
completion:^(NSString *_Nullable token, NSError *_Nullable error) {
- result(error != nil ? error.flutterError : token);
+ [self sendResult:result forObject:token error:error];
}];
} else if ([@"reauthenticateWithEmailAndPassword" isEqualToString:call.method]) {
NSString *email = call.arguments[@"email"];
@@ -177,7 +179,7 @@
[[self getAuth:call.arguments].currentUser
reauthenticateWithCredential:credential
completion:^(NSError *_Nullable error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"reauthenticateWithGoogleCredential" isEqualToString:call.method]) {
NSString *idToken = call.arguments[@"idToken"];
@@ -187,7 +189,7 @@
[[self getAuth:call.arguments].currentUser
reauthenticateWithCredential:credential
completion:^(NSError *_Nullable error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"reauthenticateWithFacebookCredential" isEqualToString:call.method]) {
NSString *accessToken = call.arguments[@"accessToken"];
@@ -195,7 +197,7 @@
[[self getAuth:call.arguments].currentUser
reauthenticateWithCredential:credential
completion:^(NSError *_Nullable error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"reauthenticateWithTwitterCredential" isEqualToString:call.method]) {
NSString *authToken = call.arguments[@"authToken"];
@@ -205,7 +207,7 @@
[[self getAuth:call.arguments].currentUser
reauthenticateWithCredential:credential
completion:^(NSError *_Nullable error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"reauthenticateWithGithubCredential" isEqualToString:call.method]) {
NSString *token = call.arguments[@"token"];
@@ -213,7 +215,7 @@
[[self getAuth:call.arguments].currentUser
reauthenticateWithCredential:credential
completion:^(NSError *_Nullable error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
} else if ([@"linkWithEmailAndPassword" isEqualToString:call.method]) {
NSString *email = call.arguments[@"email"];
@@ -260,24 +262,32 @@
} else if ([@"linkWithGithubCredential" isEqualToString:call.method]) {
NSString *token = call.arguments[@"token"];
FIRAuthCredential *credential = [FIRGitHubAuthProvider credentialWithToken:token];
- [[self getAuth:call.arguments].currentUser linkWithCredential:credential
- completion:^(FIRUser *user, NSError *error) {
- [self sendResult:result
- forUser:user
- error:error];
- }];
+ [[self getAuth:call.arguments].currentUser
+ linkWithCredential:credential
+ completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
+ [self sendResult:result forUser:user error:error];
+ }];
+ } else if ([@"unlinkCredential" isEqualToString:call.method]) {
+ NSString *provider = call.arguments[@"provider"];
+ [[self getAuth:call.arguments].currentUser
+ unlinkFromProvider:provider
+ completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
+ [self sendResult:result forUser:user error:error];
+ }];
} else if ([@"updateEmail" isEqualToString:call.method]) {
NSString *email = call.arguments[@"email"];
[[self getAuth:call.arguments].currentUser updateEmail:email
completion:^(NSError *error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result
+ forObject:nil
+ error:error];
}];
} else if ([@"updatePassword" isEqualToString:call.method]) {
NSString *password = call.arguments[@"password"];
[[self getAuth:call.arguments].currentUser updatePassword:password
completion:^(NSError *error) {
[self sendResult:result
- forUser:nil
+ forObject:nil
error:error];
}];
} else if ([@"updateProfile" isEqualToString:call.method]) {
@@ -290,20 +300,14 @@
changeRequest.photoURL = [NSURL URLWithString:call.arguments[@"photoUrl"]];
}
[changeRequest commitChangesWithCompletion:^(NSError *error) {
- [self sendResult:result forUser:nil error:error];
+ [self sendResult:result forObject:nil error:error];
}];
- } else if ([@"updateEmail" isEqualToString:call.method]) {
- NSString *toEmail = call.arguments[@"email"];
- [[self getAuth:call.arguments].currentUser updateEmail:toEmail
- completion:^(NSError *_Nullable error) {
- [self sendResult:result forUser:nil error:error];
- }];
} else if ([@"signInWithCustomToken" isEqualToString:call.method]) {
NSString *token = call.arguments[@"token"];
[[self getAuth:call.arguments]
signInWithCustomToken:token
- completion:^(FIRAuthDataResult *dataResult, NSError *error) {
- [self sendResult:result forUser:dataResult.user error:error];
+ completion:^(FIRAuthDataResult *authResult, NSError *error) {
+ [self sendResult:result forUser:authResult.user error:error];
}];
} else if ([@"startListeningAuthState" isEqualToString:call.method]) {
@@ -332,7 +336,7 @@
result(nil);
} else {
result([FlutterError
- errorWithCode:@"not_found"
+ errorWithCode:@"ERROR_LISTENER_NOT_FOUND"
message:[NSString stringWithFormat:@"Listener with identifier '%d' not found.",
identifier.intValue]
details:nil]);
@@ -364,14 +368,15 @@
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationId
verificationCode:smsCode];
- [[self getAuth:call.arguments] signInWithCredential:credential
- completion:^(FIRUser *user, NSError *error) {
- [self sendResult:result forUser:user error:error];
- }];
+ [[self getAuth:call.arguments]
+ signInWithCredential:credential
+ completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
+ [self sendResult:result forUser:user error:error];
+ }];
} else if ([@"setLanguageCode" isEqualToString:call.method]) {
NSString *language = call.arguments[@"language"];
[[self getAuth:call.arguments] setLanguageCode:language];
- [self sendResult:result forUser:nil error:nil];
+ [self sendResult:result forObject:nil error:nil];
} else {
result(FlutterMethodNotImplemented);
}
@@ -397,24 +402,20 @@
}
- (void)sendResult:(FlutterResult)result forUser:(FIRUser *)user error:(NSError *)error {
- if (error != nil) {
- result(error.flutterError);
- } else if (user == nil) {
- result(nil);
- } else {
- result([self dictionaryFromUser:user]);
- }
+ [self sendResult:result
+ forObject:(user != nil ? [self dictionaryFromUser:user] : nil)
+ error:error];
}
-- (void)sendResult:(FlutterResult)result
- forProviders:(NSArray<NSString *> *)providers
- error:(NSError *)error {
+- (void)sendResult:(FlutterResult)result forObject:(NSObject *)object error:(NSError *)error {
if (error != nil) {
- result(error.flutterError);
- } else if (providers == nil) {
+ result([FlutterError errorWithCode:error.firAuthErrorCode
+ message:error.localizedDescription
+ details:nil]);
+ } else if (object == nil) {
result(nil);
} else {
- result(providers);
+ result(object);
}
}
diff --git a/packages/firebase_auth/lib/firebase_auth.dart b/packages/firebase_auth/lib/firebase_auth.dart
index 9392695..1c377e5 100755
--- a/packages/firebase_auth/lib/firebase_auth.dart
+++ b/packages/firebase_auth/lib/firebase_auth.dart
@@ -52,6 +52,7 @@
}
/// Represents user profile data that can be updated by [updateProfile]
+///
/// The purpose of having separate class with a map is to give possibility
/// to check if value was set to null or not provided
class UserUpdateInfo {
@@ -59,13 +60,13 @@
final Map<String, String> _updateData = <String, String>{};
set displayName(String displayName) =>
- _updateData["displayName"] = displayName;
+ _updateData['displayName'] = displayName;
- String get displayName => _updateData["displayName"];
+ String get displayName => _updateData['displayName'];
- set photoUrl(String photoUri) => _updateData["photoUrl"] = photoUri;
+ set photoUrl(String photoUri) => _updateData['photoUrl'] = photoUri;
- String get photoUrl => _updateData["photoUrl"];
+ String get photoUrl => _updateData['photoUrl'];
}
/// Represents a user.
@@ -92,6 +93,10 @@
/// Obtains the id token for the current user, forcing a [refresh] if desired.
///
+ /// Useful when authenticating against your own backend. Use our server
+ /// SDKs or follow the official documentation to securely verify the
+ /// integrity and validity of this token.
+ ///
/// Completes with an error if the user is signed out.
Future<String> getIdToken({bool refresh = false}) async {
return await FirebaseAuth.channel
@@ -101,12 +106,14 @@
});
}
+ /// Initiates email verification for the user.
Future<void> sendEmailVerification() async {
await FirebaseAuth.channel.invokeMethod(
'sendEmailVerification', <String, String>{'app': _app.name});
}
- /// Manually refreshes the data of the current user (for example, attached providers, display name, and so on).
+ /// Manually refreshes the data of the current user (for example,
+ /// attached providers, display name, and so on).
Future<void> reload() async {
await FirebaseAuth.channel
.invokeMethod('reload', <String, String>{'app': _app.name});
@@ -119,6 +126,21 @@
}
/// Updates the email address of the user.
+ ///
+ /// The original email address recipient will receive an email that allows
+ /// them to revoke the email address change, in order to protect them
+ /// from account hijacking.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the email address is malformed.
+ /// • `ERROR_EMAIL_ALREADY_IN_USE` - If the email is already in use by a different account.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> updateEmail(String email) async {
assert(email != null);
return await FirebaseAuth.channel.invokeMethod(
@@ -128,6 +150,19 @@
}
/// Updates the password of the user.
+ ///
+ /// Anonymous users who update both their email and password will no
+ /// longer be anonymous. They will be able to log in with these credentials.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_WEAK_PASSWORD` - If the password is not strong enough.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> updatePassword(String password) async {
assert(password != null);
return await FirebaseAuth.channel.invokeMethod(
@@ -137,6 +172,10 @@
}
/// Updates the user profile information.
+ ///
+ /// Errors:
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
Future<void> updateProfile(UserUpdateInfo userUpdateInfo) async {
assert(userUpdateInfo != null);
final Map<String, String> data = userUpdateInfo._updateData;
@@ -147,6 +186,96 @@
);
}
+ /// Detaches Email & Password from this user.
+ ///
+ /// This detaches the Email & Password from the current user. This will
+ /// prevent the user from signing in to this account with those credentials.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_NO_SUCH_PROVIDER` - If the user does not have an Email & Password linked to their account.
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ Future<void> unlinkEmailAndPassword() async {
+ return await FirebaseAuth.channel.invokeMethod(
+ 'unlinkCredential',
+ <String, String>{'provider': 'password', 'app': _app.name},
+ );
+ }
+
+ /// Detaches Google from this user.
+ ///
+ /// This detaches the Google Account from the current user. This will
+ /// prevent the user from signing in to this account with those credentials.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_NO_SUCH_PROVIDER` - If the user does not have a Google Account linked to their account.
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ Future<void> unlinkGoogleCredential() async {
+ return await FirebaseAuth.channel.invokeMethod(
+ 'unlinkCredential',
+ <String, String>{'provider': 'google.com', 'app': _app.name},
+ );
+ }
+
+ /// Detaches Facebook from this user.
+ ///
+ /// This detaches the Facebook Account from the current user. This will
+ /// prevent the user from signing in to this account with those credentials.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_NO_SUCH_PROVIDER` - If the user does not have a Facebook Account linked to their account.
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ Future<void> unlinkFacebookCredential() async {
+ return await FirebaseAuth.channel.invokeMethod(
+ 'unlinkCredential',
+ <String, String>{'provider': 'facebook.com', 'app': _app.name},
+ );
+ }
+
+ /// Detaches Twitter from this user.
+ ///
+ /// This detaches the Twitter Account from the current user. This will
+ /// prevent the user from signing in to this account with those credentials.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_NO_SUCH_PROVIDER` - If the user does not have a Twitter Account linked to their account.
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ Future<void> unlinkTwitterCredential() async {
+ return await FirebaseAuth.channel.invokeMethod(
+ 'unlinkCredential',
+ <String, String>{'provider': 'twitter.com', 'app': _app.name},
+ );
+ }
+
+ /// Detaches Github from this user.
+ ///
+ /// This detaches the Github Account from the current user. This will
+ /// prevent the user from signing in to this account with those credentials.
+ ///
+ /// **Important**: This is a security sensitive operation that requires
+ /// the user to have recently signed in.
+ ///
+ /// Errors:
+ /// • `ERROR_NO_SUCH_PROVIDER` - If the user does not have a Github Account linked to their account.
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ Future<void> unlinkGithubCredential() async {
+ return await FirebaseAuth.channel.invokeMethod(
+ 'unlinkCredential',
+ <String, String>{'provider': 'github.com', 'app': _app.name},
+ );
+ }
+
@override
String toString() {
return '$runtimeType($_data)';
@@ -221,9 +350,11 @@
/// returned instead. If there is any other existing user signed in, that
/// user will be signed out.
///
- /// Will throw a PlatformException if
- /// FIRAuthErrorCodeOperationNotAllowed - Indicates that anonymous accounts are not enabled. Enable them in the Auth section of the Firebase console.
- /// See FIRAuthErrors for a list of error codes that are common to all API methods.
+ /// **Important**: You must enable Anonymous accounts in the Auth section
+ /// of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Anonymous accounts are not enabled.
Future<FirebaseUser> signInAnonymously() async {
final Map<dynamic, dynamic> data = await channel
.invokeMethod('signInAnonymously', <String, String>{"app": app.name});
@@ -231,6 +362,15 @@
return currentUser;
}
+ /// Tries to create a new user account with the given email address and password.
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// Errors:
+ /// • `ERROR_WEAK_PASSWORD` - If the password is not strong enough.
+ /// • `ERROR_INVALID_CREDENTIAL` - If the email address is malformed.
+ /// • `ERROR_EMAIL_ALREADY_IN_USE` - If the email is already in use by a different account.
Future<FirebaseUser> createUserWithEmailAndPassword({
@required String email,
@required String password,
@@ -245,17 +385,33 @@
return currentUser;
}
- Future<List<String>> fetchProvidersForEmail({
+ /// Returns a list of sign-in methods that can be used to sign in a given
+ /// user (identified by its main email address).
+ ///
+ /// This method is useful when you support multiple authentication mechanisms
+ /// if you want to implement an email-first authentication flow.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [email] address is malformed.
+ /// • `ERROR_USER_NOT_FOUND` - If there is no user corresponding to the given [email] address.
+ Future<List<String>> fetchSignInMethodsForEmail({
@required String email,
}) async {
assert(email != null);
final List<dynamic> providers = await channel.invokeMethod(
- 'fetchProvidersForEmail',
+ 'fetchSignInMethodsForEmail',
<String, String>{'email': email, 'app': app.name},
);
return providers?.cast<String>();
}
+ /// Triggers the Firebase Authentication backend to send a password-reset
+ /// email to the given email address, which must correspond to an existing
+ /// user of your app.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_EMAIL` - If the [email] address is malformed.
+ /// • `ERROR_USER_NOT_FOUND` - If there is no user corresponding to the given [email] address.
Future<void> sendPasswordResetEmail({
@required String email,
}) async {
@@ -266,6 +422,21 @@
);
}
+ /// Tries to sign in a user with the given email address and password.
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// **Important**: You must enable Email & Password accounts in the Auth
+ /// section of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_EMAIL` - If the [email] address is malformed.
+ /// • `ERROR_WRONG_PASSWORD` - If the [password] is wrong.
+ /// • `ERROR_USER_NOT_FOUND` - If there is no user corresponding to the given [email] address, or if the user has been deleted.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_TOO_MANY_REQUESTS` - If there was too many attempts to sign in as this user.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<FirebaseUser> signInWithEmailAndPassword({
@required String email,
@required String password,
@@ -280,46 +451,23 @@
return currentUser;
}
- Future<FirebaseUser> signInWithFacebook(
- {@required String accessToken}) async {
- assert(accessToken != null);
- final Map<dynamic, dynamic> data = await channel.invokeMethod(
- 'signInWithFacebook',
- <String, String>{'accessToken': accessToken, 'app': app.name});
- final FirebaseUser currentUser = FirebaseUser._(data, app);
- return currentUser;
- }
-
- /// Signs in with a Twitter account using the specified credentials.
+ /// Tries to sign in a user with the given Google [idToken] and [accessToken].
///
- /// The returned future completes with the signed-in user or a [PlatformException], if sign in failed.
- Future<FirebaseUser> signInWithTwitter({
- @required String authToken,
- @required String authTokenSecret,
- }) async {
- assert(authToken != null);
- assert(authTokenSecret != null);
- final Map<dynamic, dynamic> data = await channel.invokeMethod(
- 'signInWithTwitter', <String, String>{
- 'authToken': authToken,
- 'authTokenSecret': authTokenSecret,
- 'app': app.name
- });
- final FirebaseUser currentUser = FirebaseUser._(data, app);
- return currentUser;
- }
-
- Future<FirebaseUser> signInWithGithub({@required String token}) async {
- assert(token != null);
- final Map<dynamic, dynamic> data =
- await channel.invokeMethod('signInWithGithub', <String, String>{
- 'token': token,
- 'app': app.name,
- });
- final FirebaseUser currentUser = FirebaseUser._(data, app);
- return currentUser;
- }
-
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// If the user doesn't have an account already, one will be created automatically.
+ ///
+ /// **Important**: You must enable Google accounts in the Auth section
+ /// of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [authToken] or [authTokenSecret] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL` - If there already exists an account with the email address asserted by Google.
+ /// Resolve this case by calling [fetchSignInMethodsForEmail] and then asking the user to sign in using one of them.
+ /// This error will only be thrown if the "One account per email address" setting is enabled in the Firebase console (recommended).
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Google accounts are not enabled.
Future<FirebaseUser> signInWithGoogle({
@required String idToken,
@required String accessToken,
@@ -331,13 +479,119 @@
<String, String>{
'idToken': idToken,
'accessToken': accessToken,
- 'app': app.name
+ 'app': app.name,
},
);
final FirebaseUser currentUser = FirebaseUser._(data, app);
return currentUser;
}
+ /// Tries to sign in a user with the given Facebook [accessToken].
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// If the user doesn't have an account already, one will be created automatically.
+ ///
+ /// **Important**: You must enable Facebook accounts in the Auth section
+ /// of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [accessToken] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL` - If there already exists an account with the email address asserted by Facebook.
+ /// Resolve this case by calling [fetchSignInMethodsForEmail] and then asking the user to sign in using one of them.
+ /// This error will only be thrown if the "One account per email address" setting is enabled in the Firebase console (recommended).
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Facebook accounts are not enabled.
+ Future<FirebaseUser> signInWithFacebook(
+ {@required String accessToken}) async {
+ assert(accessToken != null);
+ final Map<dynamic, dynamic> data =
+ await channel.invokeMethod('signInWithFacebook', <String, String>{
+ 'accessToken': accessToken,
+ 'app': app.name,
+ });
+ final FirebaseUser currentUser = FirebaseUser._(data, app);
+ return currentUser;
+ }
+
+ /// Tries to sign in a user with the given Twitter [authToken] and [authTokenSecret].
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// If the user doesn't have an account already, one will be created automatically.
+ ///
+ /// **Important**: You must enable Twitter accounts in the Auth section
+ /// of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [authToken] or [authTokenSecret] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL` - If there already exists an account with the email address asserted by Twitter.
+ /// Resolve this case by calling [fetchSignInMethodsForEmail] and then asking the user to sign in using one of them.
+ /// This error will only be thrown if the "One account per email address" setting is enabled in the Firebase console (recommended).
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Twitter accounts are not enabled.
+ Future<FirebaseUser> signInWithTwitter({
+ @required String authToken,
+ @required String authTokenSecret,
+ }) async {
+ assert(authToken != null);
+ assert(authTokenSecret != null);
+ final Map<dynamic, dynamic> data =
+ await channel.invokeMethod('signInWithTwitter', <String, String>{
+ 'authToken': authToken,
+ 'authTokenSecret': authTokenSecret,
+ 'app': app.name,
+ });
+ final FirebaseUser currentUser = FirebaseUser._(data, app);
+ return currentUser;
+ }
+
+ /// Tries to sign in a user with the given Github [token].
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// If the user doesn't have an account already, one will be created automatically.
+ ///
+ /// **Important**: You must enable Github accounts in the Auth section
+ /// of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [token] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL` - If there already exists an account with the email address asserted by Github.
+ /// Resolve this case by calling [fetchSignInMethodsForEmail] and then asking the user to sign in using one of them.
+ /// This error will only be thrown if the "One account per email address" setting is enabled in the Firebase console (recommended).
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Github accounts are not enabled.
+ Future<FirebaseUser> signInWithGithub({
+ @required String token,
+ }) async {
+ assert(token != null);
+ final Map<dynamic, dynamic> data =
+ await channel.invokeMethod('signInWithGithub', <String, String>{
+ 'token': token,
+ 'app': app.name,
+ });
+ final FirebaseUser currentUser = FirebaseUser._(data, app);
+ return currentUser;
+ }
+
+ /// Tries to sign in a user with the given Phone [verificationId] and [smsCode].
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// If the user doesn't have an account already, one will be created automatically.
+ ///
+ /// **Important**: You must enable Phone accounts in the Auth section
+ /// of the Firebase console before being able to use them.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [verificationId] or [smsCode] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Phone accounts are not enabled.
Future<FirebaseUser> signInWithPhoneNumber({
@required String verificationId,
@required String smsCode,
@@ -347,13 +601,55 @@
<String, String>{
'verificationId': verificationId,
'smsCode': smsCode,
- 'app': app.name
+ 'app': app.name,
},
);
final FirebaseUser currentUser = FirebaseUser._(data, app);
return currentUser;
}
+ /// Starts the phone number verification process for the given phone number.
+ ///
+ /// Either sends an SMS with a 6 digit code to the phone number specified,
+ /// or sign's the user in and [verificationCompleted] is called.
+ ///
+ /// No duplicated SMS will be sent out upon re-entry (before timeout).
+ ///
+ /// Make sure to test all scenarios below:
+ /// • You directly get logged in if Google Play Services verified the phone
+ /// number instantly or helped you auto-retrieve the verification code.
+ /// • Auto-retrieve verification code timed out.
+ /// • Error cases when you receive [verificationFailed] callback.
+ ///
+ /// [phoneNumber] The phone number for the account the user is signing up
+ /// for or signing into. Make sure to pass in a phone number with country
+ /// code prefixed with plus sign ('+').
+ ///
+ /// [timeout] The maximum amount of time you are willing to wait for SMS
+ /// auto-retrieval to be completed by the library. Maximum allowed value
+ /// is 2 minutes. Use 0 to disable SMS-auto-retrieval. Setting this to 0
+ /// will also cause [codeAutoRetrievalTimeout] to be called immediately.
+ /// If you specified a positive value less than 30 seconds, library will
+ /// default to 30 seconds.
+ ///
+ /// [forceResendingToken] The [forceResendingToken] obtained from [codeSent]
+ /// callback to force re-sending another verification SMS before the
+ /// auto-retrieval timeout.
+ ///
+ /// [verificationCompleted] This callback must be implemented.
+ /// It will trigger when an SMS is auto-retrieved or the phone number has
+ /// been instantly verified. The callback will provide a [FirebaseUser].
+ ///
+ /// [verificationFailed] This callback must be implemented.
+ /// Triggered when an error occurred during phone number verification.
+ ///
+ /// [codeSent] Optional callback.
+ /// It will trigger when an SMS has been sent to the users phone,
+ /// and will include a [verificationId] and [forceResendingToken].
+ ///
+ /// [codeAutoRetrievalTimeout] Optional callback.
+ /// It will trigger when SMS auto-retrieval times out and provide a
+ /// [verificationId].
Future<void> verifyPhoneNumber({
@required String phoneNumber,
@required Duration timeout,
@@ -377,12 +673,30 @@
'phoneNumber': phoneNumber,
'timeout': timeout.inMilliseconds,
'forceResendingToken': forceResendingToken,
- 'app': app.name
+ 'app': app.name,
};
await channel.invokeMethod('verifyPhoneNumber', params);
}
+ /// Tries to sign in a user with a given Custom Token [token].
+ ///
+ /// If successful, it also signs the user in into the app and updates
+ /// the [onAuthStateChanged] stream.
+ ///
+ /// Use this method after you retrieve a Firebase Auth Custom Token from your server.
+ ///
+ /// If the user identified by the [uid] specified in the token doesn't
+ /// have an account already, one will be created automatically.
+ ///
+ /// Read how to use Custom Token authentication and the cases where it is
+ /// useful in [the guides](https://firebase.google.com/docs/auth/android/custom-auth).
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CUSTOM_TOKEN` - The custom token format is incorrect.
+ /// Please check the documentation.
+ /// • `ERROR_CUSTOM_TOKEN_MISMATCH` - Invalid configuration.
+ /// Ensure your app's SHA1 is correct in the Firebase console.
Future<FirebaseUser> signInWithCustomToken({@required String token}) async {
assert(token != null);
final Map<dynamic, dynamic> data = await channel.invokeMethod(
@@ -393,12 +707,16 @@
return currentUser;
}
+ /// Signs out the current user and clears it from the disk cache.
+ ///
+ /// If successful, it signs the user out of the app and updates
+ /// the [onAuthStateChanged] stream.
Future<void> signOut() async {
return await channel
.invokeMethod("signOut", <String, String>{'app': app.name});
}
- /// Asynchronously gets current user, or `null` if there is none.
+ /// Returns the currently signed-in [FirebaseUser] or [null] if there is none.
Future<FirebaseUser> currentUser() async {
final Map<dynamic, dynamic> data = await channel
.invokeMethod("currentUser", <String, String>{'app': app.name});
@@ -407,12 +725,19 @@
return currentUser;
}
- /// Links email account with current user and returns [Future<FirebaseUser>]
- /// basically current user with additional email information
+ /// Links the given [email] and [password] to the current user.
///
- /// throws [PlatformException] when
- /// 1. email address is already used
- /// 2. wrong email and password provided
+ /// This allows the user to sign in to this account in the future with
+ /// the given [email] and [password].
+ ///
+ /// Errors:
+ /// • `ERROR_WEAK_PASSWORD` - If the password is not strong enough.
+ /// • `ERROR_INVALID_CREDENTIAL` - If the email address is malformed.
+ /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the email is already in use by a different account.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has an Email & Password linked.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<FirebaseUser> linkWithEmailAndPassword({
@required String email,
@required String password,
@@ -427,14 +752,18 @@
return currentUser;
}
- /// Links google account with current user and returns [Future<FirebaseUser>]
+ /// Links the Google Account to the current user using [idToken] and [accessToken].
///
- /// throws [PlatformException] when
- /// 1. No current user provided (user has not logged in)
- /// 2. No google credentials were found for given [idToken] and [accessToken]
- /// 3. Google account already linked with another [FirebaseUser]
- /// Detailed documentation on possible error causes can be found in [Android docs](https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseUser#exceptions_4) and [iOS docs](https://firebase.google.com/docs/reference/ios/firebaseauth/api/reference/Classes/FIRUser#/c:objc(cs)FIRUser(im)linkWithCredential:completion:)
- /// TODO: Throw custom exceptions with error codes indicating cause of exception
+ /// This allows the user to sign in to this account in the future with
+ /// the given Google Account.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [idToken] or [accessToken] is malformed or has expired.
+ /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the Google account is already in use by a different account.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has a Google account linked.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Google accounts are not enabled.
Future<FirebaseUser> linkWithGoogleCredential({
@required String idToken,
@required String accessToken,
@@ -446,51 +775,107 @@
<String, String>{
'idToken': idToken,
'accessToken': accessToken,
- 'app': app.name
+ 'app': app.name,
},
);
final FirebaseUser currentUser = FirebaseUser._(data, app);
return currentUser;
}
+ /// Links the Facebook Account to the current user using [accessToken].
+ ///
+ /// This allows the user to sign in to this account in the future with
+ /// the given Facebook Account.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [accessToken] is malformed or has expired.
+ /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the Facebook account is already in use by a different account.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has a Facebook account linked.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Facebook accounts are not enabled.
Future<FirebaseUser> linkWithFacebookCredential({
@required String accessToken,
}) async {
assert(accessToken != null);
final Map<dynamic, dynamic> data = await channel.invokeMethod(
'linkWithFacebookCredential',
- <String, String>{'accessToken': accessToken, 'app': app.name},
- );
- final FirebaseUser currentUser = FirebaseUser._(data, app);
- return currentUser;
- }
-
- Future<FirebaseUser> linkWithTwitterCredential({
- @required String authToken,
- @required String authTokenSecret,
- }) async {
- final Map<dynamic, dynamic> data = await channel.invokeMethod(
- 'linkWithTwitterCredential',
<String, String>{
+ 'accessToken': accessToken,
'app': app.name,
- 'authToken': authToken,
- 'authTokenSecret': authTokenSecret,
},
);
final FirebaseUser currentUser = FirebaseUser._(data, app);
return currentUser;
}
- Future<FirebaseUser> linkWithGithubCredential(
- {@required String token}) async {
- assert(token != null);
+ /// Links the Twitter Account to the current user using [authToken] and [authTokenSecret].
+ ///
+ /// This allows the user to sign in to this account in the future with
+ /// the given Twitter Account.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [authToken] or [authTokenSecret] is malformed or has expired.
+ /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the Twitter account is already in use by a different account.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has a Twitter account linked.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Twitter accounts are not enabled.
+ Future<FirebaseUser> linkWithTwitterCredential({
+ @required String authToken,
+ @required String authTokenSecret,
+ }) async {
+ assert(authToken != null);
+ assert(authTokenSecret != null);
final Map<dynamic, dynamic> data = await channel.invokeMethod(
- 'linkWithGithubCredential',
- <String, String>{'app': app.name, 'token': token});
+ 'linkWithTwitterCredential',
+ <String, String>{
+ 'authToken': authToken,
+ 'authTokenSecret': authTokenSecret,
+ 'app': app.name,
+ },
+ );
final FirebaseUser currentUser = FirebaseUser._(data, app);
return currentUser;
}
+ /// Links the Github Account to the current user using [token].
+ ///
+ /// This allows the user to sign in to this account in the future with
+ /// the given Github Account.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [token] is malformed or has expired.
+ /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the Github account is already in use by a different account.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve.
+ /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has a Github account linked.
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Github accounts are not enabled.
+ Future<FirebaseUser> linkWithGithubCredential({
+ @required String token,
+ }) async {
+ assert(token != null);
+ final Map<dynamic, dynamic> data = await channel.invokeMethod(
+ 'linkWithGithubCredential',
+ <String, String>{
+ 'app': app.name,
+ 'token': token,
+ },
+ );
+ final FirebaseUser currentUser = FirebaseUser._(data, app);
+ return currentUser;
+ }
+
+ /// Reauthenticates the current user with given [email] and [password].
+ ///
+ /// This is used to prevent or resolve `ERROR_REQUIRES_RECENT_LOGIN`
+ /// response to operations that require a recent sign-in.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [email] and/or [password] are incorrect.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> reauthenticateWithEmailAndPassword({
@required String email,
@required String password,
@@ -503,6 +888,16 @@
);
}
+ /// Reauthenticates the current user with the Google Account specified by [idToken] and [accessToken].
+ ///
+ /// This is used to prevent or resolve `ERROR_REQUIRES_RECENT_LOGIN`
+ /// response to operations that require a recent sign-in.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [idToken] or [accessToken] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> reauthenticateWithGoogleCredential({
@required String idToken,
@required String accessToken,
@@ -519,6 +914,16 @@
);
}
+ /// Reauthenticates the current user with the Facebook Account specified by [accessToken].
+ ///
+ /// This is used to prevent or resolve `ERROR_REQUIRES_RECENT_LOGIN`
+ /// response to operations that require a recent sign-in.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [accessToken] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> reauthenticateWithFacebookCredential({
@required String accessToken,
}) {
@@ -529,6 +934,16 @@
);
}
+ /// Reauthenticates the current user with the Twitter Account specified by [authToken] and [authTokenSecret].
+ ///
+ /// This is used to prevent or resolve `ERROR_REQUIRES_RECENT_LOGIN`
+ /// response to operations that require a recent sign-in.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [authToken] or [authTokenSecret] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> reauthenticateWithTwitterCredential({
@required String authToken,
@required String authTokenSecret,
@@ -543,10 +958,25 @@
);
}
+ /// Reauthenticates the current user with the Github Account specified by [token].
+ ///
+ /// This is used to prevent or resolve `ERROR_REQUIRES_RECENT_LOGIN`
+ /// response to operations that require a recent sign-in.
+ ///
+ /// Errors:
+ /// • `ERROR_INVALID_CREDENTIAL` - If the [token] is malformed or has expired.
+ /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console)
+ /// • `ERROR_USER_NOT_FOUND` - If the user has been deleted (for example, in the Firebase console)
+ /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that Email & Password accounts are not enabled.
Future<void> reauthenticateWithGithubCredential({@required String token}) {
assert(token != null);
- return channel.invokeMethod('reauthenticateWithGithubCredential',
- <String, String>{'app': app.name, 'token': token});
+ return channel.invokeMethod(
+ 'reauthenticateWithGithubCredential',
+ <String, String>{
+ 'app': app.name,
+ 'token': token,
+ },
+ );
}
/// Sets the user-facing language code for auth operations that can be
@@ -560,7 +990,7 @@
});
}
- Future<dynamic> _callHandler(MethodCall call) async {
+ Future<void> _callHandler(MethodCall call) async {
switch (call.method) {
case 'onAuthStateChanged':
_onAuthStageChangedHandler(call);
diff --git a/packages/firebase_auth/test/firebase_auth_test.dart b/packages/firebase_auth/test/firebase_auth_test.dart
index 868d4ff..ae83be6 100755
--- a/packages/firebase_auth/test/firebase_auth_test.dart
+++ b/packages/firebase_auth/test/firebase_auth_test.dart
@@ -52,7 +52,7 @@
case "updateProfile":
return null;
break;
- case "fetchProvidersForEmail":
+ case "fetchSignInMethodsForEmail":
return List<String>(0);
break;
case "verifyPhoneNumber":
@@ -135,16 +135,16 @@
);
});
- test('fetchProvidersForEmail', () async {
+ test('fetchSignInMethodsForEmail', () async {
final List<String> providers =
- await auth.fetchProvidersForEmail(email: kMockEmail);
+ await auth.fetchSignInMethodsForEmail(email: kMockEmail);
expect(providers, isNotNull);
expect(providers.length, 0);
expect(
log,
<Matcher>[
isMethodCall(
- 'fetchProvidersForEmail',
+ 'fetchSignInMethodsForEmail',
arguments: <String, String>{
'email': kMockEmail,
'app': auth.app.name
@@ -154,6 +154,86 @@
);
});
+ test('linkWithTwitterCredential', () async {
+ final FirebaseUser user = await auth.linkWithTwitterCredential(
+ authToken: kMockIdToken,
+ authTokenSecret: kMockAccessToken,
+ );
+ verifyUser(user);
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'linkWithTwitterCredential',
+ arguments: <String, String>{
+ 'authToken': kMockIdToken,
+ 'authTokenSecret': kMockAccessToken,
+ 'app': auth.app.name,
+ },
+ ),
+ ],
+ );
+ });
+
+ test('signInWithTwitter', () async {
+ final FirebaseUser user = await auth.signInWithTwitter(
+ authToken: kMockIdToken,
+ authTokenSecret: kMockAccessToken,
+ );
+ verifyUser(user);
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'signInWithTwitter',
+ arguments: <String, String>{
+ 'authToken': kMockIdToken,
+ 'authTokenSecret': kMockAccessToken,
+ 'app': auth.app.name,
+ },
+ ),
+ ],
+ );
+ });
+
+ test('linkWithGithubCredential', () async {
+ final FirebaseUser user = await auth.linkWithGithubCredential(
+ token: kMockGithubToken,
+ );
+ verifyUser(user);
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'linkWithGithubCredential',
+ arguments: <String, String>{
+ 'token': kMockGithubToken,
+ 'app': auth.app.name,
+ },
+ ),
+ ],
+ );
+ });
+
+ test('signInWithGithub', () async {
+ final FirebaseUser user = await auth.signInWithGithub(
+ token: kMockGithubToken,
+ );
+ verifyUser(user);
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall(
+ 'signInWithGithub',
+ arguments: <String, String>{
+ 'token': kMockGithubToken,
+ 'app': auth.app.name,
+ },
+ ),
+ ],
+ );
+ });
+
test('linkWithEmailAndPassword', () async {
final FirebaseUser user = await auth.linkWithEmailAndPassword(
email: kMockEmail,
@@ -637,6 +717,96 @@
]);
});
+ test('unlinkEmailAndPassword', () async {
+ final FirebaseUser user = await auth.currentUser();
+ await user.unlinkEmailAndPassword();
+ expect(log, <Matcher>[
+ isMethodCall(
+ 'currentUser',
+ arguments: <String, String>{'app': auth.app.name},
+ ),
+ isMethodCall(
+ 'unlinkCredential',
+ arguments: <String, String>{
+ 'app': auth.app.name,
+ 'provider': 'password',
+ },
+ ),
+ ]);
+ });
+
+ test('unlinkGoogleCredential', () async {
+ final FirebaseUser user = await auth.currentUser();
+ await user.unlinkGoogleCredential();
+ expect(log, <Matcher>[
+ isMethodCall(
+ 'currentUser',
+ arguments: <String, String>{'app': auth.app.name},
+ ),
+ isMethodCall(
+ 'unlinkCredential',
+ arguments: <String, String>{
+ 'app': auth.app.name,
+ 'provider': 'google.com',
+ },
+ ),
+ ]);
+ });
+
+ test('unlinkFacebookCredential', () async {
+ final FirebaseUser user = await auth.currentUser();
+ await user.unlinkFacebookCredential();
+ expect(log, <Matcher>[
+ isMethodCall(
+ 'currentUser',
+ arguments: <String, String>{'app': auth.app.name},
+ ),
+ isMethodCall(
+ 'unlinkCredential',
+ arguments: <String, String>{
+ 'app': auth.app.name,
+ 'provider': 'facebook.com',
+ },
+ ),
+ ]);
+ });
+
+ test('unlinkTwitterCredential', () async {
+ final FirebaseUser user = await auth.currentUser();
+ await user.unlinkTwitterCredential();
+ expect(log, <Matcher>[
+ isMethodCall(
+ 'currentUser',
+ arguments: <String, String>{'app': auth.app.name},
+ ),
+ isMethodCall(
+ 'unlinkCredential',
+ arguments: <String, String>{
+ 'app': auth.app.name,
+ 'provider': 'twitter.com',
+ },
+ ),
+ ]);
+ });
+
+ test('unlinkGithubCredential', () async {
+ final FirebaseUser user = await auth.currentUser();
+ await user.unlinkGithubCredential();
+ expect(log, <Matcher>[
+ isMethodCall(
+ 'currentUser',
+ arguments: <String, String>{'app': auth.app.name},
+ ),
+ isMethodCall(
+ 'unlinkCredential',
+ arguments: <String, String>{
+ 'app': auth.app.name,
+ 'provider': 'github.com',
+ },
+ ),
+ ]);
+ });
+
test('signInWithCustomToken', () async {
final FirebaseUser user =
await auth.signInWithCustomToken(token: kMockCustomToken);