Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sign in with Apple Display Name API and unit test #10068

Merged
merged 25 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
62e4fb6
Add api addtion for SIWA and add Function for Unit test
aiwenisevan Aug 3, 2022
47fd1b5
Adding Unit test for apple signIn flow in FIROAuthProviderTests and F…
aiwenisevan Aug 4, 2022
8578f96
remove AuthSample.xcodeproj fiels and application.plist file under Sa…
aiwenisevan Aug 9, 2022
8ca23e4
Revert "remove AuthSample.xcodeproj fiels and application.plist file …
aiwenisevan Aug 9, 2022
8c53928
remove changes in AuthSample 1/5
aiwenisevan Aug 9, 2022
32723a4
remove changes in AuthSample 2/5
aiwenisevan Aug 9, 2022
ecea19e
remove changes in AuthSample 3/5
aiwenisevan Aug 9, 2022
b004ef5
remove changes in AuthSample 4/5
aiwenisevan Aug 9, 2022
e1450ea
remove changes in AuthSample 5/5
aiwenisevan Aug 9, 2022
1b890f4
overwrite with master firles to remove white space changes in the two…
aiwenisevan Aug 9, 2022
9e4c88e
Delete Application.plist file
aiwenisevan Aug 9, 2022
66f2db7
Clean extra methods in FIROAuthCredentialInternal and related files t…
aiwenisevan Aug 9, 2022
f2853ae
Add displayName in FIRVerifyAssertionRequest
aiwenisevan Aug 10, 2022
a26395d
Fix issues in comments, merge GoogleAuthprovider, fix files in tests
aiwenisevan Aug 12, 2022
2dac690
Merge branch 'master' into apiAdditionforSIWA
aiwenisevan Aug 12, 2022
f640537
fix issues
aiwenisevan Aug 12, 2022
1d8d3c8
Clang-format all files
aiwenisevan Aug 12, 2022
c3389c6
Adding comment for testSignInWithCredentialSuccess
aiwenisevan Aug 12, 2022
cfe6f4e
Adding comments for testSignInWithCredentialSuccess
aiwenisevan Aug 12, 2022
d38c34e
Merge branch 'master' into apiAdditionforSIWA
rosalyntan Feb 16, 2023
3dcc540
Updates to reflect approved API.
rosalyntan Feb 16, 2023
9c54381
Address review comments.
rosalyntan Feb 21, 2023
b7d0afe
Update unit tests.
rosalyntan Feb 21, 2023
05a9c50
Update changelog.
rosalyntan Mar 3, 2023
b062ec1
Remove JSON pretty-print and add one more unit test.
rosalyntan Mar 3, 2023
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
9 changes: 9 additions & 0 deletions FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthCredential.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ - (instancetype)initWithProviderID:(NSString *)providerID
rawNonce:(nullable NSString *)rawNonce
accessToken:(nullable NSString *)accessToken
secret:(nullable NSString *)secret
fullName:(nullable NSPersonNameComponents *)fullName
pendingToken:(nullable NSString *)pendingToken {
self = [super initWithProvider:providerID];
if (self) {
Expand All @@ -53,6 +54,7 @@ - (instancetype)initWithProviderID:(NSString *)providerID
_accessToken = accessToken;
_pendingToken = pendingToken;
_secret = secret;
_fullName = fullName;
}
return self;
}
Expand All @@ -65,6 +67,7 @@ - (instancetype)initWithProviderID:(NSString *)providerID
rawNonce:nil
accessToken:nil
secret:nil
fullName:nil
pendingToken:nil];
if (self) {
_OAuthResponseURLString = OAuthResponseURLString;
Expand All @@ -81,6 +84,7 @@ - (nullable instancetype)initWithVerifyAssertionResponse:(FIRVerifyAssertionResp
rawNonce:nil
accessToken:response.oauthAccessToken
secret:response.oauthSecretToken
fullName:nil
pendingToken:response.pendingToken];
}
return nil;
Expand All @@ -94,6 +98,7 @@ - (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request {
request.sessionID = _sessionID;
request.providerOAuthTokenSecret = _secret;
request.pendingToken = _pendingToken;
request.fullName = _fullName;
}

#pragma mark - NSSecureCoding
Expand All @@ -108,11 +113,14 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
NSString *accessToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"accessToken"];
NSString *pendingToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"pendingToken"];
NSString *secret = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"secret"];
NSPersonNameComponents *fullName = [aDecoder decodeObjectOfClass:[NSPersonNameComponents class]
forKey:@"fullName"];
self = [self initWithProviderID:self.provider
IDToken:IDToken
rawNonce:rawNonce
accessToken:accessToken
secret:secret
fullName:fullName
pendingToken:pendingToken];
return self;
}
Expand All @@ -123,6 +131,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.accessToken forKey:@"accessToken"];
[aCoder encodeObject:self.pendingToken forKey:@"pendingToken"];
[aCoder encodeObject:self.secret forKey:@"secret"];
[aCoder encodeObject:self.fullName forKey:@"fullName"];
}

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,27 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, readonly, nullable) NSString *pendingToken;

