Skip to content

Commit 4a5a861

Browse files
AetherallCedric
authored and
Cedric
committed
feat(oauth): added signIn and link for oauth
1 parent 90a2589 commit 4a5a861

File tree

8 files changed

+314
-35
lines changed

8 files changed

+314
-35
lines changed

packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java

+112-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
import com.facebook.react.bridge.WritableArray;
3131
import com.facebook.react.bridge.WritableMap;
3232
import com.google.android.gms.tasks.OnCompleteListener;
33+
import com.google.android.gms.tasks.OnFailureListener;
34+
import com.google.android.gms.tasks.OnSuccessListener;
35+
import com.google.android.gms.tasks.Task;
3336
import com.google.firebase.FirebaseApp;
3437
import com.google.firebase.FirebaseException;
3538
import com.google.firebase.FirebaseNetworkException;
@@ -56,6 +59,7 @@
5659
import com.google.firebase.auth.MultiFactorResolver;
5760
import com.google.firebase.auth.MultiFactorSession;
5861
import com.google.firebase.auth.OAuthProvider;
62+
import com.google.firebase.auth.OAuthCredential;
5963
import com.google.firebase.auth.PhoneAuthCredential;
6064
import com.google.firebase.auth.PhoneAuthOptions;
6165
import com.google.firebase.auth.PhoneAuthProvider;
@@ -203,7 +207,6 @@ public void addIdTokenListener(final String appName) {
203207

204208
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
205209
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
206-
207210
if (!mIdTokenListeners.containsKey(appName)) {
208211
FirebaseAuth.IdTokenListener newIdTokenListener =
209212
firebaseAuth1 -> {
@@ -839,6 +842,87 @@ private void signInWithCredential(
839842
}
840843
}
841844

845+
@ReactMethod
846+
public void signInWithProvider(String appName, String providerId, @Nullable String email, Promise promise){
847+
OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId);
848+
if(email != null){
849+
provider.addCustomParameter("login_hint", email);
850+
}
851+
Activity activity = getCurrentActivity();
852+
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
853+
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
854+
855+
OnSuccessListener onSuccess = new OnSuccessListener<AuthResult>(){
856+
@Override
857+
public void onSuccess(AuthResult authResult) {
858+
Log.d(TAG, "signInWithProvider:onComplete:success");
859+
promiseWithAuthResult(authResult, promise);
860+
}
861+
};
862+
863+
OnFailureListener onFailure = new OnFailureListener(){
864+
@Override
865+
public void onFailure(@NonNull Exception e) {
866+
Log.w(TAG, "signInWithProvider:onComplete:failure", e);
867+
promiseRejectAuthException(promise, e);
868+
}
869+
};
870+
871+
872+
Task<AuthResult> pendingResultTask = firebaseAuth.getPendingAuthResult();
873+
if(pendingResultTask != null){
874+
pendingResultTask
875+
.addOnSuccessListener(onSuccess)
876+
.addOnFailureListener(onFailure);
877+
} else {
878+
firebaseAuth
879+
.startActivityForSignInWithProvider(activity, provider.build())
880+
.addOnSuccessListener(onSuccess)
881+
.addOnFailureListener(onFailure);
882+
}
883+
}
884+
885+
@ReactMethod
886+
public void linkWithProvider(String appName, String providerId, @Nullable String email, Promise promise){
887+
OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId);
888+
if(email != null){
889+
provider.addCustomParameter("login_hint", email);
890+
}
891+
Activity activity = getCurrentActivity();
892+
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
893+
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
894+
FirebaseUser firebaseUser = firebaseAuth.getCurrentUser();
895+
896+
OnSuccessListener onSuccess = new OnSuccessListener<AuthResult>(){
897+
@Override
898+
public void onSuccess(AuthResult authResult) {
899+
Log.d(TAG, "linkWithProvider:onComplete:success");
900+
promiseWithAuthResult(authResult, promise);
901+
}
902+
};
903+
904+
OnFailureListener onFailure = new OnFailureListener(){
905+
@Override
906+
public void onFailure(@NonNull Exception e) {
907+
Log.w(TAG, "linkInWithProvider:onComplete:failure", e);
908+
promiseRejectAuthException(promise, e);
909+
}
910+
};
911+
912+
913+
Task<AuthResult> pendingResultTask = firebaseAuth.getPendingAuthResult();
914+
if(pendingResultTask != null){
915+
pendingResultTask
916+
.addOnSuccessListener(onSuccess)
917+
.addOnFailureListener(onFailure);
918+
} else {
919+
firebaseUser
920+
.startActivityForLinkWithProvider(activity, provider.build())
921+
.addOnSuccessListener(onSuccess)
922+
.addOnFailureListener(onFailure);
923+
}
924+
}
925+
842926
/**
843927
* signInWithPhoneNumber
844928
*
@@ -1866,6 +1950,30 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) {
18661950
WritableMap authResultMap = Arguments.createMap();
18671951
WritableMap userMap = firebaseUserToMap(authResult.getUser());
18681952

1953+
if(authResult.getCredential() != null){
1954+
if(authResult.getCredential() instanceof OAuthCredential){
1955+
OAuthCredential creds = (OAuthCredential) authResult.getCredential();
1956+
WritableMap credentialMap = Arguments.createMap();
1957+
1958+
credentialMap.putString("providerId", creds.getProvider());
1959+
credentialMap.putString("signInMethod", creds.getSignInMethod());
1960+
1961+
if(creds.getIdToken() != null){
1962+
credentialMap.putString("idToken", creds.getIdToken());
1963+
}
1964+
1965+
if(creds.getAccessToken() != null){
1966+
credentialMap.putString("accessToken", creds.getAccessToken());
1967+
}
1968+
1969+
if(creds.getSecret() != null){
1970+
credentialMap.putString("secret", creds.getSecret());
1971+
}
1972+
1973+
authResultMap.putMap("credential", credentialMap);
1974+
}
1975+
}
1976+
18691977
if (authResult.getAdditionalUserInfo() != null) {
18701978
WritableMap additionalUserInfoMap = Arguments.createMap();
18711979

@@ -1906,6 +2014,7 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) {
19062014
*/
19072015
private void promiseRejectAuthException(Promise promise, Exception exception) {
19082016
WritableMap error = getJSError(exception);
2017+
19092018
final String sessionId = error.getString("sessionId");
19102019
final MultiFactorResolver multiFactorResolver = mCachedResolvers.get(sessionId);
19112020
WritableMap resolverAsMap = Arguments.createMap();
@@ -1927,6 +2036,8 @@ private WritableMap getJSError(Exception exception) {
19272036
String message = exception.getMessage();
19282037
String invalidEmail = "The email address is badly formatted.";
19292038

2039+
System.out.print(exception);
2040+
19302041
try {
19312042
FirebaseAuthException authException = (FirebaseAuthException) exception;
19322043
code = authException.getErrorCode();

packages/auth/e2e/auth.e2e.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -912,12 +912,11 @@ describe('auth()', function () {
912912
});
913913

914914
describe('signInWithPopup', function () {
915-
it('should throw an unsupported error', function () {
916-
(() => {
917-
firebase.auth().signInWithPopup();
918-
}).should.throw(
919-
'firebase.auth().signInWithPopup() is unsupported by the native Firebase SDKs.',
920-
);
915+
it('should trigger the oauth flow', async function () {
916+
await (async () => {
917+
const provider = new firebase.auth.OAuthProvider('oidc.react.com');
918+
await firebase.auth().signInWithPopup(provider);
919+
}).should.not.throw();
921920
});
922921
});
923922

@@ -1025,12 +1024,11 @@ describe('auth()', function () {
10251024
});
10261025

10271026
describe('signInWithRedirect()', function () {
1028-
it('should throw an unsupported error', function () {
1029-
(() => {
1030-
firebase.auth().signInWithRedirect();
1031-
}).should.throw(
1032-
'firebase.auth().signInWithRedirect() is unsupported by the native Firebase SDKs.',
1033-
);
1027+
it('should trigger the oauth flow', async function () {
1028+
await (async () => {
1029+
const provider = new firebase.auth.OAuthProvider('oidc.react.com');
1030+
await firebase.auth().signInWithRedirect(provider);
1031+
}).should.not.throw();
10341032
});
10351033
});
10361034

packages/auth/e2e/provider.e2e.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,7 @@ describe('auth() -> Providers', function () {
149149
describe('OAuthProvider', function () {
150150
describe('constructor', function () {
151151
it('should throw an unsupported error', function () {
152-
(() => new firebase.auth.OAuthProvider()).should.throw(
153-
'`new OAuthProvider()` is not supported on the native Firebase SDKs.',
154-
);
152+
(() => new firebase.auth.OAuthProvider('oidc.react.com')).should.not.throw();
155153
});
156154
});
157155

packages/auth/ios/RNFBAuth/RNFBAuthModule.m

+115-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
static NSString *const keyHandleCodeInApp = @"handleCodeInApp";
4646
static NSString *const keyDynamicLinkDomain = @"dynamicLinkDomain";
4747
static NSString *const keyAdditionalUserInfo = @"additionalUserInfo";
48+
static NSString *const keyCredential = @"credential";
49+
static NSString *const keyIdToken = @"idToken";
50+
static NSString *const keyAccessToken = @"accessToken";
51+
static NSString *const keySecret = @"secret";
4852
static NSString *const AUTH_STATE_CHANGED_EVENT = @"auth_state_changed";
4953
static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed";
5054
static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_changed";
@@ -56,6 +60,7 @@
5660
static __strong NSMutableDictionary<NSString *, FIRAuthCredential *> *credentials;
5761
static __strong NSMutableDictionary<NSString *, FIRMultiFactorResolver *> *cachedResolver;
5862
static __strong NSMutableDictionary<NSString *, FIRMultiFactorSession *> *cachedSessions;
63+
static __strong NSMutableDictionary<NSString *, FIROAuthProvider *> *oAuthProviders;
5964

6065
@implementation RNFBAuthModule
6166
#pragma mark -
@@ -77,6 +82,7 @@ - (id)init {
7782
credentials = [[NSMutableDictionary alloc] init];
7883
cachedResolver = [[NSMutableDictionary alloc] init];
7984
cachedSessions = [[NSMutableDictionary alloc] init];
85+
oAuthProviders = [[NSMutableDictionary alloc] init];
8086
});
8187
return self;
8288
}
@@ -104,6 +110,7 @@ - (void)invalidate {
104110
[credentials removeAllObjects];
105111
[cachedResolver removeAllObjects];
106112
[cachedSessions removeAllObjects];
113+
[oAuthProviders removeAllObjects];
107114
}
108115

109116
#pragma mark -
@@ -571,6 +578,73 @@ - (void)invalidate {
571578
}];
572579
}
573580

581+
582+
RCT_EXPORT_METHOD(signInWithProvider
583+
: (FIRApp *)firebaseApp
584+
: (NSString *)providerID
585+
: (NSString *_Nullable)email
586+
: (RCTPromiseResolveBlock)resolve
587+
: (RCTPromiseRejectBlock)reject) {
588+
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:providerID];
589+
[oAuthProviders setValue:provider forKey:providerID];
590+
591+
if (email) {
592+
[provider setCustomParameters:@{@"login_hint": email}];
593+
}
594+
595+
[provider getCredentialWithUIDelegate:nil
596+
completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) {
597+
if (error) {
598+
[self promiseRejectAuthException:reject error:error];
599+
}
600+
601+
if (credential) {
602+
[[FIRAuth auth] signInWithCredential:credential
603+
completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
604+
if (error) {
605+
[self promiseRejectAuthException:reject error:error];
606+
}
607+
else {
608+
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:credential];
609+
}
610+
}];
611+
}
612+
}];
613+
}
614+
615+
RCT_EXPORT_METHOD(linkWithProvider
616+
: (FIRApp *)firebaseApp
617+
: (NSString *)providerID
618+
: (NSString *_Nullable)email
619+
: (RCTPromiseResolveBlock)resolve
620+
: (RCTPromiseRejectBlock)reject) {
621+
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:providerID];
622+
[oAuthProviders setValue:provider forKey:providerID];
623+
624+
if (email) {
625+
[provider setCustomParameters:@{@"login_hint": email}];
626+
}
627+
628+
[provider getCredentialWithUIDelegate:nil
629+
completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) {
630+
if (error) {
631+
[self promiseRejectAuthException:reject error:error];
632+
}
633+
634+
if (credential) {
635+
[[FIRAuth auth].currentUser linkWithCredential:credential
636+
completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
637+
if (error) {
638+
[self promiseRejectAuthException:reject error:error];
639+
}
640+
else {
641+
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:credential];
642+
}
643+
}];
644+
}
645+
}];
646+
}
647+
574648
RCT_EXPORT_METHOD(confirmPasswordReset
575649
: (FIRApp *)firebaseApp
576650
: (NSString *)code
@@ -1315,7 +1389,14 @@ - (void)promiseWithUser:(RCTPromiseResolveBlock)resolve
13151389

13161390
- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve
13171391
rejecter:(RCTPromiseRejectBlock)reject
1318-
authResult:(FIRAuthDataResult *)authResult {
1392+
authResult:(FIRAuthDataResult *)authResult {
1393+
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:nil];
1394+
}
1395+
1396+
- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve
1397+
rejecter:(RCTPromiseRejectBlock)reject
1398+
authResult:(FIRAuthDataResult *)authResult
1399+
credential:(FIRAuthCredential *_Nullable)credential {
13191400
if (authResult && authResult.user) {
13201401
NSMutableDictionary *authResultDict = [NSMutableDictionary dictionary];
13211402

@@ -1347,6 +1428,39 @@ - (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve
13471428
[authResultDict setValue:[NSNull null] forKey:keyAdditionalUserInfo];
13481429
}
13491430

1431+
if (credential && ([credential isKindOfClass:[FIROAuthCredential class]])) {
1432+
NSMutableDictionary *credentialDict = [NSMutableDictionary dictionary];
1433+
FIROAuthCredential* oAuthCredential = (FIROAuthCredential *)credential;
1434+
1435+
[credentialDict setValue:oAuthCredential.provider forKey:keyProviderId];
1436+
1437+
if (oAuthCredential.IDToken) {
1438+
[credentialDict setValue:oAuthCredential.IDToken forKey:keyIdToken];
1439+
}
1440+
else {
1441+
[credentialDict setValue:[NSNull null] forKey:keyIdToken];
1442+
}
1443+
1444+
if (oAuthCredential.accessToken) {
1445+
[credentialDict setValue:oAuthCredential.accessToken forKey:keyAccessToken];
1446+
}
1447+
else {
1448+
[credentialDict setValue:[NSNull null] forKey:keyAccessToken];
1449+
}
1450+
1451+
if (oAuthCredential.accessToken) {
1452+
[credentialDict setValue:oAuthCredential.secret forKey:keySecret];
1453+
}
1454+
else {
1455+
[credentialDict setValue:[NSNull null] forKey:keySecret];
1456+
}
1457+
1458+
[authResultDict setValue:credentialDict forKey:keyCredential];
1459+
}
1460+
else {
1461+
[authResultDict setValue:[NSNull null] forKey:keyCredential];
1462+
}
1463+
13501464
[authResultDict setValue:[self firebaseUserToDict:authResult.user] forKey:keyUser];
13511465
resolve(authResultDict);
13521466
} else {

packages/auth/lib/User.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,14 @@ export default class User {
310310
);
311311
}
312312

313-
linkWithPopup() {
314-
throw new Error(
315-
'firebase.auth.User.linkWithPopup() is unsupported by the native Firebase SDKs.',
316-
);
313+
linkWithPopup(provider) {
314+
return this._auth.native
315+
.linkWithProvider(provider.providerId, provider.customParameters?.login_hint)
316+
.then(userCredential => this._auth._setUserCredential(userCredential));
317317
}
318318

319-
linkWithRedirect() {
320-
throw new Error(
321-
'firebase.auth.User.linkWithRedirect() is unsupported by the native Firebase SDKs.',
322-
);
319+
async linkWithRedirect(provider) {
320+
return this.linkWithPopup(provider);
323321
}
324322

325323
reauthenticateWithPhoneNumber() {

0 commit comments

Comments
 (0)