Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions packages/auth/__tests__/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,50 @@ describe('Auth', function () {
});
});

describe('UserCredential', function () {
it('should include OAuth credential data returned by native auth result', function () {
const returnedCredential = {
providerId: 'oidc.test-provider',
accessToken: 'access-token',
idToken: 'id-token',
secret: null,
};
const nativeUserCredential = {
additionalUserInfo: {
isNewUser: false,
profile: null,
providerId: 'oidc.test-provider',
username: null,
},
credential: returnedCredential,
user: {
uid: 'test-user-id',
displayName: null,
email: 'test@example.com',
emailVerified: true,
isAnonymous: false,
metadata: {
lastSignInTime: '2023-01-01T00:00:00.000Z',
creationTime: '2023-01-01T00:00:00.000Z',
},
multiFactor: {
enrolledFactors: [],
},
phoneNumber: null,
tenantId: null,
photoURL: null,
providerData: [],
providerId: 'firebase',
},
};
// @ts-ignore test
const userCredential = auth()._setUserCredential(nativeUserCredential);

expect(userCredential.user.uid).toBe('test-user-id');
expect(userCredential.credential).toEqual(returnedCredential);
});
});

describe('getMultiFactorResolver', function () {
it('should return null if no resolver object is found', function () {
const unknownError = NativeFirebaseError.fromEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import com.google.firebase.auth.MultiFactorInfo;
import com.google.firebase.auth.MultiFactorResolver;
import com.google.firebase.auth.MultiFactorSession;
import com.google.firebase.auth.OAuthCredential;
import com.google.firebase.auth.OAuthProvider;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthOptions;
Expand Down Expand Up @@ -2373,6 +2374,14 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) {

authResultMap.putMap("additionalUserInfo", additionalUserInfoMap);
}

WritableMap credentialMap = authCredentialToMap(authResult.getCredential());
if (credentialMap != null) {
authResultMap.putMap("credential", credentialMap);
} else {
authResultMap.putNull("credential");
}

authResultMap.putMap("user", userMap);

promise.resolve(authResultMap);
Expand All @@ -2382,6 +2391,22 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) {
}
}

@Nullable
private WritableMap authCredentialToMap(@Nullable AuthCredential authCredential) {
if (!(authCredential instanceof OAuthCredential)) {
return null;
}

OAuthCredential oauthCredential = (OAuthCredential) authCredential;
WritableMap credentialMap = Arguments.createMap();
SharedUtils.mapPutValue("providerId", oauthCredential.getProvider(), credentialMap);
SharedUtils.mapPutValue("accessToken", oauthCredential.getAccessToken(), credentialMap);
SharedUtils.mapPutValue("idToken", oauthCredential.getIdToken(), credentialMap);
SharedUtils.mapPutValue("secret", oauthCredential.getSecret(), credentialMap);

return credentialMap;
}

/**
* promiseRejectAuthException
*
Expand Down
44 changes: 41 additions & 3 deletions packages/auth/ios/RNFBAuth/RNFBAuthModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@
static NSString *const keyUser = @"user";
static NSString *const keyEmail = @"email";
static NSString *const keyAndroid = @"android";
static NSString *const keyIdToken = @"idToken";
static NSString *const keySecret = @"secret";
static NSString *const keyProfile = @"profile";
static NSString *const keyNewUser = @"isNewUser";
static NSString *const keyUsername = @"username";
static NSString *const keyCredential = @"credential";
static NSString *const keyMultiFactor = @"multiFactor";
static NSString *const keyPhotoUrl = @"photoURL";
static NSString *const keyBundleId = @"bundleId";
static NSString *const keyInstallApp = @"installApp";
static NSString *const keyAccessToken = @"accessToken";
static NSString *const keyProviderId = @"providerId";
static NSString *const keyPhoneNumber = @"phoneNumber";
static NSString *const keyDisplayName = @"displayName";
Expand Down Expand Up @@ -664,7 +668,8 @@ - (void)invalidate {

[self promiseWithAuthResult:resolve
rejecter:reject
authResult:authResult];
authResult:authResult
credential:credential];
}];
}
}];
Expand Down Expand Up @@ -1304,7 +1309,8 @@ - (void)invalidate {

[self promiseWithAuthResult:resolve
rejecter:reject
authResult:authResult];
authResult:authResult
credential:credential];
}];
}
}];
Expand Down Expand Up @@ -1433,7 +1439,8 @@ - (void)invalidate {

[self promiseWithAuthResult:resolve
rejecter:reject
authResult:authResult];
authResult:authResult
credential:credential];
}];
}
}];
Expand Down Expand Up @@ -1730,6 +1737,13 @@ - (void)promiseWithUser:(RCTPromiseResolveBlock)resolve
- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject
authResult:(FIRAuthDataResult *)authResult {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:nil];
}

- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject
authResult:(FIRAuthDataResult *)authResult
credential:(FIRAuthCredential *)credential {
if (authResult && authResult.user) {
NSMutableDictionary *authResultDict = [NSMutableDictionary dictionary];

Expand Down Expand Up @@ -1761,13 +1775,37 @@ - (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve
[authResultDict setValue:[NSNull null] forKey:keyAdditionalUserInfo];
}

FIRAuthCredential *resultCredential = authResult.credential ?: credential;
NSDictionary *credentialDict = [self oauthResultCredentialToDict:resultCredential];
if (credentialDict) {
[authResultDict setValue:credentialDict forKey:keyCredential];
} else {
[authResultDict setValue:[NSNull null] forKey:keyCredential];
}

[authResultDict setValue:[self firebaseUserToDict:authResult.user] forKey:keyUser];
resolve(authResultDict);
} else {
[self promiseNoUser:resolve rejecter:reject isError:YES];
}
}

- (NSDictionary *)oauthResultCredentialToDict:(FIRAuthCredential *)authCredential {
if (![authCredential isKindOfClass:[FIROAuthCredential class]]) {
return nil;
}

FIROAuthCredential *oauthCredential = (FIROAuthCredential *)authCredential;
NSMutableDictionary *credentialDict = [NSMutableDictionary dictionary];

[credentialDict setValue:oauthCredential.provider ?: [NSNull null] forKey:keyProviderId];
[credentialDict setValue:oauthCredential.accessToken ?: [NSNull null] forKey:keyAccessToken];
[credentialDict setValue:oauthCredential.IDToken ?: [NSNull null] forKey:keyIdToken];
[credentialDict setValue:oauthCredential.secret ?: [NSNull null] forKey:keySecret];

return credentialDict;
}

- (NSArray<NSObject *> *)convertProviderData:(NSArray<id<FIRUserInfo>> *)providerData {
NSMutableArray *output = [NSMutableArray array];

Expand Down
28 changes: 27 additions & 1 deletion packages/auth/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,28 @@ export namespace FirebaseAuthTypes {
secret: string;
}

/**
* Represents the OAuth credential returned by a sign-in, link, or reauthentication operation.
*/
export interface OAuthCredential {
/**
* The authentication provider ID for the credential. For example, 'facebook.com', or 'google.com'.
*/
providerId: string;
/**
* The OAuth access token associated with the credential, if one was returned by the provider.
*/
accessToken?: string | null;
/**
* The OAuth ID token associated with the credential, if one was returned by the provider.
*/
idToken?: string | null;
/**
* The OAuth access token secret associated with the credential, if one was returned by the provider.
*/
secret?: string | null;
}

/**
* Interface that represents an auth provider. Implemented by other providers.
*/
Expand Down Expand Up @@ -507,13 +529,17 @@ export namespace FirebaseAuthTypes {
* information that was returned from the identity provider. operationType could be 'signIn' for
* a sign-in operation, 'link' for a linking operation and 'reauthenticate' for a re-authentication operation.
*
* TODO @salakar; missing credential, operationType
* TODO @salakar; missing operationType
*/
export interface UserCredential {
/**
* Any additional user information assigned to the user.
*/
additionalUserInfo?: AdditionalUserInfo;
/**
* The OAuth credential returned by the identity provider, if one was returned.
*/
credential?: OAuthCredential | null;
/**
* Returns the {@link User} interface of this credential.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ class FirebaseAuthModule extends FirebaseModule {
this.emitter.emit(this.eventNameForApp('onUserChanged'), this._user);
return {
additionalUserInfo: userCredential.additionalUserInfo,
credential: userCredential.credential ?? null,
user,
};
}
Expand Down
10 changes: 10 additions & 0 deletions packages/auth/lib/web/RNFBAuthModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ function multiFactorInfoToObject(multiFactorInfo) {
*/
function authResultToObject(userCredential) {
const additional = getAdditionalUserInfo(userCredential);
const credential = OAuthProvider.credentialFromResult(userCredential);

return {
user: userToObject(userCredential.user),
additionalUserInfo: {
Expand All @@ -233,6 +235,14 @@ function authResultToObject(userCredential) {
providerId: additional.providerId,
username: additional.username,
},
credential: credential
? {
providerId: credential.providerId,
accessToken: credential.accessToken ?? null,
idToken: credential.idToken ?? null,
secret: credential.secret ?? null,
}
: null,
};
}

Expand Down
2 changes: 2 additions & 0 deletions packages/auth/type-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ const emailCredential = EmailAuthProvider.credential('test@example.com', 'passwo
signInWithCredential(authInstance, emailCredential).then(
(credential: FirebaseAuthTypes.UserCredential) => {
console.log(credential.user.email);
console.log(credential.credential?.accessToken);
console.log(credential.credential?.idToken);
},
);

Expand Down