Skip to content

Commit

Permalink
[Passkey #7] FIRUser startPasskeyEnrollmentWithName:completion: & fin…
Browse files Browse the repository at this point in the history
…alizePasskeyEnrollmentWithPlatformCredential:completion: implementation (#11991)
  • Loading branch information
Xiaoshouzi-gh authored Oct 24, 2023
1 parent 076d2e4 commit 16ef4bf
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 2 deletions.
1 change: 0 additions & 1 deletion FirebaseAuth/Sources/Auth/FIRAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
#import "FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.h"
#import "FirebaseAuth/Sources/Utilities/FIRAuthExceptionUtils.h"
#import "FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h"
#import "FirebaseAuth/Sources/Utilities/NSData+FIRBase64.h"

#if TARGET_OS_IOS
#import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
Expand Down
50 changes: 50 additions & 0 deletions FirebaseAuth/Sources/Backend/RPC/Proto/FIRPasskeyInfo.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2023 Google LLC
*
* 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 "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPasskeyInfo.h"

NS_ASSUME_NONNULL_BEGIN

/**
@var kNameKey
@brief The name of the field in the response JSON for name.
*/
static const NSString *kNameKey = @"name";

/**
@var kCredentialIdKey
@brief The name of the field in the response JSON for credential ID.
*/
static const NSString *kCredentialIdKey = @"credentialId";

@implementation FIRPasskeyInfo

- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
self = [super init];
if (self) {
if (dictionary[kNameKey]) {
_name = dictionary[kNameKey];
}
if (dictionary[kCredentialIdKey]) {
_credentialID = dictionary[kCredentialIdKey];
}
}
return self;
}

@end

NS_ASSUME_NONNULL_END
26 changes: 26 additions & 0 deletions FirebaseAuth/Sources/Backend/RPC/Proto/FIRPasskeyInfo_Internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2023 Google LLC
*
* 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 "FirebaseAuth/Sources/Backend/RPC/Proto/FIRAuthProto.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPasskeyInfo.h"

NS_ASSUME_NONNULL_BEGIN

@interface FIRPasskeyInfo () <FIRAuthProto>

@end

NS_ASSUME_NONNULL_END
39 changes: 39 additions & 0 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIRPasskeyInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2023 Google LLC
*
* 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/LICENSE2.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>

NS_ASSUME_NONNULL_BEGIN

/**
@class FIRPasskeyInfo
@brief Passkey Info
*/
NS_SWIFT_NAME(PasskeyInfo) API_UNAVAILABLE(watchos) @interface FIRPasskeyInfo : NSObject

/**
@brief Passkey name
*/
@property(nonatomic, readonly) NSString *name;

/**
@brief Passkey credential ID
*/
@property(nonatomic, readonly) NSString *credentialID;

@end

NS_ASSUME_NONNULL_END
50 changes: 50 additions & 0 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIRUser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@
#import "FIRAuth.h"
#import "FIRAuthDataResult.h"
#import "FIRMultiFactor.h"
#import "FIRPasskeyInfo.h"
#import "FIRUserInfo.h"

@class FIRAuthTokenResult;
@class FIRPhoneAuthCredential;
@class FIRUserProfileChangeRequest;
@class FIRUserMetadata;
@protocol FIRAuthUIDelegate;
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
@class ASAuthorizationPlatformPublicKeyCredentialRegistration;
@class ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest;
#endif

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -123,6 +128,11 @@ NS_SWIFT_NAME(User)
@property(nonatomic, readonly, nonnull)
FIRMultiFactor *multiFactor API_UNAVAILABLE(macos, tvos, watchos);

/** @property enrolledPasskeys
@brief a list of user enrolled passkey object.
*/
@property(nonatomic, readonly) NSArray<FIRPasskeyInfo *> *enrolledPasskeys API_UNAVAILABLE(watchos);

/** @fn init
@brief This class should not be instantiated.
@remarks To retrieve the current user, use `Auth.currentUser`. To sign a user
Expand Down Expand Up @@ -162,6 +172,46 @@ NS_SWIFT_NAME(User)
completion:(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(updateEmail(to:completion:));

#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
/**
@fn startPasskeyEnrollmentWithName:completion:
@brief Start the passkey enrollment creating a plaform public key creation request with the
challenge from GCIP backend.
@param name The name for the passkey to be created.
@param completion Optionally; the block which is invoked when start passkey enrollment flow
finishes.
@remarks Possible error codes: // TODO @liubinj fill this after
*/
- (void)startPasskeyEnrollmentWithName:(nullable NSString *)name
completion:
(nullable void (^)(
ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest
*_Nullable request,
NSError *_Nullable error))completion
NS_SWIFT_NAME(startPasskeyEnrollment(with:completion:))
API_AVAILABLE(macos(12.0), ios(15.0), tvos(16.0));

/**
@fn finalizePasskeyEnrollmentWithPlatformCredential:completion:
@brief Finalize the passkey enrollment with the platfrom public key credential.
@param platformCredential The name for the passkey to be created.
@param completion Optionally; a block which is invoked when the finalize enroll with passkey flow
finishes, or is canceled
@remarks Possible error codes: // TODO @liubinj fill this after
*/
- (void)finalizePasskeyEnrollmentWithPlatformCredential:
(ASAuthorizationPlatformPublicKeyCredentialRegistration *)platformCredential
completion:(nullable void (^)(
FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error))completion
NS_SWIFT_NAME(finalizePasskeyEnrollment(with:completion:))
API_AVAILABLE(macos(12.0), ios(15.0), tvos(16.0));
#endif

/** @fn updatePassword:completion:
@brief Updates the password for the user. On success, the cached user profile data is updated.
Expand Down
1 change: 1 addition & 0 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FirebaseAuth.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#import "FIRMultiFactorSession.h"
#import "FIROAuthCredential.h"
#import "FIROAuthProvider.h"
#import "FIRPasskeyInfo.h"
#import "FIRTwitterAuthProvider.h"
#import "FIRUser.h"
#import "FIRUserInfo.h"
Expand Down
111 changes: 110 additions & 1 deletion FirebaseAuth/Sources/User/FIRUser.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#import "FirebaseAuth/Sources/Backend/RPC/FIRDeleteAccountResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRFinalizePasskeyEnrollmentRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRFinalizePasskeyEnrollmentResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeRequest.h"
Expand All @@ -43,6 +45,8 @@
#import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRStartPasskeyEnrollmentRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRStartPasskeyEnrollmentResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionResponse.h"
#import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenRequest.h"
Expand All @@ -61,9 +65,12 @@
#import "FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h"

#if TARGET_OS_IOS
#import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPhoneAuthProvider.h"
#endif

#import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
#import <AuthenticationServices/AuthenticationServices.h>
#endif

NS_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -583,6 +590,108 @@ - (void)setTokenService:(FIRSecureTokenService *)tokenService

#pragma mark -

#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
- (void)startPasskeyEnrollmentWithName:(nullable NSString *)name
completion:
(nullable void (^)(
ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest
*_Nullable request,
NSError *_Nullable error))completion {
FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;

FIRStartPasskeyEnrollmentRequest *request =
[[FIRStartPasskeyEnrollmentRequest alloc] initWithIDToken:self.rawAccessToken
requestConfiguration:requestConfiguration];
[FIRAuthBackend
startPasskeyEnrollment:request
callback:^(FIRStartPasskeyEnrollmentResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
completion(nil, error);
return;
} else {
// cached the passkey name. This is needed when calling
// finalizePasskeyEnrollment
self.passkeyName = name;
NSData *challengeInData =
[[NSData alloc] initWithBase64EncodedString:response.challenge
options:0];
NSData *userIdInData =
[[NSData alloc] initWithBase64EncodedString:response.userID options:0];

ASAuthorizationPlatformPublicKeyCredentialProvider *provider =
[[ASAuthorizationPlatformPublicKeyCredentialProvider alloc]
initWithRelyingPartyIdentifier:response.rpID];
ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest *request =
[provider
createCredentialRegistrationRequestWithChallenge:challengeInData
name:name
userID:userIdInData];
completion(request, nil);
}
}];
}

- (void)finalizePasskeyEnrollmentWithPlatformCredential:
(ASAuthorizationPlatformPublicKeyCredentialRegistration *)platformCredential
completion:(nullable void (^)(
FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error))completion {
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
FIRAuthDataResultCallback decoratedCallback =
[FIRAuth.auth signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;

NSString *credentialID = [platformCredential.credentialID base64EncodedStringWithOptions:0];
NSString *clientDataJson =
[platformCredential.rawClientDataJSON base64EncodedStringWithOptions:0];
NSString *attestationObject =
[platformCredential.rawAttestationObject base64EncodedStringWithOptions:0];
// If passkey name is not provided, we will provide a firebase formatted default name.

if (self.passkeyName != nil || [self.passkeyName isEqual:@""]) {
self.passkeyName = @"Unnamed account (Apple)";
}
FIRFinalizePasskeyEnrollmentRequest *request =
[[FIRFinalizePasskeyEnrollmentRequest alloc] initWithIDToken:self.rawAccessToken
name:self.passkeyName
credentialID:credentialID
clientDataJson:clientDataJson
attestationObject:attestationObject
requestConfiguration:requestConfiguration];

[FIRAuthBackend
finalizePasskeyEnrollment:request
callback:^(FIRFinalizePasskeyEnrollmentResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
decoratedCallback(nil, error);
} else {
[FIRAuth.auth
completeSignInWithAccessToken:response.idToken
accessTokenExpirationDate:nil
refreshToken:response.refreshToken
anonymous:NO
callback:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
if (error) {
completion(nil, error);
return;
}

FIRAuthDataResult *authDataResult =
user ? [[FIRAuthDataResult alloc]
initWithUser:user
additionalUserInfo:nil]
: nil;
decoratedCallback(authDataResult, error);
}];
}
}];
});
}
#endif // #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST

/** @fn updateEmail:password:callback:
@brief Updates email address and/or password for the current user.
@remarks May fail if there is already an email/password-based account for the same email
Expand Down
6 changes: 6 additions & 0 deletions FirebaseAuth/Sources/User/FIRUser_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ typedef void (^FIRVerifyBeforeUpdateEmailCallback)(NSError *_Nullable error);
*/
@property(nonatomic, copy, readonly) NSString *rawAccessToken;

/**
@property passkeyName
@brief A cached passkey name that being passed from startPasskeyEnrollmentWithName:completion: call
and consumed at finalizePasskeyEnrollmentWithPlatformCredential:completion: call
*/
@property(nonatomic, copy, nullable) NSString *passkeyName;
/** @property auth
@brief A weak reference to a FIRAuth instance associated with this instance.
*/
Expand Down
Loading

0 comments on commit 16ef4bf

Please sign in to comment.