Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 421e62a

Browse files
committed
Adding support for FirebaseUser.unlink(providerId)
* Unlink an auth provider from a user account
1 parent a90c328 commit 421e62a

File tree

5 files changed

+88
-69
lines changed

5 files changed

+88
-69
lines changed

packages/firebase_auth/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.6.3
2+
3+
* Adding support for FirebaseUser.unlink(providerId)
4+
15
## 0.6.2+1
26

37
* Bump Android dependencies to latest.

packages/firebase_auth/android/src/main/java/io/flutter/plugins/firebaseauth/FirebaseAuthPlugin.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ public void onMethodCall(MethodCall call, Result result) {
118118
break;
119119
case "linkWithTwitterCredential":
120120
handleLinkWithTwitterCredential(call, result, getAuth(call));
121+
case "unlink":
122+
handleUnlink(call, result, getAuth(call));
121123
break;
122124
case "updateEmail":
123125
handleUpdateEmail(call, result, getAuth(call));
@@ -434,6 +436,15 @@ private void handleSignInWithCustomToken(
434436
.addOnCompleteListener(new SignInCompleteListener(result));
435437
}
436438

439+
private void handleUnlink(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
440+
Map<String, String> arguments = call.arguments();
441+
String providerId = arguments.get("providerId");
442+
firebaseAuth
443+
.getCurrentUser()
444+
.unlink(providerId)
445+
.addOnCompleteListener(new SignInCompleteListener(result));
446+
}
447+
437448
private void handleSignOut(MethodCall call, final Result result, FirebaseAuth firebaseAuth) {
438449
firebaseAuth.signOut();
439450
result.success(null);

packages/firebase_auth/ios/Classes/FirebaseAuthPlugin.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
199199
completion:^(FIRUser *user, NSError *error) {
200200
[self sendResult:result forUser:user error:error];
201201
}];
202+
[[FIRAuth auth].currentUser linkWithCredential:credential
203+
completion:^(FIRUser *user, NSError *error) {
204+
[self sendResult:result forUser:user error:error];
205+
}];
206+
} else if ([@"unlink" isEqualToString:call.method]) {
207+
NSString *providerId = call.arguments[@"providerId"];
208+
[[FIRAuth auth].currentUser unlinkFromProvider:providerId
209+
completion:^(FIRUser *user, NSError *error) {
210+
[self sendResult:result forUser:user error:error];
211+
}];
202212
} else if ([@"updateEmail" isEqualToString:call.method]) {
203213
NSString *email = call.arguments[@"email"];
204214
[[self getAuth:call.arguments].currentUser updateEmail:email
@@ -212,6 +222,10 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
212222
completion:^(NSError *error) {
213223
[self sendResult:result forUser:nil error:error];
214224
}];
225+
[[FIRAuth auth].currentUser updatePassword:password
226+
completion:^(NSError *error) {
227+
[self sendResult:result forUser:nil error:error];
228+
}];
215229
} else if ([@"updateProfile" isEqualToString:call.method]) {
216230
FIRUserProfileChangeRequest *changeRequest =
217231
[[self getAuth:call.arguments].currentUser profileChangeRequest];

packages/firebase_auth/lib/firebase_auth.dart

Lines changed: 41 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class FirebaseUserMetadata {
1616
final Map<dynamic, dynamic> _data;
1717

1818
int get creationTimestamp => _data['creationTimestamp'];
19+
1920
int get lastSignInTimestamp => _data['lastSignInTimestamp'];
2021
}
2122

@@ -57,8 +58,7 @@ class UserUpdateInfo {
5758
/// Container of data that will be send in update request
5859
final Map<String, String> _updateData = <String, String>{};
5960

60-
set displayName(String displayName) =>
61-
_updateData["displayName"] = displayName;
61+
set displayName(String displayName) => _updateData["displayName"] = displayName;
6262

6363
String get displayName => _updateData["displayName"];
6464

@@ -70,9 +70,7 @@ class UserUpdateInfo {
7070
/// Represents a user.
7171
class FirebaseUser extends UserInfo {
7272
FirebaseUser._(Map<dynamic, dynamic> data, FirebaseApp app)
73-
: providerData = data['providerData']
74-
.map<UserInfo>((dynamic item) => UserInfo._(item, app))
75-
.toList(),
73+
: providerData = data['providerData'].map<UserInfo>((dynamic item) => UserInfo._(item, app)).toList(),
7674
_metadata = FirebaseUserMetadata._(data),
7775
super._(data, app);
7876

@@ -93,28 +91,24 @@ class FirebaseUser extends UserInfo {
9391
///
9492
/// Completes with an error if the user is signed out.
9593
Future<String> getIdToken({bool refresh = false}) async {
96-
return await FirebaseAuth.channel
97-
.invokeMethod('getIdToken', <String, dynamic>{
94+
return await FirebaseAuth.channel.invokeMethod('getIdToken', <String, dynamic>{
9895
'refresh': refresh,
9996
'app': app.name,
10097
});
10198
}
10299

103100
Future<void> sendEmailVerification() async {
104-
await FirebaseAuth.channel.invokeMethod(
105-
'sendEmailVerification', <String, String>{'app': app.name});
101+
await FirebaseAuth.channel.invokeMethod('sendEmailVerification', <String, String>{'app': app.name});
106102
}
107103

108104
/// Manually refreshes the data of the current user (for example, attached providers, display name, and so on).
109105
Future<void> reload() async {
110-
await FirebaseAuth.channel
111-
.invokeMethod('reload', <String, String>{'app': app.name});
106+
await FirebaseAuth.channel.invokeMethod('reload', <String, String>{'app': app.name});
112107
}
113108

114109
/// Deletes the user record from your Firebase project's database.
115110
Future<void> delete() async {
116-
await FirebaseAuth.channel
117-
.invokeMethod('delete', <String, String>{'app': app.name});
111+
await FirebaseAuth.channel.invokeMethod('delete', <String, String>{'app': app.name});
118112
}
119113

120114
/// Updates the email address of the user.
@@ -165,8 +159,7 @@ typedef void PhoneCodeSent(String verificationId, [int forceResendingToken]);
165159
typedef void PhoneCodeAutoRetrievalTimeout(String verificationId);
166160

167161
class FirebaseAuth {
168-
FirebaseAuth({FirebaseApp app})
169-
: app = app != null ? app : FirebaseApp.instance {
162+
FirebaseAuth({FirebaseApp app}) : app = app != null ? app : FirebaseApp.instance {
170163
channel.setMethodCallHandler(_callHandler);
171164
}
172165

@@ -179,12 +172,10 @@ class FirebaseAuth {
179172
'plugins.flutter.io/firebase_auth',
180173
);
181174

182-
final Map<int, StreamController<FirebaseUser>> _authStateChangedControllers =
183-
<int, StreamController<FirebaseUser>>{};
175+
final Map<int, StreamController<FirebaseUser>> _authStateChangedControllers = <int, StreamController<FirebaseUser>>{};
184176

185177
static int nextHandle = 0;
186-
final Map<int, Map<String, dynamic>> _phoneAuthCallbacks =
187-
<int, Map<String, dynamic>>{};
178+
final Map<int, Map<String, dynamic>> _phoneAuthCallbacks = <int, Map<String, dynamic>>{};
188179

189180
FirebaseApp app;
190181

@@ -194,15 +185,13 @@ class FirebaseAuth {
194185

195186
StreamController<FirebaseUser> controller;
196187
controller = StreamController<FirebaseUser>.broadcast(onListen: () {
197-
_handle = channel.invokeMethod('startListeningAuthState',
198-
<String, String>{"app": app.name}).then<int>((dynamic v) => v);
188+
_handle = channel.invokeMethod('startListeningAuthState', <String, String>{"app": app.name}).then<int>((dynamic v) => v);
199189
_handle.then((int handle) {
200190
_authStateChangedControllers[handle] = controller;
201191
});
202192
}, onCancel: () {
203193
_handle.then((int handle) async {
204-
await channel.invokeMethod("stopListeningAuthState",
205-
<String, dynamic>{"id": handle, "app": app.name});
194+
await channel.invokeMethod("stopListeningAuthState", <String, dynamic>{"id": handle, "app": app.name});
206195
_authStateChangedControllers.remove(handle);
207196
});
208197
});
@@ -220,8 +209,7 @@ class FirebaseAuth {
220209
/// FIRAuthErrorCodeOperationNotAllowed - Indicates that anonymous accounts are not enabled. Enable them in the Auth section of the Firebase console.
221210
/// See FIRAuthErrors for a list of error codes that are common to all API methods.
222211
Future<FirebaseUser> signInAnonymously() async {
223-
final Map<dynamic, dynamic> data = await channel
224-
.invokeMethod('signInAnonymously', <String, String>{"app": app.name});
212+
final Map<dynamic, dynamic> data = await channel.invokeMethod('signInAnonymously', <String, String>{"app": app.name});
225213
final FirebaseUser currentUser = FirebaseUser._(data, app);
226214
return currentUser;
227215
}
@@ -275,12 +263,9 @@ class FirebaseAuth {
275263
return currentUser;
276264
}
277265

278-
Future<FirebaseUser> signInWithFacebook(
279-
{@required String accessToken}) async {
266+
Future<FirebaseUser> signInWithFacebook({@required String accessToken}) async {
280267
assert(accessToken != null);
281-
final Map<dynamic, dynamic> data = await channel.invokeMethod(
282-
'signInWithFacebook',
283-
<String, String>{'accessToken': accessToken, 'app': app.name});
268+
final Map<dynamic, dynamic> data = await channel.invokeMethod('signInWithFacebook', <String, String>{'accessToken': accessToken, 'app': app.name});
284269
final FirebaseUser currentUser = FirebaseUser._(data, app);
285270
return currentUser;
286271
}
@@ -294,12 +279,7 @@ class FirebaseAuth {
294279
}) async {
295280
assert(authToken != null);
296281
assert(authTokenSecret != null);
297-
final Map<dynamic, dynamic> data = await channel.invokeMethod(
298-
'signInWithTwitter', <String, String>{
299-
'authToken': authToken,
300-
'authTokenSecret': authTokenSecret,
301-
'app': app.name
302-
});
282+
final Map<dynamic, dynamic> data = await channel.invokeMethod('signInWithTwitter', <String, String>{'authToken': authToken, 'authTokenSecret': authTokenSecret, 'app': app.name});
303283
final FirebaseUser currentUser = FirebaseUser._(data, app);
304284
return currentUser;
305285
}
@@ -312,11 +292,7 @@ class FirebaseAuth {
312292
assert(accessToken != null);
313293
final Map<dynamic, dynamic> data = await channel.invokeMethod(
314294
'signInWithGoogle',
315-
<String, String>{
316-
'idToken': idToken,
317-
'accessToken': accessToken,
318-
'app': app.name
319-
},
295+
<String, String>{'idToken': idToken, 'accessToken': accessToken, 'app': app.name},
320296
);
321297
final FirebaseUser currentUser = FirebaseUser._(data, app);
322298
return currentUser;
@@ -328,11 +304,7 @@ class FirebaseAuth {
328304
}) async {
329305
final Map<dynamic, dynamic> data = await channel.invokeMethod(
330306
'signInWithPhoneNumber',
331-
<String, String>{
332-
'verificationId': verificationId,
333-
'smsCode': smsCode,
334-
'app': app.name
335-
},
307+
<String, String>{'verificationId': verificationId, 'smsCode': smsCode, 'app': app.name},
336308
);
337309
final FirebaseUser currentUser = FirebaseUser._(data, app);
338310
return currentUser;
@@ -378,16 +350,13 @@ class FirebaseAuth {
378350
}
379351

380352
Future<void> signOut() async {
381-
return await channel
382-
.invokeMethod("signOut", <String, String>{'app': app.name});
353+
return await channel.invokeMethod("signOut", <String, String>{'app': app.name});
383354
}
384355

385356
/// Asynchronously gets current user, or `null` if there is none.
386357
Future<FirebaseUser> currentUser() async {
387-
final Map<dynamic, dynamic> data = await channel
388-
.invokeMethod("currentUser", <String, String>{'app': app.name});
389-
final FirebaseUser currentUser =
390-
data == null ? null : FirebaseUser._(data, app);
358+
final Map<dynamic, dynamic> data = await channel.invokeMethod("currentUser", <String, String>{'app': app.name});
359+
final FirebaseUser currentUser = data == null ? null : FirebaseUser._(data, app);
391360
return currentUser;
392361
}
393362

@@ -427,11 +396,7 @@ class FirebaseAuth {
427396
assert(accessToken != null);
428397
final Map<dynamic, dynamic> data = await channel.invokeMethod(
429398
'linkWithGoogleCredential',
430-
<String, String>{
431-
'idToken': idToken,
432-
'accessToken': accessToken,
433-
'app': app.name
434-
},
399+
<String, String>{'idToken': idToken, 'accessToken': accessToken, 'app': app.name},
435400
);
436401
final FirebaseUser currentUser = FirebaseUser._(data, app);
437402
return currentUser;
@@ -465,6 +430,19 @@ class FirebaseAuth {
465430
return currentUser;
466431
}
467432

433+
Future<FirebaseUser> unlink({
434+
@required String providerId,
435+
}) async {
436+
final Map<dynamic, dynamic> data = await channel.invokeMethod(
437+
'unlink',
438+
<String, String>{
439+
'providerId': providerId,
440+
},
441+
);
442+
final FirebaseUser currentUser = FirebaseUser._(data, app);
443+
return currentUser;
444+
}
445+
468446
/// Sets the user-facing language code for auth operations that can be
469447
/// internationalized, such as [sendEmailVerification]. This language
470448
/// code should follow the conventions defined by the IETF in BCP47.
@@ -483,25 +461,21 @@ class FirebaseAuth {
483461
break;
484462
case 'phoneVerificationCompleted':
485463
final int handle = call.arguments['handle'];
486-
final PhoneVerificationCompleted verificationCompleted =
487-
_phoneAuthCallbacks[handle]['PhoneVerificationCompleted'];
464+
final PhoneVerificationCompleted verificationCompleted = _phoneAuthCallbacks[handle]['PhoneVerificationCompleted'];
488465
verificationCompleted(await currentUser());
489466
break;
490467
case 'phoneVerificationFailed':
491468
final int handle = call.arguments['handle'];
492-
final PhoneVerificationFailed verificationFailed =
493-
_phoneAuthCallbacks[handle]['PhoneVerificationFailed'];
469+
final PhoneVerificationFailed verificationFailed = _phoneAuthCallbacks[handle]['PhoneVerificationFailed'];
494470
final Map<dynamic, dynamic> exception = call.arguments['exception'];
495-
verificationFailed(
496-
AuthException(exception['code'], exception['message']));
471+
verificationFailed(AuthException(exception['code'], exception['message']));
497472
break;
498473
case 'phoneCodeSent':
499474
final int handle = call.arguments['handle'];
500475
final String verificationId = call.arguments['verificationId'];
501476
final int forceResendingToken = call.arguments['forceResendingToken'];
502477

503-
final PhoneCodeSent codeSent =
504-
_phoneAuthCallbacks[handle]['PhoneCodeSent'];
478+
final PhoneCodeSent codeSent = _phoneAuthCallbacks[handle]['PhoneCodeSent'];
505479
if (forceResendingToken == null) {
506480
codeSent(verificationId);
507481
} else {
@@ -510,8 +484,7 @@ class FirebaseAuth {
510484
break;
511485
case 'phoneCodeAutoRetrievalTimeout':
512486
final int handle = call.arguments['handle'];
513-
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
514-
_phoneAuthCallbacks[handle]['PhoneCodeAutoRetrievealTimeout'];
487+
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout = _phoneAuthCallbacks[handle]['PhoneCodeAutoRetrievealTimeout'];
515488
final String verificationId = call.arguments['verificationId'];
516489
codeAutoRetrievalTimeout(verificationId);
517490
break;
@@ -522,8 +495,7 @@ class FirebaseAuth {
522495
final Map<dynamic, dynamic> data = call.arguments["user"];
523496
final int id = call.arguments["id"];
524497

525-
final FirebaseUser currentUser =
526-
data != null ? FirebaseUser._(data, app) : null;
498+
final FirebaseUser currentUser = data != null ? FirebaseUser._(data, app) : null;
527499
_authStateChangedControllers[id].add(currentUser);
528500
}
529501
}

packages/firebase_auth/test/firebase_auth_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,24 @@ void main() {
287287
);
288288
});
289289

290+
test('unlink', () async {
291+
final FirebaseUser user = await auth.unlink(
292+
providerId: kMockProviderId,
293+
);
294+
verifyUser(user);
295+
expect(
296+
log,
297+
<Matcher>[
298+
isMethodCall(
299+
'unlink',
300+
arguments: <String, String>{
301+
'providerId': kMockProviderId,
302+
},
303+
),
304+
],
305+
);
306+
});
307+
290308
test('signInWithFacebook', () async {
291309
final FirebaseUser user = await auth.signInWithFacebook(
292310
accessToken: kMockAccessToken,

0 commit comments

Comments
 (0)