Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
6 changes: 3 additions & 3 deletions docs/auth/social-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ async function onAppleButtonPress() {
}

// Create a Firebase credential from the response
const { identityToken, nonce } = appleAuthRequestResponse;
const appleCredential = AppleAuthProvider.credential(identityToken, nonce);
const { identityToken, nonce, fullName } = appleAuthRequestResponse;
const appleCredential = auth.AppleAuthProvider.credential(identityToken, nonce, fullName);

// Sign the user in with the credential
return signInWithCredential(getAuth(), appleCredential);
return auth().signInWithAppleCredential(appleCredential);
}
```

Expand Down
5 changes: 5 additions & 0 deletions packages/auth/__tests__/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import auth, {
setPersistence,
signInAnonymously,
signInWithCredential,
signInWithAppleCredential,
signInWithCustomToken,
signInWithEmailAndPassword,
signInWithEmailLink,
Expand Down Expand Up @@ -362,6 +363,10 @@ describe('Auth', function () {
expect(signInWithCredential).toBeDefined();
});

it('`signInWithAppleCredential` function is properly exposed to end user', function () {
expect(signInWithAppleCredential).toBeDefined();
});

it('`signInWithCustomToken` function is properly exposed to end user', function () {
expect(signInWithCustomToken).toBeDefined();
});
Expand Down
74 changes: 56 additions & 18 deletions packages/auth/ios/RNFBAuth/RNFBAuthModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#import <Firebase/Firebase.h>
#import <Foundation/Foundation.h>
#import <React/RCTUtils.h>

#import "RNFBApp/RCTConvert+FIRApp.h"
Expand Down Expand Up @@ -588,25 +589,35 @@ - (void)invalidate {
token:authToken
secret:authSecret
firebaseApp:firebaseApp];
if (credential == nil) {
[RNFBSharedUtils rejectPromiseWithUserInfo:reject
userInfo:(NSMutableDictionary *)@{
@"code" : @"invalid-credential",
@"message" : @"The supplied auth credential is malformed, "
@"has expired or is not currently supported.",
}];
}
DLog(@"using app SignInWithCredential: %@", firebaseApp.name);

[[FIRAuth authWithApp:firebaseApp]
signInWithCredential:credential
completion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
[self _signInWithCredential:credential firebaseApp:firebaseApp resolve:resolve reject:reject];
}

RCT_EXPORT_METHOD(signInWithAppleCredential
: (FIRApp *)firebaseApp
: (NSString *)provider
: (NSString *)authToken
: (NSString *)authSecret
: (NSDictionary *)fullNameDict
: (RCTPromiseResolveBlock)resolve
: (RCTPromiseRejectBlock)reject) {
NSPersonNameComponents *fullName = [[NSPersonNameComponents alloc] init];
id (^safeString)(id) = ^id(id value) {
return (value && value != [NSNull null]) ? value : @"";
};

fullName.givenName = safeString(fullNameDict[@"givenName"]);
fullName.middleName = safeString(fullNameDict[@"middleName"]);
fullName.familyName = safeString(fullNameDict[@"familyName"]);
fullName.namePrefix = safeString(fullNameDict[@"namePrefix"]);
fullName.nameSuffix = safeString(fullNameDict[@"nameSuffix"]);
fullName.nickname = safeString(fullNameDict[@"nickname"]);

FIRAuthCredential *credential = [FIROAuthProvider appleCredentialWithIDToken:authToken
rawNonce:authSecret
fullName:fullName];

[self _signInWithCredential:credential firebaseApp:firebaseApp resolve:resolve reject:reject];
}

RCT_EXPORT_METHOD(signInWithProvider
Expand Down Expand Up @@ -1421,6 +1432,33 @@ - (FIRAuthCredential *)getCredentialForProvider:(NSString *)provider
return credential;
}

- (void)_signInWithCredential:(FIRAuthCredential *)credential
firebaseApp:(FIRApp *)firebaseApp
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
if (credential == nil) {
[RNFBSharedUtils rejectPromiseWithUserInfo:reject
userInfo:(NSMutableDictionary *)@{
@"code" : @"invalid-credential",
@"message" : @"The supplied auth credential is malformed, "
@"has expired or is not currently supported.",
}];
return;
}

DLog(@"using app SignInWithCredential: %@", firebaseApp.name);

[[FIRAuth authWithApp:firebaseApp]
signInWithCredential:credential
completion:^(FIRAuthDataResult *authResult, NSError *error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseWithAuthResult:resolve rejecter:reject authResult:authResult];
}
}];
}

// This is here to protect against bugs in the iOS SDK which don't
// correctly refresh the user object when performing certain operations
- (void)reloadAndReturnUser:(FIRUser *)user
Expand Down
103 changes: 102 additions & 1 deletion packages/auth/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,52 @@ export namespace FirebaseAuthTypes {
secret: string;
}

/**
* An optional full name shared by the user when using Sign In With Apple.
*
* These fields are populated with values that the user authorized.
*
* @platform ios iOS
*/
interface AppleRequestResponseFullName {
/**
* Pre-nominal letters denoting title, salutation, or honorific, e.g. Dr., Mr.
*/
namePrefix: string | null;

/**
* Name bestowed upon an individual by one's parents, e.g. Johnathan
*/
givenName: string | null;

/**
* Secondary given name chosen to differentiate those with the same first name, e.g. Maple
*/
middleName: string | null;

/**
* Name passed from one generation to another to indicate lineage, e.g. Appleseed
*/
familyName: string | null;

/**
* Post-nominal letters denoting degree, accreditation, or other honor, e.g. Esq., Jr., Ph.D.
*/
nameSuffix: string | null;

/**
* Name substituted for the purposes of familiarity, e.g. "Johnny"
*/
nickname: string | null;
}

export interface AppleAuthCredential {
providerId: string;
token: string;
secret: string;
fullName: AppleRequestResponseFullName;
}

/**
* Interface that represents an auth provider. Implemented by other providers.
*/
Expand Down Expand Up @@ -236,6 +282,31 @@ export namespace FirebaseAuthTypes {
credentialWithLink: (email: string, emailLink: string) => AuthCredential;
}

/**
* Interface that represents an Apple auth provider.
*/
export interface AppleAuthProvider extends AuthProvider {
/**
* The provider ID of the provider.
*
* @platform ios iOS
*/
PROVIDER_ID: string;
/**
* Creates a new `AuthCredential`.
*
* @returns {@link auth.AuthCredential}.
* @param token A provider token.
* @param secret A provider secret.
* @param fullName An `AppleRequestResponseFullName` object
*/
credential: (
token: string | null,
secret: string,
fullName?: AppleRequestResponseFullName | null,
) => AuthCredential;
}

/**
*
*/
Expand Down Expand Up @@ -321,7 +392,7 @@ export namespace FirebaseAuthTypes {
* firebase.auth.AppleAuthProvider;
* ```
*/
AppleAuthProvider: AuthProvider;
AppleAuthProvider: AppleAuthProvider;
/**
* Github auth provider implementation.
*
Expand Down Expand Up @@ -1873,6 +1944,36 @@ export namespace FirebaseAuthTypes {
*/
signInWithCredential(credential: AuthCredential): Promise<UserCredential>;