/** @fn initWithProviderId:IDToken:accessToken:secret:pendingToken
/** @property fullName
@brief The full name of the user associated with this OAuthCredential.
*/
@property(nonatomic, readonly, nullable) NSPersonNameComponents *fullName;

/** @fn initWithProviderId:IDToken:rawNonce:accessToken:secret:fullName:pendingToken
@brief Designated initializer.
@param providerID The provider ID associated with the credential being created.
@param IDToken The ID Token associated with the credential being created.
@param rawNonce The raw nonce associated with the Auth credential being created.
@param accessToken The access token associated with the credential being created.
@param secret The secret associated with the credential being created.
@param fullName The full name associated with the credential being created.
@param pendingToken The pending token associated with the credential being created.
*/
- (instancetype)initWithProviderID:(NSString *)providerID
IDToken:(nullable NSString *)IDToken
rawNonce:(nullable NSString *)rawNonce
accessToken:(nullable NSString *)accessToken
secret:(nullable NSString *)secret
fullName:(nullable NSPersonNameComponents *)fullName
pendingToken:(nullable NSString *)pendingToken NS_DESIGNATED_INITIALIZER;

/** @fn initWithProviderId:sessionID:OAuthResponseURLString:
Expand Down
16 changes: 16 additions & 0 deletions FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthProvider.m
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
rawNonce:nil
accessToken:accessToken
secret:nil
fullName:nil
pendingToken:nil];
}

Expand All @@ -98,6 +99,7 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
rawNonce:nil
accessToken:accessToken
secret:nil
fullName:nil
pendingToken:nil];
}

Expand All @@ -110,6 +112,7 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
rawNonce:rawNonce
accessToken:accessToken
secret:nil
fullName:nil
pendingToken:nil];
}

Expand All @@ -121,6 +124,19 @@ + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
rawNonce:rawNonce
accessToken:nil
secret:nil
fullName:nil
pendingToken:nil];
}

+ (FIROAuthCredential *)appleCredentialWithIDToken:(NSString *)IDToken
rosalyntan marked this conversation as resolved.
Show resolved Hide resolved
rawNonce:(nullable NSString *)rawNonce
fullName:(nullable NSPersonNameComponents *)fullName {
return [[FIROAuthCredential alloc] initWithProviderID:@"apple.com"
IDToken:IDToken
rawNonce:rawNonce
accessToken:nil
secret:nil
fullName:fullName
pendingToken:nil];
}

Expand Down
5 changes: 5 additions & 0 deletions FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, assign) BOOL autoCreate;

/** @property fullName
@brief A full name from the IdP.
*/
@property(nonatomic, copy, nullable) NSPersonNameComponents *fullName;

/** @fn initWithEndpoint:requestConfiguration:
@brief Please use initWithProviderID:requestConfifuration instead.
*/
Expand Down
43 changes: 43 additions & 0 deletions FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@
*/
static NSString *const kReturnSecureTokenKey = @"returnSecureToken";

/** @var kUserKey
@brief The key for the "user" value in the request. The value is a JSON object that contains the
name of the user.
*/
static NSString *const kUserKey = @"user";

/** @var kNameKey
@brief The key for the "name" value in the request. The value is a JSON object that contains the
first and/or last name of the user.
*/
static NSString *const kNameKey = @"name";

/** @var kFirstNameKey
@brief The key for the "firstName" value in the request.
*/
static NSString *const kFirstNameKey = @"firstName";

/** @var kLastNameKey
@brief The key for the "lastName" value in the request.
*/
static NSString *const kLastNameKey = @"lastName";

