Skip to content

PR: Auth for watchOS #4632

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

Closed
wants to merge 9 commits into from
Closed
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
26 changes: 25 additions & 1 deletion Firebase/Auth/Source/Auth/FIRAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -1136,10 +1136,16 @@ - (void)sendSignInLinkToEmail:(nonnull NSString *)email
kMissingEmailInvalidParameterExceptionReason];
}

/*
The sign in can't happen fully in-app, i.e. watchOS does not support universal links but watchOS has a mail client that will open this link appropriately
in a browser. From the browser you trigger a cloud function which sends a push notification to complete the sign-in.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that something manual that you set up, or are you saying that's how it'll work automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see: #4621 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@renkelvin can you please take a look at this bit specifically?

I'm comfortable merging the email/password sign in changes now, I'm not sure about the email link and will defer to @renkelvin for Auth's view of this (see the comment for the full solution and context).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Maybe add a link to #4621 (comment) to make it easier to other developers?

*/
#if !TARGET_OS_WATCH
if (!actionCodeSettings.handleCodeInApp) {
[FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason:
kHandleCodeInAppFalseExceptionReason];
}
#endif
FIRGetOOBConfirmationCodeRequest *request =
[FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:email
actionCodeSettings:actionCodeSettings
Expand Down Expand Up @@ -1820,11 +1826,17 @@ - (BOOL)saveUser:(nullable FIRUser *)user
success = [_keychainServices removeDataForKey:userKey error:outError];
} else {
// Encode the user object.
#if TARGET_OS_WATCH
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:false];
#else
NSMutableData *archiveData = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
#endif
[archiver encodeObject:user forKey:userKey];
[archiver finishEncoding];

#if TARGET_OS_WATCH
NSData *archiveData = archiver.encodedData;
#endif
// Save the user object's encoded value.
success = [_keychainServices setData:archiveData forKey:userKey error:outError];
}
Expand Down Expand Up @@ -1868,8 +1880,14 @@ - (BOOL)getUser:(FIRUser *_Nullable *)outUser
*outUser = nil;
return YES;
}
#if TARGET_OS_WATCH
NSError *error;
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingFromData:encodedUserData error:&error];
#else
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
#endif
FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
user.auth = self;
*outUser = user;
Expand Down Expand Up @@ -2027,8 +2045,14 @@ - (nullable FIRUser *)getStoredUserForAccessGroup:(NSString *_Nullable)accessGro
return nil;
}

#if TARGET_OS_WATCH
NSError *error;
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingFromData:encodedUserData error:&error];
#else
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
#endif
user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
} else {
user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ + (void)getCredentialWithCompletion:(FIRGameCenterCredentialCallback)completion
`localPlayer.displayname`. For more information, check
https://developer.apple.com/documentation/gamekit/gkplayer
**/
#if TARGET_OS_WATCH
completion(nil, [FIRAuthErrorUtils gameCenterNotSupportedError]);
#else
NSString *displayName = localPlayer.alias;
FIRGameCenterAuthCredential *credential =
[[FIRGameCenterAuthCredential alloc] initWithPlayerID:localPlayer.playerID
Expand All @@ -78,6 +81,7 @@ + (void)getCredentialWithCompletion:(FIRGameCenterCredentialCallback)completion
timestamp:timestamp
displayName:displayName];
completion(credential, nil);
#endif
}
}
}];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

#include <TargetConditionals.h>
#if !TARGET_OS_OSX && !TARGET_OS_TV
#if !TARGET_OS_OSX && !TARGET_OS_TV && !TARGET_OS_WATCH

#import "FIRPhoneAuthProvider.h"

Expand Down
14 changes: 14 additions & 0 deletions Firebase/Auth/Source/SystemService/FIRAuthAPNSTokenManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
#if !TARGET_OS_OSX

#import <Foundation/Foundation.h>

#if TARGET_OS_WATCH
#import <WatchKit/WatchKit.h>
#else
#import <UIKit/UIKit.h>
#endif

@class FIRAuthAPNSToken;

Expand Down Expand Up @@ -56,12 +61,21 @@ typedef void (^FIRAuthAPNSTokenCallback)(FIRAuthAPNSToken *_Nullable token,
*/
- (instancetype)init NS_UNAVAILABLE;

#if TARGET_OS_WATCH
/** @fn initWithApplication:bundle
@brief Initializes the instance.
@param application The @c WKExtension to request the token from.
@return The initialized instance.
*/
- (instancetype)initWithApplication:(WKExtension *)application NS_DESIGNATED_INITIALIZER;
#else
/** @fn initWithApplication:bundle
@brief Initializes the instance.
@param application The @c UIApplication to request the token from.
@return The initialized instance.
*/
- (instancetype)initWithApplication:(UIApplication *)application NS_DESIGNATED_INITIALIZER;
#endif

/** @fn getTokenWithCallback:
@brief Attempts to get the APNs token.
Expand Down
13 changes: 13 additions & 0 deletions Firebase/Auth/Source/SystemService/FIRAuthAPNSTokenManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,31 @@
static const NSTimeInterval kLegacyRegistrationTimeout = 30;

@implementation FIRAuthAPNSTokenManager {
#if TARGET_OS_WATCH

/** @var _application
@brief The @c WKExtension to request the token from.
*/
WKExtension *_application;
#else

/** @var _application
@brief The @c UIApplication to request the token from.
*/
UIApplication *_application;
#endif

/** @var _pendingCallbacks
@brief The list of all pending callbacks for the APNs token.
*/
NSMutableArray<FIRAuthAPNSTokenCallback> *_pendingCallbacks;
}