/**
* Signs the user in with a generated Apple-specific credential.
*
* @platform ios iOS
*
* #### Example
*
* ```js
* // Generate an Apple credential
* const appleCredential = (
* auth.AppleAuthProvider.credential(identityToken, nonce, fullName)
* );
* // Sign the user in with the credential
* const userCredential = await firebase.auth().signInWithAppleCredential(appleCredential);
* ```
*
* @error auth/account-exists-with-different-credential Thrown if there already exists an account with the email address asserted by the credential.
* @error auth/invalid-credential Thrown if the credential is malformed or has expired.
* @error auth/operation-not-allowed Thrown if the type of account corresponding to the credential is not enabled. Enable the account type in the Firebase Console, under the Auth tab.
* @error auth/user-disabled Thrown if the user corresponding to the given credential has been disabled.
* @error auth/user-not-found Thrown if signing in with a credential from firebase.auth.EmailAuthProvider.credential and there is no user corresponding to the given email.
* @error auth/wrong-password Thrown if signing in with a credential from firebase.auth.EmailAuthProvider.credential and the password is invalid for the given email, or if the account corresponding to the email does not have a password set.
* @error auth/invalid-verification-code Thrown if the credential is a firebase.auth.PhoneAuthProvider.credential and the verification code of the credential is not valid.
* @error auth/invalid-verification-id Thrown if the credential is a firebase.auth.PhoneAuthProvider.credential and the verification ID of the credential is not valid.
* @param credential A generated `AuthCredential`, for example from social auth.
*/
signInWithAppleCredential(
credential: AuthCredential | AppleAuthCredential,
): Promise<UserCredential>;