/** @var kReturnIDPCredentialKey
rosalyntan marked this conversation as resolved.
Show resolved Hide resolved
@brief The key for the "returnIdpCredential" value in the request.
*/
Expand Down Expand Up @@ -148,6 +170,26 @@ - (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)
if (_inputEmail) {
[queryItems addObject:[NSURLQueryItem queryItemWithName:kIdentifierKey value:_inputEmail]];
}

if (_fullName.givenName || _fullName.familyName) {
NSMutableDictionary *nameDict = [[NSMutableDictionary alloc] init];
if (_fullName.givenName) {
nameDict[kFirstNameKey] = _fullName.givenName;
}
if (_fullName.familyName) {
nameDict[kLastNameKey] = _fullName.familyName;
}
NSDictionary *userDict = [NSDictionary dictionaryWithObject:nameDict forKey:kNameKey];
NSData *userJson = [NSJSONSerialization dataWithJSONObject:userDict
options:NSJSONWritingPrettyPrinted
error:error];
[queryItems
addObject:[NSURLQueryItem
queryItemWithName:kUserKey
value:[[NSString alloc] initWithData:userJson
encoding:NSUTF8StringEncoding]]];
}

[components setQueryItems:queryItems];
NSMutableDictionary *body = [@{
kRequestURIKey : _requestURI ?: @"http://localhost", // Unused by server, but required
Expand All @@ -171,6 +213,7 @@ - (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)
if (_sessionID) {
body[kSessionIDKey] = _sessionID;
}

if (self.tenantID) {
body[kTenantIDKey] = self.tenantID;
}
Expand Down
16 changes: 16 additions & 0 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIROAuthProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ NS_SWIFT_NAME(OAuthProvider)
IDToken:(NSString *)IDToken
rawNonce:(nullable NSString *)rawNonce;

/** @fn appleCredentialWithIDToken:rawNonce:fullName:
* @brief Creates an `AuthCredential` for the Sign in with Apple OAuth 2 provider identified by ID
* token, raw nonce, and full name. This method is specific to the Sign in with Apple OAuth 2
* provider as this provider requires the full name to be passed explicitly.
*
* @param IDToken The IDToken associated with the Sign in with Apple Auth credential being created.
* @param rawNonce The raw nonce associated with the Sign in with Apple Auth credential being
* created.
* @param fullName The full name associated with the Sign in with Apple Auth credential being
* created.
* @return An `AuthCredential`.
*/
+ (FIROAuthCredential *)appleCredentialWithIDToken:(NSString *)IDToken
rawNonce:(nullable NSString *)rawNonce
fullName:(nullable NSPersonNameComponents *)fullName;
rosalyntan marked this conversation as resolved.
Show resolved Hide resolved

/** @fn init
@brief This class is not meant to be initialized.
*/
Expand Down
8 changes: 4 additions & 4 deletions FirebaseAuth/Tests/Sample/Sample/MainViewController+OAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,10 @@ - (void)reauthenticateWithApple {
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
ASAuthorizationAppleIDCredential* appleIDCredential = authorization.credential;
NSString *IDToken = [NSString stringWithUTF8String:[appleIDCredential.identityToken bytes]];
FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com"
IDToken:IDToken
rawNonce:self.appleRawNonce
accessToken:nil];
FIROAuthCredential *credential =
[FIROAuthProvider appleCredentialWithIDToken:IDToken
rawNonce:self.appleRawNonce
fullName:appleIDCredential.fullName];

if ([appleIDCredential.state isEqualToString:@"signIn"]) {
[FIRAuth.auth signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
Expand Down
80 changes: 80 additions & 0 deletions FirebaseAuth/Tests/Unit/FIRAuthTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@
*/
static NSString *const kDisplayName = @"User Doe";

/** @var kFakeGivenName
@brief The fake user given name.
*/
static NSString *const kFakeGivenName = @"Firstname";

/** @var kFakeFamilyName
@brief The fake user family name.
*/
static NSString *const kFakeFamilyName = @"Lastname";

/** @var kGoogleUD
@brief The fake user ID under Google Sign-In.
*/
Expand All @@ -160,6 +170,16 @@
*/
static NSString *const kGoogleIDToken = @"GOOGLE_ID_TOKEN";

/** @var kAppleAuthProviderID
@brief The provider ID for Apple Sign-In.
*/
static NSString *const kAppleAuthProviderID = @"apple.com";

/** @var kAppleIDToken
@brief The fake ID token from Apple Sign-In.
*/
static NSString *const kAppleIDToken = @"APPLE_ID_TOKEN";

/** @var kCustomToken
@brief The fake custom token to sign in.
*/
Expand Down Expand Up @@ -1374,6 +1394,66 @@ - (void)testSignInWithGoogleCredentialFailure {
OCMVerifyAll(_mockBackend);
}

/** @fn testSignInWithAppleCredentialFullNameInRequest
@brief Tests the flow of a successful @c signInWithCredential:completion: call
with an Apple Sign-In credential with a full name. This test differentiates from
@c testSignInWithCredentialSuccess only in verifying the full name.
*/
- (void)testSignInWithAppleCredentialFullNameInRequestSuccess {
NSPersonNameComponents *fullName = [[NSPersonNameComponents alloc] init];
fullName.givenName = kFakeGivenName;
fullName.familyName = kFakeFamilyName;
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, kAppleAuthProviderID);
XCTAssertEqualObjects(request.providerIDToken, kAppleIDToken);
// Verify that the full name is passed to the backend request.
XCTAssertEqualObjects(request.fullName, fullName);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVerifyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVerifyAssertionResponse providerID]).andReturn(kAppleAuthProviderID);
[self stubTokensWithMockResponse:mockVerifyAssertionResponse];
callback(mockVerifyAssertionResponse, nil);
});
});
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
FIRGetAccountInfoResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.accessToken, kAccessToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockAppleUserInfo = OCMClassMock([FIRGetAccountInfoResponseProviderUserInfo class]);
OCMStub([mockAppleUserInfo providerID]).andReturn(kAppleAuthProviderID);
id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
OCMStub([mockGetAccountInfoResponseUser providerUserInfo])
.andReturn((@[ mockAppleUserInfo ]));
id mockGetAccountInfoResponse = OCMClassMock([FIRGetAccountInfoResponse class]);
OCMStub([mockGetAccountInfoResponse users]).andReturn(@[
mockGetAccountInfoResponseUser
]);
callback(mockGetAccountInfoResponse, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
FIRAuthCredential *appleCredential = [FIROAuthProvider appleCredentialWithIDToken:kAppleIDToken
rawNonce:nil
fullName:fullName];
[[FIRAuth auth]
signInWithCredential:appleCredential
completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
XCTAssertNotNil([FIRAuth auth].currentUser);
rosalyntan marked this conversation as resolved.
Show resolved Hide resolved
OCMVerifyAll(_mockBackend);
}

/** @fn testSignInAnonymouslySuccess
@brief Tests the flow of a successful @c signInAnonymouslyWithCompletion: call.
*/
Expand Down
29 changes: 29 additions & 0 deletions FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@
*/
static NSString *const kFakeProviderID = @"fakeProviderID";

/** @var kFakeGivenName
@brief A fake given name for testing.
*/
static NSString *const kFakeGivenName = @"fakeGivenName";

/** @var kFakeFamilyName
@brief A fake family name for testing.
*/
static NSString *const kFakeFamilyName = @"fakeFamilyName";

/** @var kFakeAPIKey
@brief A fake API key.
*/
Expand Down Expand Up @@ -238,6 +248,25 @@ - (void)testObtainingOAuthCredentialNoIDToken {
XCTAssertNil(OAuthCredential.IDToken);
}

/** @fn testObtainingOAuthCredentialWithFullName
@brief Tests the correct creation of an OAuthCredential with a fullName.
*/
- (void)testObtainingOAuthCredentialWithFullName {
NSPersonNameComponents *fullName = [[NSPersonNameComponents alloc] init];
fullName.givenName = kFakeGivenName;
fullName.familyName = kFakeFamilyName;
FIRAuthCredential *credential = [FIROAuthProvider appleCredentialWithIDToken:kFakeIDToken
rawNonce:nil
fullName:fullName];

XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
XCTAssertEqualObjects(OAuthCredential.provider, @"apple.com");
XCTAssertEqualObjects(OAuthCredential.IDToken, kFakeIDToken);
XCTAssertEqualObjects(OAuthCredential.fullName, fullName);
XCTAssertNil(OAuthCredential.accessToken);
}

/** @fn testObtainingOAuthCredentialWithIDToken
@brief Tests the correct creation of an OAuthCredential with an IDToken
*/
Expand Down