Skip to content

Commit

Permalink
OAuth2:
Browse files Browse the repository at this point in the history
- add new method -postProcessAuthenticationDataDict: to condense the stored token response to the needed essentials, saving space and steering clear of issues if the token response contains null values that make secret serialization to property list format fail
- factor out access to clientID and clientSecret into methods
- make -sendTokenRequestToConnection:… subclass able
- improved error handling if property list serialization fails

OIDC:
- add support for OpenID Connect Dynamic Client Registration
- on by default for servers offering the endpoint
- including support for expiration, preservation and caching
- add additional error code for client registration failure

HTTP Pipeline:
- factor out User-Agent template composition method to make it available
- extend OCHTTPRequest with JSON-specific method to easily instantiate POST requests with JSON payload
  • Loading branch information
felix-schwarz committed Jan 8, 2021
1 parent cd30940 commit 85e09cb
Show file tree
Hide file tree
Showing 14 changed files with 590 additions and 37 deletions.
8 changes: 8 additions & 0 deletions ownCloudSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@
DC98BDF821E73EFF003B5658 /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC98BDF721E73EFF003B5658 /* Network.framework */; settings = {ATTRIBUTES = (Required, ); }; };
DC9B4D3922E987EF0089BF78 /* OCClaim.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1D4D3E20DC2281005A3DFC /* OCClaim.m */; };
DC9B4D3A22E987EF0089BF78 /* OCClaim.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1D4D3D20DC2281005A3DFC /* OCClaim.h */; settings = {ATTRIBUTES = (Public, ); }; };
DC9D22EA25A8754200CF5675 /* OCHTTPRequest+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = DC9D22E825A8754200CF5675 /* OCHTTPRequest+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; };
DC9D22EB25A8754200CF5675 /* OCHTTPRequest+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = DC9D22E925A8754200CF5675 /* OCHTTPRequest+JSON.m */; };
DCA35D4D24CF685B00DBE2B0 /* OCDiagnosticNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA35D4B24CF685B00DBE2B0 /* OCDiagnosticNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DCA35D4E24CF685B00DBE2B0 /* OCDiagnosticNode.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA35D4C24CF685B00DBE2B0 /* OCDiagnosticNode.m */; };
DCA35D5524CF688700DBE2B0 /* OCDiagnosticSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA35D5324CF688700DBE2B0 /* OCDiagnosticSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -1134,6 +1136,8 @@
DC98BDF321E73ECE003B5658 /* OCCoreNetworkMonitorSignalProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCCoreNetworkMonitorSignalProvider.h; sourceTree = "<group>"; };
DC98BDF421E73ECE003B5658 /* OCCoreNetworkMonitorSignalProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCCoreNetworkMonitorSignalProvider.m; sourceTree = "<group>"; };
DC98BDF721E73EFF003B5658 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
DC9D22E825A8754200CF5675 /* OCHTTPRequest+JSON.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OCHTTPRequest+JSON.h"; sourceTree = "<group>"; };
DC9D22E925A8754200CF5675 /* OCHTTPRequest+JSON.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "OCHTTPRequest+JSON.m"; sourceTree = "<group>"; };
DCA35D4B24CF685B00DBE2B0 /* OCDiagnosticNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCDiagnosticNode.h; sourceTree = "<group>"; };
DCA35D4C24CF685B00DBE2B0 /* OCDiagnosticNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCDiagnosticNode.m; sourceTree = "<group>"; };
DCA35D5324CF688700DBE2B0 /* OCDiagnosticSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCDiagnosticSource.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2089,6 +2093,8 @@
DC8556EB204DEA2900189B9A /* OCHTTPDAVRequest.h */,
DC00DB1C219B120300C82737 /* OCHTTPDAVMultistatusResponse.m */,
DC00DB1B219B120300C82737 /* OCHTTPDAVMultistatusResponse.h */,
DC9D22E925A8754200CF5675 /* OCHTTPRequest+JSON.m */,
DC9D22E825A8754200CF5675 /* OCHTTPRequest+JSON.h */,
);
path = Request;
sourceTree = "<group>";
Expand Down Expand Up @@ -3073,6 +3079,7 @@
DCDD9B18222989E50052A001 /* OCRecipient.h in Headers */,
DCADC03F2072774200DB8E83 /* OCQuery+Internal.h in Headers */,
DC4A2C5E20D4608100A47260 /* OCIssueChoice.h in Headers */,
DC9D22EA25A8754200CF5675 /* OCHTTPRequest+JSON.h in Headers */,
DC07C29221244FD800B815A4 /* OCExtension.h in Headers */,
DCC8FA25202B259D00EB6701 /* OCSyncRecord.h in Headers */,
DC2D646821C3D71000EB26FD /* OCCore+Thumbnails.h in Headers */,
Expand Down Expand Up @@ -3599,6 +3606,7 @@
DC3CE066242A49E100AB8B88 /* OCMessagePresenter.m in Sources */,
DCD038A12542CA4500F97534 /* NSString+OCClassSettings.m in Sources */,
DC68057E212EB438006C3B1F /* OCExtensionMatch.m in Sources */,
DC9D22EB25A8754200CF5675 /* OCHTTPRequest+JSON.m in Sources */,
DC302AEF221EAC55003218C6 /* OCProxyProgress.m in Sources */,
DC4B1172220830F20062BCDD /* OCHTTPPipelineBackend.m in Sources */,
DCE26621211348B00001FB2C /* OCCore+CommandLocalImport.m in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions ownCloudSDK/Authentication/OCAuthenticationMethod.m
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ - (void)flushCachedAuthenticationSecret
@synchronized(self)
{
_cachedAuthenticationSecret = nil;
[self willChangeValueForKey:@"authenticationDataKnownInvalidDate"];
_authenticationDataKnownInvalidDate = nil;
[self didChangeValueForKey:@"authenticationDataKnownInvalidDate"];
}
}

Expand Down
11 changes: 11 additions & 0 deletions ownCloudSDK/Authentication/OCAuthenticationMethodOAuth2.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSInteger, OCAuthenticationOAuth2TokenRequestType)
{
OCAuthenticationOAuth2TokenRequestTypeAuthorizationCode,
OCAuthenticationOAuth2TokenRequestTypeRefreshToken
};

@interface OCAuthenticationMethodOAuth2 : OCAuthenticationMethod <OCClassSettingsSupport>

@property(strong,nullable,class,nonatomic) Class browserSessionClass;
Expand All @@ -38,9 +44,14 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)redirectURIForConnection:(OCConnection *)connection;
- (NSDictionary<NSString *, NSString *> *)prepareAuthorizationRequestParameters:(NSDictionary<NSString *,NSString *> *)parameters forConnection:(OCConnection *)connection options:(nullable OCAuthenticationMethodBookmarkAuthenticationDataGenerationOptions)options;
- (NSDictionary<NSString *, NSString *> *)tokenRefreshParametersForRefreshToken:(NSString *)refreshToken;
- (NSDictionary<NSString *, id> *)postProcessAuthenticationDataDict:(NSDictionary<NSString *, id> *)authDataDict;
- (void)retrieveEndpointInformationForConnection:(OCConnection *)connection completionHandler:(void(^)(NSError *error))completionHandler;
- (nullable NSString *)scope;
- (nullable NSString *)prompt;
- (NSString *)clientID;
- (NSString *)clientSecret;

- (void)sendTokenRequestToConnection:(OCConnection *)connection withParameters:(NSDictionary<NSString*,NSString*> *)parameters requestType:(OCAuthenticationOAuth2TokenRequestType)requestType completionHandler:(void(^)(NSError * _Nullable error, NSDictionary * _Nullable jsonResponseDict, NSData * _Nullable authenticationData))completionHandler;

@end

Expand Down
64 changes: 49 additions & 15 deletions ownCloudSDK/Authentication/OCAuthenticationMethodOAuth2.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,15 @@

static OA2DictKeyPath OA2TokenResponse = @"tokenResponse";
static OA2DictKeyPath OA2BearerString = @"bearerString";

// FOR ADDITIONS, ALSO UPDATE -postProcessAuthenticationDataDict: !!
static OA2DictKeyPath OA2AccessToken = @"tokenResponse.access_token";
static OA2DictKeyPath OA2RefreshToken = @"tokenResponse.refresh_token";
static OA2DictKeyPath OA2ExpiresInSecs = @"tokenResponse.expires_in";
static OA2DictKeyPath OA2TokenType = @"tokenResponse.token_type";
static OA2DictKeyPath OA2MessageURL = @"tokenResponse.message_url";
static OA2DictKeyPath OA2UserID = @"tokenResponse.user_id";

#define OA2RefreshSafetyMarginInSeconds 120

typedef NS_ENUM(NSInteger, OCAuthenticationOAuth2TokenRequestType)
{
OCAuthenticationOAuth2TokenRequestTypeAuthorizationCode,
OCAuthenticationOAuth2TokenRequestTypeRefreshToken
};

#ifndef __IPHONE_13_0
#define __IPHONE_13_0 130000
#endif /* __IPHONE_13_0 */
Expand Down Expand Up @@ -206,6 +200,30 @@ - (void)retrieveEndpointInformationForConnection:(OCConnection *)connection comp
completionHandler(OCError(OCErrorFeatureNotImplemented));
}

