diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 9f63d829a..bc87cd6f4 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -372,6 +372,12 @@ 34A663321E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; 34A663331E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; 34A663341E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; }; + 34A6638B1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638C1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638D1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; + 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */; }; 34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */; }; 34FEA6AE1DB6E083005C9212 /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; }; 34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; }; @@ -545,6 +551,8 @@ 347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 34A663261E871DD40060B664 /* OIDIDToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDIDToken.h; sourceTree = ""; }; 34A663271E871DD40060B664 /* OIDIDToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDIDToken.m; sourceTree = ""; }; + 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRPProfileCode.m; sourceTree = ""; }; + 34A663911E886AED0060B664 /* OIDRPProfileCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRPProfileCode.h; sourceTree = ""; }; 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = ""; }; 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = ""; }; 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDLoopbackHTTPServer.h; sourceTree = ""; }; @@ -810,6 +818,8 @@ 60140F821DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m */, 60140F841DE43C8C00DA0DC3 /* OIDRegistrationResponseTests.h */, 60140F851DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m */, + 34A663911E886AED0060B664 /* OIDRPProfileCode.h */, + 34A6638A1E8865090060B664 /* OIDRPProfileCode.m */, 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */, 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */, ); @@ -1487,6 +1497,7 @@ 3417421E1C5D82D3000EF209 /* OIDServiceDiscoveryTests.m in Sources */, 3417421F1C5D82D3000EF209 /* OIDTokenRequestTests.m in Sources */, 341742181C5D82D3000EF209 /* OIDAuthorizationResponseTests.m in Sources */, + 34A6638B1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341742171C5D82D3000EF209 /* OIDAuthorizationRequestTests.m in Sources */, 3417421A1C5D82D3000EF209 /* OIDGrantTypesTests.m in Sources */, 3417421B1C5D82D3000EF209 /* OIDResponseTypesTests.m in Sources */, @@ -1511,6 +1522,7 @@ 341AA50D1E7F3A9B00FCA5C6 /* OIDTokenRequestTests.m in Sources */, 341AA5091E7F3A9B00FCA5C6 /* OIDResponseTypesTests.m in Sources */, 341AA4D91E7F393500FCA5C6 /* OIDAuthorizationRequestTests.m in Sources */, + 34A6638C1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341AA5101E7F3A9B00FCA5C6 /* OIDRegistrationRequestTests.m in Sources */, 341AA5111E7F3A9B00FCA5C6 /* OIDRegistrationResponseTests.m in Sources */, 341AA5081E7F3A9B00FCA5C6 /* OIDGrantTypesTests.m in Sources */, @@ -1531,6 +1543,7 @@ 341AA5001E7F3A9400FCA5C6 /* OIDTokenRequestTests.m in Sources */, 341AA4FC1E7F3A9400FCA5C6 /* OIDResponseTypesTests.m in Sources */, 341AA4F81E7F3A3000FCA5C6 /* OIDAuthorizationRequestTests.m in Sources */, + 34A6638D1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 341AA5031E7F3A9400FCA5C6 /* OIDRegistrationRequestTests.m in Sources */, 341AA5041E7F3A9400FCA5C6 /* OIDRegistrationResponseTests.m in Sources */, 341AA4FB1E7F3A9400FCA5C6 /* OIDGrantTypesTests.m in Sources */, @@ -1614,6 +1627,7 @@ 343AAA7F1E8346B400F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAA731E8346B400F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAA761E8346B400F9D36E /* OIDGrantTypesTests.m in Sources */, + 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1691,6 +1705,7 @@ 343AAB8B1E8349CE00F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAB7F1E8349CE00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB821E8349CE00F9D36E /* OIDGrantTypesTests.m in Sources */, + 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAB801E8349CE00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1745,6 +1760,7 @@ 343AAB991E8349CF00F9D36E /* OIDRegistrationRequestTests.m in Sources */, 343AAB8D1E8349CF00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB901E8349CF00F9D36E /* OIDGrantTypesTests.m in Sources */, + 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */, 343AAB8E1E8349CF00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UnitTests/OIDRPProfileCode.h b/UnitTests/OIDRPProfileCode.h new file mode 100644 index 000000000..aae89cff9 --- /dev/null +++ b/UnitTests/OIDRPProfileCode.h @@ -0,0 +1,38 @@ +/*! @file OIDRPProfileCode.h + @brief AppAuth iOS SDK + @copyright + Copyright 2017 Google Inc. All Rights Reserved. + @copydetails + 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 + +#import "OIDAuthorizationUICoordinator.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDAuthorizationUICoordinatorNonInteractive : NSObject { + // private variables + NSURLSession *_urlSession; + __weak id _session; +} +@end + +@interface OIDRPProfileCode : XCTestCase { + OIDAuthorizationUICoordinatorNonInteractive *_coordinator; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDRPProfileCode.m b/UnitTests/OIDRPProfileCode.m new file mode 100644 index 000000000..e17765ab7 --- /dev/null +++ b/UnitTests/OIDRPProfileCode.m @@ -0,0 +1,285 @@ +/*! @file OIDRPProfileCode.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 Google Inc. All Rights Reserved. + @copydetails + 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 "OIDRPProfileCode.h" + +#import +#import "AppAuth.h" + +static NSString *const kRedirectURI = @"com.example.app:/oauth2redirect/example-provider"; + +static NSString *const kTestURIBase = + @"https://rp.certification.openid.net:8080/appauth-ios-macos/"; + +/*! @brief A UI Coordinator for testing, has no user agent and doesn't support user interaction. + Simply performs the authorization request as a GET request, and looks for a redirect in + the response. + */ +@interface OIDAuthorizationUICoordinatorNonInteractive () +@end + +@implementation OIDAuthorizationUICoordinatorNonInteractive + +- (BOOL)presentAuthorizationWithURL:(NSURL *)URL session:(id)session { + _session = session; + NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:URL] mutableCopy]; + NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; + _urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; + [[_urlSession dataTaskWithRequest:URLRequest + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + NSDictionary* headers = [(NSHTTPURLResponse *)response allHeaderFields]; + NSString *location = [headers objectForKey:@"Location"]; + NSURL *url = [NSURL URLWithString:location]; + [session resumeAuthorizationFlowWithURL:url]; + }] resume]; + + return YES; +} + +- (void)dismissAuthorizationAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (completion) completion(); +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler { + // Disables HTTP redirection in the NSURLSession + completionHandler(NULL); +} +@end + +@interface OIDAuthorizationFlowSessionImplementation : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithRequest:(OIDAuthorizationRequest *)request + NS_DESIGNATED_INITIALIZER; + +@end + +@interface OIDRPProfileCode () + +typedef void (^PostRegistrationCallback)(OIDServiceConfiguration *configuration, + OIDRegistrationResponse *registrationResponse, + NSError *error + ); + +@end + +@implementation OIDRPProfileCode + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +- (void)doRegistrationWithIssuer:(NSURL *)issuer callback:(PostRegistrationCallback)callback { + NSURL *redirectURI = [NSURL URLWithString:kRedirectURI]; + + // discovers endpoints + [OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer + completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) { + + if (!configuration) { + callback(nil, nil, error); + return; + } + + OIDRegistrationRequest *request = + [[OIDRegistrationRequest alloc] initWithConfiguration:configuration + redirectURIs:@[ redirectURI ] + responseTypes:nil + grantTypes:nil + subjectType:nil + tokenEndpointAuthMethod:@"client_secret_basic" + additionalParameters:nil]; + // performs registration request + [OIDAuthorizationService performRegistrationRequest:request + completion:^(OIDRegistrationResponse *_Nullable regResp, NSError *_Nullable error) { + if (regResp) { + callback(configuration, regResp, nil); + } else { + callback(nil, nil, error); + } + }]; + }]; +} + + +- (void)testRP_response_type_code { + [self codeFlowWithExchangeForTest:@"rp-response_type-code"]; +} +- (void)codeFlowWithExchangeForTest:(NSString *)test { + + [kTestURIBase stringByAppendingString:test]; + + NSString *issuerString = [kTestURIBase stringByAppendingString:test]; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Discovery and registration should complete."]; + + XCTestExpectation *auth_complete = + [self expectationWithDescription:@"Authorization should complete."]; + + XCTestExpectation *token_exchange = + [self expectationWithDescription:@"Token Exchange should complete."]; + + NSURL *issuer = [NSURL URLWithString:issuerString]; + + [self doRegistrationWithIssuer:issuer callback:^(OIDServiceConfiguration *configuration, + OIDRegistrationResponse *registrationResponse, + NSError *error) { + [expectation fulfill]; + XCTAssertNotNil(configuration); + XCTAssertNotNil(registrationResponse); + XCTAssertNil(error); + + NSURL *redirectURI = [NSURL URLWithString:kRedirectURI]; + // builds authentication request + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:registrationResponse.clientID + clientSecret:registrationResponse.clientSecret + scopes:@[ OIDScopeOpenID, OIDScopeProfile ] + redirectURL:redirectURI + responseType:OIDResponseTypeCode + additionalParameters:nil]; + + _coordinator = [[OIDAuthorizationUICoordinatorNonInteractive alloc] init]; + + [OIDAuthorizationService + presentAuthorizationRequest:request + UICoordinator:_coordinator + callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *error) { + [auth_complete fulfill]; + XCTAssertNotNil(authorizationResponse); + XCTAssertNil(error); + + OIDTokenRequest *tokenExchangeRequest = [authorizationResponse tokenExchangeRequest]; + [OIDAuthorizationService + performTokenRequest:tokenExchangeRequest + callback:^(OIDTokenResponse *_Nullable tokenResponse, + NSError *_Nullable tokenError) { + + [token_exchange fulfill]; + XCTAssertNotNil(tokenResponse); + XCTAssertNil(tokenError); + + // testRP_id_token_sig_none + XCTAssertNotNil(tokenResponse.idToken); + }]; + }]; + + }]; + [self waitForExpectationsWithTimeout:20 handler:nil]; +} + +- (void)testRP_id_token_sig_none { + [self codeFlowWithExchangeForTest:@"rp-id_token-sig-none"]; +} + +- (void)testRP_token_endpoint_client_secret_basic { + [self codeFlowWithExchangeForTest:@"rp-token_endpoint-client_secret_basic"]; +} + +- (void)testRP_id_token_aud { + [self codeFlowWithExchangeInvalidIDToken:@"rp-id_token-aud"]; +} +- (void)testRP_id_token_iat { + [self codeFlowWithExchangeInvalidIDToken:@"rp-id_token-iat"]; +} +- (void)testRP_id_token_sub { + [self codeFlowWithExchangeInvalidIDToken:@"rp-id_token-sub"]; +} +- (void)testRP_id_token_issuer_mismatch { + [self codeFlowWithExchangeInvalidIDToken:@"rp-id_token-issuer-mismatch"]; +} +// + +- (void)codeFlowWithExchangeInvalidIDToken:(NSString*) testName { + + XCTestExpectation *expectation = + [self expectationWithDescription:@"Discovery and registration should complete."]; + + XCTestExpectation *auth_complete = + [self expectationWithDescription:@"Authorization should complete."]; + + XCTestExpectation *token_exchange = + [self expectationWithDescription:@"Token Exchange should complete."]; + + NSString *issuerString = [kTestURIBase stringByAppendingString:testName]; + + NSURL *issuer = [NSURL URLWithString:issuerString]; + + [self doRegistrationWithIssuer:issuer callback:^(OIDServiceConfiguration *configuration, + OIDRegistrationResponse *registrationResponse, + NSError *error) { + [expectation fulfill]; + XCTAssertNotNil(configuration); + XCTAssertNotNil(registrationResponse); + XCTAssertNil(error); + + NSURL *redirectURI = [NSURL URLWithString:kRedirectURI]; + // builds authentication request + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:registrationResponse.clientID + clientSecret:registrationResponse.clientSecret + scopes:@[ OIDScopeOpenID, OIDScopeProfile ] + redirectURL:redirectURI + responseType:OIDResponseTypeCode + additionalParameters:nil]; + + _coordinator = [[OIDAuthorizationUICoordinatorNonInteractive alloc] init]; + + [OIDAuthorizationService + presentAuthorizationRequest:request + UICoordinator:_coordinator + callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *error) { + [auth_complete fulfill]; + XCTAssertNotNil(authorizationResponse); + XCTAssertNil(error); + + OIDTokenRequest *tokenExchangeRequest = [authorizationResponse tokenExchangeRequest]; + [OIDAuthorizationService + performTokenRequest:tokenExchangeRequest + callback:^(OIDTokenResponse *_Nullable tokenResponse, + NSError *_Nullable tokenError) { + + [token_exchange fulfill]; + XCTAssertNil(tokenResponse); + XCTAssertNotNil(tokenError); + }]; + }]; + + }]; + [self waitForExpectationsWithTimeout:20 handler:nil]; +} + +@end + +