Skip to content

Commit

Permalink
Allow applications to use the "code id_token" response_type
Browse files Browse the repository at this point in the history
This response type is required to build a FAPI compliant client, and as
the application can verify the id_token signature and c_hash before
calling the token endpoint there is no reason to block applications from
using code id_token.

Other response types are still blocked as they don't appear to make sense
in the context of a native app.

The 'id_token code' response_type is also permitted as
https://tools.ietf.org/html/rfc6749#section-3.1.1 explicitly allows
this.

closes openid#292
  • Loading branch information
jogu authored and WilliamDenniss committed Sep 21, 2018
1 parent 2c934c9 commit dd945e1
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 18 deletions.
6 changes: 5 additions & 1 deletion Source/OIDAuthState.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ typedef void (^OIDAuthStateAuthorizationCallback)(OIDAuthState *_Nullable authSt
@property(nonatomic, weak, nullable) id<OIDAuthStateErrorDelegate> errorDelegate;

/*! @brief Convenience method to create a @c OIDAuthState by presenting an authorization request
and performing the authorization code exchange in the case of code flow requests.
and performing the authorization code exchange in the case of code flow requests. For
the hybrid flow, the caller should validate the id_token and c_hash, then perform the token
request (@c OIDAuthorizationService.performTokenRequest:callback:)
and update the OIDAuthState with the results (@c
OIDAuthState.updateWithTokenResponse:error:).
@param authorizationRequest The authorization request to present.
@param externalUserAgent A external user agent that can present an external user-agent request.
@param callback The method called when the request has completed or failed.
Expand Down
9 changes: 7 additions & 2 deletions Source/OIDAuthState.m
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,13 @@ @implementation OIDAuthState {
callback(authState, tokenError);
}];
} else {
// implicit or hybrid flow (hybrid flow assumes code is not for this
// client)
// hybrid flow (code id_token). Two possible cases:
// 1. The code is not for this client, ie. will be sent to a
// webservice that performs the id token verification and token
// exchange
// 2. The code is for this client and, for security reasons, the
// application developer must verify the id_token signature and
// c_hash before calling the token endpoint
OIDAuthState *authState = [[OIDAuthState alloc]
initWithAuthorizationResponse:authorizationResponse];
callback(authState, authorizationError);
Expand Down
32 changes: 20 additions & 12 deletions Source/OIDAuthorizationRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
/*! @brief Assertion text for unsupported response types.
*/
static NSString *const OIDOAuthUnsupportedResponseTypeMessage =
@"The response_type \"%@\" isn't supported. AppAuth only supports the \"code\" response_type.";
@"The response_type \"%@\" isn't supported. AppAuth only supports the \"code\" or \"code id_token\" response_type.";

/*! @brief Code challenge request method.
*/
Expand All @@ -105,6 +105,23 @@ - (instancetype)init
additionalParameters:)
)

/*! @brief Check if the response type is one AppAuth supports
@remarks AppAuth only supports the `code` and `code id_token` response types.
@see https://github.com/openid/AppAuth-iOS/issues/98
@see https://github.com/openid/AppAuth-iOS/issues/292
*/
+ (BOOL)isSupportedResponseType:(NSString *)responseType
{
NSString *codeIdToken = [@[OIDResponseTypeCode, OIDResponseTypeIDToken]
componentsJoinedByString:@" "];
NSString *idTokenCode = [@[OIDResponseTypeIDToken, OIDResponseTypeCode]
componentsJoinedByString:@" "];

return [responseType isEqualToString:OIDResponseTypeCode]
|| [responseType isEqualToString:codeIdToken]
|| [responseType isEqualToString:idTokenCode];
}

