diff --git a/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m index 069d4f3660..a58b1bf060 100644 --- a/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m +++ b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential.m @@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN @interface FIROAuthCredential () +@property(nonatomic, nullable) NSString *rawNonce; + - (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; @end @@ -40,12 +42,14 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithProviderID:(NSString *)providerID IDToken:(nullable NSString *)IDToken + rawNonce:(nullable NSString *)rawNonce accessToken:(nullable NSString *)accessToken secret:(nullable NSString *)secret pendingToken:(nullable NSString *)pendingToken { self = [super initWithProvider:providerID]; if (self) { _IDToken = IDToken; + _rawNonce = rawNonce; _accessToken = accessToken; _pendingToken = pendingToken; _secret = secret; @@ -56,8 +60,12 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithProviderID:(NSString *)providerID sessionID:(NSString *)sessionID OAuthResponseURLString:(NSString *)OAuthResponseURLString { - self = - [self initWithProviderID:providerID IDToken:nil accessToken:nil secret:nil pendingToken:nil]; + self = [self initWithProviderID:providerID + IDToken:nil + rawNonce:nil + accessToken:nil + secret:nil + pendingToken:nil]; if (self) { _OAuthResponseURLString = OAuthResponseURLString; _sessionID = sessionID; @@ -71,6 +79,7 @@ NS_ASSUME_NONNULL_BEGIN response.oauthSecretToken.length) { return [self initWithProviderID:response.providerID IDToken:response.oauthIDToken + rawNonce:nil accessToken:response.oauthAccessToken secret:response.oauthSecretToken pendingToken:response.pendingToken]; @@ -80,6 +89,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { request.providerIDToken = _IDToken; + request.providerRawNonce = _rawNonce; request.providerAccessToken = _accessToken; request.requestURI = _OAuthResponseURLString; request.sessionID = _sessionID; @@ -95,11 +105,13 @@ NS_ASSUME_NONNULL_BEGIN - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { NSString *IDToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"IDToken"]; + NSString *rawNonce = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"rawNonce"]; NSString *accessToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"accessToken"]; NSString *pendingToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"pendingToken"]; NSString *secret = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"secret"]; self = [self initWithProviderID:self.provider IDToken:IDToken + rawNonce:rawNonce accessToken:accessToken secret:secret pendingToken:pendingToken]; @@ -108,6 +120,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.IDToken forKey:@"IDToken"]; + [aCoder encodeObject:self.IDToken forKey:@"rawNonce"]; [aCoder encodeObject:self.accessToken forKey:@"accessToken"]; [aCoder encodeObject:self.pendingToken forKey:@"pendingToken"]; [aCoder encodeObject:self.secret forKey:@"secret"]; diff --git a/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h index 6bc7f4e854..f484b5c103 100644 --- a/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h +++ b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthCredential_Internal.h @@ -46,12 +46,14 @@ NS_ASSUME_NONNULL_BEGIN @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 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 pendingToken:(nullable NSString *)pendingToken NS_DESIGNATED_INITIALIZER; diff --git a/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m index c84add6ca4..0f07aee307 100644 --- a/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m +++ b/Firebase/Auth/Source/AuthProvider/OAuth/FIROAuthProvider.m @@ -71,6 +71,7 @@ static NSString *const kAuthTypeSignInWithRedirect = @"signInWithRedirect"; accessToken:(nullable NSString *)accessToken { return [[FIROAuthCredential alloc] initWithProviderID:providerID IDToken:IDToken + rawNonce:nil accessToken:accessToken secret:nil pendingToken:nil]; @@ -80,11 +81,35 @@ static NSString *const kAuthTypeSignInWithRedirect = @"signInWithRedirect"; accessToken:(NSString *)accessToken { return [[FIROAuthCredential alloc] initWithProviderID:providerID IDToken:nil + rawNonce:nil accessToken:accessToken secret:nil pendingToken:nil]; } ++ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID + IDToken:(NSString *)IDToken + rawNonce:(nullable NSString *)rawNonce + accessToken:(nullable NSString *)accessToken { + return [[FIROAuthCredential alloc] initWithProviderID:providerID + IDToken:IDToken + rawNonce:rawNonce + accessToken:accessToken + secret:nil + pendingToken:nil]; +} + ++ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID + IDToken:(NSString *)IDToken + rawNonce:(nullable NSString *)rawNonce { + return [[FIROAuthCredential alloc] initWithProviderID:providerID + IDToken:IDToken + rawNonce:rawNonce + accessToken:nil + secret:nil + pendingToken:nil]; +} + + (instancetype)providerWithProviderID:(NSString *)providerID { return [[self alloc]initWithProviderID:providerID auth:[FIRAuth auth]]; } diff --git a/Firebase/Auth/Source/Backend/FIRAuthBackend.m b/Firebase/Auth/Source/Backend/FIRAuthBackend.m index 2684357773..53505f77d1 100644 --- a/Firebase/Auth/Source/Backend/FIRAuthBackend.m +++ b/Firebase/Auth/Source/Backend/FIRAuthBackend.m @@ -342,6 +342,11 @@ static NSString *const kInvalidSessionInfoErrorMessage = @"INVALID_SESSION_INFO" */ static NSString *const kSessionExpiredErrorMessage = @"SESSION_EXPIRED"; +/** @var kMissingOrInvalidNonceErrorMessage + @brief This is the error message the server will respond with if the nonce is missing or invalid. + */ +static NSString *const kMissingOrInvalidNonceErrorMessage = @"MISSING_OR_INVALID_NONCE"; + /** @var kMissingAppTokenErrorMessage @brief This is the error message the server will respond with if the APNS token is missing in a verifyClient request. @@ -1176,6 +1181,10 @@ static id gBackendImplementation; return [FIRAuthErrorUtils captchaCheckFailedErrorWithMessage:serverErrorMessage]; } + if ([shortErrorMessage isEqualToString:kMissingOrInvalidNonceErrorMessage]) { + return [FIRAuthErrorUtils missingOrInvalidNonceErrorWithMessage:serverDetailErrorMessage]; + } + // In this case we handle an error that might be specified in the underlying errors dictionary, // the error message in determined based on the @c reason key in the dictionary. if (errorDictionary[kErrorsKey]) { diff --git a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h index 595ee9b32a..d0316117a8 100644 --- a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h +++ b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.h @@ -66,6 +66,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, copy, nullable) NSString *providerIDToken; +/** @property providerRawNonce + @brief An raw nonce from the IDP. + */ +@property(nonatomic, copy, nullable) NSString *providerRawNonce; + /** @property returnIDPCredential @brief Whether the response should return the IDP credential directly. */ diff --git a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m index 3a819d7a8e..9aa326e739 100644 --- a/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m +++ b/Firebase/Auth/Source/Backend/RPC/FIRVerifyAssertionRequest.m @@ -33,6 +33,11 @@ static NSString *const kProviderIDKey = @"providerId"; */ static NSString *const kProviderIDTokenKey = @"id_token"; +/** @var kProviderNonceKey + @brief The key for the "nonce" value in the request. + */ +static NSString *const kProviderNonceKey = @"nonce"; + /** @var kProviderAccessTokenKey @brief The key for the "access_token" value in the request. */ @@ -115,6 +120,11 @@ static NSString *const kSessionIDKey = @"sessionId"; value:_providerIDToken]]; } + if (_providerRawNonce) { + [queryItems addObject:[NSURLQueryItem queryItemWithName:kProviderNonceKey + value:_providerRawNonce]]; + } + if (_providerAccessToken) { [queryItems addObject:[NSURLQueryItem queryItemWithName:kProviderAccessTokenKey value:_providerAccessToken]]; diff --git a/Firebase/Auth/Source/Public/FIRAuthErrors.h b/Firebase/Auth/Source/Public/FIRAuthErrors.h index e58192707f..73b0b21337 100644 --- a/Firebase/Auth/Source/Public/FIRAuthErrors.h +++ b/Firebase/Auth/Source/Public/FIRAuthErrors.h @@ -343,6 +343,10 @@ typedef NS_ENUM(NSInteger, FIRAuthErrorCode) { */ FIRAuthErrorCodeGameKitNotLinked = 17076, + /** Indicates that the nonce is missing or invalid. + */ + FIRAuthErrorCodeMissingOrInvalidNonce = 17094, + /** Indicates an error for when the client identifier is missing. */ FIRAuthErrorCodeMissingClientIdentifier = 17993, diff --git a/Firebase/Auth/Source/Public/FIROAuthProvider.h b/Firebase/Auth/Source/Public/FIROAuthProvider.h index 57635e94e2..68a9aa4be0 100644 --- a/Firebase/Auth/Source/Public/FIROAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIROAuthProvider.h @@ -85,6 +85,35 @@ NS_SWIFT_NAME(OAuthProvider) + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID accessToken:(NSString *)accessToken; +/** @fn credentialWithProviderID:IDToken:rawNonce:accessToken: + @brief Creates an `FIRAuthCredential` for that OAuth 2 provider identified by providerID, ID + token, raw nonce and access token. + + @param providerID The provider ID associated with the Auth credential being created. + @param IDToken The IDToken associated with the Auth credential being created. + @param rawNonce The raw nonce associated with the Auth credential being created. + @param accessToken The accessstoken associated with the Auth credential be created, if + available. + @return A FIRAuthCredential for the specified provider ID, ID token and access token. + */ ++ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID + IDToken:(NSString *)IDToken + rawNonce:(nullable NSString *)rawNonce + accessToken:(nullable NSString *)accessToken; + +/** @fn credentialWithProviderID:IDToken:rawNonce: + @brief Creates an `FIRAuthCredential` for that OAuth 2 provider identified by providerID using + an ID token and raw nonce. + + @param providerID The provider ID associated with the Auth credential being created. + @param IDToken The IDToken associated with the Auth credential being created. + @param rawNonce The raw nonce associated with the Auth credential being created. + @return A FIRAuthCredential. + */ ++ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID + IDToken:(NSString *)IDToken + rawNonce:(nullable NSString *)rawNonce; + /** @fn init @brief This class is not meant to be initialized. */ diff --git a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h index 1357f101e6..31bbf5b311 100644 --- a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h +++ b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.h @@ -556,6 +556,13 @@ NS_ASSUME_NONNULL_BEGIN */ + (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status; +/** @fn missingOrInvalidNonceErrorWithMessage: + @brief Constructs an @c NSError with the code and message provided. + @param message Error message from the backend, if any. + @return The nullable NSError instance associated with the given error message, if one is found. +*/ ++ (NSError *)missingOrInvalidNonceErrorWithMessage:(nullable NSString *)message; + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m index aeb4f71d5b..146c14f29b 100644 --- a/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m +++ b/Firebase/Auth/Source/Utilities/FIRAuthErrorUtils.m @@ -463,6 +463,12 @@ static NSString *const kFIRAuthErrorMessageDynamicLinkNotActivated = static NSString *const kFIRAuthErrorMessageRejectedCredential = @"The request contains malformed or mismatching credentials."; +/** @var kFIRAuthErrorMessageMissingOrInvalidNonce + @brief Error message constant describing @c FIRAuthErrorCodeMissingOrInvalidNonce errors. + */ +static NSString *const kFIRAuthErrorMessageMissingOrInvalidNonce = + @"The request contains malformed or mismatched credentials."; + /** @var FIRAuthErrorDescription @brief The error descrioption, based on the error code. @remarks No default case so that we get a compiler warning if a new value was added to the enum. @@ -599,6 +605,8 @@ static NSString *FIRAuthErrorDescription(FIRAuthErrorCode code) { return kFIRAuthErrorMessageDynamicLinkNotActivated; case FIRAuthErrorCodeRejectedCredential: return kFIRAuthErrorMessageRejectedCredential; + case FIRAuthErrorCodeMissingOrInvalidNonce: + return kFIRAuthErrorMessageMissingOrInvalidNonce; } } @@ -738,6 +746,8 @@ static NSString *const FIRAuthErrorCodeString(FIRAuthErrorCode code) { return @"ERROR_DYNAMIC_LINK_NOT_ACTIVATED"; case FIRAuthErrorCodeRejectedCredential: return @"ERROR_REJECTED_CREDENTIAL"; + case FIRAuthErrorCodeMissingOrInvalidNonce: + return @"ERROR_MISSING_OR_INVALID_NONCE"; } } @@ -1179,6 +1189,10 @@ static NSString *const FIRAuthErrorCodeString(FIRAuthErrorCode code) { return [self errorWithCode:FIRAuthInternalErrorCodeInvalidDynamicLinkDomain message:message]; } ++ (NSError *)missingOrInvalidNonceErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingOrInvalidNonce message:message]; +} + + (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status { NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status]; return [self errorWithCode:FIRAuthInternalErrorCodeKeychainError userInfo:@{ diff --git a/Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h b/Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h index 27b0bf4067..8f34bdfc8d 100644 --- a/Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h +++ b/Firebase/Auth/Source/Utilities/FIRAuthInternalErrors.h @@ -393,6 +393,11 @@ typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) { FIRAuthInternalErrorCodeGameKitNotLinked = FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeGameKitNotLinked, + /** Indicates that the nonce is missing or invalid. + */ + FIRAuthInternalErrorCodeMissingOrInvalidNonce = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMissingOrInvalidNonce, + /** Indicates that a non-null user was expected as an argmument to the operation but a null user was provided. */