Skip to content
Merged
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
21 changes: 11 additions & 10 deletions Example/Auth/Tests/FIRAuthTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1863,16 +1863,17 @@ - (void)testUpdateCurrentUserFailureNetworkError {
[self waitForSignInWithAccessToken:kTestAccessToken
APIKey:kTestAPIKey
completion:nil];
NSString *kTestAPIKey2 = @"fakeAPIKey2";
FIRUser *user2 = [FIRAuth auth].currentUser;
user2.requestConfiguration = [[FIRAuthRequestConfiguration alloc]initWithAPIKey:kTestAPIKey2];
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils networkErrorWithUnderlyingError:[NSError new]]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] updateCurrentUser:user2 completion:^(NSError *_Nullable error) {
XCTAssertEqual(error.code, FIRAuthErrorCodeNetworkError);
[expectation fulfill];
}];
NSString *kTestAPIKey2 = @"fakeAPIKey2";
FIRUser *user2 = [FIRAuth auth].currentUser;
user2.requestConfiguration = [[FIRAuthRequestConfiguration alloc]initWithAPIKey:kTestAPIKey2];
NSError *underlyingError = [NSError errorWithDomain:@"Test Error" code:1 userInfo:nil];
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils networkErrorWithUnderlyingError:underlyingError]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] updateCurrentUser:user2 completion:^(NSError *_Nullable error) {
XCTAssertEqual(error.code, FIRAuthErrorCodeNetworkError);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
Expand Down
3 changes: 2 additions & 1 deletion Example/Auth/Tests/FIRPhoneAuthProviderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,8 @@ - (void)testVerifyPhoneNumberInTestModeFailure {
// Assert that the app credential is nil when in test mode.
XCTAssertNil(request.appCredential);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
callback(nil, [FIRAuthErrorUtils networkErrorWithUnderlyingError:[NSError new]]);
NSError *underlying = [NSError errorWithDomain:@"Test Error" code:1 userInfo:nil];
callback(nil, [FIRAuthErrorUtils networkErrorWithUnderlyingError:underlying]);
});
});