- (NSDictionary<NSString *, id> *)postProcessAuthenticationDataDict:(NSDictionary<NSString *, id> *)authDataDict
{
NSDictionary<NSString *, id> *tokenResponseDict;

// JSON *could* contain unneeded/unrelated NSNull.null values, which can't be serialized as NSPropertyList, so limit
// the entries in the response dictionary to what is actually needed
if ((tokenResponseDict = authDataDict[OA2TokenResponse]) != nil)
{
NSMutableDictionary<NSString *, id> *condensedAuthDataDict = [authDataDict mutableCopy];
NSMutableDictionary<NSString *, id> *condensedTokenResponse = [NSMutableDictionary new];

condensedTokenResponse[@"access_token"] = tokenResponseDict[@"access_token"];
condensedTokenResponse[@"refresh_token"] = tokenResponseDict[@"refresh_token"];
condensedTokenResponse[@"expires_in"] = tokenResponseDict[@"expires_in"];
condensedTokenResponse[@"user_id"] = tokenResponseDict[@"user_id"];

condensedAuthDataDict[OA2TokenResponse] = condensedTokenResponse;

return (condensedAuthDataDict);
}

return (authDataDict);
}

- (nullable NSString *)scope
{
return (nil);
Expand All @@ -216,6 +234,16 @@ - (nullable NSString *)prompt
return (nil);
}

