From 03de1a698f3eb29977dfd431253f1a436973b4b8 Mon Sep 17 00:00:00 2001 From: Thomas Carayol Date: Thu, 25 Mar 2021 17:45:51 +0100 Subject: [PATCH 1/2] RevocationEndpoint: parse endpoint from discovery and store it in the configuration. --- Source/AppAuthCore/OIDServiceConfiguration.h | 18 ++++++++++ Source/AppAuthCore/OIDServiceConfiguration.m | 38 ++++++++++++++++++-- Source/AppAuthCore/OIDServiceDiscovery.h | 12 +++++-- Source/AppAuthCore/OIDServiceDiscovery.m | 5 +++ UnitTests/OIDServiceDiscoveryTests.m | 3 ++ 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Source/AppAuthCore/OIDServiceConfiguration.h b/Source/AppAuthCore/OIDServiceConfiguration.h index a072a478f..92ee2df36 100644 --- a/Source/AppAuthCore/OIDServiceConfiguration.h +++ b/Source/AppAuthCore/OIDServiceConfiguration.h @@ -54,6 +54,10 @@ typedef void (^OIDServiceConfigurationCreated) */ @property(nonatomic, readonly, nullable) NSURL *endSessionEndpoint; +/*! @brief The token revocation endpoint URI. + */ +@property(nonatomic, readonly, nullable) NSURL *revocationEndpoint; + /*! @brief The discovery document. */ @property(nonatomic, readonly, nullable) OIDServiceDiscovery *discoveryDocument; @@ -108,6 +112,20 @@ typedef void (^OIDServiceConfigurationCreated) registrationEndpoint:(nullable NSURL *)registrationEndpoint endSessionEndpoint:(nullable NSURL *)endSessionEndpoint; +/*! @param authorizationEndpoint The authorization endpoint URI. + @param tokenEndpoint The token exchange and refresh endpoint URI. + @param issuer The OpenID Connect issuer. + @param registrationEndpoint The dynamic client registration endpoint URI. + @param endSessionEndpoint The end session endpoint (logout) URI. + @param revocationEndpoint The token revocation endpoint URI. + */ +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + issuer:(nullable NSURL *)issuer + registrationEndpoint:(nullable NSURL *)registrationEndpoint + endSessionEndpoint:(nullable NSURL *)endSessionEndpoint + revocationEndpoint:(nullable NSURL *)revocationEndpoint; + /*! @param discoveryDocument The discovery document from which to extract the required OAuth configuration. */ diff --git a/Source/AppAuthCore/OIDServiceConfiguration.m b/Source/AppAuthCore/OIDServiceConfiguration.m index ca48a8c33..cf319bc1e 100644 --- a/Source/AppAuthCore/OIDServiceConfiguration.m +++ b/Source/AppAuthCore/OIDServiceConfiguration.m @@ -42,6 +42,10 @@ */ static NSString *const kEndSessionEndpointKey = @"endSessionEndpoint"; +/*! @brief The key for the @c revocationEndpoint property. + */ +static NSString *const kRevocationEndpointKey = @"revocationEndpoint"; + /*! @brief The key for the @c discoveryDocument property. */ static NSString *const kDiscoveryDocumentKey = @"discoveryDocument"; @@ -55,6 +59,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint issuer:(nullable NSURL *)issuer registrationEndpoint:(nullable NSURL *)registrationEndpoint endSessionEndpoint:(nullable NSURL *)endSessionEndpoint + revocationEndpoint:(nullable NSURL *)revocationEndpoint discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument NS_DESIGNATED_INITIALIZER; @@ -72,7 +77,8 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint tokenEndpoint:(NSURL *)tokenEndpoint issuer:(nullable NSURL *)issuer registrationEndpoint:(nullable NSURL *)registrationEndpoint - endSessionEndpoint:(nullable OIDServiceDiscovery *)endSessionEndpoint + endSessionEndpoint:(nullable NSURL *)endSessionEndpoint + revocationEndpoint:(nullable NSURL *)revocationEndpoint discoveryDocument:(nullable OIDServiceDiscovery *)discoveryDocument { self = [super init]; @@ -82,6 +88,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint _issuer = [issuer copy]; _registrationEndpoint = [registrationEndpoint copy]; _endSessionEndpoint = [endSessionEndpoint copy]; + _revocationEndpoint = [revocationEndpoint copy]; _discoveryDocument = [discoveryDocument copy]; } return self; @@ -94,6 +101,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint issuer:nil registrationEndpoint:nil endSessionEndpoint:nil + revocationEndpoint:nil discoveryDocument:nil]; } @@ -105,6 +113,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint issuer:nil registrationEndpoint:registrationEndpoint endSessionEndpoint:nil + revocationEndpoint:nil discoveryDocument:nil]; } @@ -116,6 +125,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint issuer:issuer registrationEndpoint:nil endSessionEndpoint:nil + revocationEndpoint:nil discoveryDocument:nil]; } @@ -128,6 +138,7 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint issuer:issuer registrationEndpoint:registrationEndpoint endSessionEndpoint:nil + revocationEndpoint:nil discoveryDocument:nil]; } @@ -141,6 +152,22 @@ - (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint issuer:issuer registrationEndpoint:registrationEndpoint endSessionEndpoint:endSessionEndpoint + revocationEndpoint:nil + discoveryDocument:nil]; +} + +- (instancetype)initWithAuthorizationEndpoint:(NSURL *)authorizationEndpoint + tokenEndpoint:(NSURL *)tokenEndpoint + issuer:(nullable NSURL *)issuer + registrationEndpoint:(nullable NSURL *)registrationEndpoint + endSessionEndpoint:(nullable NSURL *)endSessionEndpoint + revocationEndpoint:(nullable NSURL *)revocationEndpoint { + return [self initWithAuthorizationEndpoint:authorizationEndpoint + tokenEndpoint:tokenEndpoint + issuer:issuer + registrationEndpoint:registrationEndpoint + endSessionEndpoint:endSessionEndpoint + revocationEndpoint:revocationEndpoint discoveryDocument:nil]; } @@ -150,6 +177,7 @@ - (instancetype)initWithDiscoveryDocument:(OIDServiceDiscovery *) discoveryDocum issuer:discoveryDocument.issuer registrationEndpoint:discoveryDocument.registrationEndpoint endSessionEndpoint:discoveryDocument.endSessionEndpoint + revocationEndpoint:discoveryDocument.revocationEndpoint discoveryDocument:discoveryDocument]; } @@ -180,6 +208,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { forKey:kRegistrationEndpointKey]; NSURL *endSessionEndpoint = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kEndSessionEndpointKey]; + NSURL *revocationEndpoint = [aDecoder decodeObjectOfClass:[NSURL class] + forKey:kRevocationEndpointKey]; // We don't accept nil authorizationEndpoints or tokenEndpoints. if (!authorizationEndpoint || !tokenEndpoint) { return nil; @@ -193,6 +223,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { issuer:issuer registrationEndpoint:registrationEndpoint endSessionEndpoint:endSessionEndpoint + revocationEndpoint:revocationEndpoint discoveryDocument:discoveryDocument]; } @@ -203,6 +234,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_registrationEndpoint forKey:kRegistrationEndpointKey]; [aCoder encodeObject:_discoveryDocument forKey:kDiscoveryDocumentKey]; [aCoder encodeObject:_endSessionEndpoint forKey:kEndSessionEndpointKey]; + [aCoder encodeObject:_revocationEndpoint forKey:kRevocationEndpointKey]; } #pragma mark - description @@ -210,11 +242,13 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { - (NSString *)description { return [NSString stringWithFormat: @"OIDServiceConfiguration authorizationEndpoint: %@, tokenEndpoint: %@, " - "registrationEndpoint: %@, endSessionEndpoint: %@, discoveryDocument: [%@]", + "registrationEndpoint: %@, endSessionEndpoint: %@, revocationEndpoint: %@, " + "discoveryDocument: [%@]", _authorizationEndpoint, _tokenEndpoint, _registrationEndpoint, _endSessionEndpoint, + _revocationEndpoint, _discoveryDocument]; } diff --git a/Source/AppAuthCore/OIDServiceDiscovery.h b/Source/AppAuthCore/OIDServiceDiscovery.h index 3998fa12b..f2b1419b4 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.h +++ b/Source/AppAuthCore/OIDServiceDiscovery.h @@ -84,12 +84,18 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly, nullable) NSURL *registrationEndpoint; -/* @brief OPTIONAL. URL of the OP's RP-Initiated Logout endpoint. - @remarks end_session_endpoint - @seealso http://openid.net/specs/openid-connect-session-1_0.html#OPMetadata +/*! @brief OPTIONAL. URL of the OP's RP-Initiated Logout endpoint. + @remarks end_session_endpoint + @seealso http://openid.net/specs/openid-connect-session-1_0.html#OPMetadata */ @property(nonatomic, readonly, nullable) NSURL *endSessionEndpoint; +/*! @brief OPTIONAL. URL of the OP's Token Revocation endpoint. + @remarks revocation_endpoint + @seealso https://tools.ietf.org/html/rfc7009 + */ +@property(nonatomic, readonly, nullable) NSURL *revocationEndpoint; + /*! @brief RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST support the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used, although those diff --git a/Source/AppAuthCore/OIDServiceDiscovery.m b/Source/AppAuthCore/OIDServiceDiscovery.m index 739275251..ffb6704ff 100644 --- a/Source/AppAuthCore/OIDServiceDiscovery.m +++ b/Source/AppAuthCore/OIDServiceDiscovery.m @@ -32,6 +32,7 @@ static NSString *const kJWKSURLKey = @"jwks_uri"; static NSString *const kRegistrationEndpointKey = @"registration_endpoint"; static NSString *const kEndSessionEndpointKey = @"end_session_endpoint"; +static NSString *const kRevocationEndpointKey = @"revocation_endpoint"; static NSString *const kScopesSupportedKey = @"scopes_supported"; static NSString *const kResponseTypesSupportedKey = @"response_types_supported"; static NSString *const kResponseModesSupportedKey = @"response_modes_supported"; @@ -242,6 +243,10 @@ - (nullable NSURL *)endSessionEndpoint { return [NSURL URLWithString:_discoveryDictionary[kEndSessionEndpointKey]]; } +- (nullable NSURL *)revocationEndpoint { + return [NSURL URLWithString:_discoveryDictionary[kRevocationEndpointKey]]; +} + - (nullable NSArray *)scopesSupported { return _discoveryDictionary[kScopesSupportedKey]; } diff --git a/UnitTests/OIDServiceDiscoveryTests.m b/UnitTests/OIDServiceDiscoveryTests.m index 17d6cd19e..e52edcae4 100644 --- a/UnitTests/OIDServiceDiscoveryTests.m +++ b/UnitTests/OIDServiceDiscoveryTests.m @@ -45,6 +45,7 @@ static NSString *const kJWKSURLKey = @"jwks_uri"; static NSString *const kRegistrationEndpointKey = @"registration_endpoint"; static NSString *const kEndSessionEndpointKey = @"end_session_endpoint"; +static NSString *const kRevocationEndpointKey = @"revocation_endpoint"; static NSString *const kScopesSupportedKey = @"scopes_supported"; static NSString *const kResponseTypesSupportedKey = @"response_types_supported"; static NSString *const kResponseModesSupportedKey = @"response_modes_supported"; @@ -110,6 +111,7 @@ + (NSDictionary *)completeServiceDiscoveryDictionary { kJWKSURLKey : @"http://www.example.com/jwks", kRegistrationEndpointKey : @"Registration Endpoint", kEndSessionEndpointKey : @"https://www.example.com/logout", + kRevocationEndpointKey : @"https://www.example.com/revoke", kScopesSupportedKey : @"Scopes Supported", kResponseTypesSupportedKey : @"Response Types Supported", kResponseModesSupportedKey : @"Response Modes Supported", @@ -508,6 +510,7 @@ - (void)testField_##_field_ { TestURLFieldBackedBy(jwksURL, kJWKSURLKey, kTestURL) TestURLFieldBackedBy(registrationEndpoint, kRegistrationEndpointKey, kTestURL) TestURLFieldBackedBy(endSessionEndpoint, kEndSessionEndpointKey, kTestURL) +TestURLFieldBackedBy(revocationEndpoint, kRevocationEndpointKey, kTestURL) TestFieldBackedBy(scopesSupported, kScopesSupportedKey, @"Scopes Supported") TestFieldBackedBy(responseTypesSupported, kResponseTypesSupportedKey, @"Response Types Supported") TestFieldBackedBy(responseModesSupported, kResponseModesSupportedKey, @"Response Modes Supported") From e6529a7cd3a315b46545cd832396b4aee3e62584 Mon Sep 17 00:00:00 2001 From: Thomas Carayol Date: Tue, 30 Mar 2021 11:46:30 +0200 Subject: [PATCH 2/2] RevocationEndpoint: add RevokeToken request & response --- AppAuth.xcodeproj/project.pbxproj | 120 +++++++++++ Source/AppAuthCore/OIDError.h | 5 + Source/AppAuthCore/OIDErrorUtilities.m | 1 + Source/AppAuthCore/OIDRevokeTokenRequest.h | 89 ++++++++ Source/AppAuthCore/OIDRevokeTokenRequest.m | 203 +++++++++++++++++++ Source/AppAuthCore/OIDRevokeTokenResponse.h | 47 +++++ Source/AppAuthCore/OIDRevokeTokenResponse.m | 78 +++++++ Source/AppAuthTV/OIDTVAuthorizationService.h | 21 ++ Source/AppAuthTV/OIDTVAuthorizationService.m | 90 ++++++++ UnitTests/OIDRevokeTokenRequestTests.h | 32 +++ UnitTests/OIDRevokeTokenRequestTests.m | 124 +++++++++++ UnitTests/OIDRevokeTokenResponseTests.h | 35 ++++ UnitTests/OIDRevokeTokenResponseTests.m | 72 +++++++ 13 files changed, 917 insertions(+) create mode 100644 Source/AppAuthCore/OIDRevokeTokenRequest.h create mode 100644 Source/AppAuthCore/OIDRevokeTokenRequest.m create mode 100644 Source/AppAuthCore/OIDRevokeTokenResponse.h create mode 100644 Source/AppAuthCore/OIDRevokeTokenResponse.m create mode 100644 UnitTests/OIDRevokeTokenRequestTests.h create mode 100644 UnitTests/OIDRevokeTokenRequestTests.m create mode 100644 UnitTests/OIDRevokeTokenResponseTests.h create mode 100644 UnitTests/OIDRevokeTokenResponseTests.m diff --git a/AppAuth.xcodeproj/project.pbxproj b/AppAuth.xcodeproj/project.pbxproj index 27449610b..f82685900 100644 --- a/AppAuth.xcodeproj/project.pbxproj +++ b/AppAuth.xcodeproj/project.pbxproj @@ -160,6 +160,58 @@ 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */; }; 2DEB066124CF5CE000DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; 2DEB066224CF5CFB00DF47E7 /* OIDTVTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */; }; + 30BF67CD26130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67CE26130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67CF26130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67D026130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67D126130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67D226130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67D326130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */; }; + 30BF67D426130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67D526130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67D626130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67D726130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67D826130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67D926130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67DA26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67DB26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67DC26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67DD26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF67DE26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */; }; + 30BF680826130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680926130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680A26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680B26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680C26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680D26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680E26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */; }; + 30BF680F26130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681026130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681126130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681226130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681326130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681426130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681526130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681626130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681726130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681826130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF681926130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */; }; + 30BF6900261329BF007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF6914261329BF007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF6961261329C2007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF699B261329C4007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF69D5261329C6007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF69FC261329C7007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF6A23261329C8007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF6A5D261329CA007756F9 /* OIDRevokeTokenRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */; }; + 30BF6AAC26132A4D007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6AC026132A4E007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6AC126132A4F007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6AFB26132A51007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6B0F26132A53007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6B3626132A54007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6B4A26132A55007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; + 30BF6B5E26132A57007756F9 /* OIDRevokeTokenResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */; }; 340DAE571D5821A100EC285B /* OIDAuthorizationService+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */; }; 340DAE581D5821A100EC285B /* OIDExternalUserAgentMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE281D581FE700EC285B /* OIDExternalUserAgentMac.m */; }; 340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */ = {isa = PBXBuildFile; fileRef = 340DAE2A1D581FE700EC285B /* OIDAuthState+Mac.m */; }; @@ -781,6 +833,14 @@ 2DEB065524CA1D9300DF47E7 /* OIDTVTokenRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDTVTokenRequest.m; sourceTree = ""; }; 2DEB065F24CF5CDF00DF47E7 /* OIDTVTokenRequestTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDTVTokenRequestTests.h; sourceTree = ""; }; 2DEB066024CF5CE000DF47E7 /* OIDTVTokenRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDTVTokenRequestTests.m; sourceTree = ""; }; + 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRevokeTokenRequest.h; sourceTree = ""; }; + 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDRevokeTokenRequest.m; sourceTree = ""; }; + 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRevokeTokenResponse.h; sourceTree = ""; }; + 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDRevokeTokenResponse.m; sourceTree = ""; }; + 30BF6840261329A6007756F9 /* OIDRevokeTokenRequestTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRevokeTokenRequestTests.h; sourceTree = ""; }; + 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDRevokeTokenRequestTests.m; sourceTree = ""; }; + 30BF6A7126132A49007756F9 /* OIDRevokeTokenResponseTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OIDRevokeTokenResponseTests.h; sourceTree = ""; }; + 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OIDRevokeTokenResponseTests.m; sourceTree = ""; }; 340DAE251D581FE700EC285B /* OIDAuthorizationService+Mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OIDAuthorizationService+Mac.h"; sourceTree = ""; }; 340DAE261D581FE700EC285B /* OIDAuthorizationService+Mac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "OIDAuthorizationService+Mac.m"; sourceTree = ""; }; 340DAE271D581FE700EC285B /* OIDExternalUserAgentMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDExternalUserAgentMac.h; sourceTree = ""; }; @@ -1235,6 +1295,10 @@ 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */, 34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */, 0396974C1FA827AD003D1FB2 /* OIDURLSessionProviderTests.m */, + 30BF6840261329A6007756F9 /* OIDRevokeTokenRequestTests.h */, + 30BF6841261329A6007756F9 /* OIDRevokeTokenRequestTests.m */, + 30BF6A7126132A49007756F9 /* OIDRevokeTokenResponseTests.h */, + 30BF6A7226132A49007756F9 /* OIDRevokeTokenResponseTests.m */, ); path = UnitTests; sourceTree = ""; @@ -1333,6 +1397,10 @@ 341741D81C5D8243000EF209 /* OIDURLQueryComponent.m */, 039697441FA8258D003D1FB2 /* OIDURLSessionProvider.h */, 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */, + 30BF67CA26130A6D007756F9 /* OIDRevokeTokenRequest.h */, + 30BF67CB26130A6D007756F9 /* OIDRevokeTokenRequest.m */, + 30BF680526130B0A007756F9 /* OIDRevokeTokenResponse.h */, + 30BF680626130B0A007756F9 /* OIDRevokeTokenResponse.m */, ); path = AppAuthCore; sourceTree = ""; @@ -1370,6 +1438,7 @@ files = ( 2D0BB86C249D5BAF005BA653 /* AppAuthEnterpriseUserAgent.h in Headers */, 2D91B842249053190005B197 /* OIDExternalUserAgentIOSCustomBrowser.h in Headers */, + 30BF67D326130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, 2D91B83E249053190005B197 /* OIDAuthorizationResponse.h in Headers */, 2D91B83F249053190005B197 /* OIDScopes.h in Headers */, 2D91B840249053190005B197 /* OIDExternalUserAgentRequest.h in Headers */, @@ -1379,6 +1448,7 @@ 2D91B847249053190005B197 /* OIDTokenRequest.h in Headers */, 2D91B848249053190005B197 /* OIDScopeUtilities.h in Headers */, 2D91B849249053190005B197 /* OIDTokenResponse.h in Headers */, + 30BF680E26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 2D91B84A249053190005B197 /* OIDEndSessionResponse.h in Headers */, 2D91B84B249053190005B197 /* OIDServiceDiscovery.h in Headers */, 2D91B84C249053190005B197 /* OIDGrantTypes.h in Headers */, @@ -1409,6 +1479,7 @@ files = ( 2D93861B24B38810009A12D7 /* OIDAuthorizationResponse.h in Headers */, 2D93861F24B3881B009A12D7 /* OIDAuthState.h in Headers */, + 30BF67D226130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, 2D93863724B38827009A12D7 /* OIDGrantTypes.h in Headers */, 2D93862F24B38826009A12D7 /* OIDEndSessionResponse.h in Headers */, 2D93863924B38827009A12D7 /* OIDIDToken.h in Headers */, @@ -1418,6 +1489,7 @@ 2D93864124B38828009A12D7 /* OIDServiceConfiguration.h in Headers */, 2D93862124B3881B009A12D7 /* OIDAuthStateChangeDelegate.h in Headers */, 2D93862A24B3881C009A12D7 /* OIDExternalUserAgentSession.h in Headers */, + 30BF680D26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 2D93863324B38826009A12D7 /* OIDRegistrationResponse.h in Headers */, 2D93864324B38828009A12D7 /* OIDServiceDiscovery.h in Headers */, 2D93862624B3881C009A12D7 /* OIDError.h in Headers */, @@ -1470,6 +1542,7 @@ 342F42AE2177B1FC00574F24 /* OIDGrantTypes.h in Headers */, 342F42AF2177B1FC00574F24 /* OIDURLSessionProvider.h in Headers */, 342F42B12177B1FC00574F24 /* OIDRegistrationResponse.h in Headers */, + 30BF680C26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 342F42B22177B1FC00574F24 /* OIDExternalUserAgent.h in Headers */, 342F42B32177B1FC00574F24 /* OIDExternalUserAgentSession.h in Headers */, 342F42B42177B1FC00574F24 /* OIDServiceConfiguration.h in Headers */, @@ -1482,6 +1555,7 @@ 342F42BC2177B1FC00574F24 /* OIDTokenUtilities.h in Headers */, 3489709C2178F40600ABEED4 /* AppAuthCore.h in Headers */, 342F42BD2177B1FC00574F24 /* OIDError.h in Headers */, + 30BF67D126130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1497,6 +1571,7 @@ F9A7082E2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.h in Headers */, 34A663291E871DD40060B664 /* OIDIDToken.h in Headers */, 343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */, + 30BF680826130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */, 343AAAF41E83499000F9D36E /* OIDScopeUtilities.h in Headers */, 343AAAF81E83499000F9D36E /* OIDTokenResponse.h in Headers */, @@ -1520,6 +1595,7 @@ 343AAAF91E83499000F9D36E /* OIDTokenUtilities.h in Headers */, 343AAAEC1E83499000F9D36E /* OIDError.h in Headers */, A6DEABAB2018E5C50022AC32 /* OIDExternalUserAgentIOS.h in Headers */, + 30BF67CD26130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1542,6 +1618,7 @@ 343AAB0A1E83499100F9D36E /* OIDResponseTypes.h in Headers */, A6DEAB9C2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAB0B1E83499100F9D36E /* OIDScopes.h in Headers */, + 30BF680926130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */, A6DEABB12018ECE80022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, @@ -1554,6 +1631,7 @@ 343AAB011E83499100F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAAFB1E83499100F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB051E83499100F9D36E /* OIDErrorUtilities.h in Headers */, + 30BF67CE26130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1576,6 +1654,7 @@ 343AAB221E83499200F9D36E /* OIDResponseTypes.h in Headers */, A6DEAB9D2018E4AD0022AC32 /* OIDExternalUserAgent.h in Headers */, 343AAB231E83499200F9D36E /* OIDScopes.h in Headers */, + 30BF680A26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */, A6DEABB22018ECE90022AC32 /* OIDEndSessionRequest.h in Headers */, 343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */, @@ -1588,6 +1667,7 @@ 343AAB191E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */, 343AAB131E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB1D1E83499200F9D36E /* OIDErrorUtilities.h in Headers */, + 30BF67CF26130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1597,6 +1677,7 @@ files = ( 343AAB9D1E834A8A00F9D36E /* AppAuth.h in Headers */, A6DEABA72018E4BA0022AC32 /* OIDExternalUserAgentSession.h in Headers */, + 30BF67D026130A6D007756F9 /* OIDRevokeTokenRequest.h in Headers */, 343AAADF1E83494400F9D36E /* OIDExternalUserAgentMac.h in Headers */, 343AAAE01E83494400F9D36E /* OIDAuthState+Mac.h in Headers */, 343AAADD1E83494400F9D36E /* OIDRedirectHTTPHandler.h in Headers */, @@ -1606,6 +1687,7 @@ 55A094D220DFBB12000045D1 /* OIDURLSessionProvider.h in Headers */, 343AAB411E83499200F9D36E /* OIDTokenUtilities.h in Headers */, A6DEABA32018E4B70022AC32 /* OIDExternalUserAgentRequest.h in Headers */, + 30BF680B26130B0A007756F9 /* OIDRevokeTokenResponse.h in Headers */, 343AAB371E83499200F9D36E /* OIDRegistrationResponse.h in Headers */, 343AAB2B1E83499200F9D36E /* OIDAuthorizationRequest.h in Headers */, 343AAB3B1E83499200F9D36E /* OIDScopes.h in Headers */, @@ -2193,6 +2275,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 30BF6A5D261329CA007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, 2D81120C24C103F300984DA7 /* OIDScopesTests.m in Sources */, 2DA8D82624C6190400FDFB34 /* OIDTVAuthorizationResponseTests.m in Sources */, 2D81121224C103F300984DA7 /* OIDURLQueryComponentTests.m in Sources */, @@ -2209,6 +2292,7 @@ 2D81120724C103CC00984DA7 /* OIDAuthorizationResponseTests.m in Sources */, 2D81121424C103F300984DA7 /* OIDRegistrationRequestTests.m in Sources */, 2D81120924C103F200984DA7 /* OIDGrantTypesTests.m in Sources */, + 30BF6B5E26132A57007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, 2D81121624C103F300984DA7 /* OIDRPProfileCode.m in Sources */, 2D81120624C103C800984DA7 /* OIDAuthorizationRequestTests.m in Sources */, 2D81121124C103F300984DA7 /* OIDTokenUtilitiesTests.m in Sources */, @@ -2226,6 +2310,7 @@ 2D91B824249053190005B197 /* OIDAuthState.m in Sources */, 2D91B826249053190005B197 /* OIDTokenResponse.m in Sources */, 2D91B827249053190005B197 /* OIDErrorUtilities.m in Sources */, + 30BF681926130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 2D91B828249053190005B197 /* OIDURLQueryComponent.m in Sources */, 2D91B829249053190005B197 /* OIDAuthorizationRequest.m in Sources */, 2D91B82A249053190005B197 /* OIDAuthorizationService.m in Sources */, @@ -2233,6 +2318,7 @@ 2D91B82C249053190005B197 /* OIDTokenUtilities.m in Sources */, 2D91B82D249053190005B197 /* OIDServiceDiscovery.m in Sources */, 2D91B82E249053190005B197 /* OIDTokenRequest.m in Sources */, + 30BF67DE26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 2D91B830249053190005B197 /* OIDEndSessionRequest.m in Sources */, 2D91B832249053190005B197 /* OIDServiceConfiguration.m in Sources */, 2D91B833249053190005B197 /* OIDRegistrationResponse.m in Sources */, @@ -2262,6 +2348,7 @@ 2D93863824B38827009A12D7 /* OIDGrantTypes.m in Sources */, 2D93864824B38828009A12D7 /* OIDTokenResponse.m in Sources */, 2D93863624B38827009A12D7 /* OIDRegistrationRequest.m in Sources */, + 30BF681826130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 2D93863224B38826009A12D7 /* OIDEndSessionRequest.m in Sources */, 2D93864424B38828009A12D7 /* OIDServiceDiscovery.m in Sources */, 2DEB065724CA1D9300DF47E7 /* OIDTVTokenRequest.m in Sources */, @@ -2269,6 +2356,7 @@ 2D93863A24B38827009A12D7 /* OIDIDToken.m in Sources */, 2D93864F24B38840009A12D7 /* OIDTVAuthorizationRequest.m in Sources */, 2D93864624B38828009A12D7 /* OIDTokenRequest.m in Sources */, + 30BF67DD26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 2D93865024B38840009A12D7 /* OIDTVAuthorizationResponse.m in Sources */, 2D93864224B38828009A12D7 /* OIDServiceConfiguration.m in Sources */, 2D93864A24B38829009A12D7 /* OIDTokenUtilities.m in Sources */, @@ -2298,6 +2386,7 @@ 340DAEBC1D582AF100EC285B /* OIDRedirectHTTPHandler.m in Sources */, 340DAE5D1D5821AB00EC285B /* OIDAuthState.m in Sources */, 341310CF1E6F944B00D5DEE5 /* OIDTokenUtilities.m in Sources */, + 30BF681026130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 341310C71E6F944B00D5DEE5 /* OIDGrantTypes.m in Sources */, 341310C51E6F944B00D5DEE5 /* OIDRegistrationResponse.m in Sources */, 341310CB1E6F944B00D5DEE5 /* OIDServiceConfiguration.m in Sources */, @@ -2305,6 +2394,7 @@ 341310CC1E6F944B00D5DEE5 /* OIDServiceDiscovery.m in Sources */, 341310CA1E6F944B00D5DEE5 /* OIDScopeUtilities.m in Sources */, 340DAE5C1D5821AB00EC285B /* OIDAuthorizationService.m in Sources */, + 30BF67D526130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, CF37C06F1F1FC21A00662E41 /* OIDEndSessionRequest.m in Sources */, A6DEAB832017A7030022AC32 /* OIDEndSessionResponse.m in Sources */, 341310CD1E6F944B00D5DEE5 /* OIDTokenRequest.m in Sources */, @@ -2330,8 +2420,10 @@ A6DEABAA2018E5B50022AC32 /* OIDExternalUserAgentIOS.m in Sources */, 341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */, 34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */, + 30BF680F26130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, CF6431F41F228A980075B6B5 /* OIDEndSessionResponse.m in Sources */, 341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */, + 30BF67D426130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */, 60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */, 341741E81C5D8243000EF209 /* OIDTokenRequest.m in Sources */, @@ -2362,6 +2454,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 30BF6900261329BF007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, 34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */, 341742211C5D82D3000EF209 /* OIDURLQueryComponentTests.m in Sources */, 341742201C5D82D3000EF209 /* OIDTokenResponseTests.m in Sources */, @@ -2378,6 +2471,7 @@ 60140F831DE43BAF00DA0DC3 /* OIDRegistrationRequestTests.m in Sources */, A5EEF29A20D821960044F470 /* OIDTokenUtilitiesTests.m in Sources */, 60140F861DE43CC700DA0DC3 /* OIDRegistrationResponseTests.m in Sources */, + 30BF6AAC26132A4D007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, 341742191C5D82D3000EF209 /* OIDAuthStateTests.m in Sources */, 3417421D1C5D82D3000EF209 /* OIDServiceConfigurationTests.m in Sources */, 3417421C1C5D82D3000EF209 /* OIDScopesTests.m in Sources */, @@ -2397,6 +2491,7 @@ 341AA5071E7F3A9B00FCA5C6 /* OIDAuthStateTests.m in Sources */, 341AA50E1E7F3A9B00FCA5C6 /* OIDTokenResponseTests.m in Sources */, 341AA50D1E7F3A9B00FCA5C6 /* OIDTokenRequestTests.m in Sources */, + 30BF6914261329BF007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, 341AA5091E7F3A9B00FCA5C6 /* OIDResponseTypesTests.m in Sources */, 341AA4D91E7F393500FCA5C6 /* OIDAuthorizationRequestTests.m in Sources */, 34A6638C1E8865090060B664 /* OIDRPProfileCode.m in Sources */, @@ -2404,6 +2499,7 @@ 341AA5111E7F3A9B00FCA5C6 /* OIDRegistrationResponseTests.m in Sources */, 341AA5081E7F3A9B00FCA5C6 /* OIDGrantTypesTests.m in Sources */, 341AA5061E7F3A9B00FCA5C6 /* OIDAuthorizationResponseTests.m in Sources */, + 30BF6AC026132A4E007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2420,6 +2516,7 @@ 341AA4FA1E7F3A9400FCA5C6 /* OIDAuthStateTests.m in Sources */, 341AA5011E7F3A9400FCA5C6 /* OIDTokenResponseTests.m in Sources */, 341AA5001E7F3A9400FCA5C6 /* OIDTokenRequestTests.m in Sources */, + 30BF6961261329C2007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, 341AA4FC1E7F3A9400FCA5C6 /* OIDResponseTypesTests.m in Sources */, 341AA4F81E7F3A3000FCA5C6 /* OIDAuthorizationRequestTests.m in Sources */, 34A6638D1E8865090060B664 /* OIDRPProfileCode.m in Sources */, @@ -2427,6 +2524,7 @@ 341AA5041E7F3A9400FCA5C6 /* OIDRegistrationResponseTests.m in Sources */, 341AA4FB1E7F3A9400FCA5C6 /* OIDGrantTypesTests.m in Sources */, 341AA4F91E7F3A9400FCA5C6 /* OIDAuthorizationResponseTests.m in Sources */, + 30BF6AC126132A4F007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2437,8 +2535,10 @@ 341310DA1E6F944D00D5DEE5 /* OIDScopes.m in Sources */, 341310D51E6F944D00D5DEE5 /* OIDFieldMapping.m in Sources */, 341E709B1DE18796004353C1 /* OIDAuthState.m in Sources */, + 30BF681126130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 341E70981DE18796004353C1 /* OIDAuthorizationRequest.m in Sources */, 34AF73681FB4E4B10022335F /* OIDURLSessionProvider.m in Sources */, + 30BF67D626130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 341310D71E6F944D00D5DEE5 /* OIDRegistrationRequest.m in Sources */, 341310DD1E6F944D00D5DEE5 /* OIDServiceDiscovery.m in Sources */, 341E70991DE18796004353C1 /* OIDAuthorizationResponse.m in Sources */, @@ -2480,9 +2580,11 @@ 342F42902177B1FC00574F24 /* OIDClientMetadataParameters.m in Sources */, 06C19E9D22B474AD00C19CE1 /* OIDEndSessionRequest.m in Sources */, 342F42912177B1FC00574F24 /* OIDTokenUtilities.m in Sources */, + 30BF681726130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 342F42922177B1FC00574F24 /* OIDServiceDiscovery.m in Sources */, 342F42932177B1FC00574F24 /* OIDTokenRequest.m in Sources */, 342F42962177B1FC00574F24 /* OIDServiceConfiguration.m in Sources */, + 30BF67DC26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 342F42972177B1FC00574F24 /* OIDRegistrationResponse.m in Sources */, 342F42982177B1FC00574F24 /* OIDURLSessionProvider.m in Sources */, 342F42992177B1FC00574F24 /* OIDScopes.m in Sources */, @@ -2503,8 +2605,10 @@ A6DEABAF2018E5D80022AC32 /* OIDExternalUserAgentIOS.m in Sources */, F9A7082F2355ED74004B3E6D /* OIDExternalUserAgentCatalyst.m in Sources */, 343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */, + 30BF681326130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 34A663311E871DD40060B664 /* OIDIDToken.m in Sources */, A6DEAB862017A7060022AC32 /* OIDEndSessionResponse.m in Sources */, + 30BF67D826130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */, 343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */, 343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */, @@ -2549,6 +2653,8 @@ 343AAA731E8346B400F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAA761E8346B400F9D36E /* OIDGrantTypesTests.m in Sources */, 34A6638E1E8865090060B664 /* OIDRPProfileCode.m in Sources */, + 30BF699B261329C4007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, + 30BF6AFB26132A51007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, A6CEB11D2007E49F009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAA741E8346B400F9D36E /* OIDAuthorizationResponseTests.m in Sources */, A5EEF29720D821120044F470 /* OIDTokenUtilitiesTests.m in Sources */, @@ -2570,9 +2676,11 @@ 34A663321E871DD40060B664 /* OIDIDToken.m in Sources */, 343AAB781E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB731E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, + 30BF681426130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 343AAB701E8349B000F9D36E /* OIDError.m in Sources */, A6DEAB8B2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB7B1E8349B000F9D36E /* OIDTokenRequest.m in Sources */, + 30BF67D926130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 343AAB7C1E8349B000F9D36E /* OIDTokenResponse.m in Sources */, A6DEAB872017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB791E8349B000F9D36E /* OIDServiceConfiguration.m in Sources */, @@ -2601,9 +2709,11 @@ 34A663331E871DD40060B664 /* OIDIDToken.m in Sources */, 343AAB641E8349B000F9D36E /* OIDScopeUtilities.m in Sources */, 343AAB5F1E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */, + 30BF681526130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 343AAB5C1E8349B000F9D36E /* OIDError.m in Sources */, A6DEAB8C2017A7160022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB671E8349B000F9D36E /* OIDTokenRequest.m in Sources */, + 30BF67DA26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 343AAB681E8349B000F9D36E /* OIDTokenResponse.m in Sources */, A6DEAB882017A70B0022AC32 /* OIDEndSessionResponse.m in Sources */, 343AAB651E8349B000F9D36E /* OIDServiceConfiguration.m in Sources */, @@ -2635,6 +2745,8 @@ 343AAB7F1E8349CE00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB821E8349CE00F9D36E /* OIDGrantTypesTests.m in Sources */, 34A6638F1E8865090060B664 /* OIDRPProfileCode.m in Sources */, + 30BF69D5261329C6007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, + 30BF6B0F26132A53007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, A6CEB11E2007E4A1009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAB801E8349CE00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, A5EEF29820D8211A0044F470 /* OIDTokenUtilitiesTests.m in Sources */, @@ -2652,6 +2764,7 @@ A6DEAB8D2017A7170022AC32 /* OIDEndSessionRequest.m in Sources */, 343AAB451E8349AF00F9D36E /* OIDAuthorizationService.m in Sources */, 343AAB431E8349AF00F9D36E /* OIDAuthorizationRequest.m in Sources */, + 30BF681626130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 343AAAD91E83493D00F9D36E /* OIDRedirectHTTPHandler.m in Sources */, 343AAB551E8349AF00F9D36E /* OIDTokenUtilities.m in Sources */, 343AAB4D1E8349AF00F9D36E /* OIDGrantTypes.m in Sources */, @@ -2659,6 +2772,7 @@ 343AAB511E8349AF00F9D36E /* OIDServiceConfiguration.m in Sources */, 343AAB441E8349AF00F9D36E /* OIDAuthorizationResponse.m in Sources */, 343AAB521E8349AF00F9D36E /* OIDServiceDiscovery.m in Sources */, + 30BF67DB26130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, 343AAB501E8349AF00F9D36E /* OIDScopeUtilities.m in Sources */, 343AAAE21E83494F00F9D36E /* OIDLoopbackHTTPServer.m in Sources */, 343AAB531E8349AF00F9D36E /* OIDTokenRequest.m in Sources */, @@ -2695,6 +2809,8 @@ 343AAB8D1E8349CF00F9D36E /* OIDAuthorizationRequestTests.m in Sources */, 343AAB901E8349CF00F9D36E /* OIDGrantTypesTests.m in Sources */, 34A663901E8865090060B664 /* OIDRPProfileCode.m in Sources */, + 30BF69FC261329C7007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, + 30BF6B3626132A54007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, A6CEB11F2007E4A2009D492A /* OIDEndSessionRequestTests.m in Sources */, 343AAB8E1E8349CF00F9D36E /* OIDAuthorizationResponseTests.m in Sources */, A5EEF29920D8211B0044F470 /* OIDTokenUtilitiesTests.m in Sources */, @@ -2716,9 +2832,11 @@ 34A663301E871DD40060B664 /* OIDIDToken.m in Sources */, 3474240C1E7F4BA000D3E6D6 /* OIDScopeUtilities.m in Sources */, 347424071E7F4BA000D3E6D6 /* OIDRegistrationResponse.m in Sources */, + 30BF681226130B0A007756F9 /* OIDRevokeTokenResponse.m in Sources */, 347424041E7F4BA000D3E6D6 /* OIDError.m in Sources */, 3474240F1E7F4BA000D3E6D6 /* OIDTokenRequest.m in Sources */, 347424101E7F4BA000D3E6D6 /* OIDTokenResponse.m in Sources */, + 30BF67D726130A6D007756F9 /* OIDRevokeTokenRequest.m in Sources */, A6DEAB852017A7050022AC32 /* OIDEndSessionResponse.m in Sources */, 3474240D1E7F4BA000D3E6D6 /* OIDServiceConfiguration.m in Sources */, 347424031E7F4BA000D3E6D6 /* OIDClientMetadataParameters.m in Sources */, @@ -2745,6 +2863,7 @@ 348970872177B3B000ABEED4 /* OIDAuthStateTests.m in Sources */, 348970882177B3B000ABEED4 /* OIDTokenResponseTests.m in Sources */, 348970892177B3B000ABEED4 /* OIDTokenRequestTests.m in Sources */, + 30BF6A23261329C8007756F9 /* OIDRevokeTokenRequestTests.m in Sources */, 3489708A2177B3B000ABEED4 /* OIDResponseTypesTests.m in Sources */, 3489708B2177B3B000ABEED4 /* OIDRegistrationRequestTests.m in Sources */, 3489708C2177B3B000ABEED4 /* OIDAuthorizationRequestTests.m in Sources */, @@ -2752,6 +2871,7 @@ 3489708E2177B3B000ABEED4 /* OIDRPProfileCode.m in Sources */, 3489708F2177B3B000ABEED4 /* OIDAuthorizationResponseTests.m in Sources */, 348970902177B3B000ABEED4 /* OIDTokenUtilitiesTests.m in Sources */, + 30BF6B4A26132A55007756F9 /* OIDRevokeTokenResponseTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/AppAuthCore/OIDError.h b/Source/AppAuthCore/OIDError.h index 5131f0ad4..a274090dd 100644 --- a/Source/AppAuthCore/OIDError.h +++ b/Source/AppAuthCore/OIDError.h @@ -225,6 +225,11 @@ typedef NS_ENUM(NSInteger, OIDErrorCodeOAuth) { @see https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError */ OIDErrorCodeOAuthInvalidClientMetadata = -13, + + /*! @remarks unsupported_token_type + @see https://tools.ietf.org/html/rfc7009#section-2.2.1 + */ + OIDErrorCodeOAuthUnsupportedTokenType = -14, /*! @brief An authorization error occurring on the client rather than the server. For example, due to a state mismatch or misconfiguration. Should be treated as an unrecoverable diff --git a/Source/AppAuthCore/OIDErrorUtilities.m b/Source/AppAuthCore/OIDErrorUtilities.m index 3b3c06075..a00239173 100644 --- a/Source/AppAuthCore/OIDErrorUtilities.m +++ b/Source/AppAuthCore/OIDErrorUtilities.m @@ -152,6 +152,7 @@ + (OIDErrorCodeOAuth)OAuthErrorCodeFromString:(NSString *)errorCode { @"invalid_client": @(OIDErrorCodeOAuthInvalidClient), @"invalid_grant": @(OIDErrorCodeOAuthInvalidGrant), @"unsupported_grant_type": @(OIDErrorCodeOAuthUnsupportedGrantType), + @"unsupported_token_type": @(OIDErrorCodeOAuthUnsupportedTokenType), }; NSNumber *code = errorCodes[errorCode]; if (code) { diff --git a/Source/AppAuthCore/OIDRevokeTokenRequest.h b/Source/AppAuthCore/OIDRevokeTokenRequest.h new file mode 100644 index 000000000..4ed854ce6 --- /dev/null +++ b/Source/AppAuthCore/OIDRevokeTokenRequest.h @@ -0,0 +1,89 @@ +/*! @file OIDRevokeTokenRequest.h + @brief AppAuth iOS SDK + @copyright + Copyright 2017 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 + +@class OIDServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Represents a revoke token request. + @see https://tools.ietf.org/html/rfc7009#section-2.1 + */ +@interface OIDRevokeTokenRequest : NSObject + +/*! @brief The service's configuration. + @remarks This configuration specifies how to connect to a particular OAuth provider. + Configurations may be created manually, or via an OpenID Connect Discovery Document. + */ +@property(nonatomic, readonly) OIDServiceConfiguration *configuration; + +/*! @brief REQUIRED. The token that the client wants to get revoked. + @remarks token + */ +@property(nonatomic, readonly) NSString *token; + +/*! @brief OPTIONAL. A hint about the type of the token + submitted for revocation. Clients MAY pass this parameter in + order to help the authorization server to optimize the token + lookup. + @remarks token_type_hint + */ +@property(nonatomic, readonly, nullable) NSString *tokenTypeHint; + +/*! @brief The client identifier. + @remarks client_id + @see https://tools.ietf.org/html/rfc6749#section-4.1.3 + */ +@property(nonatomic, readonly) NSString *clientID; + +/*! @brief The client secret. + @remarks client_secret + @see https://tools.ietf.org/html/rfc6749#section-2.3.1 + */ +@property(nonatomic, readonly, nullable) NSString *clientSecret; + +/*! @internal + @brief Unavailable. Please use @c initWithConfiguration:token:tokenTypeHint:clientID:clientSecret:. + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @brief Designated initializer. + @param configuration The service's configuration. + @param token The previously issued ID Token + @param tokenTypeHint The client's post-logout redirect URI. + */ +- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration + token:(NSString *)token + tokenTypeHint:(nullable NSString *)tokenTypeHint + clientID:(NSString *)clientID + clientSecret:(nullable NSString *)clientSecret +NS_DESIGNATED_INITIALIZER; + +/*! @brief Designated initializer for NSSecureCoding. + @param aDecoder Unarchiver object to decode + */ +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +/*! @brief Constructs an @c NSURLRequest representing the revoke token request. + @return An @c NSURLRequest representing the token request. + */ +- (NSURLRequest *)URLRequest; +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthCore/OIDRevokeTokenRequest.m b/Source/AppAuthCore/OIDRevokeTokenRequest.m new file mode 100644 index 000000000..bfe45a9d2 --- /dev/null +++ b/Source/AppAuthCore/OIDRevokeTokenRequest.m @@ -0,0 +1,203 @@ +/*! @file OIDEndSessionRequest.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 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 "OIDRevokeTokenRequest.h" + +#import "OIDDefines.h" +#import "OIDServiceConfiguration.h" +#import "OIDURLQueryComponent.h" +#import "OIDTokenUtilities.h" + +/*! @brief The key for the @c configuration property for @c NSSecureCoding + */ +static NSString *const kConfigurationKey = @"configuration"; + +/*! @brief Key used to encode the @c token property for @c NSSecureCoding, and on the URL request. + */ +static NSString *const kTokenKey = @"token"; + +/*! @brief Key used to encode the @c tokenTypeHint property for @c NSSecureCoding, and on the URL request. + */ +static NSString *const kTokenTypeHintKey = @"token_type_hint"; + +/*! @brief Key used to encode the @c clientID property for @c NSSecureCoding + */ +static NSString *const kClientIDKey = @"client_id"; + +/*! @brief Key used to encode the @c clientSecret property for @c NSSecureCoding + */ +static NSString *const kClientSecretKey = @"client_secret"; + +/*! @brief Assertion text for missing revoke_token_endpoint. + */ +static NSString *const OIDMissingRevokeTokenEndpointMessage = +@"The service configuration is missing an revoke_token_endpoint."; + +@implementation OIDRevokeTokenRequest + +- (instancetype)init +OID_UNAVAILABLE_USE_INITIALIZER( + @selector(initWithConfiguration: + token: + tokenTypeHint: + clientID: + clientSecret:) + ) + +- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration + token:(NSString *)token + tokenTypeHint:(nullable NSString *)tokenTypeHint + clientID:(NSString *)clientID + clientSecret:(nullable NSString *)clientSecret +{ + self = [super init]; + if (self) { + _configuration = [configuration copy]; + _token = [token copy]; + _tokenTypeHint = [tokenTypeHint copy]; + _clientID = [clientID copy]; + _clientSecret = [clientSecret copy]; + } + return self; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(nullable NSZone *)zone { + // The documentation for NSCopying specifically advises us to return a reference to the original + // instance in the case where instances are immutable (as ours is): + // "Implement NSCopying by retaining the original instead of creating a new copy when the class + // and its contents are immutable." + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + OIDServiceConfiguration *configuration = [aDecoder decodeObjectOfClass:[OIDServiceConfiguration class] forKey:kConfigurationKey]; + + NSString *token = [aDecoder decodeObjectOfClass:[NSString class] forKey:kTokenKey]; + NSString *tokenTypeHint = [aDecoder decodeObjectOfClass:[NSString class] forKey:kTokenTypeHintKey]; + NSString *clientID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientIDKey]; + NSString *clientSecret = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientSecretKey]; + + self = [super init]; + if (self) { + _configuration = [configuration copy]; + _token = [token copy]; + _tokenTypeHint = [tokenTypeHint copy]; + _clientID = [clientID copy]; + _clientSecret = [clientSecret copy]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_configuration forKey:kConfigurationKey]; + [aCoder encodeObject:_token forKey:kTokenKey]; + [aCoder encodeObject:_tokenTypeHint forKey:kTokenTypeHintKey]; + [aCoder encodeObject:_clientID forKey:kClientIDKey]; + [aCoder encodeObject:_clientSecret forKey:kClientSecretKey]; +} + +#pragma mark - NSObject overrides + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, request: %@>", + NSStringFromClass([self class]), + (void *)self, + self.revokeTokenRequestURL]; +} + +#pragma mark - + +/*! @brief Constructs the request URI. + @return A URL representing the token revocation request. + @see https://tools.ietf.org/html/rfc7009#section-2.1 + */ +- (NSURL *)revokeTokenRequestURL { + return _configuration.revocationEndpoint; +} + +/*! @brief Constructs the request body data by combining the request parameters using the + "application/x-www-form-urlencoded" format. + @return The data to pass to the token revocation request URL. + @see https://tools.ietf.org/html/rfc7009#section-2.1 + */ +- (OIDURLQueryComponent *)revokeTokenRequestBody { + OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; + + // Add parameters, as applicable. + [query addParameter:kTokenKey value:_token]; + + if (_tokenTypeHint) { + [query addParameter:kTokenTypeHintKey value:_tokenTypeHint]; + } + + return query; +} + +- (NSURLRequest *)URLRequest { + static NSString *const kHTTPPost = @"POST"; + static NSString *const kHTTPContentTypeHeaderKey = @"Content-Type"; + static NSString *const kHTTPContentTypeHeaderValue = + @"application/x-www-form-urlencoded; charset=UTF-8"; + + NSURL *tokenRequestURL = [self revokeTokenRequestURL]; + NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:tokenRequestURL] mutableCopy]; + URLRequest.HTTPMethod = kHTTPPost; + [URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey]; + + OIDURLQueryComponent *bodyParameters = [self revokeTokenRequestBody]; + NSMutableDictionary *httpHeaders = [[NSMutableDictionary alloc] init]; + + if (_clientSecret) { + // The client id and secret are encoded using the "application/x-www-form-urlencoded" + // encoding algorithm per RFC 6749 Section 2.3.1. + // https://tools.ietf.org/html/rfc6749#section-2.3.1 + NSString *encodedClientID = [OIDTokenUtilities formUrlEncode:_clientID]; + NSString *encodedClientSecret = [OIDTokenUtilities formUrlEncode:_clientSecret]; + + NSString *credentials = + [NSString stringWithFormat:@"%@:%@", encodedClientID, encodedClientSecret]; + NSData *plainData = [credentials dataUsingEncoding:NSUTF8StringEncoding]; + NSString *basicAuth = [plainData base64EncodedStringWithOptions:kNilOptions]; + + NSString *authValue = [NSString stringWithFormat:@"Basic %@", basicAuth]; + [httpHeaders setObject:authValue forKey:@"Authorization"]; + } else { + [bodyParameters addParameter:kClientIDKey value:_clientID]; + } + + // Constructs request with the body string and headers. + NSString *bodyString = [bodyParameters URLEncodedParameters]; + NSData *body = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; + URLRequest.HTTPBody = body; + + for (id header in httpHeaders) { + [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; + } + + return URLRequest; +} + +@end diff --git a/Source/AppAuthCore/OIDRevokeTokenResponse.h b/Source/AppAuthCore/OIDRevokeTokenResponse.h new file mode 100644 index 000000000..6a3f9940c --- /dev/null +++ b/Source/AppAuthCore/OIDRevokeTokenResponse.h @@ -0,0 +1,47 @@ +/*! @file OIDRevokeTokenResponse.h + @brief AppAuth iOS SDK + @copyright + Copyright 2017 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 + +@class OIDRevokeTokenRequest; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Represents the response to a Revoke Token request. + @see https://tools.ietf.org/html/rfc7009#section-2.2 + */ +@interface OIDRevokeTokenResponse : NSObject + +/*! @brief The request which was serviced. + */ +@property(nonatomic, readonly) OIDRevokeTokenRequest *request; + +/*! @internal + @brief Unavailable. Please use initWithRequest:. + */ +- (instancetype)init NS_UNAVAILABLE; + +/*! @brief Designated initializer. + @param request The serviced request. + */ +- (instancetype)initWithRequest:(OIDRevokeTokenRequest *)request + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthCore/OIDRevokeTokenResponse.m b/Source/AppAuthCore/OIDRevokeTokenResponse.m new file mode 100644 index 000000000..33f259e32 --- /dev/null +++ b/Source/AppAuthCore/OIDRevokeTokenResponse.m @@ -0,0 +1,78 @@ +/*! @file OIDRevokeTokenResponse.m + @brief AppAuth iOS SDK + @copyright + Copyright 2017 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 "OIDRevokeTokenResponse.h" + +#import "OIDDefines.h" +#import "OIDRevokeTokenRequest.h" + +/*! @brief Key used to encode the @c request property for @c NSSecureCoding + */ +static NSString *const kRequestKey = @"request"; + +@implementation OIDRevokeTokenResponse + +#pragma mark - Initializers + +- (instancetype)init + OID_UNAVAILABLE_USE_INITIALIZER(@selector(initWithRequest:)) + +- (instancetype)initWithRequest:(OIDRevokeTokenRequest *)request { + self = [super init]; + if (self) { + _request = [request copy]; + } + return self; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(nullable NSZone *)zone { + // The documentation for NSCopying specifically advises us to return a reference to the original + // instance in the case where instances are immutable (as ours is): + // "Implement NSCopying by retaining the original instead of creating a new copy when the class + // and its contents are immutable." + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + OIDRevokeTokenRequest *request = + [aDecoder decodeObjectOfClass:[OIDRevokeTokenRequest class] forKey:kRequestKey]; + self = [self initWithRequest:request]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_request forKey:kRequestKey]; +} + +#pragma mark - NSObject overrides + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, request: %@>", + NSStringFromClass([self class]), + (void *)self, + _request]; +} +@end diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.h b/Source/AppAuthTV/OIDTVAuthorizationService.h index 93158579c..669ce1f22 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationService.h +++ b/Source/AppAuthTV/OIDTVAuthorizationService.h @@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN @class OIDAuthState; +@class OIDRevokeTokenRequest; +@class OIDRevokeTokenResponse; @class OIDTVAuthorizationRequest; @class OIDTVAuthorizationResponse; @class OIDTVServiceConfiguration; @@ -57,6 +59,15 @@ typedef void (^OIDTVAuthorizationCompletion) */ typedef void (^OIDTVAuthorizationCancelBlock)(void); +/*! @brief Represents the type of block used as a callback for token revocation methods of + @c OIDTVAuthorizationService. + @param revokeTokenResponse The revoke token response, if available. + @param error The error if an error occurred. + */ +typedef void (^OIDRevokeTokenCallback)(OIDRevokeTokenResponse *_Nullable revokeTokenResponse, + NSError *_Nullable error); + + /*! @brief Performs authorization flows designed for TVs and other limited input devices. */ @interface OIDTVAuthorizationService : NSObject @@ -106,6 +117,16 @@ typedef void (^OIDTVAuthorizationCancelBlock)(void); initialization:(OIDTVAuthorizationInitialization)initialization completion:(OIDTVAuthorizationCompletion)completion; +/*! @brief Performs a token revocation request. + @param request The revoke token request. + @param callback Block that is called on the success or failure of the token revocation. + The authorization server responds with HTTP status code 200 if the + token has been revoked successfully or if the client submitted an + invalid token. + @see https://tools.ietf.org/html/rfc7009 + */ ++ (void)performRevokeTokenRequest:(OIDRevokeTokenRequest *)request + callback:(OIDRevokeTokenCallback)callback; @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/OIDTVAuthorizationService.m b/Source/AppAuthTV/OIDTVAuthorizationService.m index eaa4d6bfc..51e02ebe2 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationService.m +++ b/Source/AppAuthTV/OIDTVAuthorizationService.m @@ -22,6 +22,8 @@ #import "OIDAuthState.h" #import "OIDDefines.h" #import "OIDErrorUtilities.h" +#import "OIDRevokeTokenRequest.h" +#import "OIDRevokeTokenResponse.h" #import "OIDServiceDiscovery.h" #import "OIDURLQueryComponent.h" #import "OIDURLSessionProvider.h" @@ -282,4 +284,92 @@ + (OIDTVAuthorizationCancelBlock)authorizeTVRequest:(OIDTVAuthorizationRequest * return cancelBlock; } ++ (void)performRevokeTokenRequest:(OIDRevokeTokenRequest *)request + callback:(OIDRevokeTokenCallback)callback { + + NSURLRequest *URLRequest = [request URLRequest]; + + AppAuthRequestTrace(@"Revoke Token Request: %@\nHeaders:%@\nHTTPBody: %@", + URLRequest.URL, + URLRequest.allHTTPHeaderFields, + [[NSString alloc] initWithData:URLRequest.HTTPBody + encoding:NSUTF8StringEncoding]); + + NSURLSession *session = [OIDURLSessionProvider session]; + [[session dataTaskWithRequest:URLRequest + completionHandler:^(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + // A network error or server error occurred. + NSString *errorDescription = + [NSString stringWithFormat:@"Connection error making revoke token request to '%@': %@.", + URLRequest.URL, + error.localizedDescription]; + NSError *returnedError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError + underlyingError:error + description:errorDescription]; + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, returnedError); + }); + return; + } + + NSHTTPURLResponse *HTTPURLResponse = (NSHTTPURLResponse *)response; + NSInteger statusCode = HTTPURLResponse.statusCode; + AppAuthRequestTrace(@"Revoke Token Response: HTTP Status %d\nHTTPBody: %@", + (int)statusCode, + [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + if (statusCode != 200) { + // A server error occurred. + NSError *serverError = + [OIDErrorUtilities HTTPErrorWithHTTPResponse:HTTPURLResponse data:data]; + + // HTTP 4xx may indicate an RFC6749 Section 5.2 error response, attempts to parse as such. + if (statusCode >= 400 && statusCode < 500) { + NSError *jsonDeserializationError; + NSDictionary *> *json = + [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonDeserializationError]; + + // If the HTTP 4xx response parses as JSON and has an 'error' key, it's an OAuth error. + // These errors are special as they indicate a problem with the authorization grant. + if (json[OIDOAuthErrorFieldError]) { + NSError *oauthError = + [OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthTokenErrorDomain + OAuthResponse:json + underlyingError:serverError]; + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, oauthError); + }); + return; + } + } + + // Status code indicates this is an error, but not an RFC6749 Section 5.2 error. + NSString *errorDescription = + [NSString stringWithFormat:@"Non-200 HTTP response (%d) making token request to '%@'.", + (int)statusCode, + URLRequest.URL]; + NSError *returnedError = + [OIDErrorUtilities errorWithCode:OIDErrorCodeServerError + underlyingError:serverError + description:errorDescription]; + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, returnedError); + }); + return; + } + + OIDRevokeTokenResponse *revokeTokenResponse = + [[OIDRevokeTokenResponse alloc] initWithRequest:request]; + + // Success + dispatch_async(dispatch_get_main_queue(), ^{ + callback(revokeTokenResponse, nil); + }); + }] resume]; +} + + @end diff --git a/UnitTests/OIDRevokeTokenRequestTests.h b/UnitTests/OIDRevokeTokenRequestTests.h new file mode 100644 index 000000000..0ed058155 --- /dev/null +++ b/UnitTests/OIDRevokeTokenRequestTests.h @@ -0,0 +1,32 @@ +/*! @file OIDRevokeTokenRequestTests.h + @brief AppAuth iOS SDK + @copyright + Copyright 2015 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 + +@class OIDRevokeTokenRequest; + +NS_ASSUME_NONNULL_BEGIN + +@interface OIDRevokeTokenRequestTests : XCTestCase + +/*! @brief Creates a new @c OIDRevokeTokenRequest for testing. + */ ++ (OIDRevokeTokenRequest *)testInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDRevokeTokenRequestTests.m b/UnitTests/OIDRevokeTokenRequestTests.m new file mode 100644 index 000000000..509c711ec --- /dev/null +++ b/UnitTests/OIDRevokeTokenRequestTests.m @@ -0,0 +1,124 @@ +// +// OIDRevokeTokenRequestTests.m +// AppAuth +// +// Created by Thomas Carayol on 30/03/2021. +// Copyright © 2021 OpenID Foundation. All rights reserved. +// + +#import "OIDRevokeTokenRequestTests.h" + +#import "OIDAuthorizationResponseTests.h" +#import "OIDServiceConfigurationTests.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDAuthorizationRequest.h" +#import "Source/AppAuthCore/OIDAuthorizationResponse.h" +#import "Source/AppAuthCore/OIDServiceConfiguration.h" +#import "Source/AppAuthCore/OIDRevokeTokenRequest.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" + +@implementation OIDRevokeTokenRequestTests + ++ (OIDRevokeTokenRequest *)testInstance { + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstance]; + OIDRevokeTokenRequest *request = + [[OIDRevokeTokenRequest alloc] initWithConfiguration:authResponse.request.configuration + token:authResponse.accessToken + tokenTypeHint:authResponse.tokenType + clientID:authResponse.request.clientID + clientSecret:authResponse.request.clientSecret]; + return request; +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + process and checking to make sure the source and destination instances are equivalent. + */ +- (void)testCopying { + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstance]; + OIDRevokeTokenRequest *request = [[self class] testInstance]; + + XCTAssertEqualObjects(request.configuration.authorizationEndpoint, + authResponse.request.configuration.authorizationEndpoint, + @"Request and response authorization endpoints should be equal."); + XCTAssertEqualObjects(request.token, authResponse.accessToken, + @"Request and response access token should be equal."); + XCTAssertEqualObjects(request.tokenTypeHint, authResponse.tokenType, + @"Request and response token type should be equal."); + XCTAssertEqualObjects(request.clientID, authResponse.request.clientID, + @"Request and response clientID should be equal."); + XCTAssertEqualObjects(request.clientSecret, authResponse.request.clientSecret, + @"Request and response clientSecret should be equal."); + + OIDRevokeTokenRequest *requestCopy = [request copy]; + + // Not a full test of the configuration deserialization, but should be sufficient as a smoke test + // to make sure the configuration IS actually getting carried along in the copy implementation. + XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, + request.configuration.authorizationEndpoint, @""); + + XCTAssertEqualObjects(requestCopy.token, request.token, @""); + XCTAssertEqualObjects(requestCopy.tokenTypeHint, request.tokenTypeHint, @""); + XCTAssertEqualObjects(request.clientID, request.clientID, @""); + XCTAssertEqualObjects(request.clientSecret, request.clientSecret, @""); +} + +/*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and + checking to make sure the source and destination instances are equivalent. + */ +- (void)testSecureCoding { + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstance]; + OIDRevokeTokenRequest *request = [[self class] testInstance]; + + XCTAssertEqualObjects(request.configuration.authorizationEndpoint, + authResponse.request.configuration.authorizationEndpoint, + @"Request and response authorization endpoints should be equal."); + XCTAssertEqualObjects(request.token, authResponse.accessToken, + @"Request and response authorization codes should be equal."); + XCTAssertEqualObjects(request.tokenTypeHint, authResponse.tokenType, + @"Request and response token type should be equal."); + XCTAssertEqualObjects(request.clientID, authResponse.request.clientID, + @"Request and response clientID should be equal."); + XCTAssertEqualObjects(request.clientSecret, authResponse.request.clientSecret, + @"Request and response clientSecret should be equal."); + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; + OIDRevokeTokenRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + // Not a full test of the configuration deserialization, but should be sufficient as a smoke test + // to make sure the configuration IS actually getting serialized and deserialized in the + // NSSecureCoding implementation. We'll leave it up to the OIDServiceConfiguration tests to make + // sure the NSSecureCoding implementation of that class is correct. + XCTAssertEqualObjects(requestCopy.configuration.authorizationEndpoint, + request.configuration.authorizationEndpoint, @""); + + XCTAssertEqualObjects(requestCopy.token, request.token, @""); + XCTAssertEqualObjects(requestCopy.tokenTypeHint, request.tokenTypeHint, @""); + XCTAssertEqualObjects(requestCopy.clientID, request.clientID, @""); + XCTAssertEqualObjects(requestCopy.clientSecret, request.clientSecret, @""); +} + +- (void)testURLRequestBasicClientAuth { + OIDRevokeTokenRequest *request = [[self class] testInstance]; + NSURLRequest* urlRequest = [request URLRequest]; + + id authorization = [urlRequest.allHTTPHeaderFields objectForKey:@"Authorization"]; + XCTAssertNotNil(authorization); +} + +- (void)testURLRequestBody { + OIDRevokeTokenRequest *request = [[self class] testInstance]; + NSURLRequest* urlRequest = [request URLRequest]; + XCTAssertNotNil(urlRequest.HTTPBody); +} + +@end + +#pragma GCC diagnostic pop diff --git a/UnitTests/OIDRevokeTokenResponseTests.h b/UnitTests/OIDRevokeTokenResponseTests.h new file mode 100644 index 000000000..09d7bf8f2 --- /dev/null +++ b/UnitTests/OIDRevokeTokenResponseTests.h @@ -0,0 +1,35 @@ +/*! @file OIDRevokeTokenResponseTests.h + @brief AppAuth iOS SDK + @copyright + Copyright 2015 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 + +@class OIDRevokeTokenResponse; + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief Unit tests for @c OIDRevokeTokenResponse. + */ +@interface OIDRevokeTokenResponseTests : XCTestCase + +/*! @brief Creates a new @c OIDRevokeTokenResponse for testing. + */ ++ (OIDRevokeTokenResponse *)testInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDRevokeTokenResponseTests.m b/UnitTests/OIDRevokeTokenResponseTests.m new file mode 100644 index 000000000..2df6495d3 --- /dev/null +++ b/UnitTests/OIDRevokeTokenResponseTests.m @@ -0,0 +1,72 @@ +/*! @file OIDRevokeTokenResponseTests.m + @brief AppAuth iOS SDK + @copyright + Copyright 2015 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 "OIDRevokeTokenResponseTests.h" + +#import "OIDRevokeTokenRequestTests.h" + +#if SWIFT_PACKAGE +@import AppAuthCore; +#else +#import "Source/AppAuthCore/OIDRevokeTokenRequest.h" +#import "Source/AppAuthCore/OIDRevokeTokenResponse.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" + +@implementation OIDRevokeTokenResponseTests + ++ (OIDRevokeTokenResponse *)testInstance { + OIDRevokeTokenRequest *request = [OIDRevokeTokenRequestTests testInstance]; + OIDRevokeTokenResponse *response = + [[OIDRevokeTokenResponse alloc] initWithRequest:request]; + return response; +} + +/*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying + process and checking to make sure the source and destination instances are equivalent. + */ +- (void)testCopying { + OIDRevokeTokenResponse *response = [[self class] testInstance]; + XCTAssertNotNil(response.request, @""); + + OIDRevokeTokenResponse *responseCopy = [response copy]; + XCTAssertNotNil(responseCopy.request, @""); +} + +/*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and + checking to make sure the source and destination instances are equivalent. + */ +- (void)testSecureCoding { + OIDRevokeTokenResponse *response = [[self class] testInstance]; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:response]; + OIDRevokeTokenResponse *responseCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + // Not a full test of the request deserialization, but should be sufficient as a smoke test + // to make sure the request IS actually getting serialized and deserialized in the + // NSSecureCoding implementation. We'll leave it up to the OIDAuthorizationRequest tests to make + // sure the NSSecureCoding implementation of that class is correct. + XCTAssertNotNil(responseCopy.request, @""); +} + +@end + +#pragma GCC diagnostic pop