Skip to content

Commit

Permalink
fix(auth): fix an issue where unenroll would not throw a FirebaseExce…
Browse files Browse the repository at this point in the history
…ption (#10572)

* fix(auth): fix an issue where unenroll would not throw a FirebaseException

* fix(auth): fix an issue where unenroll would not throw a FirebaseException

* fix(auth): fix an issue where unenroll would not throw a FirebaseException

* fix(auth): fix an issue where unenroll would not throw a FirebaseException

* fix(auth): fix an issue where unenroll would not throw a FirebaseException

* fix(auth): fix an issue where unenroll would not throw a FirebaseException
  • Loading branch information
Lyokone committed Mar 9, 2023
1 parent 344899c commit 8dba33e
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 129 deletions.
2 changes: 2 additions & 0 deletions packages/firebase_auth/firebase_auth/example/lib/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ class _AuthGateState extends State<AuthGate> {
hintText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
autofillHints: const [AutofillHints.email],
validator: (value) =>
value != null && value.isNotEmpty
? null
Expand Down
269 changes: 144 additions & 125 deletions packages/firebase_auth/firebase_auth/example/lib/profile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,143 +102,162 @@ class _ProfilePageState extends State<ProfilePage> {
Center(
child: SizedBox(
width: 400,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
children: [
CircleAvatar(
maxRadius: 60,
backgroundImage: NetworkImage(
user.photoURL ?? placeholderImage,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
children: [
CircleAvatar(
maxRadius: 60,
backgroundImage: NetworkImage(
user.photoURL ?? placeholderImage,
),
),
),
Positioned.directional(
textDirection: Directionality.of(context),
end: 0,
bottom: 0,
child: Material(
clipBehavior: Clip.antiAlias,
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(40),
child: InkWell(
onTap: () async {
final photoURL = await getPhotoURLFromUser();
Positioned.directional(
textDirection: Directionality.of(context),
end: 0,
bottom: 0,
child: Material(
clipBehavior: Clip.antiAlias,
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(40),
child: InkWell(
onTap: () async {
final photoURL = await getPhotoURLFromUser();

if (photoURL != null) {
await user.updatePhotoURL(photoURL);
}
},
radius: 50,
child: const SizedBox(
width: 35,
height: 35,
child: Icon(Icons.edit),
if (photoURL != null) {
await user.updatePhotoURL(photoURL);
}
},
radius: 50,
child: const SizedBox(
width: 35,
height: 35,
child: Icon(Icons.edit),
),
),
),
),
)
],
),
const SizedBox(height: 10),
TextField(
textAlign: TextAlign.center,
controller: controller,
decoration: const InputDecoration(
border: InputBorder.none,
floatingLabelBehavior: FloatingLabelBehavior.never,
alignLabelWithHint: true,
label: Center(
child: Text(
'Click to add a display name',
)
],
),
const SizedBox(height: 10),
TextField(
textAlign: TextAlign.center,
controller: controller,
decoration: const InputDecoration(
border: InputBorder.none,
floatingLabelBehavior: FloatingLabelBehavior.never,
alignLabelWithHint: true,
label: Center(
child: Text(
'Click to add a display name',
),
),
),
),
),
Text(user.email ?? user.phoneNumber ?? 'User'),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (userProviders.contains('phone'))
const Icon(Icons.phone),
if (userProviders.contains('password'))
const Icon(Icons.mail),
if (userProviders.contains('google.com'))
SizedBox(
width: 24,
child: Image.network(
'https://upload.wikimedia.org/wikipedia/commons/0/09/IOS_Google_icon.png',
Text(user.email ?? user.phoneNumber ?? 'User'),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (userProviders.contains('phone'))
const Icon(Icons.phone),
if (userProviders.contains('password'))
const Icon(Icons.mail),
if (userProviders.contains('google.com'))
SizedBox(
width: 24,
child: Image.network(
'https://upload.wikimedia.org/wikipedia/commons/0/09/IOS_Google_icon.png',
),
),
),
],
),
const SizedBox(height: 20),
TextButton(
onPressed: () {
user.sendEmailVerification();
},
child: const Text('Verify Email'),
),
const SizedBox(height: 20),
TextButton(
onPressed: () async {
final a = await user.multiFactor.getEnrolledFactors();
print(a);
},
child: const Text('Get enrolled factors'),
),
const SizedBox(height: 20),
TextFormField(
controller: phoneController,
decoration: const InputDecoration(
icon: Icon(Icons.phone),
hintText: '+33612345678',
labelText: 'Phone number',
],
),
),
const SizedBox(height: 20),
TextButton(
onPressed: () async {
final session = await user.multiFactor.getSession();
await auth.verifyPhoneNumber(
multiFactorSession: session,
phoneNumber: phoneController.text,
verificationCompleted: (_) {},
verificationFailed: print,
codeSent:
(String verificationId, int? resendToken) async {
final smsCode = await getSmsCodeFromUser(context);

if (smsCode != null) {
// Create a PhoneAuthCredential with the code
final credential = PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: smsCode,
);
const SizedBox(height: 20),
TextButton(
onPressed: () {
user.sendEmailVerification();
},
child: const Text('Verify Email'),
),
TextButton(
onPressed: () async {
final a = await user.multiFactor.getEnrolledFactors();
print(a);
},
child: const Text('Get enrolled factors'),
),
TextFormField(
controller: phoneController,
decoration: const InputDecoration(
icon: Icon(Icons.phone),
hintText: '+33612345678',
labelText: 'Phone number',
),
),
const SizedBox(height: 20),
TextButton(
onPressed: () async {
final session = await user.multiFactor.getSession();
await auth.verifyPhoneNumber(
multiFactorSession: session,
phoneNumber: phoneController.text,
verificationCompleted: (_) {},
verificationFailed: print,
codeSent: (
String verificationId,
int? resendToken,
) async {
final smsCode = await getSmsCodeFromUser(context);

try {
await user.multiFactor.enroll(
PhoneMultiFactorGenerator.getAssertion(
credential,
),
if (smsCode != null) {
// Create a PhoneAuthCredential with the code
final credential = PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: smsCode,
);
} on FirebaseAuthException catch (e) {
print(e.message);

try {
await user.multiFactor.enroll(
PhoneMultiFactorGenerator.getAssertion(
credential,
),
);
} on FirebaseAuthException catch (e) {
print(e.message);
}
}
}
},
codeAutoRetrievalTimeout: print,
);
},
child: const Text('Verify Number For MFA'),
),
const SizedBox(height: 20),
TextButton(
onPressed: _signOut,
child: const Text('Sign out'),
),
],
},
codeAutoRetrievalTimeout: print,
);
},
child: const Text('Verify Number For MFA'),
),
TextButton(
onPressed: () async {
try {
final enrolledFactors =
await user.multiFactor.getEnrolledFactors();

await user.multiFactor.unenroll(
factorUid: enrolledFactors.first.uid,
);
// Show snackbar
ScaffoldSnackbar.of(context).show('MFA unenrolled');
} catch (e) {
print(e);
}
},
child: const Text('Unenroll MFA'),
),
const Divider(),
TextButton(
onPressed: _signOut,
child: const Text('Sign out'),
),
],
),
),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class MultiFactor {
/// [multiFactorInfo] is the [MultiFactorInfo] of the second factor to unenroll.
/// Only one of [factorUid] or [multiFactorInfo] should be provided.
Future<void> unenroll({String? factorUid, MultiFactorInfo? multiFactorInfo}) {
assert(
(factorUid != null && multiFactorInfo == null) ||
(factorUid == null && multiFactorInfo != null),
'Exactly one of `factorUid` or `multiFactorInfo` must be provided',
);
return _delegate.unenroll(
factorUid: factorUid,
multiFactorInfo: multiFactorInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class MethodChannelMultiFactor extends MultiFactorPlatform {
Future<void> unenroll({
String? factorUid,
MultiFactorInfo? multiFactorInfo,
}) {
}) async {
final uidToUnenroll = factorUid ?? multiFactorInfo?.uid;
if (uidToUnenroll == null) {
throw ArgumentError(
Expand All @@ -76,7 +76,7 @@ class MethodChannelMultiFactor extends MultiFactorPlatform {
}

try {
return _api.unenroll(
await _api.unenroll(
auth.app.name,
uidToUnenroll,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class MultiFactorWeb extends MultiFactorPlatform {
Future<void> unenroll({
String? factorUid,
MultiFactorInfo? multiFactorInfo,
}) {
}) async {
final uidToUnenroll = factorUid ?? multiFactorInfo?.uid;
if (uidToUnenroll == null) {
throw ArgumentError(
Expand All @@ -59,7 +59,7 @@ class MultiFactorWeb extends MultiFactorPlatform {
}

try {
return _webMultiFactorUser.unenroll(
await _webMultiFactorUser.unenroll(
uidToUnenroll,
);
} catch (e) {
Expand Down
Loading

0 comments on commit 8dba33e

Please sign in to comment.