diff options
Diffstat (limited to 'AuthSamples/ApiTests/FirebaseAuthApiTests.m')
-rw-r--r-- | AuthSamples/ApiTests/FirebaseAuthApiTests.m | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/AuthSamples/ApiTests/FirebaseAuthApiTests.m b/AuthSamples/ApiTests/FirebaseAuthApiTests.m new file mode 100644 index 0000000..3a3d622 --- /dev/null +++ b/AuthSamples/ApiTests/FirebaseAuthApiTests.m @@ -0,0 +1,539 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <Foundation/Foundation.h> +#import <XCTest/XCTest.h> + +#import "FIRApp.h" +#import "FirebaseAuth.h" +#import "AuthCredentials.h" + +#ifdef NO_NETWORK +#import "ioReplayer/IORManager.h" +#import "ioReplayer/IORTestCase.h" +#endif + +#import <GTMSessionFetcher/GTMSessionFetcher.h> +#import <GTMSessionFetcher/GTMSessionFetcherService.h> + +/** The user name string for Custom Auth testing account. */ +static NSString *const kCustomAuthTestingAccountUserID = KCUSTOM_AUTH_USER_ID; + +/** The url for obtaining a valid custom token string used to test Custom Auth. */ +static NSString *const kCustomTokenUrl = KCUSTOM_AUTH_TOKEN_URL; + +/** Facebook app access token that will be used for Facebook Graph API, which is different from + * account access token. + */ +static NSString *const kFacebookAppAccessToken = KFACEBOOK_APP_ACCESS_TOKEN; + +/** Facebook app ID that will be used for Facebook Graph API. */ +static NSString *const kFacebookAppID = KFACEBOOK_APP_ID; + +static NSString *const kFacebookGraphApiAuthority = @"graph.facebook.com"; + +static NSString *const kFacebookTestAccountName = KFACEBOOK_USER_NAME; + +static NSString *const kGoogleTestAccountName = KGOOGLE_USER_NAME; + +/** The invalid custom token string for testing Custom Auth. */ +static NSString *const kInvalidCustomToken = @"invalid token."; + +/** The testing email address for testCreateAccountWithEmailAndPassword. */ +static NSString *const kTestingEmailToCreateUser = @"abc@xyz.com"; + +/** The testing email address for testSignInExistingUserWithEmailAndPassword. */ +static NSString *const kExistingTestingEmailToSignIn = @"456@abc.com"; + +/** Error message for invalid custom token sign in. */ +NSString *kInvalidTokenErrorMessage = + @"The custom token format is incorrect. Please check the documentation."; + +NSString *kGoogleCliendId = KGOOGLE_CLIENT_ID; + +/** Refresh token of Google test account to exchange for access token. Refresh token never expires + * unless user revokes it. If this refresh token expires, tests in record mode will fail and this + * token needs to be updated. + */ +NSString *kGoogleTestAccountRefreshToken = KGOOGLE_TEST_ACCOUNT_REFRESH_TOKEN; + +static NSTimeInterval const kExpectationsTimeout = 30; + +#ifdef NO_NETWORK +#define SKIP_IF_ON_MOBILE_HARNESS \ + if ([ITUIOSTestUtil isOnMobileHarness]) { \ + NSLog(@"Skipping '%@' on mobile harness", NSStringFromSelector(_cmd)); \ + return; \ + } +#else +#define SKIP_IF_ON_MOBILE_HARNESS +#endif + +#ifdef NO_NETWORK +@interface ApiTests : IORTestCase +#else +@interface ApiTests : XCTestCase +#endif +@end + +@implementation ApiTests + +/** To reset the app so that each test sees the app in a clean state. */ +- (void)setUp { + [super setUp]; + [self signOut]; +} + +#pragma mark - Tests + +/** + * This test runs in replay mode by default. To run in a different mode follow the instructions + * below. + * + * Blaze: --test_arg=\'--networkReplayMode=(replay|record|disabled|observe)\' + * + * Xcode: + * Update the following flag in the xcscheme. + * --networkReplayMode=(replay|record|disabled|observe) + */ +- (void)testCreateAccountWithEmailAndPassword { + SKIP_IF_ON_MOBILE_HARNESS + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + XCTestExpectation *expectation = + [self expectationWithDescription:@"Created account with email and password."]; + [auth createUserWithEmail:kTestingEmailToCreateUser + password:@"password" + completion:^(FIRUser *user, NSError *error) { + if (error) { + NSLog(@"createUserWithEmail has error: %@", error); + } + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in creating account. Error: %@", + error.localizedDescription); + } + }]; + + XCTAssertEqualObjects(auth.currentUser.email, kTestingEmailToCreateUser); + + // Clean up the created Firebase user for future runs. + [self deleteCurrentFirebaseUser]; +} + +- (void)testLinkAnonymousAccountToFacebookAccount { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + [self signInAnonymously]; + + NSDictionary *userInfoDict = [self createFacebookTestingAccount]; + NSString *facebookAccessToken = userInfoDict[@"access_token"]; + NSLog(@"Facebook testing account access token is: %@", facebookAccessToken); + NSString *facebookAccountId = userInfoDict[@"id"]; + NSLog(@"Facebook testing account id is: %@", facebookAccountId); + + FIRAuthCredential *credential = + [FIRFacebookAuthProvider credentialWithAccessToken:facebookAccessToken]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Facebook linking finished."]; + [auth.currentUser linkWithCredential:credential + completion:^(FIRUser *user, NSError *error) { + if (error) { + NSLog(@"Link to Facebok error: %@", error); + } + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in linking to Facebook. Error: %@", + error.localizedDescription); + } + }]; + NSArray<id<FIRUserInfo>> *providerData = auth.currentUser.providerData; + XCTAssertEqual([providerData count], 1); + XCTAssertEqualObjects([providerData[0] providerID], @"facebook.com"); + + // Clean up the created Firebase/Facebook user for future runs. + [self deleteCurrentFirebaseUser]; + [self deleteFacebookTestingAccountbyId:facebookAccountId]; +} + +- (void)testSignInAnonymously { + [self signInAnonymously]; + XCTAssertTrue([FIRAuth auth].currentUser.anonymous); + [self deleteCurrentFirebaseUser]; +} + +- (void)testSignInExistingUserWithEmailAndPassword { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + XCTestExpectation *expectation = + [self expectationWithDescription:@"Signed in existing account with email and password."]; + [auth signInWithEmail:kExistingTestingEmailToSignIn + password:@"password" + completion:^(FIRUser *user, NSError *error) { + if (error) { + NSLog(@"Signing in existing account has error: %@", error); + } + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in signing in existing account. Error: %@", + error.localizedDescription); + } + }]; + + XCTAssertEqualObjects(auth.currentUser.email, kExistingTestingEmailToSignIn); +} + +- (void)testSignInWithValidCustomAuthToken { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + + NSError *error; + NSString *customToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:kCustomTokenUrl] + encoding:NSUTF8StringEncoding + error:&error]; + if (!customToken) { + XCTFail(@"There was an error retrieving the custom token: %@", error); + } + NSLog(@"The valid token is: %@", customToken); + + XCTestExpectation *expectation = + [self expectationWithDescription:@"CustomAuthToken sign-in finished."]; + + [auth signInWithCustomToken:customToken + completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (error) { + NSLog(@"Valid token sign in error: %@", error); + } + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in CustomAuthToken sign in. Error: %@", + error.localizedDescription); + } + }]; + + XCTAssertEqualObjects(auth.currentUser.uid, kCustomAuthTestingAccountUserID); +} + +- (void)testSignInWithInvalidCustomAuthToken { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + XCTestExpectation *expectation = + [self expectationWithDescription:@"Invalid CustomAuthToken sign-in finished."]; + + [auth signInWithCustomToken:kInvalidCustomToken + completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + + XCTAssertEqualObjects(error.localizedDescription, kInvalidTokenErrorMessage); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in CustomAuthToken sign in. Error: %@", + error.localizedDescription); + } + }]; +} + +- (void)testSignInWithFaceboook { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + + NSDictionary *userInfoDict = [self createFacebookTestingAccount]; + NSString *facebookAccessToken = userInfoDict[@"access_token"]; + NSLog(@"Facebook testing account access token is: %@", facebookAccessToken); + NSString *facebookAccountId = userInfoDict[@"id"]; + NSLog(@"Facebook testing account id is: %@", facebookAccountId); + + FIRAuthCredential *credential = + [FIRFacebookAuthProvider credentialWithAccessToken:facebookAccessToken]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Facebook sign-in finished."]; + + [auth signInWithCredential:credential + completion:^(FIRUser *user, NSError *error) { + if (error) { + NSLog(@"Facebook sign in error: %@", error); + } + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in Facebook sign in. Error: %@", + error.localizedDescription); + } + }]; + XCTAssertEqualObjects(auth.currentUser.displayName, kFacebookTestAccountName); + + // Clean up the created Firebase/Facebook user for future runs. + [self deleteCurrentFirebaseUser]; + [self deleteFacebookTestingAccountbyId:facebookAccountId]; +} + +- (void)testSignInWithGoogle { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + NSDictionary *userInfoDict = [self getGoogleAccessToken]; + NSString *googleAccessToken = userInfoDict[@"access_token"]; + NSString *googleIdToken = userInfoDict[@"id_token"]; + FIRAuthCredential *credential = + [FIRGoogleAuthProvider credentialWithIDToken:googleIdToken accessToken:googleAccessToken]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Signing in with Google finished."]; + [auth signInWithCredential:credential + completion:^(FIRUser *user, NSError *error) { + if (error) { + NSLog(@"Signing in with Google had error: %@", error); + } + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in Signing in with Google. Error: %@", + error.localizedDescription); + } + }]; + XCTAssertEqualObjects(auth.currentUser.displayName, kGoogleTestAccountName); + + // Clean up the created Firebase/Facebook user for future runs. + [self deleteCurrentFirebaseUser]; +} + +#pragma mark - Helpers + +/** Sign out current account. */ +- (void)signOut { + NSError *signOutError; + BOOL status = [[FIRAuth auth] signOut:&signOutError]; + + // Just log the error because we don't want to fail the test if signing out + // fails. + if (!status) { + NSLog(@"Error signing out: %@", signOutError); + } +} + +/** Creates a Facebook testing account using Facebook Graph API and return a dictionary that + * constains "id", "access_token", "login_url", "email" and "password" of the created account. + */ +- (NSDictionary *)createFacebookTestingAccount { + // Build the URL. + NSString *urltoCreateTestUser = + [NSString stringWithFormat:@"https://%@/%@/accounts/test-users", kFacebookGraphApiAuthority, + kFacebookAppID]; + // Build the POST request. + NSString *bodyString = + [NSString stringWithFormat:@"installed=true&name=%@&permissions=read_stream&access_token=%@", + kFacebookTestAccountName, kFacebookAppAccessToken]; + NSData *postData = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; + GTMSessionFetcherService *service = [[GTMSessionFetcherService alloc] init]; + GTMSessionFetcher *fetcher = [service fetcherWithURLString:urltoCreateTestUser]; + fetcher.bodyData = postData; + [fetcher setRequestValue:@"text/plain" forHTTPHeaderField:@"Content-Type"]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Creating Facebook account finished."]; + __block NSData *data = nil; + [fetcher beginFetchWithCompletionHandler:^(NSData *receivedData, NSError *error) { + if (error) { + NSLog(@"Creating Facebook account finished with error: %@", error); + return; + } + data = receivedData; + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in creating Facebook account. Error: %@", + error.localizedDescription); + } + }]; + NSString *userInfo = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSLog(@"The info of created Facebook testing account is: %@", userInfo); + // Parses the access token from the JSON data. + NSDictionary *userInfoDict = + [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + return userInfoDict; +} + +/** Clean up the created user for tests' future runs. */ +- (void)deleteCurrentFirebaseUser { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + NSLog(@"Could not obtain auth object."); + } + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Delete current user finished."]; + [auth.currentUser deleteWithCompletion:^(NSError *_Nullable error) { + if (error) { + XCTFail(@"Failed to delete user. Error: %@.", error); + } + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in deleting user. Error: %@", + error.localizedDescription); + } + }]; +} + +- (void)signInAnonymously { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Anonymousy sign-in finished."]; + [auth signInAnonymouslyWithCompletion:^(FIRUser *user, NSError *error) { + if (error) { + NSLog(@"Anonymousy sign in error: %@", error); + } + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in anonymousy sign in. Error: %@", + error.localizedDescription); + } + }]; +} + +/** Delete a Facebook testing account by account Id using Facebook Graph API. */ +- (void)deleteFacebookTestingAccountbyId:(NSString *)accountId { + // Build the URL. + NSString *urltoDeleteTestUser = + [NSString stringWithFormat:@"https://%@/%@", kFacebookGraphApiAuthority, accountId]; + + // Build the POST request. + NSString *bodyString = + [NSString stringWithFormat:@"method=delete&access_token=%@", kFacebookAppAccessToken]; + NSData *postData = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; + GTMSessionFetcherService *service = [[GTMSessionFetcherService alloc] init]; + GTMSessionFetcher *fetcher = [service fetcherWithURLString:urltoDeleteTestUser]; + fetcher.bodyData = postData; + [fetcher setRequestValue:@"text/plain" forHTTPHeaderField:@"Content-Type"]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Deleting Facebook account finished."]; + [fetcher beginFetchWithCompletionHandler:^(NSData *receivedData, NSError *error) { + NSString *deleteResult = + [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; + NSLog(@"The result of deleting Facebook account is: %@", deleteResult); + if (error) { + NSLog(@"Deleting Facebook account finished with error: %@", error); + } + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in deleting Facebook account. Error: %@", + error.localizedDescription); + } + }]; +} + +/** Sends http request to Google OAuth2 token server to use refresh token to exchange for Google + * access token. Returns a dictionary that constains "access_token", "token_type", "expires_in" and + * "id_token". + */ +- (NSDictionary *)getGoogleAccessToken { + NSString *googleOauth2TokenServerUrl = @"https://www.googleapis.com/oauth2/v4/token"; + NSString *bodyString = + [NSString stringWithFormat:@"client_id=%@&grant_type=refresh_token&refresh_token=%@", + kGoogleCliendId, kGoogleTestAccountRefreshToken]; + NSData *postData = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; + GTMSessionFetcherService *service = [[GTMSessionFetcherService alloc] init]; + GTMSessionFetcher *fetcher = [service fetcherWithURLString:googleOauth2TokenServerUrl]; + fetcher.bodyData = postData; + [fetcher setRequestValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Exchanging Google account tokens finished."]; + __block NSData *data = nil; + [fetcher beginFetchWithCompletionHandler:^(NSData *receivedData, NSError *error) { + if (error) { + NSLog(@"Exchanging Google account tokens finished with error: %@", error); + return; + } + data = receivedData; + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout + handler:^(NSError *error) { + if (error != nil) { + XCTFail(@"Failed to wait for expectations " + @"in exchanging Google account tokens. Error: %@", + error.localizedDescription); + } + }]; + NSString *userInfo = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSLog(@"The info of exchanged result is: %@", userInfo); + NSDictionary *userInfoDict = + [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + return userInfoDict; +} +@end |