diff options
Diffstat (limited to 'Example/Auth')
32 files changed, 663 insertions, 29 deletions
diff --git a/Example/Auth/ApiTests/AuthCredentialsTemplate.h b/Example/Auth/ApiTests/AuthCredentialsTemplate.h index a8bf379..09eb62a 100644 --- a/Example/Auth/ApiTests/AuthCredentialsTemplate.h +++ b/Example/Auth/ApiTests/AuthCredentialsTemplate.h @@ -50,6 +50,9 @@ The name of the test user for Facebook Login. $KCUSTOM_AUTH_TOKEN_URL A URL to return a Custom Auth token. +$KCUSTOM_AUTH_TOKEN_EXPIRED_URL +A URL to return an expired Custom Auth token. + $KCUSTOM_AUTH_USER_ID The ID of the test user in the Custom Auth token. */ @@ -61,4 +64,5 @@ The ID of the test user in the Custom Auth token. #define KFACEBOOK_APP_ACCESS_TOKEN $KFACEBOOK_APP_ACCESS_TOKEN #define KFACEBOOK_USER_NAME $KFACEBOOK_USER_NAME #define KCUSTOM_AUTH_TOKEN_URL $KCUSTOM_AUTH_TOKEN_URL +#define KCUSTOM_AUTH_TOKEN_EXPIRED_URL $KCUSTOM_AUTH_TOKEN_EXPIRED_URL #define KCUSTOM_AUTH_USER_ID $KCUSTOM_AUTH_USER_ID diff --git a/Example/Auth/ApiTests/FirebaseAuthApiTests.m b/Example/Auth/ApiTests/FirebaseAuthApiTests.m index 454d9dd..741814c 100644 --- a/Example/Auth/ApiTests/FirebaseAuthApiTests.m +++ b/Example/Auth/ApiTests/FirebaseAuthApiTests.m @@ -36,6 +36,10 @@ 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; +/** The url for obtaining an expired but valid custom token string used to test Custom Auth failure. + */ +static NSString *const kExpiredCustomTokenUrl = KCUSTOM_AUTH_TOKEN_EXPIRED_URL; + /** Facebook app access token that will be used for Facebook Graph API, which is different from * account access token. */ @@ -59,6 +63,12 @@ static NSString *const kTestingEmailToCreateUser = @"abc@xyz.com"; /** The testing email address for testSignInExistingUserWithEmailAndPassword. */ static NSString *const kExistingTestingEmailToSignIn = @"456@abc.com"; +/** The testing email address for testUpdatingUsersEmail. */ +static NSString *const kNewTestingEmail = @"updatedEmail@abc.com"; + +/** The testing password for testSignInExistingUserWithModifiedEmailAndPassword. */ +static NSString *const kNewTestingPasswordToSignIn = @"password_new"; + /** Error message for invalid custom token sign in. */ NSString *kInvalidTokenErrorMessage = @"The custom token format is incorrect. Please check the documentation."; @@ -71,7 +81,7 @@ NSString *kGoogleCliendId = KGOOGLE_CLIENT_ID; */ NSString *kGoogleTestAccountRefreshToken = KGOOGLE_TEST_ACCOUNT_REFRESH_TOKEN; -static NSTimeInterval const kExpectationsTimeout = 30; +static NSTimeInterval const kExpectationsTimeout = 10; #ifdef NO_NETWORK #define SKIP_IF_ON_MOBILE_HARNESS \ @@ -141,6 +151,38 @@ static NSTimeInterval const kExpectationsTimeout = 30; [self deleteCurrentFirebaseUser]; } +- (void)testUpdatingUsersEmail { + SKIP_IF_ON_MOBILE_HARNESS + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + + __block NSError *apiError; + XCTestExpectation *expectation = + [self expectationWithDescription:@"Created account with email and password."]; + [auth createUserWithEmail:kTestingEmailToCreateUser + password:@"password" + completion:^(FIRUser *user, NSError *error) { + apiError = error; + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout handler:nil]; + expectation = [self expectationWithDescription:@"Created account with email and password."]; + XCTAssertEqualObjects(auth.currentUser.email, kTestingEmailToCreateUser); + XCTAssertNil(apiError); + [auth.currentUser updateEmail:kNewTestingEmail + completion:^(NSError *_Nullable error) { + apiError = error; + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout handler:nil]; + XCTAssertNil(apiError); + XCTAssertEqualObjects(auth.currentUser.email, kNewTestingEmail); + // Clean up the created Firebase user for future runs. + [self deleteCurrentFirebaseUser]; +} + - (void)testLinkAnonymousAccountToFacebookAccount { FIRAuth *auth = [FIRAuth auth]; if (!auth) { @@ -253,6 +295,92 @@ static NSTimeInterval const kExpectationsTimeout = 30; XCTAssertEqualObjects(auth.currentUser.uid, kCustomAuthTestingAccountUserID); } +- (void)testSignInWithValidCustomAuthExpiredToken { + FIRAuth *auth = [FIRAuth auth]; + if (!auth) { + XCTFail(@"Could not obtain auth object."); + } + + NSError *error; + NSString *customToken = + [NSString stringWithContentsOfURL:[NSURL URLWithString:kExpiredCustomTokenUrl] + encoding:NSUTF8StringEncoding + error:&error]; + if (!customToken) { + XCTFail(@"There was an error retrieving the custom token: %@", error); + } + XCTestExpectation *expectation = + [self expectationWithDescription:@"CustomAuthToken sign-in finished."]; + + __block NSError *apiError; + [auth signInWithCustomToken:customToken + completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (error) { + apiError = 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); + } + }]; + + XCTAssertNil(auth.currentUser); + XCTAssertEqual(apiError.code, FIRAuthErrorCodeInvalidCustomToken); +} + +- (void)testInMemoryUserAfterSignOut { + 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); + } + XCTestExpectation *expectation = + [self expectationWithDescription:@"CustomAuthToken sign-in finished."]; + __block NSError *rpcError; + [auth signInWithCustomToken:customToken + completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (error) { + rpcError = 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); + XCTAssertNil(rpcError); + FIRUser *inMemoryUser = auth.currentUser; + XCTestExpectation *expectation1 = [self expectationWithDescription:@"Profile data change."]; + [auth signOut:NULL]; + rpcError = nil; + NSString *newEmailAddress = [self fakeRandomEmail]; + XCTAssertNotEqualObjects(newEmailAddress, inMemoryUser.email); + [inMemoryUser updateEmail:newEmailAddress completion:^(NSError *_Nullable error) { + rpcError = error; + [expectation1 fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationsTimeout handler:nil]; + XCTAssertEqualObjects(inMemoryUser.email, newEmailAddress); + XCTAssertNil(rpcError); + XCTAssertNil(auth.currentUser); +} + - (void)testSignInWithInvalidCustomAuthToken { FIRAuth *auth = [FIRAuth auth]; if (!auth) { @@ -354,6 +482,17 @@ static NSTimeInterval const kExpectationsTimeout = 30; #pragma mark - Helpers +/** Generate fake random email address */ +- (NSString *)fakeRandomEmail { + NSMutableString *fakeEmail = [[NSMutableString alloc] init]; + for (int i=0; i<10; i++) { + [fakeEmail appendString: + [NSString stringWithFormat:@"%c", 'a' + arc4random_uniform('z' - 'a' + 1)]]; + } + [fakeEmail appendString:@"@gmail.com"]; + return fakeEmail; +} + /** Sign out current account. */ - (void)signOut { NSError *signOutError; diff --git a/Example/Auth/App/GoogleService-Info.plist b/Example/Auth/App/GoogleService-Info.plist index 89afffe..3f7547f 100644 --- a/Example/Auth/App/GoogleService-Info.plist +++ b/Example/Auth/App/GoogleService-Info.plist @@ -10,8 +10,6 @@ <string>correct_client_id</string> <key>REVERSED_CLIENT_ID</key> <string>correct_reversed_client_id</string> - <key>ANDROID_CLIENT_ID</key> - <string>correct_android_client_id</string> <key>GOOGLE_APP_ID</key> <string>1:123:ios:123abc</string> <key>GCM_SENDER_ID</key> diff --git a/Example/Auth/App/tvOS/AppDelegate.h b/Example/Auth/App/tvOS/AppDelegate.h new file mode 100644 index 0000000..013891c --- /dev/null +++ b/Example/Auth/App/tvOS/AppDelegate.h @@ -0,0 +1,21 @@ +// 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 <UIKit/UIKit.h> + +@interface AppDelegate : UIResponder <UIApplicationDelegate> + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/Example/Auth/App/tvOS/AppDelegate.m b/Example/Auth/App/tvOS/AppDelegate.m new file mode 100644 index 0000000..3935032 --- /dev/null +++ b/Example/Auth/App/tvOS/AppDelegate.m @@ -0,0 +1,63 @@ +// 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 FirebaseCore; +@import FirebaseAuth; + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + [FIRApp configure]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for + // certain types of temporary interruptions (such as an incoming phone call or SMS message) or + // when the user quits the application and it begins the transition to the background state. Use + // this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. + // Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store + // enough application state information to restore your application to its current state in case + // it is terminated later. If your application supports background execution, this method is + // called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the active state; here you can undo + // many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If + // the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also + // applicationDidEnterBackground:. +} + +@end diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..7f06667 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 0000000..d29f024 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..7f06667 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..7f06667 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 0000000..d29f024 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 0000000..b03ded1 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "size" : "1280x768", + "idiom" : "tv", + "filename" : "App Icon - App Store.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "400x240", + "idiom" : "tv", + "filename" : "App Icon.imagestack", + "role" : "primary-app-icon" + }, + { + "size" : "2320x720", + "idiom" : "tv", + "filename" : "Top Shelf Image Wide.imageset", + "role" : "top-shelf-image-wide" + }, + { + "size" : "1920x720", + "idiom" : "tv", + "filename" : "Top Shelf Image.imageset", + "role" : "top-shelf-image" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 0000000..16a370d --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json b/Example/Auth/App/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..d746a60 --- /dev/null +++ b/Example/Auth/App/tvOS/Assets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "11.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "tv", + "extent" : "full-screen", + "minimum-system-version" : "9.0", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/Example/Auth/App/tvOS/Info.plist b/Example/Auth/App/tvOS/Info.plist new file mode 100644 index 0000000..02942a3 --- /dev/null +++ b/Example/Auth/App/tvOS/Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>UIMainStoryboardFile</key> + <string>Main</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array> + <key>UIUserInterfaceStyle</key> + <string>Automatic</string> +</dict> +</plist> diff --git a/Example/Auth/App/tvOS/Main.storyboard b/Example/Auth/App/tvOS/Main.storyboard new file mode 100644 index 0000000..72d5e22 --- /dev/null +++ b/Example/Auth/App/tvOS/Main.storyboard @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="13122.16" systemVersion="17A278a" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> + <dependencies> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> + <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="tne-QT-ifu"> + <objects> + <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> + <rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> + <viewLayoutGuide key="safeArea" id="wu6-TO-1qx"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + </objects> + </scene> + </scenes> +</document> diff --git a/Example/Auth/App/tvOS/ViewController.h b/Example/Auth/App/tvOS/ViewController.h new file mode 100644 index 0000000..b6115b8 --- /dev/null +++ b/Example/Auth/App/tvOS/ViewController.h @@ -0,0 +1,19 @@ +// 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 <UIKit/UIKit.h> + +@interface ViewController : UIViewController + +@end diff --git a/Example/Auth/App/tvOS/ViewController.m b/Example/Auth/App/tvOS/ViewController.m new file mode 100644 index 0000000..6d4676b --- /dev/null +++ b/Example/Auth/App/tvOS/ViewController.m @@ -0,0 +1,33 @@ +// 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 "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Example/Auth/App/tvOS/main.m b/Example/Auth/App/tvOS/main.m new file mode 100644 index 0000000..d9e6654 --- /dev/null +++ b/Example/Auth/App/tvOS/main.m @@ -0,0 +1,22 @@ +// 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 <UIKit/UIKit.h> +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 5326463..31c103a 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -38,12 +38,18 @@ #import "UserInfoViewController.h" #import "UserTableViewCell.h" +NS_ASSUME_NONNULL_BEGIN -/*! @typedef textInputCompletionBlock +/** @typedef textInputCompletionBlock @brief The type of callback used to report text input prompt results. */ typedef void (^textInputCompletionBlock)(NSString *_Nullable userInput); +/** @typedef testAutomationCallback + @brief The type of callback used when automatically testing an API. + */ +typedef void (^testAutomationCallback)(NSError *_Nullable error); + /** @var kTokenGetButtonText @brief The text of the "Get Token" button. */ @@ -2187,18 +2193,20 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { @completion A completion block to be executed after the provider is unlinked. */ - (void)unlinkFromProvider:(NSString *)provider - completion:(void(^)(NSError *_Nullable))completion { + completion:(nullable testAutomationCallback)completion { [[self user] unlinkFromProvider:provider completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { if (error) { [self logFailure:@"unlink auth provider failed" error:error]; - completion(error); - } else { - [self logSuccess:@"unlink auth provider succeeded."]; if (completion) { - completion(nil); + completion(error); } + return; + } + [self logSuccess:@"unlink auth provider succeeded."]; + if (completion) { + completion(nil); } [self showTypicalUIForUserUpdateResultsWithTitle:kUnlinkTitle error:error]; }]; @@ -2642,7 +2650,7 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { @completion A completion block to be executed after successful phone number sign in. */ - (void)signInWithPhoneNumber:(NSString *_Nullable)phoneNumber - completion:(void(^)(NSError *_Nullable))completion { + completion:(nullable testAutomationCallback)completion { [self showSpinner:^{ [[AppManager phoneAuthProvider] verifyPhoneNumber:phoneNumber UIDelegate:nil @@ -2707,15 +2715,23 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { FIRAuthCredential *credential = [[AppManager phoneAuthProvider] credentialWithVerificationID:verificationID verificationCode:verificationCode]; - [[AppManager auth] signInWithCredential:credential - completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[AppManager auth] signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { [self hideSpinner:^{ if (error) { [self logFailure:@"failed to verify phone number" error:error]; [self showMessagePrompt:error.localizedDescription]; return; } + if (_isNewUserToggleOn) { + NSString *newUserString = result.additionalUserInfo.isNewUser ? + @"New user" : @"Existing user"; + [self showMessagePromptWithTitle:@"New or Existing" + message:newUserString + showCancelButton:NO + completion:nil]; + } }]; }]; }]; @@ -2727,7 +2743,7 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { @completion A completion block to be executed after phone number is updated. */ - (void)updatePhoneNumber:(NSString *_Nullable)phoneNumber - completion:(void(^)(NSError *_Nullable))completion{ + completion:(nullable testAutomationCallback)completion { [self showSpinner:^{ [[AppManager phoneAuthProvider] verifyPhoneNumber:phoneNumber UIDelegate:nil @@ -2736,7 +2752,9 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { if (error) { [self logFailure:@"failed to send verification code" error:error]; [self showMessagePrompt:error.localizedDescription]; - completion(error); + if (completion) { + completion(error); + } return; } [self logSuccess:@"Code sent"]; @@ -2757,7 +2775,9 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { if (error) { [self logFailure:@"update phone number failed" error:error]; [self showMessagePrompt:error.localizedDescription]; - completion(error); + if (completion) { + completion(error); + } } else { [self logSuccess:@"update phone number succeeded."]; if (completion) { @@ -2794,7 +2814,7 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { @completion A completion block to be executed after linking phone number. */ - (void)linkPhoneNumber:(NSString *_Nullable)phoneNumber - completion:(void(^)(NSError *_Nullable))completion{ + completion:(nullable testAutomationCallback)completion { [self showSpinner:^{ [[AppManager phoneAuthProvider] verifyPhoneNumber:phoneNumber UIDelegate:nil @@ -2804,7 +2824,9 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { if (error) { [self logFailure:@"failed to send verification code" error:error]; [self showMessagePrompt:error.localizedDescription]; - completion(error); + if (completion) { + completion(error); + } return; } [self logSuccess:@"Code sent"]; @@ -2845,7 +2867,9 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { if (error) { [self logFailure:@"failed to verify phone number" error:error]; [self showMessagePrompt:error.localizedDescription]; - completion(error); + if (completion) { + completion(error); + } return; } }]; @@ -3147,3 +3171,5 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { } @end + +NS_ASSUME_NONNULL_END diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 6391b08..b22c600 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -430,6 +430,8 @@ static const NSTimeInterval kWaitInterval = .5; dispatch_async(FIRAuthGlobalWorkQueue(), ^() { id mockVerifyPhoneResponse = OCMClassMock([FIRVerifyPhoneNumberResponse class]); [self stubTokensWithMockResponse:mockVerifyPhoneResponse]; + // Stub isNewUser flag in the response. + OCMStub([mockVerifyPhoneResponse isNewUser]).andReturn(YES); callback(mockVerifyPhoneResponse, nil); }); }); @@ -440,10 +442,12 @@ static const NSTimeInterval kWaitInterval = .5; [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID verificationCode:kVerificationCode]; - [[FIRAuth auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user, - NSError *_Nullable error) { + [[FIRAuth auth] signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authDataResult, + NSError *_Nullable error) { XCTAssertTrue([NSThread isMainThread]); - [self assertUser:user]; + [self assertUser:authDataResult.user]; + XCTAssertTrue(authDataResult.additionalUserInfo.isNewUser); XCTAssertNil(error); [expectation fulfill]; }]; diff --git a/Example/Auth/Tests/FIRVerifyPasswordRequestTest.m b/Example/Auth/Tests/FIRVerifyPasswordRequestTest.m index 54ba7b0..2359c46 100644 --- a/Example/Auth/Tests/FIRVerifyPasswordRequestTest.m +++ b/Example/Auth/Tests/FIRVerifyPasswordRequestTest.m @@ -18,9 +18,9 @@ #import "FIRAuthErrors.h" #import "FIRAuthBackend.h" +#import "FIRFakeBackendRPCIssuer.h" #import "FIRVerifyPasswordRequest.h" #import "FIRVerifyPasswordResponse.h" -#import "FIRFakeBackendRPCIssuer.h" /** @var kTestAPIKey @brief Fake API key used for testing. @@ -125,9 +125,10 @@ static NSString *const kExpectedAPIURL = @brief Tests the verify password request. */ - (void)testVerifyPasswordRequest { - FIRVerifyPasswordRequest * request = [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail - password:kTestPassword - requestConfiguration:_requestConfiguration]; + FIRVerifyPasswordRequest * request = + [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail + password:kTestPassword + requestConfiguration:_requestConfiguration]; request.returnSecureToken = NO; [FIRAuthBackend verifyPassword:request callback:^(FIRVerifyPasswordResponse *_Nullable response, @@ -147,9 +148,10 @@ static NSString *const kExpectedAPIURL = @brief Tests the verify password request with optional fields. */ - (void)testVerifyPasswordRequestOptionalFields { - FIRVerifyPasswordRequest * request = [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail - password:kTestPassword - requestConfiguration:_requestConfiguration]; + FIRVerifyPasswordRequest * request = + [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail + password:kTestPassword + requestConfiguration:_requestConfiguration]; request.pendingIDToken = kTestPendingToken; request.captchaChallenge = kTestCaptchaChallenge; request.captchaResponse = kTestCaptchaResponse; |