Expand Down
4 changes: 2 additions & 2 deletions Example/Auth/Tests/FIRUserTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1151,8 +1151,8 @@ - (void)testGetIDTokenResultForcingRefreshFailure {
XCTAssertEqualObjects(request.APIKey, kAPIKey);

dispatch_async(FIRAuthGlobalWorkQueue(), ^() {

callback(nil, [FIRAuthErrorUtils networkErrorWithUnderlyingError:[NSError new]]);
NSError *underlying = [NSError errorWithDomain:@"Test Error" code:1 userInfo:nil];
callback(nil, [FIRAuthErrorUtils networkErrorWithUnderlyingError:underlying]);
});
});
[user getIDTokenResultForcingRefresh:YES
Expand Down
13 changes: 13 additions & 0 deletions Firebase/Auth/Source/FIRAuthErrorUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (NSError *)unexpectedErrorResponseWithDeserializedResponse:(id)deserializedResponse;

/** @fn malformedJWTErrorWithToken:underlyingError:
@brief Constructs an @c NSError with the code set to @c FIRAuthErrorCodeMalformedJWT and
populates the userInfo dictionary with an error message, the bad token, and an underlying
error that may have occurred when parsing.
@param token The token that failed to parse.
@param underlyingError The error that caused this error. If this parameter is nil, the
NSUnderlyingErrorKey value will not be set.
@remarks This error is returned when JWT parsing fails.
@returns An @c FIRAuthErrorCodeMalformedJWT error wrapping an underlying error, if available.
*/
+ (NSError *)malformedJWTErrorWithToken:(NSString *)token
underlyingError:(NSError *_Nullable)underlyingError;

/** @fn unexpectedResponseWithData:underlyingError:
@brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeUnexpectedResponse
code, and a populated @c FIRAuthErrorUserInfoDataKey key in the @c NSError.userInfo
Expand Down
22 changes: 22 additions & 0 deletions Firebase/Auth/Source/FIRAuthErrorUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@
static NSString *const kFIRAuthErrorMessageInternalError = @"An internal error has occurred, "
"print and inspect the error details for more information.";

/** @var kFIRAuthErrorMessageMalformedJWT
@brief Error message constant describing @c FIRAuthErrorCodeMalformedJWT errors.
*/
static NSString *const kFIRAuthErrorMessageMalformedJWT =
@"Failed to parse JWT. Check the userInfo dictionary for the full token.";

/** @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.
Expand Down Expand Up @@ -531,6 +537,8 @@
return kFIRAuthErrorMessageNullUser;
case FIRAuthErrorCodeWebInternalError:
return kFIRAuthErrorMessageWebInternalError;
case FIRAuthErrorCodeMalformedJWT:
return kFIRAuthErrorMessageMalformedJWT;
}
}

Expand Down Expand Up @@ -652,6 +660,8 @@
return @"ERROR_NULL_USER";
case FIRAuthErrorCodeWebInternalError:
return @"ERROR_WEB_INTERNAL_ERROR";
case FIRAuthErrorCodeMalformedJWT:
return @"ERROR_MALFORMED_JWT";
}
}

Expand Down Expand Up @@ -735,6 +745,18 @@ + (NSError *)unexpectedErrorResponseWithDeserializedResponse:(id)deserializedRes
}];
}

+ (NSError *)malformedJWTErrorWithToken:(NSString *)token
underlyingError:(NSError *_Nullable)underlyingError {
NSMutableDictionary *userInfo =
[NSMutableDictionary dictionaryWithObject:kFIRAuthErrorMessageMalformedJWT
forKey:NSLocalizedDescriptionKey];
[userInfo setObject:token forKey:FIRAuthErrorUserInfoDataKey];
if (underlyingError != nil) {
[userInfo setObject:underlyingError forKey:NSUnderlyingErrorKey];
}
return [self errorWithCode:FIRAuthInternalErrorCodeMalformedJWT userInfo:[userInfo copy]];
}

+ (NSError *)unexpectedResponseWithData:(NSData *)data
underlyingError:(NSError *)underlyingError {
return [self errorWithCode:FIRAuthInternalErrorCodeUnexpectedResponse userInfo:@{
Expand Down
3 changes: 3 additions & 0 deletions Firebase/Auth/Source/FIRAuthInternalErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,9 @@ typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) {
FIRAuthInternalErrorCodeNullUser =
FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeNullUser,

FIRAuthInternalErrorCodeMalformedJWT =
FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMalformedJWT,

/** @var FIRAuthInternalErrorCodeRPCRequestEncodingError
@brief Indicates an error encoding the RPC request.
@remarks This is typically due to some sort of unexpected input value.
Expand Down
27 changes: 20 additions & 7 deletions Firebase/Auth/Source/FIRUser.m
Original file line number Diff line number Diff line change
Expand Up @@ -851,9 +851,17 @@ - (void)getIDTokenResultForcingRefresh:(BOOL)forceRefresh
"error" out parameter.
*/
- (FIRAuthTokenResult *)parseIDToken:(NSString *)token error:(NSError **)error {
// Though this is an internal method, errors returned here are surfaced in user-visible
// callbacks.
*error = nil;
NSArray *tokenStringArray = [token componentsSeparatedByString:@"."];

// The JWT should have three parts, though we only use the second in this method.
if (tokenStringArray.count != 3) {
*error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:nil];
return nil;
}

// The token payload is always the second index of the array.
NSString *idToken = tokenStringArray[1];

Expand All @@ -863,8 +871,10 @@ - (FIRAuthTokenResult *)parseIDToken:(NSString *)token error:(NSError **)error {
[[idToken stringByReplacingOccurrencesOfString:@"_" withString:@"/"] mutableCopy];

// Replace "-" with "+"
tokenPayload =
[[tokenPayload stringByReplacingOccurrencesOfString:@"-" withString:@"+"] mutableCopy];
[tokenPayload replaceOccurrencesOfString:@"-"
withString:@"+"
options:kNilOptions
range:NSMakeRange(0, tokenPayload.length)];

// Pad the token payload with "=" signs if the payload's length is not a multiple of 4.
while ((tokenPayload.length % 4) != 0) {
Expand All @@ -874,19 +884,22 @@ - (FIRAuthTokenResult *)parseIDToken:(NSString *)token error:(NSError **)error {
[[NSData alloc] initWithBase64EncodedString:tokenPayload
options:NSDataBase64DecodingIgnoreUnknownCharacters];
if (!decodedTokenPayloadData) {
*error = [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:token];
*error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:nil];
return nil;
}
NSError *jsonError = nil;
NSJSONReadingOptions options = NSJSONReadingMutableContainers|NSJSONReadingAllowFragments;
NSDictionary *tokenPayloadDictionary =
[NSJSONSerialization JSONObjectWithData:decodedTokenPayloadData
options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments
error:error];
if (*error) {
options:options
error:&jsonError];
if (jsonError != nil) {
*error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:jsonError];
return nil;
}

if (!tokenPayloadDictionary) {
*error = [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:token];
*error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:nil];
return nil;
}

Expand Down
5 changes: 5 additions & 0 deletions Firebase/Auth/Source/Public/FIRAuthErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ typedef NS_ENUM(NSInteger, FIRAuthErrorCode) {
/** Indicates an internal error occurred.
*/
FIRAuthErrorCodeInternalError = 17999,

/** Raised when a JWT fails to parse correctly. May be accompanied by an underlying error
describing which step of the JWT parsing process failed.
*/
FIRAuthErrorCodeMalformedJWT = 18000,
} NS_SWIFT_NAME(AuthErrorCode);

@end