diff --git a/packages/firebase_auth/firebase_auth/lib/firebase_auth.dart b/packages/firebase_auth/firebase_auth/lib/firebase_auth.dart index d69a573c6352..4d005d8618b8 100755 --- a/packages/firebase_auth/firebase_auth/lib/firebase_auth.dart +++ b/packages/firebase_auth/firebase_auth/lib/firebase_auth.dart @@ -14,8 +14,8 @@ import 'package:flutter/foundation.dart'; export 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart' show FirebaseAuthException, - FirebaseAuthMultiFactorException, MultiFactorInfo, + MultiFactorSession, PhoneMultiFactorInfo, IdTokenResult, UserMetadata, diff --git a/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart b/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart index 742399cfe156..a81644649a3c 100644 --- a/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart +++ b/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart @@ -492,10 +492,16 @@ class FirebaseAuth extends FirebasePluginPlatform { /// - Thrown if the credential is a [PhoneAuthProvider.credential] and the /// verification ID of the credential is not valid.id. Future signInWithCredential(AuthCredential credential) async { - return UserCredential._( - this, - await _delegate.signInWithCredential(credential), - ); + try { + return UserCredential._( + this, + await _delegate.signInWithCredential(credential), + ); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Tries to sign in a user with a given custom token. @@ -519,7 +525,14 @@ class FirebaseAuth extends FirebasePluginPlatform { /// - **invalid-custom-token**: /// - Thrown if the custom token format is incorrect. Future signInWithCustomToken(String token) async { - return UserCredential._(this, await _delegate.signInWithCustomToken(token)); + try { + return UserCredential._( + this, await _delegate.signInWithCustomToken(token)); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Attempts to sign in a user with the given email address and password. @@ -545,10 +558,16 @@ class FirebaseAuth extends FirebasePluginPlatform { required String email, required String password, }) async { - return UserCredential._( - this, - await _delegate.signInWithEmailAndPassword(email, password), - ); + try { + return UserCredential._( + this, + await _delegate.signInWithEmailAndPassword(email, password), + ); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Signs in using an email address and email sign-in link. @@ -570,10 +589,16 @@ class FirebaseAuth extends FirebasePluginPlatform { required String email, required String emailLink, }) async { - return UserCredential._( - this, - await _delegate.signInWithEmailLink(email, emailLink), - ); + try { + return UserCredential._( + this, + await _delegate.signInWithEmailLink(email, emailLink), + ); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Signs in with an AuthProvider using native authentication flow. @@ -584,10 +609,16 @@ class FirebaseAuth extends FirebasePluginPlatform { Future signInWithAuthProvider( AuthProvider provider, ) async { - return UserCredential._( - this, - await _delegate.signInWithAuthProvider(provider), - ); + try { + return UserCredential._( + this, + await _delegate.signInWithAuthProvider(provider), + ); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Starts a sign-in flow for a phone number. @@ -624,7 +655,13 @@ class FirebaseAuth extends FirebasePluginPlatform { /// /// This method is only available on web based platforms. Future signInWithPopup(AuthProvider provider) async { - return UserCredential._(this, await _delegate.signInWithPopup(provider)); + try { + return UserCredential._(this, await _delegate.signInWithPopup(provider)); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Authenticates a Firebase client using a full-page redirect flow. @@ -632,7 +669,13 @@ class FirebaseAuth extends FirebasePluginPlatform { /// To handle the results and errors for this operation, refer to /// [getRedirectResult]. Future signInWithRedirect(AuthProvider provider) { - return _delegate.signInWithRedirect(provider); + try { + return _delegate.signInWithRedirect(provider); + } on FirebaseAuthMultiFactorExceptionPlatform catch (e) { + throw FirebaseAuthMultiFactorException._(this, e); + } catch (e) { + rethrow; + } } /// Signs out the current user. diff --git a/packages/firebase_auth/firebase_auth/lib/src/multi_factor.dart b/packages/firebase_auth/firebase_auth/lib/src/multi_factor.dart index 4fa685133f85..086ab36c4a0f 100644 --- a/packages/firebase_auth/firebase_auth/lib/src/multi_factor.dart +++ b/packages/firebase_auth/firebase_auth/lib/src/multi_factor.dart @@ -17,10 +17,10 @@ class MultiFactor { /// /// [displayName] can be used to provide a display name for the second factor. Future enroll( - MultiFactorAssertionPlatform assertion, { + MultiFactorAssertion assertion, { String? displayName, }) async { - return _delegate.enroll(assertion, displayName: displayName); + return _delegate.enroll(assertion._delegate, displayName: displayName); } /// Unenrolls a second factor from this user. @@ -45,9 +45,68 @@ class MultiFactor { class PhoneMultiFactorGenerator { /// Transforms a PhoneAuthCredential into a [MultiFactorAssertion] /// which can be used to confirm ownership of a phone second factor. - static MultiFactorAssertionPlatform getAssertion( + static MultiFactorAssertion getAssertion( PhoneAuthCredential credential, ) { - return PhoneMultiFactorGeneratorPlatform.instance.getAssertion(credential); + final assertion = + PhoneMultiFactorGeneratorPlatform.instance.getAssertion(credential); + return MultiFactorAssertion._(assertion); } } + +/// Represents an assertion that the Firebase Authentication server +/// can use to authenticate a user as part of a multi-factor flow. +class MultiFactorAssertion { + final MultiFactorAssertionPlatform _delegate; + + MultiFactorAssertion._(this._delegate) { + MultiFactorAssertionPlatform.verifyExtends(_delegate); + } +} + +/// Utility class that contains methods to resolve second factor +/// requirements on users that have opted into two-factor authentication. +class MultiFactorResolver { + final FirebaseAuth _auth; + final MultiFactorResolverPlatform _delegate; + + MultiFactorResolver._(this._auth, this._delegate) { + MultiFactorResolverPlatform.verifyExtends(_delegate); + } + + /// List of [MultiFactorInfo] which represents the available + /// second factors that can be used to complete the sign-in for the current session. + List get hints => _delegate.hints; + + /// A MultiFactorSession, an opaque session identifier for the current sign-in flow. + MultiFactorSession get session => _delegate.session; + + /// Completes sign in with a second factor using an MultiFactorAssertion which + /// confirms that the user has successfully completed the second factor challenge. + Future resolveSignIn( + MultiFactorAssertion assertion, + ) async { + final credential = await _delegate.resolveSignIn(assertion._delegate); + return UserCredential._(_auth, credential); + } +} + +/// MultiFactor exception related to Firebase Authentication. Check the error code +/// and message for more details. +class FirebaseAuthMultiFactorException extends FirebaseAuthException { + final FirebaseAuth _auth; + final FirebaseAuthMultiFactorExceptionPlatform _delegate; + + FirebaseAuthMultiFactorException._(this._auth, this._delegate) + : super( + code: _delegate.code, + message: _delegate.message, + email: _delegate.email, + credential: _delegate.credential, + phoneNumber: _delegate.phoneNumber, + tenantId: _delegate.tenantId, + ); + + MultiFactorResolver get resolver => + MultiFactorResolver._(_auth, _delegate.resolver); +} diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/firebase_auth_multi_factor_exception.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/firebase_auth_multi_factor_exception.dart index d928fc4cc3ec..1b5663452b76 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/firebase_auth_multi_factor_exception.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/firebase_auth_multi_factor_exception.dart @@ -8,11 +8,11 @@ import 'package:meta/meta.dart'; /// MultiFactor exception related to Firebase Authentication. Check the error code /// and message for more details. -class FirebaseAuthMultiFactorException extends FirebaseAuthException +class FirebaseAuthMultiFactorExceptionPlatform extends FirebaseAuthException implements Exception { // ignore: public_member_api_docs @protected - FirebaseAuthMultiFactorException({ + FirebaseAuthMultiFactorExceptionPlatform({ String? message, required String code, String? email, diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart index 3097890269ac..211323cc4809 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart @@ -74,7 +74,7 @@ FirebaseException platformExceptionToFirebaseAuthException( ); } -FirebaseAuthMultiFactorException parseMultiFactorError( +FirebaseAuthMultiFactorExceptionPlatform parseMultiFactorError( Map details) { final code = details['code'] as String?; final message = details['message'] as String?; @@ -124,7 +124,7 @@ FirebaseAuthMultiFactorException parseMultiFactorError( auth, ); - return FirebaseAuthMultiFactorException( + return FirebaseAuthMultiFactorExceptionPlatform( code: code ?? 'Unknown', message: message, resolver: multiFactorResolver, diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_multi_factor.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_multi_factor.dart index 90a9537e74ec..fcb9f93b0808 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_multi_factor.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_multi_factor.dart @@ -56,20 +56,39 @@ class MultiFactorSession { final String id; } +/// {@template .multiFactorAssertion} /// Represents an assertion that the Firebase Authentication server /// can use to authenticate a user as part of a multi-factor flow. -class MultiFactorAssertionPlatform {} +/// {@endtemplate} +class MultiFactorAssertionPlatform extends PlatformInterface { + /// {@macro .multiFactorAssertion} + MultiFactorAssertionPlatform() : super(token: _token); + + static final Object _token = Object(); + + /// Ensures that any delegate class has extended a [MultiFactorResolverPlatform]. + static void verifyExtends(MultiFactorAssertionPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + } +} -/// {@macro .platformInterfaceMultiFactorResolverPlatform} +/// {@template .platformInterfaceMultiFactorResolverPlatform} /// Utility class that contains methods to resolve second factor /// requirements on users that have opted into two-factor authentication. /// {@endtemplate} -class MultiFactorResolverPlatform { +class MultiFactorResolverPlatform extends PlatformInterface { /// {@macro .platformInterfaceMultiFactorResolverPlatform} - const MultiFactorResolverPlatform( + MultiFactorResolverPlatform( this.hints, this.session, - ); + ) : super(token: _token); + + static final Object _token = Object(); + + /// Ensures that any delegate class has extended a [MultiFactorResolverPlatform]. + static void verifyExtends(MultiFactorResolverPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + } /// List of [MultiFactorInfo] which represents the available /// second factors that can be used to complete the sign-in for the current session. diff --git a/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart b/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart index 55b07c67112a..252e8c8c72ff 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart @@ -50,7 +50,7 @@ FirebaseAuthException getFirebaseAuthException( firebaseError as auth_interop.MultiFactorError, ); - return FirebaseAuthMultiFactorException( + return FirebaseAuthMultiFactorExceptionPlatform( code: code, message: message, email: firebaseError.email,