Skip to content

Commit

Permalink
dded ID Token parsing and validation.
Browse files Browse the repository at this point in the history
– New OIDIDToken object to parse ID Tokens.
– Token exchange now validates the ID Token pursuant to OpenID Connect Core Section 3.1.3.7. (see code comments for details).
  • Loading branch information
WilliamDenniss committed Jan 5, 2018
1 parent fdb59f3 commit dc55b5c
Show file tree
Hide file tree
Showing 10 changed files with 472 additions and 4 deletions.
28 changes: 28 additions & 0 deletions AppAuth.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@
34AF736B1FB4E4B30022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; };
34AF736C1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; };
34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 039697451FA8258D003D1FB2 /* OIDURLSessionProvider.m */; };
34A663291E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; };
34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; };
34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; };
34A6632C1E871DD40060B664 /* OIDIDToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 34A663261E871DD40060B664 /* OIDIDToken.h */; settings = {ATTRIBUTES = (Public, ); }; };
34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A6632E1E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A6632F1E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A663301E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A663311E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A663321E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A663331E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34A663341E871DD40060B664 /* OIDIDToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A663271E871DD40060B664 /* OIDIDToken.m */; };
34D5EC451E6D1AD900814354 /* OIDSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */; };
34FEA6AE1DB6E083005C9212 /* OIDLoopbackHTTPServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */; };
34FEA6AF1DB6E083005C9212 /* OIDLoopbackHTTPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FEA6AD1DB6E083005C9212 /* OIDLoopbackHTTPServer.m */; };
Expand Down Expand Up @@ -543,6 +555,8 @@
343AAAC21E8348A900F9D36E /* AppAuth.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppAuth.framework; sourceTree = BUILT_PRODUCTS_DIR; };
343AAACA1E8348AA00F9D36E /* AppAuth_macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppAuth_macOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
347423F61E7F4B5600D3E6D6 /* libAppAuth-watchOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libAppAuth-watchOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
34A663261E871DD40060B664 /* OIDIDToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDIDToken.h; sourceTree = "<group>"; };
34A663271E871DD40060B664 /* OIDIDToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OIDIDToken.m; sourceTree = "<group>"; };
34D5EC431E6D1AD900814354 /* OIDAppAuthTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OIDAppAuthTests-Bridging-Header.h"; sourceTree = "<group>"; };
34D5EC441E6D1AD900814354 /* OIDSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDSwiftTests.swift; sourceTree = "<group>"; };
34FEA6AC1DB6E083005C9212 /* OIDLoopbackHTTPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OIDLoopbackHTTPServer.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -756,6 +770,8 @@
60140F7B1DE42E1000DA0DC3 /* OIDRegistrationRequest.m */,
341741C51C5D8243000EF209 /* OIDGrantTypes.h */,
341741C61C5D8243000EF209 /* OIDGrantTypes.m */,
34A663261E871DD40060B664 /* OIDIDToken.h */,
34A663271E871DD40060B664 /* OIDIDToken.m */,
341741C71C5D8243000EF209 /* OIDResponseTypes.h */,
341741C81C5D8243000EF209 /* OIDResponseTypes.m */,
341741C91C5D8243000EF209 /* OIDScopes.h */,
Expand Down Expand Up @@ -876,6 +892,7 @@
343AAAE81E83499000F9D36E /* OIDAuthStateChangeDelegate.h in Headers */,
343AAA6B1E83465500F9D36E /* AppAuth.h in Headers */,
343AAA6E1E83466B00F9D36E /* OIDAuthorizationUICoordinatorIOS.h in Headers */,
34A663291E871DD40060B664 /* OIDIDToken.h in Headers */,
343AAAF21E83499000F9D36E /* OIDResponseTypes.h in Headers */,
343AAAF71E83499000F9D36E /* OIDTokenRequest.h in Headers */,
343AAAF41E83499000F9D36E /* OIDScopeUtilities.h in Headers */,
Expand Down Expand Up @@ -916,6 +933,7 @@
343AAB9B1E834A8800F9D36E /* AppAuth.h in Headers */,
343AAB001E83499100F9D36E /* OIDAuthStateChangeDelegate.h in Headers */,
343AAB081E83499100F9D36E /* OIDRegistrationRequest.h in Headers */,
34A6632A1E871DD40060B664 /* OIDIDToken.h in Headers */,
343AAB101E83499100F9D36E /* OIDTokenResponse.h in Headers */,
343AAAFC1E83499100F9D36E /* OIDAuthorizationResponse.h in Headers */,
343AAB0C1E83499100F9D36E /* OIDScopeUtilities.h in Headers */,
Expand Down Expand Up @@ -944,6 +962,7 @@
343AAB9C1E834A8900F9D36E /* AppAuth.h in Headers */,
343AAB181E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */,
343AAB201E83499200F9D36E /* OIDRegistrationRequest.h in Headers */,
34A6632B1E871DD40060B664 /* OIDIDToken.h in Headers */,
343AAB281E83499200F9D36E /* OIDTokenResponse.h in Headers */,
343AAB141E83499200F9D36E /* OIDAuthorizationResponse.h in Headers */,
343AAB241E83499200F9D36E /* OIDScopeUtilities.h in Headers */,
Expand Down Expand Up @@ -979,6 +998,7 @@
343AAB311E83499200F9D36E /* OIDAuthStateErrorDelegate.h in Headers */,
343AAB2F1E83499200F9D36E /* OIDAuthState.h in Headers */,
343AAB3E1E83499200F9D36E /* OIDServiceDiscovery.h in Headers */,
34A6632C1E871DD40060B664 /* OIDIDToken.h in Headers */,
343AAADE1E83494400F9D36E /* OIDAuthorizationService+Mac.h in Headers */,
343AAB2E1E83499200F9D36E /* OIDAuthorizationUICoordinator.h in Headers */,
343AAB301E83499200F9D36E /* OIDAuthStateChangeDelegate.h in Headers */,
Expand Down Expand Up @@ -1432,6 +1452,7 @@
340DAE581D5821A100EC285B /* OIDAuthorizationUICoordinatorMac.m in Sources */,
340DAE5A1D5821AB00EC285B /* OIDAuthorizationRequest.m in Sources */,
347423E41E7F3C4000D3E6D6 /* OIDAuthorizationResponse.m in Sources */,
34A6632E1E871DD40060B664 /* OIDIDToken.m in Sources */,
340DAE591D5821A100EC285B /* OIDAuthState+Mac.m in Sources */,
34AF73671FB4E4B00022335F /* OIDURLSessionProvider.m in Sources */,
341310D01E6F944B00D5DEE5 /* OIDURLQueryComponent.m in Sources */,
Expand All @@ -1445,6 +1466,7 @@
buildActionMask = 2147483647;
files = (
341741E01C5D8243000EF209 /* OIDErrorUtilities.m in Sources */,
34A6632D1E871DD40060B664 /* OIDIDToken.m in Sources */,
341741EA1C5D8243000EF209 /* OIDTokenUtilities.m in Sources */,
341741E21C5D8243000EF209 /* OIDGrantTypes.m in Sources */,
60140F7C1DE42E1000DA0DC3 /* OIDRegistrationRequest.m in Sources */,
Expand Down Expand Up @@ -1546,6 +1568,7 @@
341310D71E6F944D00D5DEE5 /* OIDRegistrationRequest.m in Sources */,
341310DD1E6F944D00D5DEE5 /* OIDServiceDiscovery.m in Sources */,
341E70991DE18796004353C1 /* OIDAuthorizationResponse.m in Sources */,
34A6632F1E871DD40060B664 /* OIDIDToken.m in Sources */,
341310DB1E6F944D00D5DEE5 /* OIDScopeUtilities.m in Sources */,
341310D61E6F944D00D5DEE5 /* OIDRegistrationResponse.m in Sources */,
341310D31E6F944D00D5DEE5 /* OIDError.m in Sources */,
Expand All @@ -1567,6 +1590,7 @@
buildActionMask = 2147483647;
files = (
343AAA881E83478900F9D36E /* OIDFieldMapping.m in Sources */,
34A663311E871DD40060B664 /* OIDIDToken.m in Sources */,
343AAA841E83478900F9D36E /* OIDAuthState.m in Sources */,
343AAA701E83467D00F9D36E /* OIDAuthState+IOS.m in Sources */,
343AAA921E83478900F9D36E /* OIDTokenResponse.m in Sources */,
Expand Down Expand Up @@ -1626,6 +1650,7 @@
343AAB741E8349B000F9D36E /* OIDRegistrationRequest.m in Sources */,
343AAB7A1E8349B000F9D36E /* OIDServiceDiscovery.m in Sources */,
343AAB6C1E8349B000F9D36E /* OIDAuthorizationResponse.m in Sources */,
34A663321E871DD40060B664 /* OIDIDToken.m in Sources */,
343AAB781E8349B000F9D36E /* OIDScopeUtilities.m in Sources */,
343AAB731E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */,
343AAB701E8349B000F9D36E /* OIDError.m in Sources */,
Expand Down Expand Up @@ -1654,6 +1679,7 @@
343AAB601E8349B000F9D36E /* OIDRegistrationRequest.m in Sources */,
343AAB661E8349B000F9D36E /* OIDServiceDiscovery.m in Sources */,
343AAB581E8349B000F9D36E /* OIDAuthorizationResponse.m in Sources */,
34A663331E871DD40060B664 /* OIDIDToken.m in Sources */,
343AAB641E8349B000F9D36E /* OIDScopeUtilities.m in Sources */,
343AAB5F1E8349B000F9D36E /* OIDRegistrationResponse.m in Sources */,
343AAB5C1E8349B000F9D36E /* OIDError.m in Sources */,
Expand Down Expand Up @@ -1716,6 +1742,7 @@
343AAB491E8349AF00F9D36E /* OIDErrorUtilities.m in Sources */,
343AAADB1E83493D00F9D36E /* OIDAuthorizationUICoordinatorMac.m in Sources */,
343AAB471E8349AF00F9D36E /* OIDClientMetadataParameters.m in Sources */,
34A663341E871DD40060B664 /* OIDIDToken.m in Sources */,
343AAB461E8349AF00F9D36E /* OIDAuthState.m in Sources */,
34AF736D1FB4E4B40022335F /* OIDURLSessionProvider.m in Sources */,
343AAB561E8349AF00F9D36E /* OIDURLQueryComponent.m in Sources */,
Expand Down Expand Up @@ -1757,6 +1784,7 @@
347424081E7F4BA000D3E6D6 /* OIDRegistrationRequest.m in Sources */,
3474240E1E7F4BA000D3E6D6 /* OIDServiceDiscovery.m in Sources */,
347424001E7F4BA000D3E6D6 /* OIDAuthorizationResponse.m in Sources */,
34A663301E871DD40060B664 /* OIDIDToken.m in Sources */,
3474240C1E7F4BA000D3E6D6 /* OIDScopeUtilities.m in Sources */,
347424071E7F4BA000D3E6D6 /* OIDRegistrationResponse.m in Sources */,
347424041E7F4BA000D3E6D6 /* OIDError.m in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Source/AppAuth.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "OIDError.h"
#import "OIDErrorUtilities.h"
#import "OIDGrantTypes.h"
#import "OIDIDToken.h"
#import "OIDRegistrationRequest.h"
#import "OIDRegistrationResponse.h"
#import "OIDResponseTypes.h"
Expand Down
1 change: 1 addition & 0 deletions Source/Framework/AppAuth.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ FOUNDATION_EXPORT const unsigned char AppAuthVersionString[];
#import <AppAuth/OIDError.h>
#import <AppAuth/OIDErrorUtilities.h>
#import <AppAuth/OIDGrantTypes.h>
#import <AppAuth/OIDIDToken.h>
#import <AppAuth/OIDRegistrationRequest.h>
#import <AppAuth/OIDRegistrationResponse.h>
#import <AppAuth/OIDResponseTypes.h>
Expand Down
115 changes: 115 additions & 0 deletions Source/OIDAuthorizationService.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "OIDAuthorizationUICoordinator.h"
#import "OIDDefines.h"
#import "OIDErrorUtilities.h"
#import "OIDIDToken.h"
#import "OIDRegistrationRequest.h"
#import "OIDRegistrationResponse.h"
#import "OIDServiceConfiguration.h"
Expand Down Expand Up @@ -357,6 +358,120 @@ + (void)performTokenRequest:(OIDTokenRequest *)request
return;
}

// If an ID Token is included in the response, validates the ID Token following the rules
// in OpenID Connect Core Section 3.1.3.7 for features that AppAuth directly supports
// (which excludes rules #1, #4, #5, #7, #8, #12, and #13). Regarding rule #6, ID Tokens
// received by this class are received via direct communication between the Client and the Token
// Endpoint, thus we are exercising the option to rely only on the TLS validation. AppAuth
// has a zero dependencies policy, and verifying the JWT signature would add a dependency.
// Users of the library are welcome to perform the JWT signature verification themselves should
// they wish.
if (tokenResponse.idToken) {
OIDIDToken *idToken = [[OIDIDToken alloc] initWithIDTokenString:tokenResponse.idToken];
if (!idToken) {
NSError *invalidIDToken =
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenParsingError
underlyingError:nil
description:@"ID Token parsing failed"];
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, invalidIDToken);
});
return;
}

// OpenID Connect Core Section 3.1.3.7. rule #1
// Not supported: AppAuth does not support JWT encryption.

// OpenID Connect Core Section 3.1.3.7. rule #2
// Validates that the issuer in the ID Token matches that of the discovery document.
NSURL *issuer = tokenResponse.request.configuration.issuer;
if (![idToken.issuer isEqual:issuer]) {
NSError *invalidIDToken =
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
underlyingError:nil
description:@"Issuer mismatch"];
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, invalidIDToken);
});
return;
}

// OpenID Connect Core Section 3.1.3.7. rule #3
// Validates that the audience of the ID Token matches the client ID.
NSString *clientID = tokenResponse.request.clientID;
if (![idToken.audience containsObject:clientID]) {
NSError *invalidIDToken =
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
underlyingError:nil
description:@"Audience mismatch"];
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, invalidIDToken);
});
return;
}

// OpenID Connect Core Section 3.1.3.7. rules #4 & #5
// Not supported.

// OpenID Connect Core Section 3.1.3.7. rule #6
// As noted above, AppAuth only supports the code flow which results in direct communication
// of the ID Token from the Token Endpoint to the Client, and we are exercising the option to
// use TSL server validation instead of checking the token signature. Users may additionally
// check the token signature should they wish.

// OpenID Connect Core Section 3.1.3.7. rules #7 & #8
// Not applicable. See rule #6.

// OpenID Connect Core Section 3.1.3.7. rule #9
// Validates that the current time is before the expiry time.
NSTimeInterval expiresAtDifference = [idToken.expiresAt timeIntervalSinceNow];
if (expiresAtDifference < 0) {
NSError *invalidIDToken =
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
underlyingError:nil
description:@"ID Token expired"];
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, invalidIDToken);
});
return;
}

// OpenID Connect Core Section 3.1.3.7. rule #10
// Validates that the issued at time is not more than +/- 5 minutes on the current time.
NSTimeInterval issuedAtDifference = [idToken.issuedAt timeIntervalSinceNow];
if (fabs(issuedAtDifference) > 300) {
NSError *invalidIDToken =
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
underlyingError:nil
description:@"Issued at time is more than 5 minutes before or after "
"the current time"];
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, invalidIDToken);
});
return;
}

// OpenID Connect Core Section 3.1.3.7. rule #11
// Validates the nonce.
NSString *nonce = authorizationResponse.request.nonce;
if (nonce && ![idToken.nonce isEqual:nonce]) {
NSError *invalidIDToken =
[OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError
underlyingError:nil
description:@"Nonce mismatch"];
dispatch_async(dispatch_get_main_queue(), ^{
callback(nil, invalidIDToken);
});
return;
}

// OpenID Connect Core Section 3.1.3.7. rules #12
// ACR is not directly supported by AppAuth.

// OpenID Connect Core Section 3.1.3.7. rules #12
// max_age is not directly supported by AppAuth.
}

// Success
dispatch_async(dispatch_get_main_queue(), ^{
callback(tokenResponse, nil);
Expand Down
7 changes: 7 additions & 0 deletions Source/OIDError.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ typedef NS_ENUM(NSInteger, OIDErrorCode) {
*/
OIDErrorCodeJSONSerializationError = -13,

/*! @brief The ID Token did not parse.
*/
OIDErrorCodeIDTokenParsingError = -14,

/*! @brief The ID Token did not pass validation (e.g. issuer, audience checks).
*/
OIDErrorCodeIDTokenFailedValidationError = -15,
};

/*! @brief Enum of all possible OAuth error codes as defined by RFC6749
Expand Down
Loading

0 comments on commit dc55b5c

Please sign in to comment.