diff --git a/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m index dc76a8c9c..4d58cf9d2 100644 --- a/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC-Carthage/Source/AppAuthExampleViewController.m @@ -177,7 +177,8 @@ - (void)doClientRegistration:(OIDServiceConfiguration *)configuration grantTypes:nil subjectType:nil tokenEndpointAuthMethod:@"client_secret_post" - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; // performs registration request [self logMessage:@"Initiating registration request"]; diff --git a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m index 2c3ebe03e..d67c7b73d 100644 --- a/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m +++ b/Examples/Example-iOS_ObjC/Source/AppAuthExampleViewController.m @@ -179,7 +179,8 @@ - (void)doClientRegistration:(OIDServiceConfiguration *)configuration grantTypes:nil subjectType:nil tokenEndpointAuthMethod:@"client_secret_post" - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; // performs registration request [self logMessage:@"Initiating registration request"]; diff --git a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift index f70540472..91cf79fa4 100644 --- a/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift +++ b/Examples/Example-iOS_Swift-Carthage/Source/AppAuthExampleViewController.swift @@ -349,7 +349,8 @@ extension AppAuthExampleViewController { grantTypes: nil, subjectType: nil, tokenEndpointAuthMethod: "client_secret_post", - additionalParameters: nil) + additionalParameters: nil, + additionalHeaders: nil) // performs registration request self.logMessage("Initiating registration request") diff --git a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m index 3d461619d..e97b0d204 100644 --- a/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m +++ b/Examples/Example-tvOS/Example-tvOS/AppAuthTVExampleViewController.m @@ -176,7 +176,8 @@ - (void)performAuthorizationWithConfiguration:(OIDTVServiceConfiguration *)confi clientId:kClientID clientSecret:kClientSecret scopes:@[ OIDScopeOpenID, OIDScopeProfile ] - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; OIDTVAuthorizationInitialization initBlock = ^(OIDTVAuthorizationResponse *_Nullable response, NSError *_Nullable error) { diff --git a/README.md b/README.md index 78f79959f..53085b3c4 100644 --- a/README.md +++ b/README.md @@ -516,7 +516,8 @@ OIDTVAuthorizationRequest *request = clientId:kClientID clientSecret:kClientSecret scopes:@[ OIDScopeOpenID, OIDScopeProfile ] - additionalParameters:nil]; + additionalParameters:nil + additionalHeaders:nil]; // performs authentication request OIDTVAuthorizationInitialization initBlock = diff --git a/Source/AppAuthCore/OIDAuthState.h b/Source/AppAuthCore/OIDAuthState.h index 68697d2ca..46c78a831 100644 --- a/Source/AppAuthCore/OIDAuthState.h +++ b/Source/AppAuthCore/OIDAuthState.h @@ -48,6 +48,12 @@ typedef void (^OIDAuthStateAction)(NSString *_Nullable accessToken, typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authState, NSError *_Nullable error); +/*! @brief The exception thrown when a developer tries to create a refresh request from an + authorization request with no authorization code. + */ +static NSString *const kRefreshTokenRequestException = + @"Attempted to create a token refresh request from a token response with no refresh token."; + /*! @brief A convenience class that retains the auth state between @c OIDAuthorizationResponse%s and @c OIDTokenResponse%s. */ @@ -267,6 +273,31 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt - (nullable OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; +/*! @brief Creates a token request suitable for refreshing an access token. + @param additionalParameters Additional parameters for the token request. + @param additionalHeaders Additional headers for the token request. + @return A @c OIDTokenRequest suitable for using a refresh token to obtain a new access token. + @discussion After performing the refresh, call @c OIDAuthState.updateWithTokenResponse:error: + to update the authorization state based on the response. Rather than doing the token refresh + yourself, you should use @c OIDAuthState.performActionWithFreshTokens:. + @see https://tools.ietf.org/html/rfc6749#section-1.5 + */ +- (nullable OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: + (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders; + +/*! @brief Creates a token request suitable for refreshing an access token. + @param additionalHeaders Additional parameters for the token request. + @return A @c OIDTokenRequest suitable for using a refresh token to obtain a new access token. + @discussion After performing the refresh, call @c OIDAuthState.updateWithTokenResponse:error: + to update the authorization state based on the response. Rather than doing the token refresh + yourself, you should use @c OIDAuthState.performActionWithFreshTokens:. + @see https://tools.ietf.org/html/rfc6749#section-1.5 + */ +- (nullable OIDTokenRequest *)tokenRefreshRequestWithAdditionalHeaders: + (nullable NSDictionary *)additionalHeaders; + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthCore/OIDAuthState.m b/Source/AppAuthCore/OIDAuthState.m index fe8a16221..cb5a22a1e 100644 --- a/Source/AppAuthCore/OIDAuthState.m +++ b/Source/AppAuthCore/OIDAuthState.m @@ -55,12 +55,6 @@ */ static NSString *const kAuthorizationErrorKey = @"authorizationError"; -/*! @brief The exception thrown when a developer tries to create a refresh request from an - authorization request with no authorization code. - */ -static NSString *const kRefreshTokenRequestException = - @"Attempted to create a token refresh request from a token response with no refresh token."; - /*! @brief Number of seconds the access token is refreshed before it actually expires. */ static const NSUInteger kExpiryTimeTolerance = 60; @@ -427,7 +421,47 @@ - (OIDTokenRequest *)tokenRefreshRequest { - (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: (NSDictionary *)additionalParameters { - // TODO: Add unit test to confirm exception is thrown when expected + if (!_refreshToken) { + [OIDErrorUtilities raiseException:kRefreshTokenRequestException]; + } + return [[OIDTokenRequest alloc] + initWithConfiguration:_lastAuthorizationResponse.request.configuration + grantType:OIDGrantTypeRefreshToken + authorizationCode:nil + redirectURL:nil + clientID:_lastAuthorizationResponse.request.clientID + clientSecret:_lastAuthorizationResponse.request.clientSecret + scope:nil + refreshToken:_refreshToken + codeVerifier:nil + additionalParameters:additionalParameters + additionalHeaders:nil]; +} + +- (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters + additionalHeaders: + (NSDictionary *)additionalHeaders { + + if (!_refreshToken) { + [OIDErrorUtilities raiseException:kRefreshTokenRequestException]; + } + return [[OIDTokenRequest alloc] + initWithConfiguration:_lastAuthorizationResponse.request.configuration + grantType:OIDGrantTypeRefreshToken + authorizationCode:nil + redirectURL:nil + clientID:_lastAuthorizationResponse.request.clientID + clientSecret:_lastAuthorizationResponse.request.clientSecret + scope:nil + refreshToken:_refreshToken + codeVerifier:nil + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; +} + +- (OIDTokenRequest *)tokenRefreshRequestWithAdditionalHeaders: + (NSDictionary *)additionalHeaders { if (!_refreshToken) { [OIDErrorUtilities raiseException:kRefreshTokenRequestException]; @@ -442,7 +476,8 @@ - (OIDTokenRequest *)tokenRefreshRequestWithAdditionalParameters: scope:nil refreshToken:_refreshToken codeVerifier:nil - additionalParameters:additionalParameters]; + additionalParameters:nil + additionalHeaders:additionalHeaders]; } #pragma mark - Stateful Actions diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.h b/Source/AppAuthCore/OIDAuthorizationResponse.h index e7552fe59..2a10c81f2 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.h +++ b/Source/AppAuthCore/OIDAuthorizationResponse.h @@ -121,7 +121,9 @@ NS_ASSUME_NONNULL_BEGIN @see https://tools.ietf.org/html/rfc6749#section-4.1.3 */ - (nullable OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: - (nullable NSDictionary *)additionalParameters; + (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders; @end diff --git a/Source/AppAuthCore/OIDAuthorizationResponse.m b/Source/AppAuthCore/OIDAuthorizationResponse.m index a8f92c75e..5c998a966 100644 --- a/Source/AppAuthCore/OIDAuthorizationResponse.m +++ b/Source/AppAuthCore/OIDAuthorizationResponse.m @@ -184,11 +184,13 @@ - (NSString *)description { #pragma mark - - (OIDTokenRequest *)tokenExchangeRequest { - return [self tokenExchangeRequestWithAdditionalParameters:nil]; + return [self tokenExchangeRequestWithAdditionalParameters:nil additionalHeaders:nil]; } - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: - (NSDictionary *)additionalParameters { + (NSDictionary *)additionalParameters + additionalHeaders: + (NSDictionary *)additionalHeaders { // TODO: add a unit test to confirm exception is thrown when expected and the request is created // with the correct parameters. if (!_authorizationCode) { @@ -204,7 +206,8 @@ - (OIDTokenRequest *)tokenExchangeRequestWithAdditionalParameters: scope:nil refreshToken:nil codeVerifier:_request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } @end diff --git a/Source/AppAuthCore/OIDTokenRequest.h b/Source/AppAuthCore/OIDTokenRequest.h index 399294e8c..1d161cd08 100644 --- a/Source/AppAuthCore/OIDTokenRequest.h +++ b/Source/AppAuthCore/OIDTokenRequest.h @@ -95,9 +95,13 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly, nullable) NSDictionary *additionalParameters; +/*! @brief The client's additional token request headers. + */ +@property(nonatomic, readonly, nullable) NSDictionary *additionalHeaders; + /*! @internal @brief Unavailable. Please use - initWithConfiguration:grantType:code:redirectURL:clientID:additionalParameters:. + initWithConfiguration:grantType:code:redirectURL:clientID:additionalParameters:additionalHeaders:. */ - (instancetype)init NS_UNAVAILABLE; @@ -113,6 +117,7 @@ NS_ASSUME_NONNULL_BEGIN @param refreshToken The refresh token. @param codeVerifier The PKCE code verifier. @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType @@ -123,7 +128,8 @@ NS_ASSUME_NONNULL_BEGIN scopes:(nullable NSArray *)scopes refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier - additionalParameters:(nullable NSDictionary *)additionalParameters; + additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders; /*! @brief Designated initializer. @param configuration The service's configuration. @@ -139,6 +145,7 @@ NS_ASSUME_NONNULL_BEGIN @param refreshToken The refresh token. @param codeVerifier The PKCE code verifier. @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration grantType:(NSString *)grantType @@ -150,6 +157,7 @@ NS_ASSUME_NONNULL_BEGIN refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer for NSSecureCoding. diff --git a/Source/AppAuthCore/OIDTokenRequest.m b/Source/AppAuthCore/OIDTokenRequest.m index 5ed8a17ef..08b0dafec 100644 --- a/Source/AppAuthCore/OIDTokenRequest.m +++ b/Source/AppAuthCore/OIDTokenRequest.m @@ -67,6 +67,11 @@ */ static NSString *const kAdditionalParametersKey = @"additionalParameters"; +/*! @brief Key used to encode the @c additionalHeaders property for + @c NSSecureCoding + */ +static NSString *const kAdditionalHeadersKey = @"additionalHeaders"; + @implementation OIDTokenRequest - (instancetype)init @@ -80,7 +85,8 @@ - (instancetype)init scope: refreshToken: codeVerifier: - additionalParameters:) + additionalParameters: + additionalHeaders:) ) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -92,7 +98,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration scopes:(nullable NSArray *)scopes refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier - additionalParameters:(nullable NSDictionary *)additionalParameters { + additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders { return [self initWithConfiguration:configuration grantType:grantType authorizationCode:code @@ -102,7 +109,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration scope:[OIDScopeUtilities scopesWithArray:scopes] refreshToken:refreshToken codeVerifier:(NSString *)codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -114,7 +122,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration scope:(nullable NSString *)scope refreshToken:(nullable NSString *)refreshToken codeVerifier:(nullable NSString *)codeVerifier - additionalParameters:(nullable NSDictionary *)additionalParameters { + additionalParameters:(nullable NSDictionary *)additionalParameters + additionalHeaders:(nullable NSDictionary *)additionalHeaders { self = [super init]; if (self) { _configuration = [configuration copy]; @@ -128,6 +137,8 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration _codeVerifier = [codeVerifier copy]; _additionalParameters = [[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES]; + _additionalHeaders = + [[NSDictionary alloc] initWithDictionary:additionalHeaders copyItems:YES]; // Additional validation for the authorization_code grant type if ([_grantType isEqual:OIDGrantTypeAuthorizationCode]) { @@ -174,9 +185,18 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { [NSDictionary class], [NSString class] ]]; + NSDictionary *additionalParameters = - [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses - forKey:kAdditionalParametersKey]; + [aDecoder decodeObjectOfClasses:additionalParameterCodingClasses forKey:kAdditionalParametersKey]; + + + NSSet *additionalHeaderCodingClasses = [NSSet setWithArray:@[ + [NSDictionary class], + [NSString class] + ]]; + + NSDictionary *additionalHeaders = + [aDecoder decodeObjectOfClasses:additionalHeaderCodingClasses forKey:kAdditionalHeadersKey]; self = [super init]; if (self) { @@ -191,6 +211,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder { _codeVerifier = [codeVerifier copy]; _additionalParameters = [[NSDictionary alloc] initWithDictionary:additionalParameters copyItems:YES]; + _additionalHeaders = + [[NSDictionary alloc] initWithDictionary:additionalHeaders copyItems:YES]; } return self; } @@ -206,6 +228,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_refreshToken forKey:kRefreshTokenKey]; [aCoder encodeObject:_codeVerifier forKey:kCodeVerifierKey]; [aCoder encodeObject:_additionalParameters forKey:kAdditionalParametersKey]; + [aCoder encodeObject:_additionalHeaders forKey:kAdditionalHeadersKey]; } #pragma mark - NSObject overrides @@ -305,6 +328,10 @@ - (NSURLRequest *)URLRequest { for (id header in httpHeaders) { [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; } + + for (id header in _additionalHeaders) { + [URLRequest setValue:httpHeaders[header] forHTTPHeaderField:header]; + } return URLRequest; } diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.h b/Source/AppAuthTV/OIDTVAuthorizationResponse.h index d3bed1e97..c57847c6e 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationResponse.h +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.h @@ -87,6 +87,25 @@ NS_ASSUME_NONNULL_BEGIN - (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: (nullable NSDictionary *)additionalParameters; +/*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. + @param additionalHeaders Additional headers for the token request. + @return A @c OIDTVTokenRequest suitable for polling the token endpoint. + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +- (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalHeaders: + (nullable NSDictionary *)additionalHeaders; + +/*! @brief Creates a token request suitable for polling the token endpoint with the @c deviceCode. + @param additionalParameters Additional parameters for the token request. + @param additionalHeaders Additional headers for the token request. + @return A @c OIDTVTokenRequest suitable for polling the token endpoint. + @see https://tools.ietf.org/html/rfc8628#section-3.4 + */ +- (nullable OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: + (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders; + @end NS_ASSUME_NONNULL_END diff --git a/Source/AppAuthTV/OIDTVAuthorizationResponse.m b/Source/AppAuthTV/OIDTVAuthorizationResponse.m index 71b9e8f04..b45fc85f3 100644 --- a/Source/AppAuthTV/OIDTVAuthorizationResponse.m +++ b/Source/AppAuthTV/OIDTVAuthorizationResponse.m @@ -149,7 +149,7 @@ - (NSString *)description { #pragma mark - - (OIDTVTokenRequest *)tokenPollRequest { - return [self tokenPollRequestWithAdditionalParameters:nil]; + return [self tokenPollRequestWithAdditionalParameters:nil additionalHeaders:nil]; } - (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: @@ -159,7 +159,32 @@ - (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: deviceCode:_deviceCode clientID:self.request.clientID clientSecret:self.request.clientSecret - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:nil]; +} + +- (OIDTVTokenRequest *)tokenPollRequestWithAdditionalHeaders: + (NSDictionary *)additionalHeaders { + return [[OIDTVTokenRequest alloc] + initWithConfiguration:(OIDTVServiceConfiguration *)self.request.configuration + deviceCode:_deviceCode + clientID:self.request.clientID + clientSecret:self.request.clientSecret + additionalParameters:nil + additionalHeaders:additionalHeaders]; +} + +- (OIDTVTokenRequest *)tokenPollRequestWithAdditionalParameters: + (NSDictionary *)additionalParameters + additionalHeaders: + (NSDictionary *)additionalHeaders { + return [[OIDTVTokenRequest alloc] + initWithConfiguration:(OIDTVServiceConfiguration *)self.request.configuration + deviceCode:_deviceCode + clientID:self.request.clientID + clientSecret:self.request.clientSecret + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; } @end diff --git a/Source/AppAuthTV/OIDTVTokenRequest.h b/Source/AppAuthTV/OIDTVTokenRequest.h index 5a81c7434..021dc9b9d 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.h +++ b/Source/AppAuthTV/OIDTVTokenRequest.h @@ -35,14 +35,14 @@ NS_ASSUME_NONNULL_BEGIN /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: or @c initWithCoder:. */ - (instancetype)init NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: or @c initWithCoder:. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -56,11 +56,13 @@ NS_ASSUME_NONNULL_BEGIN codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders NS_UNAVAILABLE; /*! @internal @brief Unavailable. Please use - @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters: + @c initWithConfiguration:deviceCode:clientID:clientSecret:additionalParameters:additionalHeaders: or @c initWithCoder:. */ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -74,6 +76,8 @@ NS_ASSUME_NONNULL_BEGIN codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders NS_UNAVAILABLE; /*! @brief Designated initializer. @@ -82,6 +86,7 @@ NS_ASSUME_NONNULL_BEGIN @param clientID The client identifier. @param clientSecret The client secret (nullable). @param additionalParameters The client's additional token request parameters. + @param additionalHeaders The client's additional token request headers. */ - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration deviceCode:(NSString *)deviceCode @@ -89,6 +94,8 @@ NS_ASSUME_NONNULL_BEGIN clientSecret:(nullable NSString *)clientSecret additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders NS_DESIGNATED_INITIALIZER; /*! @brief Designated initializer for NSSecureCoding. diff --git a/Source/AppAuthTV/OIDTVTokenRequest.m b/Source/AppAuthTV/OIDTVTokenRequest.m index 88874a817..ed5e4d3f2 100644 --- a/Source/AppAuthTV/OIDTVTokenRequest.m +++ b/Source/AppAuthTV/OIDTVTokenRequest.m @@ -43,6 +43,7 @@ - (instancetype)init OID_UNAVAILABLE_USE_INITIALIZER(@selector clientID: clientSecret: additionalParameters: + additionalHeaders: )) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -56,12 +57,15 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders OID_UNAVAILABLE_USE_INITIALIZER(@selector (initWithConfiguration: deviceCode: clientID: clientSecret: additionalParameters: + additionalHeaders: )) - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration @@ -75,19 +79,23 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration codeVerifier:(nullable NSString *)codeVerifier additionalParameters: (nullable NSDictionary *)additionalParameters + additionalHeaders: + (nullable NSDictionary *)additionalHeaders OID_UNAVAILABLE_USE_INITIALIZER(@selector (initWithConfiguration: deviceCode: clientID: clientSecret: additionalParameters: + additionalHeaders: )) - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration deviceCode:(NSString *)deviceCode clientID:(NSString *)clientID clientSecret:(NSString *)clientSecret - additionalParameters:(NSDictionary *)additionalParameters { + additionalParameters:(NSDictionary *)additionalParameters + additionalHeaders:(NSDictionary *)additionalHeaders { self = [super initWithConfiguration:configuration grantType:kOIDTVDeviceTokenGrantType authorizationCode:nil @@ -97,7 +105,8 @@ - (instancetype)initWithConfiguration:(OIDTVServiceConfiguration *)configuration scope:nil refreshToken:nil codeVerifier:nil - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; if (self) { _deviceCode = [deviceCode copy]; diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h index 32497c854..2ffbc5775 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.h @@ -48,10 +48,10 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)testTokenPollRequest; -/*! @brief Tests the @c tokenPollRequestWithAdditionalParameters method with one additional - parameter. +/*! @brief Tests the @c testTokenPollRequestWithAdditionalParametersAdditionalHeaders method with one additional + parameter and one additional header. */ -- (void)testTokenPollRequestWithAdditionalParameters; +- (void)testTokenPollRequestWithAdditionalParametersAdditionalHeaders; @end diff --git a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m index a6d1bb2f5..288228e88 100644 --- a/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m +++ b/UnitTests/AppAuthTV/OIDTVAuthorizationResponseTests.m @@ -45,6 +45,14 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey = @"B"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue = @"2"; + /*! @brief Test value for the @c clientID property. */ static NSString *const kTestClientID = @"ClientID"; @@ -262,22 +270,26 @@ - (void)testTokenPollRequest { XCTAssertEqualObjects(pollRequest.additionalParameters, @{}); } -/*! @brief Tests the @c tokenPollRequestWithAdditionalParameters method with one additional - parameter. +/*! @brief Tests the @c testTokenPollRequestWithAdditionalParametersAdditionalHeaders method with one additional + parameter and one additional header. */ -- (void)testTokenPollRequestWithAdditionalParameters { +- (void)testTokenPollRequestWithAdditionalParametersAdditionalHeaders { OIDTVAuthorizationResponse *testResponse = [self testAuthorizationResponse]; NSDictionary *testAdditionalParameters = @{kTestAdditionalParameterKey : kTestAdditionalParameterValue}; + + NSDictionary *testAdditionalHeaders = + @{kTestAdditionalHeaderKey : kTestAdditionalHeaderValue}; OIDTVTokenRequest *pollRequest = - [testResponse tokenPollRequestWithAdditionalParameters:testAdditionalParameters]; + [testResponse tokenPollRequestWithAdditionalParameters:testAdditionalParameters additionalHeaders:testAdditionalHeaders]; XCTAssertEqualObjects(pollRequest.deviceCode, kTestDeviceCode); XCTAssertEqualObjects(pollRequest.clientID, kTestClientID); XCTAssertEqualObjects(pollRequest.clientSecret, kTestClientSecret); XCTAssertEqualObjects(pollRequest.additionalParameters, testAdditionalParameters); + XCTAssertEqualObjects(pollRequest.additionalHeaders, testAdditionalHeaders); } @end diff --git a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m index cf0bf4963..4778a227b 100644 --- a/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m +++ b/UnitTests/AppAuthTV/OIDTVTokenRequestTests.m @@ -50,6 +50,14 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey = @"B"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue = @"2"; + /*! @brief Test key for the @c clientID parameter in the HTTP request. */ static NSString *const kTestClientIDKey = @"client_id"; @@ -121,7 +129,8 @@ - (OIDTVTokenRequest *)testTokenRequest { deviceCode:kDeviceCodeValue clientID:kTestClientID clientSecret:kTestClientSecret - additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue}]; + additionalParameters:@{kTestAdditionalParameterKey : kTestAdditionalParameterValue} + additionalHeaders:@{kTestAdditionalHeaderKey : kTestAdditionalHeaderValue}]; } /*! @brief Tests the initializer @@ -139,6 +148,8 @@ - (void)testInitializer { XCTAssertEqualObjects(request.clientSecret, kTestClientSecret); XCTAssertEqualObjects(request.additionalParameters, @{kTestAdditionalParameterKey:kTestAdditionalParameterValue}); + XCTAssertEqualObjects(request.additionalHeaders, + @{kTestAdditionalHeaderKey:kTestAdditionalHeaderValue}); } /*! @brief Tests the @c NSCopying implementation by round-tripping an instance through the copying diff --git a/UnitTests/OIDAuthStateTests.m b/UnitTests/OIDAuthStateTests.m index 4d7c3a8b7..d12f2a831 100644 --- a/UnitTests/OIDAuthStateTests.m +++ b/UnitTests/OIDAuthStateTests.m @@ -435,6 +435,27 @@ - (void)testIsTokenFreshHandlesTokenWithoutExpirationTime { XCTAssertEqual([authState isTokenFresh], YES, @""); } +- (void)testThatRefreshTokenExceptionWillBeRaisedForTokenRequestWithAdditionalParameters { + OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:nil tokenResponse:nil registrationResponse:nil]; + XCTAssertThrowsSpecificNamed([authState tokenRefreshRequestWithAdditionalParameters:nil], + NSException, + kRefreshTokenRequestException); +} + +- (void)testThatRefreshTokenExceptionWillBeRaisedForTokenRequestWithAdditionalHeaders { + OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:nil tokenResponse:nil registrationResponse:nil]; + XCTAssertThrowsSpecificNamed([authState tokenRefreshRequestWithAdditionalHeaders:nil], + NSException, + kRefreshTokenRequestException); +} + +- (void)testThatRefreshTokenExceptionWillBeRaisedForTokenRequestWithAdditionalParametersAndHeaders { + OIDAuthState *authState = [[OIDAuthState alloc] initWithAuthorizationResponse:nil tokenResponse:nil registrationResponse:nil]; + XCTAssertThrowsSpecificNamed([authState tokenRefreshRequestWithAdditionalHeaders:nil], + NSException, + kRefreshTokenRequestException); +} + @end #pragma GCC diagnostic pop diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 4211ef70a..2aa865238 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -48,6 +48,14 @@ */ static NSString *const kTestAdditionalParameterValue = @"1"; +/*! @brief Test key for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderKey = @"B"; + +/*! @brief Test value for the @c additionalHeaders property. + */ +static NSString *const kTestAdditionalHeaderValue = @"2"; + @implementation OIDTokenRequestTests + (OIDTokenRequest *)testInstance { @@ -56,6 +64,9 @@ + (OIDTokenRequest *)testInstance { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -66,7 +77,8 @@ + (OIDTokenRequest *)testInstance { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -76,6 +88,9 @@ + (OIDTokenRequest *)testInstanceCodeExchange { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -86,7 +101,8 @@ + (OIDTokenRequest *)testInstanceCodeExchange { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -96,6 +112,9 @@ + (OIDTokenRequest *)testInstanceCodeExchangeClientAuth { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -106,7 +125,8 @@ + (OIDTokenRequest *)testInstanceCodeExchangeClientAuth { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -116,6 +136,9 @@ + (OIDTokenRequest *)testInstanceRefresh { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; + OIDTokenRequest *request = [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode @@ -126,7 +149,8 @@ + (OIDTokenRequest *)testInstanceRefresh { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters]; + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders]; return request; } @@ -157,11 +181,17 @@ - (void)testCopying { XCTAssertEqualObjects(request.codeVerifier, authResponse.request.codeVerifier, @"Request and response codeVerifiers should be equal."); XCTAssertNotNil(request.additionalParameters, - @"Request's additionalParameters field should not be nil."); + @"Request's additionalParameters field should not be nil."); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @"The request's kTestAdditionalParameterKey additional parameter should " "be equal to kTestAdditionalParameterValue."); + XCTAssertNotNil(request.additionalHeaders, + @"Request's additionalHeaders field should not be nil."); + XCTAssertEqualObjects(request.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, + @"The request's kTestAdditionalHeaderKey additional parameter should " + "be equal to kTestAdditionalHeaderValue."); OIDTokenRequest *requestCopy = [request copy]; @@ -181,6 +211,9 @@ - (void)testCopying { XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(requestCopy.additionalHeaders, @""); + XCTAssertEqualObjects(requestCopy.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); } /*! @brief Tests the @c NSSecureCoding by round-tripping an instance through the coding process and @@ -203,6 +236,9 @@ - (void)testSecureCoding { XCTAssertNotNil(request.additionalParameters, @""); XCTAssertEqualObjects(request.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(request.additionalHeaders, @""); + XCTAssertEqualObjects(request.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request]; OIDTokenRequest *requestCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; @@ -224,6 +260,9 @@ - (void)testSecureCoding { XCTAssertNotNil(requestCopy.additionalParameters, @""); XCTAssertEqualObjects(requestCopy.additionalParameters[kTestAdditionalParameterKey], kTestAdditionalParameterValue, @""); + XCTAssertNotNil(requestCopy.additionalHeaders, @""); + XCTAssertEqualObjects(requestCopy.additionalHeaders[kTestAdditionalHeaderKey], + kTestAdditionalHeaderValue, @""); } - (void)testURLRequestNoClientAuth { @@ -248,6 +287,8 @@ - (void)testAuthorizationCodeNullRedirectURL { [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + NSDictionary *additionalHeaders = + @{ kTestAdditionalHeaderKey : kTestAdditionalHeaderValue }; XCTAssertThrows([[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration grantType:OIDGrantTypeAuthorizationCode authorizationCode:authResponse.authorizationCode @@ -257,7 +298,8 @@ - (void)testAuthorizationCodeNullRedirectURL { scopes:scopesArray refreshToken:kRefreshTokenTestValue codeVerifier:authResponse.request.codeVerifier - additionalParameters:additionalParameters], @""); + additionalParameters:additionalParameters + additionalHeaders:additionalHeaders], @""); } @end