From 147fdc86c2933dde5f9eda438623dd0206e237d6 Mon Sep 17 00:00:00 2001 From: William Denniss Date: Sat, 25 Mar 2017 11:31:19 -0500 Subject: [PATCH] Use Basic authentication when a client secret is present. --- Source/OIDTokenRequest.m | 38 ++++++++++++++++------- UnitTests/OIDAuthorizationRequestTests.h | 5 +++ UnitTests/OIDAuthorizationRequestTests.m | 21 +++++++++++-- UnitTests/OIDAuthorizationResponseTests.h | 5 +++ UnitTests/OIDAuthorizationResponseTests.m | 13 ++++++++ UnitTests/OIDTokenRequestTests.m | 38 ++++++++++++++++++++++- 6 files changed, 105 insertions(+), 15 deletions(-) diff --git a/Source/OIDTokenRequest.m b/Source/OIDTokenRequest.m index d33043f30..ef5ee30aa 100644 --- a/Source/OIDTokenRequest.m +++ b/Source/OIDTokenRequest.m @@ -219,7 +219,7 @@ - (NSURL *)tokenRequestURL { @return The data to pass to the token request URL. @see https://tools.ietf.org/html/rfc6749#section-4.1.3 */ -- (NSData *)tokenRequestBody { +- (OIDURLQueryComponent *)tokenRequestBody { OIDURLQueryComponent *query = [[OIDURLQueryComponent alloc] init]; // Add parameters, as applicable. @@ -229,12 +229,6 @@ - (NSData *)tokenRequestBody { if (_scope) { [query addParameter:kScopeKey value:_scope]; } - if (_clientID) { - [query addParameter:kClientIDKey value:_clientID]; - } - if (_clientSecret) { - [query addParameter:kClientSecretKey value:_clientSecret]; - } if (_redirectURL) { [query addParameter:kRedirectURLKey value:_redirectURL.absoluteString]; } @@ -251,10 +245,7 @@ - (NSData *)tokenRequestBody { // Add any additional parameters the client has specified. [query addParameters:_additionalParameters]; - // Construct the body string: - NSString *bodyString = [query URLEncodedParameters]; - NSData *body = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; - return body; + return query; } - (NSURLRequest *)URLRequest { @@ -267,7 +258,30 @@ - (NSURLRequest *)URLRequest { NSMutableURLRequest *URLRequest = [[NSURLRequest requestWithURL:tokenRequestURL] mutableCopy]; URLRequest.HTTPMethod = kHTTPPost; [URLRequest setValue:kHTTPContentTypeHeaderValue forHTTPHeaderField:kHTTPContentTypeHeaderKey]; - URLRequest.HTTPBody = [self tokenRequestBody]; + + OIDURLQueryComponent *bodyParameters = [self tokenRequestBody]; + NSMutableDictionary *httpHeaders = [[NSMutableDictionary alloc] init]; + + if (_clientSecret) { + NSString *credentials = [NSString stringWithFormat:@"%@:%@", _clientID, _clientSecret]; + 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; } diff --git a/UnitTests/OIDAuthorizationRequestTests.h b/UnitTests/OIDAuthorizationRequestTests.h index b5caea3ee..db387d5b6 100644 --- a/UnitTests/OIDAuthorizationRequestTests.h +++ b/UnitTests/OIDAuthorizationRequestTests.h @@ -34,6 +34,11 @@ NS_ASSUME_NONNULL_BEGIN */ + (OIDAuthorizationRequest *)testInstanceCodeFlow; +/*! @brief Creates a new @c OIDAuthorizationRequest testing a code flow request + with client authentication. + */ ++ (OIDAuthorizationRequest *)testInstanceCodeFlowClientAuth; + @end NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDAuthorizationRequestTests.m b/UnitTests/OIDAuthorizationRequestTests.m index 4dac84d17..5e4df1dbb 100644 --- a/UnitTests/OIDAuthorizationRequestTests.m +++ b/UnitTests/OIDAuthorizationRequestTests.m @@ -138,7 +138,7 @@ + (OIDAuthorizationRequest *)testInstance { [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID clientSecret:kTestClientSecret - scope:[OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]] + scope:[OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]] redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:kTestResponseType state:kTestState @@ -150,12 +150,29 @@ + (OIDAuthorizationRequest *)testInstance { } + (OIDAuthorizationRequest *)testInstanceCodeFlow { + OIDServiceConfiguration *configuration = [OIDServiceConfigurationTests testInstance]; + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration + clientId:kTestClientID + clientSecret:nil + scope:[OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]] + redirectURL:[NSURL URLWithString:kTestRedirectURL] + responseType:OIDResponseTypeCode + state:kTestState + codeVerifier:kTestCodeVerifier + codeChallenge:[[self class] codeChallenge] + codeChallengeMethod:[[self class] codeChallengeMethod] + additionalParameters:nil]; + return request; +} + ++ (OIDAuthorizationRequest *)testInstanceCodeFlowClientAuth { OIDServiceConfiguration *configuration = [OIDServiceConfigurationTests testInstance]; OIDAuthorizationRequest *request = [[OIDAuthorizationRequest alloc] initWithConfiguration:configuration clientId:kTestClientID clientSecret:kTestClientSecret - scope:[OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]] + scope:[OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]] redirectURL:[NSURL URLWithString:kTestRedirectURL] responseType:OIDResponseTypeCode state:kTestState diff --git a/UnitTests/OIDAuthorizationResponseTests.h b/UnitTests/OIDAuthorizationResponseTests.h index 15027ef31..8f7e669d4 100644 --- a/UnitTests/OIDAuthorizationResponseTests.h +++ b/UnitTests/OIDAuthorizationResponseTests.h @@ -34,6 +34,11 @@ NS_ASSUME_NONNULL_BEGIN */ + (OIDAuthorizationResponse *)testInstanceCodeFlow; +/*! @brief Creates a new @c OIDAuthorizationResponse with client authentication for testing the + code flow. + */ ++ (OIDAuthorizationResponse *)testInstanceCodeFlowClientAuth; + @end NS_ASSUME_NONNULL_END diff --git a/UnitTests/OIDAuthorizationResponseTests.m b/UnitTests/OIDAuthorizationResponseTests.m index 4262450be..f0a437555 100644 --- a/UnitTests/OIDAuthorizationResponseTests.m +++ b/UnitTests/OIDAuthorizationResponseTests.m @@ -95,6 +95,19 @@ + (OIDAuthorizationResponse *)testInstanceCodeFlow { return response; } ++ (OIDAuthorizationResponse *)testInstanceCodeFlowClientAuth { + OIDAuthorizationRequest *request = [OIDAuthorizationRequestTests testInstanceCodeFlowClientAuth]; + OIDAuthorizationResponse *response = + [[OIDAuthorizationResponse alloc] initWithRequest:request parameters:@{ + @"code" : kTestAuthorizationCode, + @"code_verifier" : kTestAuthorizationCodeVerifier, + @"state" : kTestState, + @"token_type" : OIDGrantTypeAuthorizationCode, + kTestAdditionalParameterKey : kTestAdditionalParameterValue + }]; + 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. */ diff --git a/UnitTests/OIDTokenRequestTests.m b/UnitTests/OIDTokenRequestTests.m index 282d2c2b4..3897de81c 100644 --- a/UnitTests/OIDTokenRequestTests.m +++ b/UnitTests/OIDTokenRequestTests.m @@ -61,7 +61,27 @@ + (OIDTokenRequest *)testInstance { } + (OIDTokenRequest *)testInstanceCodeExchange { - OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstance]; + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstanceCodeFlow]; + NSArray *scopesArray = + [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; + NSDictionary *additionalParameters = + @{ kTestAdditionalParameterKey : kTestAdditionalParameterValue }; + OIDTokenRequest *request = + [[OIDTokenRequest alloc] initWithConfiguration:authResponse.request.configuration + grantType:OIDGrantTypeAuthorizationCode + authorizationCode:authResponse.authorizationCode + redirectURL:authResponse.request.redirectURL + clientID:authResponse.request.clientID + clientSecret:authResponse.request.clientSecret + scopes:scopesArray + refreshToken:kRefreshTokenTestValue + codeVerifier:authResponse.request.codeVerifier + additionalParameters:additionalParameters]; + return request; +} + ++ (OIDTokenRequest *)testInstanceCodeExchangeClientAuth { + OIDAuthorizationResponse *authResponse = [OIDAuthorizationResponseTests testInstanceCodeFlowClientAuth]; NSArray *scopesArray = [OIDScopeUtilities scopesArrayWithString:authResponse.request.scope]; NSDictionary *additionalParameters = @@ -185,4 +205,20 @@ - (void)testSecureCoding { kTestAdditionalParameterValue); } +- (void)testURLRequestNoClientAuth { + OIDTokenRequest *request = [[self class] testInstanceCodeExchange]; + NSURLRequest *urlRequest = [request URLRequest]; + + id authorization = [urlRequest.allHTTPHeaderFields objectForKey:@"Authorization"]; + XCTAssertNil(authorization); +} + +- (void)testURLRequestBasicClientAuth { + OIDTokenRequest *request = [[self class] testInstanceCodeExchangeClientAuth]; + NSURLRequest* urlRequest = [request URLRequest]; + + id authorization = [urlRequest.allHTTPHeaderFields objectForKey:@"Authorization"]; + XCTAssertNotNil(authorization); +} + @end