/**
* Signs the user in with a specified provider. This is a web-compatible API along with signInWithRedirect.
* They both share the same call to the underlying native SDK signInWithProvider method.
Expand Down
19 changes: 19 additions & 0 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,25 @@
.then(userCredential => this._setUserCredential(userCredential));
}

signInWithAppleCredential(credential) {

Check warning on line 349 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L349

Added line #L349 was not covered by tests
if (credential.hasOwnProperty('fullName')) {
return this.native

Check warning on line 351 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L351

Added line #L351 was not covered by tests
.signInWithAppleCredential(credential.providerId, credential.token, credential.secret, {
namePrefix: credential.fullName.namePrefix,
givenName: credential.fullName.givenName,
middleName: credential.fullName.middleName,
familyName: credential.fullName.familyName,
nameSuffix: credential.fullName.nameSuffix,
nickname: credential.fullName.nickname,
})
.then(userCredential => this._setUserCredential(userCredential));
} else {
return this.native

Check warning on line 362 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L360-L362

Added lines #L360 - L362 were not covered by tests
.signInWithCredential(credential.providerId, credential.token, credential.secret)
.then(userCredential => this._setUserCredential(userCredential));

Check warning on line 364 in packages/auth/lib/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/index.js#L364

Added line #L364 was not covered by tests
}
}

revokeToken(authorizationCode) {
return this.native.revokeToken(authorizationCode);
}
Expand Down
12 changes: 12 additions & 0 deletions packages/auth/lib/modular/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,18 @@ export function signInWithCredential(
credential: FirebaseAuthTypes.AuthCredential,
): Promise<FirebaseAuthTypes.UserCredential>;

/**
* Asynchronously signs in with the given Apple credentials.
*
* @param auth - The Auth instance.
* @param credential - The auth credentials.
* @returns A promise that resolves with the user credentials.
*/
export function signInWithAppleCredential(
auth: Auth,
credential: FirebaseAuthTypes.AppleAuthCredential | FirebaseAuthTypes.AuthCredential,
): Promise<FirebaseAuthTypes.UserCredential>;

/**
* Asynchronously signs in using a custom token.
*
Expand Down
10 changes: 10 additions & 0 deletions packages/auth/lib/modular/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,16 @@
return auth.signInWithCredential(credential);
}

/**
* Asynchronously signs in with the given Apple credentials.
* @param {Auth} auth - The Auth instance.
* @param {AppleAuthCredential | AuthCredential} credential - The auth credentials.
* @returns {Promise<UserCredential>}
*/
export async function signInWithAppleCredential(auth, credential) {
return auth.signInWithAppleCredential(credential);

Check warning on line 262 in packages/auth/lib/modular/index.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/modular/index.js#L261-L262

Added lines #L261 - L262 were not covered by tests
}

/**
* Asynchronously signs in using a custom token.
* @param {Auth} auth - The Auth instance.
Expand Down
3 changes: 2 additions & 1 deletion packages/auth/lib/providers/AppleAuthProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
return providerId;
}

static credential(token, secret) {
static credential(token, secret, fullName) {

Check warning on line 29 in packages/auth/lib/providers/AppleAuthProvider.js

View check run for this annotation

Codecov / codecov/patch

packages/auth/lib/providers/AppleAuthProvider.js#L29

Added line #L29 was not covered by tests
return {
token,
secret,
providerId,
fullName,
};
}
}
Loading