#if TARGET_OS_WATCH
- (instancetype)initWithApplication:(WKExtension *)application {
#else
- (instancetype)initWithApplication:(UIApplication *)application {
#endif
self = [super init];
if (self) {
_application = application;
Expand Down
12 changes: 12 additions & 0 deletions Firebase/Auth/Source/SystemService/FIRAuthAppCredentialManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ - (instancetype)initWithKeychain:(FIRAuthKeychainServices *)keychain {
NSError *error;
NSData *encodedData = [_keychainServices dataForKey:kKeychainDataKey error:&error];
if (!error && encodedData) {
#if TARGET_OS_WATCH
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingFromData:encodedData error:&error];
#else
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingWithData:encodedData];
#endif
FIRAuthAppCredential *credential =
[unarchiver decodeObjectOfClass:[FIRAuthAppCredential class]
forKey:kFullCredentialKey];
Expand Down Expand Up @@ -137,11 +142,18 @@ - (void)clearCredential {
@brief Save the data in memory to the keychain ignoring any errors.
*/
- (void)saveData {
#if TARGET_OS_WATCH
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:false];
#else
NSMutableData *archiveData = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
#endif
[archiver encodeObject:_credential forKey:kFullCredentialKey];
[archiver encodeObject:_pendingReceipts forKey:kPendingReceiptsKey];
[archiver finishEncoding];
#if TARGET_OS_WATCH
NSData *archiveData = archiver.encodedData;
#endif
[_keychainServices setData:archiveData forKey:kKeychainDataKey error:NULL];
}

Expand Down
16 changes: 16 additions & 0 deletions Firebase/Auth/Source/SystemService/FIRAuthNotificationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
#if !TARGET_OS_OSX

#import <Foundation/Foundation.h>
#if TARGET_OS_WATCH
#import <WatchKit/WatchKit.h>
#else
#import <UIKit/UIKit.h>
#endif

@class FIRAuthAppCredentialManager;

Expand All @@ -40,6 +44,17 @@ typedef void (^FIRAuthNotificationForwardingCallback)(BOOL isNotificationBeingFo
*/
@property(nonatomic, assign) NSTimeInterval timeout;

#if TARGET_OS_WATCH
/** @fn initWithApplication:appCredentialManager:
@brief Initializes the instance.
@param application The extension.
@param appCredentialManager The object to handle app credentials delivered via notification.
@return The initialized instance.
*/
- (instancetype)initWithApplication:(WKExtension *)application
appCredentialManager:(FIRAuthAppCredentialManager *)appCredentialManager
NS_DESIGNATED_INITIALIZER;
#else
/** @fn initWithApplication:appCredentialManager:
@brief Initializes the instance.
@param application The application.
Expand All @@ -49,6 +64,7 @@ typedef void (^FIRAuthNotificationForwardingCallback)(BOOL isNotificationBeingFo
- (instancetype)initWithApplication:(UIApplication *)application
appCredentialManager:(FIRAuthAppCredentialManager *)appCredentialManager
NS_DESIGNATED_INITIALIZER;
#endif

/** @fn init
@brief please use initWithAppCredentialManager: instead.
Expand Down
21 changes: 20 additions & 1 deletion Firebase/Auth/Source/SystemService/FIRAuthNotificationManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,17 @@
static const NSTimeInterval kProbingTimeout = 1;

@implementation FIRAuthNotificationManager {
#if TARGET_OS_WATCH
/** @var _application
@brief The extension.
*/
WKExtension *_application;
#else
/** @var _application
@brief The application.
*/
UIApplication *_application;
#endif

/** @var _appCredentialManager
@brief The object to handle app credentials delivered via notification.
Expand All @@ -78,7 +85,11 @@ @implementation FIRAuthNotificationManager {
NSMutableArray<FIRAuthNotificationForwardingCallback> *_pendingCallbacks;
}

#if TARGET_OS_WATCH
- (instancetype)initWithApplication:(WKExtension *)application
#else
- (instancetype)initWithApplication:(UIApplication *)application
#endif
appCredentialManager:(FIRAuthAppCredentialManager *)appCredentialManager {
self = [super init];
if (self) {
Expand Down Expand Up @@ -107,12 +118,20 @@ - (void)checkNotificationForwardingWithCallback:(FIRAuthNotificationForwardingCa
kNotificationProberKey : @"This fake notification should be forwarded to Firebase Auth."
}
};
#if TARGET_OS_WATCH
if ([self->_application.delegate respondsToSelector:
@selector(didReceiveRemoteNotification:fetchCompletionHandler:)]) {
[self->_application.delegate
didReceiveRemoteNotification:proberNotification
fetchCompletionHandler:^(WKBackgroundFetchResult result) {}];
#else
if ([self->_application.delegate respondsToSelector:
@selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) {
[self->_application.delegate application:self->_application
didReceiveRemoteNotification:proberNotification
fetchCompletionHandler:^(UIBackgroundFetchResult result) {}];
#if !TARGET_OS_TV
#endif
#if !TARGET_OS_TV && !TARGET_OS_WATCH
} else if ([self->_application.delegate respondsToSelector:
@selector(application:didReceiveRemoteNotification:)]) {
[self->_application.delegate application:self->_application
Expand Down
12 changes: 12 additions & 0 deletions Firebase/Auth/Source/SystemService/FIRAuthStoredUserManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ - (FIRUser *)getStoredUserForAccessGroup:(NSString *)accessGroup
query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;

NSData *data = [self.keychainServices getItemWithQuery:query error:outError];
#if TARGET_OS_WATCH
NSError *error;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know what sort of errors are potentially expected here, and where we should be checking it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should refactor it to using GULSecureCoding eventually (may be a part of a follow up PR). No existing data migration will be required because the archive file format is the same for the secure coding.

Copy link
Contributor Author

@hohteri hohteri Feb 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do anything to this. According to the documentation:

This method throws an error if data isn’t a valid keyed archive.

while the deprecated method throws an exception if data is not a valid archive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG - @maksymmalyhin we can refactor after the fact since it's not directly related to watchOS.

NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
#else
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
#endif
FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:kStoredUserCoderKey];

return user;
Expand All @@ -100,10 +105,17 @@ - (BOOL)setStoredUser:(FIRUser *)user
query[(__bridge id)kSecAttrService] = projectIdentifier;
query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;

#if TARGET_OS_WATCH
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:false];
#else
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
#endif
[archiver encodeObject:user forKey:kStoredUserCoderKey];
[archiver finishEncoding];
#if TARGET_OS_WATCH
NSData *data = archiver.encodedData;
#endif

return [self.keychainServices setItem:data withQuery:query error:outError];
}
Expand Down
2 changes: 1 addition & 1 deletion Firebase/Auth/Source/Utilities/FIRAuthDefaultUIDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

#include <TargetConditionals.h>
#if !TARGET_OS_OSX
#if !TARGET_OS_OSX && !TARGET_OS_WATCH

#import "FIRAuthDefaultUIDelegate.h"

Expand Down
5 changes: 5 additions & 0 deletions Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (NSError *)missingOrInvalidNonceErrorWithMessage:(nullable NSString *)message;

/** @fn gameCenterNotSupportedError
@brief Constructs an @c NSError with the @c FIRAuthErrorCodeInternalError code.
@return The NSError instance associated with the FIRAuthErrorCodeInternalError.
*/
+ (NSError *)gameCenterNotSupportedError;
@end

NS_ASSUME_NONNULL_END
4 changes: 4 additions & 0 deletions Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,10 @@ + (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSSt
}];
}

+ (NSError *)gameCenterNotSupportedError; {
return [self errorWithCode:FIRAuthInternalErrorCodeGameKitNotAvailable message:@"Game Center not supported on watchOS"];
}

@end

NS_ASSUME_NONNULL_END
5 changes: 5 additions & 0 deletions Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) {
FIRAuthInternalErrorCodeGameKitNotLinked =
FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeGameKitNotLinked,

/** Indicates that the Game Center is not available on this platform.
*/
FIRAuthInternalErrorCodeGameKitNotAvailable =
FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInternalError,

/** Indicates that the nonce is missing or invalid.
*/
FIRAuthInternalErrorCodeMissingOrInvalidNonce =
Expand Down
2 changes: 1 addition & 1 deletion Firebase/Auth/Source/Utilities/FIRAuthURLPresenter.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

#include <TargetConditionals.h>
#if !TARGET_OS_OSX && !TARGET_OS_TV
#if !TARGET_OS_OSX && !TARGET_OS_TV && !TARGET_OS_WATCH

#import "FIRAuthURLPresenter.h"

Expand Down
2 changes: 1 addition & 1 deletion Firebase/Auth/Source/Utilities/FIRAuthWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

#include <TargetConditionals.h>
#if !TARGET_OS_OSX && !TARGET_OS_TV
#if !TARGET_OS_OSX && !TARGET_OS_TV && !TARGET_OS_WATCH

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
Expand Down
2 changes: 1 addition & 1 deletion Firebase/Auth/Source/Utilities/FIRAuthWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

#include <TargetConditionals.h>
#if !TARGET_OS_OSX && !TARGET_OS_TV
#if !TARGET_OS_OSX && !TARGET_OS_TV && !TARGET_OS_WATCH

#import "FIRAuthWebView.h"

Expand Down
2 changes: 1 addition & 1 deletion Firebase/Auth/Source/Utilities/FIRAuthWebViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

#include <TargetConditionals.h>
#if !TARGET_OS_OSX && !TARGET_OS_TV
#if !TARGET_OS_OSX && !TARGET_OS_TV && !TARGET_OS_WATCH

#import <UIKit/UIKit.h>

Expand Down
Loading