- (NSString *)clientID
{
return ([self classSettingForOCClassSettingsKey:OCAuthenticationMethodOAuth2ClientID]);
}

- (NSString *)clientSecret
{
return ([self classSettingForOCClassSettingsKey:OCAuthenticationMethodOAuth2ClientSecret]);
}

#pragma mark - Authentication / Deauthentication ("Login / Logout")
- (NSDictionary<NSString *, NSString *> *)authorizationHeadersForConnection:(OCConnection *)connection error:(NSError **)outError
{
Expand Down Expand Up @@ -320,7 +348,7 @@ - (void)generateBookmarkAuthenticationDataWithConnection:(OCConnection *)connect
NSDictionary<NSString *,NSString *> *parameters = @{
// OAuth2
@"response_type" : @"code",
@"client_id" : [self classSettingForOCClassSettingsKey:OCAuthenticationMethodOAuth2ClientID],
@"client_id" : [self clientID],
@"redirect_uri" : [self redirectURIForConnection:connection],

// OAuth2 PKCE
Expand Down Expand Up @@ -352,7 +380,7 @@ - (void)generateBookmarkAuthenticationDataWithConnection:(OCConnection *)connect
OCLogDebug(@"Auth session concluded with authorization code: %@", OCLogPrivate(authorizationCode));

// Send Access Token Request
[self _sendTokenRequestToConnection:connection
[self sendTokenRequestToConnection:connection
withParameters:@{
// OAuth2
@"grant_type" : @"authorization_code",
Expand Down Expand Up @@ -601,7 +629,7 @@ - (void)_refreshTokenForConnection:(OCConnection *)connection availabilityHandle
{
OCLogDebug(@"Sending token refresh request for connection (expiry=%@)..", authSecret[OA2ExpirationDate]);

[self _sendTokenRequestToConnection:connection
[self sendTokenRequestToConnection:connection
withParameters:[self tokenRefreshParametersForRefreshToken:refreshToken]
requestType:OCAuthenticationOAuth2TokenRequestTypeRefreshToken
completionHandler:^(NSError *error, NSDictionary *jsonResponseDict, NSData *authenticationData){
Expand Down Expand Up @@ -666,7 +694,7 @@ - (void)_refreshTokenForConnection:(OCConnection *)connection availabilityHandle
}
}

- (void)_sendTokenRequestToConnection:(OCConnection *)connection withParameters:(NSDictionary<NSString*,NSString*> *)parameters requestType:(OCAuthenticationOAuth2TokenRequestType)requestType completionHandler:(void(^)(NSError *error, NSDictionary *jsonResponseDict, NSData *authenticationData))completionHandler
- (void)sendTokenRequestToConnection:(OCConnection *)connection withParameters:(NSDictionary<NSString*,NSString*> *)parameters requestType:(OCAuthenticationOAuth2TokenRequestType)requestType completionHandler:(void(^)(NSError *error, NSDictionary *jsonResponseDict, NSData *authenticationData))completionHandler
{
OCHTTPRequest *tokenRequest;
NSDictionary<NSString *, id> *previousAuthSecret = (requestType == OCAuthenticationOAuth2TokenRequestTypeRefreshToken) ? [self cachedAuthenticationSecretForConnection:connection] : nil;
Expand Down Expand Up @@ -696,7 +724,7 @@ - (void)_sendTokenRequestToConnection:(OCConnection *)connection withParameters:
[self retrieveEndpointInformationForConnection:connection completionHandler:^(NSError * _Nonnull error) {
if (error == nil)
{
[self _sendTokenRequestToConnection:connection withParameters:parameters requestType:requestType completionHandler:completionHandler];
[self sendTokenRequestToConnection:connection withParameters:parameters requestType:requestType completionHandler:completionHandler];
}
else
{
Expand All @@ -719,8 +747,7 @@ - (void)_sendTokenRequestToConnection:(OCConnection *)connection withParameters:

[tokenRequest addParameters:parameters];

[tokenRequest setValue:[OCAuthenticationMethod basicAuthorizationValueForUsername:[self classSettingForOCClassSettingsKey:OCAuthenticationMethodOAuth2ClientID] passphrase:[self classSettingForOCClassSettingsKey:OCAuthenticationMethodOAuth2ClientSecret]]
forHeaderField:OCHTTPHeaderFieldNameAuthorization];
[tokenRequest setValue:[OCAuthenticationMethod basicAuthorizationValueForUsername:[self clientID] passphrase:[self clientSecret]] forHeaderField:OCHTTPHeaderFieldNameAuthorization];

// Send Token Request
[connection sendRequest:tokenRequest ephermalCompletionHandler:^(OCHTTPRequest *request, OCHTTPResponse *response, NSError *error) {
Expand Down Expand Up @@ -805,12 +832,19 @@ - (void)_sendTokenRequestToConnection:(OCConnection *)connection withParameters:
OA2TokenResponse : jsonResponseDict
};

// Give opportunity to add additional keys
authenticationDataDict = [self postProcessAuthenticationDataDict:authenticationDataDict];

OCLogDebug(@"Token authorization succeeded with: %@", OCLogPrivate(authenticationDataDict));

if ((authenticationData = [NSPropertyListSerialization dataWithPropertyList:authenticationDataDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error]) != nil)
{
completionHandler(nil, jsonResponseDict, authenticationData);
}
else if (error != nil)
{
completionHandler(OCErrorFromError(OCErrorInternal, error), nil, nil);
}
else if (error == nil)
{
completionHandler(OCError(OCErrorInternal), nil, nil);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ extern OCAuthenticationMethodIdentifier OCAuthenticationMethodIdentifierOpenIDCo

extern OCClassSettingsKey OCAuthenticationMethodOpenIDConnectRedirectURI;
extern OCClassSettingsKey OCAuthenticationMethodOpenIDConnectScope;
extern OCClassSettingsKey OCAuthenticationMethodOpenIDRegisterClient;
extern OCClassSettingsKey OCAuthenticationMethodOpenIDRegisterClientNameTemplate;

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 85e09cb

Please sign in to comment.