- (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
clientId:(NSString *)clientID
clientSecret:(nullable NSString *)clientSecret
Expand All @@ -126,11 +143,7 @@ - (instancetype)initWithConfiguration:(OIDServiceConfiguration *)configuration
_scope = [scope copy];
_redirectURL = [redirectURL copy];
_responseType = [responseType copy];
// Attention: Please refer to https://github.com/openid/AppAuth-iOS/issues/105
// If you change the restriction on response type here, you must also update initWithCoder:
if (![_responseType isEqualToString:OIDResponseTypeCode]) {
// AppAuth only supports the `code` response type.
// Discussion: https://github.com/openid/AppAuth-iOS/issues/98
if (![[self class] isSupportedResponseType:_responseType]) {
NSAssert(NO, OIDOAuthUnsupportedResponseTypeMessage, _responseType);
return nil;
}
Expand Down Expand Up @@ -209,12 +222,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder {
OIDServiceConfiguration *configuration =
[aDecoder decodeObjectOfClass:[OIDServiceConfiguration class]
forKey:kConfigurationKey];
// Attention: Please refer to https://github.com/openid/AppAuth-iOS/issues/105
// If the initializer relaxes it's restriction on the response type field, this code must also
// be updated to re-enable use of the serialized responseType value. The value of 'code' here
// is only a valid assumption for that reason.
// [aDecoder decodeObjectOfClass:[NSString class] forKey:kResponseTypeKey];
NSString *responseType = OIDResponseTypeCode;
NSString *responseType = [aDecoder decodeObjectOfClass:[NSString class] forKey:kResponseTypeKey];
NSString *clientID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientIDKey];
NSString *clientSecret = [aDecoder decodeObjectOfClass:[NSString class] forKey:kClientSecretKey];
NSString *scope = [aDecoder decodeObjectOfClass:[NSString class] forKey:kScopeKey];
Expand Down
6 changes: 5 additions & 1 deletion Source/iOS/OIDAuthState+IOS.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
@interface OIDAuthState (IOS)

/*! @brief Convenience method to create a @c OIDAuthState by presenting an authorization request
and performing the authorization code exchange in the case of code flow requests.
and performing the authorization code exchange in the case of code flow requests. For
the hybrid flow, the caller should validate the id_token and c_hash, then perform the token
request (@c OIDAuthorizationService.performTokenRequest:callback:)
and update the OIDAuthState with the results (@c
OIDAuthState.updateWithTokenResponse:error:).
@param authorizationRequest The authorization request to present.
@param presentingViewController The view controller from which to present the
@c SFSafariViewController.
Expand Down
6 changes: 5 additions & 1 deletion Source/macOS/OIDAuthState+Mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ NS_ASSUME_NONNULL_BEGIN
@interface OIDAuthState (Mac)

/*! @brief Convenience method to create a @c OIDAuthState by presenting an authorization request
and performing the authorization code exchange in the case of code flow requests.
and performing the authorization code exchange in the case of code flow requests. For
the hybrid flow, the caller should validate the id_token and c_hash, then perform the token
request (@c OIDAuthorizationService.performTokenRequest:callback:)
and update the OIDAuthState with the results (@c
OIDAuthState.updateWithTokenResponse:error:).
@param authorizationRequest The authorization request to present.
@param callback The method called when the request has completed or failed.
@return A @c OIDExternalUserAgentSession instance which will terminate when it
Expand Down
33 changes: 32 additions & 1 deletion UnitTests/OIDAuthorizationRequestTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ - (void)testSupportedResponseTypes {

NSString *scope = [OIDScopeUtilities scopesWithArray:@[ kTestScope, kTestScopeA ]];

XCTAssertThrows(
XCTAssertNoThrow(
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kTestClientID
clientSecret:kTestClientSecret
Expand All @@ -455,6 +455,22 @@ - (void)testSupportedResponseTypes {
additionalParameters:additionalParameters]
);

// https://tools.ietf.org/html/rfc6749#section-3.1.1 says the order of values does not matter
XCTAssertNoThrow(
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kTestClientID
clientSecret:kTestClientSecret
scope:scope
redirectURL:[NSURL URLWithString:kTestRedirectURL]
responseType:@"id_token code"
state:kTestState
nonce:kTestNonce
codeVerifier:kTestCodeVerifier
codeChallenge:[[self class] codeChallenge]
codeChallengeMethod:[[self class] codeChallengeMethod]
additionalParameters:additionalParameters]
);

XCTAssertThrows(
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kTestClientID
Expand All @@ -470,6 +486,21 @@ - (void)testSupportedResponseTypes {
additionalParameters:additionalParameters]
);

XCTAssertThrows(
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kTestClientID
clientSecret:kTestClientSecret
scope:scope
redirectURL:[NSURL URLWithString:kTestRedirectURL]
responseType:@"token"
state:kTestState
nonce:kTestNonce
codeVerifier:kTestCodeVerifier
codeChallenge:[[self class] codeChallenge]
codeChallengeMethod:[[self class] codeChallengeMethod]
additionalParameters:additionalParameters]
);

XCTAssertNoThrow(
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:kTestClientID
Expand Down

0 comments on commit dd945e1

Please sign in to comment.