diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 5d648a6c8..1c61157b5 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 06C19E9B22B474A200C19CE1 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; 06C19E9C22B474A600C19CE1 /* OIDEndSessionRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CF37C06B1F1FC21A00662E41 /* OIDEndSessionRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; + 1AB85393A09567E7CBB687AE /* OIDAuthorizationServiceInstanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */; }; 2D47AAE1249A87020059B5A4 /* OIDTVAuthorizationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AAD8249A87010059B5A4 /* OIDTVAuthorizationResponse.m */; }; 2D47AAE4249A87020059B5A4 /* OIDTVServiceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADA249A87010059B5A4 /* OIDTVServiceConfiguration.m */; }; 2D47AAE8249A87020059B5A4 /* OIDTVAuthorizationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D47AADD249A87010059B5A4 /* OIDTVAuthorizationRequest.m */; }; @@ -548,10 +549,12 @@ 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 */; }; + 4042060AFEB6D5DE8A86B483 /* OIDAuthorizationServiceInstanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */; }; 55A094CF20DFBB10000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55A094D020DFBB11000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55A094D120DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55A094D220DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5DEEE663EDB0D4A1C1118284 /* OIDAuthorizationServiceInstanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */; }; 60140F7A1DE4276800DA0DC3 /* OIDClientMetadataParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */; }; 60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */; }; 60140F801DE4344200DA0DC3 /* OIDRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 60140F7F1DE4344200DA0DC3 /* OIDRegistrationResponse.m */; }; @@ -563,6 +566,7 @@ 73F574372B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; 73F574382B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; 73F574392B7C42690023FFF0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 73F574332B7C42690023FFF0 /* PrivacyInfo.xcprivacy */; }; + 9BE107A4CCBF0579E2354D3B /* OIDAuthorizationServiceInstanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */; }; A5EEF29720D821120044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29820D8211A0044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; A5EEF29920D8211B0044F470 /* OIDTokenUtilitiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A5EEF1FD20CF07760044F470 /* OIDTokenUtilitiesTests.m */; }; @@ -617,8 +621,10 @@ CF37C0701F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; CF37C0711F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CF37C06C1F1FC21A00662E41 /* OIDEndSessionRequest.m */; }; CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6431F31F228A980075B6B5 /* OIDEndSessionResponse.m */; }; + DD05E93BBBA3AB6BF88CAB8D /* OIDAuthorizationServiceInstanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */; }; E56963F03064815800AC4DFE /* OIDExternalUserAgentIOSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E56963EF3064815800AC4DFE /* OIDExternalUserAgentIOSTests.m */; }; E56963F13064815800AC4DFE /* OIDExternalUserAgentIOSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E56963EF3064815800AC4DFE /* OIDExternalUserAgentIOSTests.m */; }; + F1A717E418CCA5400B300540 /* OIDAuthorizationServiceInstanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */; }; F9A7082E2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h in Headers */ = {isa = PBXBuildFile; fileRef = F9A7082C2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h */; settings = {ATTRIBUTES = (Public, ); }; }; F9A7082F2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7082D2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m */; }; /* End PBXBuildFile section */ @@ -839,6 +845,7 @@ 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 = ""; }; 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDLoopbackHTTPServer.m; sourceTree = ""; }; + 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDAuthorizationServiceInstanceTests.m; sourceTree = ""; }; 60140F781DE4262000DA0DC3 /* OIDClientMetadataParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDClientMetadataParameters.h; sourceTree = ""; }; 60140F791DE4276800DA0DC3 /* OIDClientMetadataParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDClientMetadataParameters.m; sourceTree = ""; }; 60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDRegistrationRequest.m; sourceTree = ""; }; @@ -1147,6 +1154,7 @@ 3417420B1C5D82D3000EF209 /* OIDServiceDiscoveryTests.h */, 3417420C1C5D82D3000EF209 /* OIDServiceDiscoveryTests.m */, 0A987C0100000001000A0987 /* OIDAuthorizationServiceTests.m */, + 459EB273F7CD3FCE6BEA1EA9 /* OIDAuthorizationServiceInstanceTests.m */, 3417420D1C5D82D3000EF209 /* OIDTokenRequestTests.h */, 3417420E1C5D82D3000EF209 /* OIDTokenRequestTests.m */, 3417420F1C5D82D3000EF209 /* OIDTokenResponseTests.h */, @@ -2233,6 +2241,7 @@ E56963F03064815800AC4DFE /* OIDExternalUserAgentIOSTests.m in Sources */, 3417421E1C5D82D3000EF209 /* OIDServiceDiscoveryTests.m in Sources */, 0A987C0100000003000A0987 /* OIDAuthorizationServiceTests.m in Sources */, + F1A717E418CCA5400B300540 /* OIDAuthorizationServiceInstanceTests.m in Sources */, 3417421F1C5D82D3000EF209 /* OIDTokenRequestTests.m in Sources */, 341742181C5D82D3000EF209 /* OIDAuthorizationResponseTests.m in Sources */, 34A6638B1E8865090060B664 /* OIDRPProfileCode.m in Sources */, @@ -2261,6 +2270,7 @@ 341AA50B1E7F3A9B00FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA50C1E7F3A9B00FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, 0A987C0100000005000A0987 /* OIDAuthorizationServiceTests.m in Sources */, + DD05E93BBBA3AB6BF88CAB8D /* OIDAuthorizationServiceInstanceTests.m in Sources */, 341AA5071E7F3A9B00FCA5C6 /* OIDAuthStateTests.m in Sources */, 341AA50E1E7F3A9B00FCA5C6 /* OIDTokenResponseTests.m in Sources */, 341AA50D1E7F3A9B00FCA5C6 /* OIDTokenRequestTests.m in Sources */, @@ -2285,6 +2295,7 @@ 341AA4FE1E7F3A9400FCA5C6 /* OIDServiceConfigurationTests.m in Sources */, 341AA4FF1E7F3A9400FCA5C6 /* OIDServiceDiscoveryTests.m in Sources */, 0A987C0100000004000A0987 /* OIDAuthorizationServiceTests.m in Sources */, + 5DEEE663EDB0D4A1C1118284 /* OIDAuthorizationServiceInstanceTests.m in Sources */, 341AA4FA1E7F3A9400FCA5C6 /* OIDAuthStateTests.m in Sources */, 341AA5011E7F3A9400FCA5C6 /* OIDTokenResponseTests.m in Sources */, 341AA5001E7F3A9400FCA5C6 /* OIDTokenRequestTests.m in Sources */, @@ -2412,6 +2423,7 @@ 343AAA801E8346B400F9D36E /* OIDRegistrationResponseTests.m in Sources */, 343AAA7A1E8346B400F9D36E /* OIDServiceDiscoveryTests.m in Sources */, 0A987C0100000006000A0987 /* OIDAuthorizationServiceTests.m in Sources */, + 4042060AFEB6D5DE8A86B483 /* OIDAuthorizationServiceInstanceTests.m in Sources */, 343AAA751E8346B400F9D36E /* OIDAuthStateTests.m in Sources */, 343AAA7C1E8346B400F9D36E /* OIDTokenResponseTests.m in Sources */, 343AAA7B1E8346B400F9D36E /* OIDTokenRequestTests.m in Sources */, @@ -2500,6 +2512,7 @@ 343AAB8C1E8349CE00F9D36E /* OIDRegistrationResponseTests.m in Sources */, 343AAB861E8349CE00F9D36E /* OIDServiceDiscoveryTests.m in Sources */, 0A987C0100000007000A0987 /* OIDAuthorizationServiceTests.m in Sources */, + 1AB85393A09567E7CBB687AE /* OIDAuthorizationServiceInstanceTests.m in Sources */, 343AAB811E8349CE00F9D36E /* OIDAuthStateTests.m in Sources */, 343AAB881E8349CE00F9D36E /* OIDTokenResponseTests.m in Sources */, 343AAB871E8349CE00F9D36E /* OIDTokenRequestTests.m in Sources */, @@ -2561,6 +2574,7 @@ 343AAB9A1E8349CF00F9D36E /* OIDRegistrationResponseTests.m in Sources */, 343AAB941E8349CF00F9D36E /* OIDServiceDiscoveryTests.m in Sources */, 0A987C0100000008000A0987 /* OIDAuthorizationServiceTests.m in Sources */, + 9BE107A4CCBF0579E2354D3B /* OIDAuthorizationServiceInstanceTests.m in Sources */, 343AAB8F1E8349CF00F9D36E /* OIDAuthStateTests.m in Sources */, 343AAB961E8349CF00F9D36E /* OIDTokenResponseTests.m in Sources */, 343AAB951E8349CF00F9D36E /* OIDTokenRequestTests.m in Sources */, diff --git a/CHANGELOG.md b/CHANGELOG.md index 8615f2de5..fa17ca1c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Discovery documents with a JSON `null` or a non-string value in an optional URL-valued field no longer raise `NSInvalidArgumentException`; the property reads as `nil`. The same value in a required URL field now fails with `OIDErrorCodeInvalidDiscoveryDocument`. An `authorization_endpoint` that does not parse now fails the document too, like the other required URL fields. - Deprecated CocoaPods distribution. AppAuth 3.0.0 is the last release published to CocoaPods; CocoaPods trunk becomes read-only on December 2, 2026. See [MIGRATING_FROM_COCOAPODS.md](MIGRATING_FROM_COCOAPODS.md) for moving to Swift Package Manager. Addresses issue #962. - Fix a crash (`NSInvalidArgumentException`) when a token, registration or device authorization endpoint answers with a JSON array instead of an object. The callback now gets an error. Addresses issue #987. +- Added instance-method counterparts of the `OIDAuthorizationService` class methods, and made `-init` public, so the service can be injected and faked in tests. +- Deprecated `OIDAuthorizationService.configuration`. It is never set and is always `nil`; use the request's `configuration` instead. It will be removed in 4.0. # 3.0.0 - BREAKING: Updates made to support Xcode 27. ([#972](https://github.com/openid/AppAuth-iOS/pull/972), [#973](https://github.com/openid/AppAuth-iOS/pull/973)) diff --git a/Sources/AppAuthCore/OIDAuthorizationService.h b/Sources/AppAuthCore/OIDAuthorizationService.h index c8fee5358..08524d3c0 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.h +++ b/Sources/AppAuthCore/OIDAuthorizationService.h @@ -82,18 +82,18 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg */ @interface OIDAuthorizationService : NSObject -/*! @brief The service's configuration. - @remarks Each authorization service is initialized with a configuration. This configuration - specifies how to connect to a particular OAuth provider. Clients should use separate - authorization service instances for each provider they wish to integrate with. - Configurations may be created manually, or via an OpenID Connect Discovery Document. +/*! @brief Unused, and always nil. + @discussion No initializer sets this, and nothing in AppAuth reads it. Each request carries + its own configuration (@c request.configuration). This property will be removed in + AppAuth 4.0. */ -@property(nonatomic, readonly) OIDServiceConfiguration *configuration; +@property(nonatomic, readonly) OIDServiceConfiguration *configuration + __deprecated_msg("Always nil. Use the configuration on the request instead."); -/*! @internal - @brief Unavailable. This class should not be initialized. +/*! @brief Creates a service whose instance methods call the matching class methods. + @discussion Tests can hand in a subclass that fakes the instance methods. */ -- (instancetype)init NS_UNAVAILABLE; +- (instancetype)init; /*! @brief Convenience method for creating an authorization service configuration from an OpenID Connect compliant issuer URL. @@ -165,6 +165,49 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg + (void)performRegistrationRequest:(OIDRegistrationRequest *)request completion:(OIDRegistrationCompletion)completion; +/*! @brief Instance counterpart of @c +presentAuthorizationRequest:externalUserAgent:callback:. + @param request The authorization request. + @param externalUserAgent Generic external user-agent that can present an authorization + request. + @param callback The method called when the request has completed or failed. + */ +- (id) + presentAuthorizationRequest:(OIDAuthorizationRequest *)request + externalUserAgent:(id)externalUserAgent + callback:(OIDAuthorizationCallback)callback; + +/*! @brief Instance counterpart of @c +presentEndSessionRequest:externalUserAgent:callback:. + @param request The end-session logout request. + @param externalUserAgent Generic external user-agent that can present user-agent requests. + @param callback The method called when the request has completed or failed. + */ +- (id) + presentEndSessionRequest:(OIDEndSessionRequest *)request + externalUserAgent:(id)externalUserAgent + callback:(OIDEndSessionCallback)callback; + +/*! @brief Instance counterpart of @c +performTokenRequest:callback:. + @param request The token request. + @param callback The method called when the request has completed or failed. + */ +- (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback; + +/*! @brief Instance counterpart of @c +performTokenRequest:originalAuthorizationResponse:callback:. + @param request The token request. + @param authorizationResponse The original authorization response related to this token request. + @param callback The method called when the request has completed or failed. + */ +- (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callback:(OIDTokenCallback)callback; + +/*! @brief Instance counterpart of @c +performRegistrationRequest:completion:. + @param request The registration request. + @param completion The method called when the request has completed or failed. + */ +- (void)performRegistrationRequest:(OIDRegistrationRequest *)request + completion:(OIDRegistrationCompletion)completion; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/AppAuthCore/OIDAuthorizationService.m b/Sources/AppAuthCore/OIDAuthorizationService.m index d99ade780..dce7467c7 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.m +++ b/Sources/AppAuthCore/OIDAuthorizationService.m @@ -864,6 +864,47 @@ + (void)performRegistrationRequest:(OIDRegistrationRequest *)request }] resume]; } +#pragma mark - Instance API + +- (instancetype)init { + return [super init]; +} + +- (id) + presentAuthorizationRequest:(OIDAuthorizationRequest *)request + externalUserAgent:(id)externalUserAgent + callback:(OIDAuthorizationCallback)callback { + return [OIDAuthorizationService presentAuthorizationRequest:request + externalUserAgent:externalUserAgent + callback:callback]; +} + +- (id) + presentEndSessionRequest:(OIDEndSessionRequest *)request + externalUserAgent:(id)externalUserAgent + callback:(OIDEndSessionCallback)callback { + return [OIDAuthorizationService presentEndSessionRequest:request + externalUserAgent:externalUserAgent + callback:callback]; +} + +- (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { + [self performTokenRequest:request originalAuthorizationResponse:nil callback:callback]; +} + +- (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callback:(OIDTokenCallback)callback { + [OIDAuthorizationService performTokenRequest:request + originalAuthorizationResponse:authorizationResponse + callback:callback]; +} + +- (void)performRegistrationRequest:(OIDRegistrationRequest *)request + completion:(OIDRegistrationCompletion)completion { + [OIDAuthorizationService performRegistrationRequest:request completion:completion]; +} + @end NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDAuthorizationServiceInstanceTests.m b/UnitTests/OIDAuthorizationServiceInstanceTests.m new file mode 100644 index 000000000..b00ad3180 --- /dev/null +++ b/UnitTests/OIDAuthorizationServiceInstanceTests.m @@ -0,0 +1,159 @@ +/*! @file OIDAuthorizationServiceInstanceTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2026 The AppAuth Authors. 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 "OIDAuthorizationRequestTests.h" +#import "OIDEndSessionRequestTests.h" +#import "OIDTokenRequestTests.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Sources/AppAuthCore/OIDAuthorizationResponse.h" +#import "Sources/AppAuthCore/OIDAuthorizationService.h" +#import "Sources/AppAuthCore/OIDEndSessionResponse.h" +#import "Sources/AppAuthCore/OIDError.h" +#import "Sources/AppAuthCore/OIDExternalUserAgent.h" +#import "Sources/AppAuthCore/OIDExternalUserAgentSession.h" +#import "Sources/AppAuthCore/OIDTokenRequest.h" +#import "Sources/AppAuthCore/OIDTokenResponse.h" +#endif + +// Ignore warnings about "Use of GNU statement expression extension" which is raised by our use of +// the XCTAssert___ macros. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief An external user agent that refuses to present anything. + */ +@interface OIDRefusingExternalUserAgent : NSObject +@end + +@implementation OIDRefusingExternalUserAgent + +- (BOOL)presentExternalUserAgentRequest:(id)request + session:(id)session { + return NO; +} + +- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (completion) { + completion(); + } +} + +@end + +/*! @brief Records the three-argument token request instead of performing it. + */ +@interface OIDTokenRecordingAuthorizationService : OIDAuthorizationService + +/*! @brief How many three-argument token requests were made. */ +@property(nonatomic, readonly) NSUInteger tokenRequestCount; + +/*! @brief The request passed to the most recent three-argument token request. */ +@property(nonatomic, readonly, nullable) OIDTokenRequest *lastTokenRequest; + +/*! @brief The authorization response passed to the most recent three-argument token request. */ +@property(nonatomic, readonly, nullable) + OIDAuthorizationResponse *lastOriginalAuthorizationResponse; + +@end + +@implementation OIDTokenRecordingAuthorizationService + +- (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(nullable OIDAuthorizationResponse *)authorizationResponse + callback:(OIDTokenCallback)callback { + _tokenRequestCount++; + _lastTokenRequest = request; + _lastOriginalAuthorizationResponse = authorizationResponse; +} + +@end + +/*! @brief Unit tests for the instance methods of @c OIDAuthorizationService. + */ +@interface OIDAuthorizationServiceInstanceTests : XCTestCase +@end + +@implementation OIDAuthorizationServiceInstanceTests + +/*! @brief A refused authorization request fails with @c OIDErrorCodeSafariOpenError. */ +- (void)testPresentAuthorizationRequestFailsWhenUserAgentRefuses { + OIDAuthorizationService *service = [[OIDAuthorizationService alloc] init]; + OIDRefusingExternalUserAgent *userAgent = [[OIDRefusingExternalUserAgent alloc] init]; + __block OIDAuthorizationResponse *receivedResponse; + __block NSError *receivedError; + id session = + [service presentAuthorizationRequest:[OIDAuthorizationRequestTests testInstance] + externalUserAgent:userAgent + callback:^(OIDAuthorizationResponse *_Nullable response, + NSError *_Nullable error) { + receivedResponse = response; + receivedError = error; + }]; + XCTAssertNotNil(session); + XCTAssertNil(receivedResponse); + XCTAssertEqualObjects(receivedError.domain, OIDGeneralErrorDomain); + XCTAssertEqual(receivedError.code, OIDErrorCodeSafariOpenError); +} + +/*! @brief A refused end session request fails with @c OIDErrorCodeSafariOpenError. */ +- (void)testPresentEndSessionRequestFailsWhenUserAgentRefuses { + OIDAuthorizationService *service = [[OIDAuthorizationService alloc] init]; + OIDRefusingExternalUserAgent *userAgent = [[OIDRefusingExternalUserAgent alloc] init]; + __block OIDEndSessionResponse *receivedResponse; + __block NSError *receivedError; + id session = + [service presentEndSessionRequest:[OIDEndSessionRequestTests testInstance] + externalUserAgent:userAgent + callback:^(OIDEndSessionResponse *_Nullable response, + NSError *_Nullable error) { + receivedResponse = response; + receivedError = error; + }]; + XCTAssertNotNil(session); + XCTAssertNil(receivedResponse); + XCTAssertEqualObjects(receivedError.domain, OIDGeneralErrorDomain); + XCTAssertEqual(receivedError.code, OIDErrorCodeSafariOpenError); +} + +/*! @brief The two-argument token request goes through the three-argument one, with no + authorization response. + */ +- (void)testTwoArgumentTokenRequestCallsThreeArgumentTokenRequest { + OIDTokenRecordingAuthorizationService *service = + [[OIDTokenRecordingAuthorizationService alloc] init]; + OIDTokenRequest *request = [OIDTokenRequestTests testInstance]; + [service performTokenRequest:request + callback:^(OIDTokenResponse *_Nullable response, + NSError *_Nullable error) {}]; + XCTAssertEqual(service.tokenRequestCount, 1u); + XCTAssertEqual(service.lastTokenRequest, request); + XCTAssertNil(service.lastOriginalAuthorizationResponse); +} + +@end + +NS_ASSUME_NONNULL_END + +#pragma GCC diagnostic pop