From 4bf1b864f2fa281d10f5f1d14f2913a90b997732 Mon Sep 17 00:00:00 2001 From: Jan Gorman Date: Wed, 6 Jan 2016 14:40:06 +0100 Subject: [PATCH 1/2] Add support for Keychain sharing --- AFOAuth2Manager/AFOAuth2Manager.h | 26 +++++++----- AFOAuth2Manager/AFOAuth2Manager.m | 67 ++++++++++++++++++------------- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/AFOAuth2Manager/AFOAuth2Manager.h b/AFOAuth2Manager/AFOAuth2Manager.h index f7f325a6..e1f1426d 100644 --- a/AFOAuth2Manager/AFOAuth2Manager.h +++ b/AFOAuth2Manager/AFOAuth2Manager.h @@ -24,6 +24,8 @@ #import +NS_ASSUME_NONNULL_BEGIN + @class AFOAuthCredential; /** @@ -253,13 +255,14 @@ Stores the specified OAuth credential for a given web service identifier in the Keychain. with the default Keychain Accessibilty of kSecAttrAccessibleWhenUnlocked. - @param credential The OAuth credential to be stored. - @param identifier The service identifier associated with the specified credential. + @param credential The OAuth credential to be stored. + @param identifier The service identifier associated with the specified credential. + @param accessGroup The optional access group for shared Keychains. @return Whether or not the credential was stored in the keychain. */ -+ (BOOL)storeCredential:(AFOAuthCredential *)credential - withIdentifier:(NSString *)identifier; ++ (BOOL)storeCredential:(AFOAuthCredential *)credential withIdentifier:(NSString *)identifier + accessGroup:(nullable NSString *)accessGroup; /** Stores the specified OAuth token for a given web service identifier in the Keychain. @@ -267,30 +270,33 @@ @param credential The OAuth credential to be stored. @param identifier The service identifier associated with the specified token. @param securityAccessibility The Keychain security accessibility to store the credential with. + @param accessGroup The optional access group for shared Keychains. @return Whether or not the credential was stored in the keychain. */ -+ (BOOL)storeCredential:(AFOAuthCredential *)credential - withIdentifier:(NSString *)identifier - withAccessibility:(id)securityAccessibility; ++ (BOOL)storeCredential:(AFOAuthCredential *)credential withIdentifier:(NSString *)identifier + withAccessibility:(id)securityAccessibility accessGroup:(nullable NSString *)accessGroup; /** Retrieves the OAuth credential stored with the specified service identifier from the Keychain. @param identifier The service identifier associated with the specified credential. + @param accessGroup The optional access group for shared Keychains. @return The retrieved OAuth credential. */ -+ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier; ++ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier + accessGroup:(nullable NSString *)accessGroup; /** Deletes the OAuth credential stored with the specified service identifier from the Keychain. @param identifier The service identifier associated with the specified credential. + @param accessGroup The optional access group for shared Keychains. @return Whether or not the credential was deleted from the keychain. */ -+ (BOOL)deleteCredentialWithIdentifier:(NSString *)identifier; ++ (BOOL)deleteCredentialWithIdentifier:(NSString *)identifier accessGroup:(nullable NSString *)accessGroup; @end @@ -323,3 +329,5 @@ extern NSString * const kAFOAuthRefreshGrantType; @compatibility_alias AFOAuth2Client AFOAuth2Manager; @compatibility_alias AFOAuth2RequestOperationManager AFOAuth2Manager; + +NS_ASSUME_NONNULL_END diff --git a/AFOAuth2Manager/AFOAuth2Manager.m b/AFOAuth2Manager/AFOAuth2Manager.m index 13424444..99330553 100644 --- a/AFOAuth2Manager/AFOAuth2Manager.m +++ b/AFOAuth2Manager/AFOAuth2Manager.m @@ -33,14 +33,20 @@ NSString * const kAFOAuth2CredentialServiceName = @"AFOAuthCredentialService"; -static NSDictionary * AFKeychainQueryDictionaryWithIdentifier(NSString *identifier) { +static NSDictionary *AFKeychainQueryDictionaryWithIdentifier(NSString *identifier, NSString *_Nullable accessGroup) { NSCParameterAssert(identifier); - return @{ - (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, - (__bridge id)kSecAttrService: kAFOAuth2CredentialServiceName, - (__bridge id)kSecAttrAccount: identifier - }; + NSMutableDictionary *dictionary = [@{ + (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService: kAFOAuth2CredentialServiceName, + (__bridge id)kSecAttrAccount: identifier + } mutableCopy]; + + if (accessGroup) { + dictionary[(__bridge id)kSecAttrAccessGroup] = accessGroup; + } + + return [dictionary copy]; } // See: http://tools.ietf.org/html/rfc6749#section-5.2 @@ -106,7 +112,7 @@ - (id)initWithBaseURL:(NSURL *)url if (!self) { return nil; } - + self.serviceProviderIdentifier = [self.baseURL host]; self.clientID = clientID; self.secret = secret; @@ -114,7 +120,7 @@ - (id)initWithBaseURL:(NSURL *)url self.useHTTPBasicAuthentication = YES; [self.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"]; - + return self; } @@ -269,7 +275,7 @@ - (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLS failure(error); } }]; - + return requestOperation; } @@ -289,6 +295,17 @@ @implementation AFOAuthCredential #pragma mark - +- (NSString *)description { + NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: ", NSStringFromClass([self class])]; + [description appendFormat:@"self.accessToken=%@", self.accessToken]; + [description appendFormat:@", self.tokenType=%@", self.tokenType]; + [description appendFormat:@", self.refreshToken=%@", self.refreshToken]; + [description appendFormat:@", self.expired=%d", self.expired]; + [description appendFormat:@", self.expiration=%@", self.expiration]; + [description appendString:@">"]; + return description; +} + + (instancetype)credentialWithOAuthToken:(NSString *)token tokenType:(NSString *)type { @@ -309,10 +326,6 @@ - (id)initWithOAuthToken:(NSString *)token return self; } -- (NSString *)description { - return [NSString stringWithFormat:@"<%@ accessToken:\"%@\" tokenType:\"%@\" refreshToken:\"%@\" expiration:\"%@\">", [self class], self.accessToken, self.tokenType, self.refreshToken, self.expiration]; -} - - (void)setRefreshToken:(NSString *)refreshToken { _refreshToken = refreshToken; @@ -339,9 +352,8 @@ - (BOOL)isExpired { #pragma mark Keychain -+ (BOOL)storeCredential:(AFOAuthCredential *)credential - withIdentifier:(NSString *)identifier -{ ++ (BOOL)storeCredential:(AFOAuthCredential *)credential withIdentifier:(NSString *)identifier + accessGroup:(NSString *)accessGroup { id securityAccessibility = nil; #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 43000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) #pragma clang diagnostic push @@ -352,17 +364,16 @@ + (BOOL)storeCredential:(AFOAuthCredential *)credential #pragma clang diagnostic pop #endif - return [[self class] storeCredential:credential withIdentifier:identifier withAccessibility:securityAccessibility]; + return [[self class] storeCredential:credential withIdentifier:identifier withAccessibility:securityAccessibility + accessGroup:accessGroup]; } -+ (BOOL)storeCredential:(AFOAuthCredential *)credential - withIdentifier:(NSString *)identifier - withAccessibility:(id)securityAccessibility -{ - NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy]; ++ (BOOL)storeCredential:(AFOAuthCredential *)credential withIdentifier:(NSString *)identifier + withAccessibility:(id)securityAccessibility accessGroup:(NSString *)accessGroup { + NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier, accessGroup) mutableCopy]; if (!credential) { - return [self deleteCredentialWithIdentifier:identifier]; + return [self deleteCredentialWithIdentifier:identifier accessGroup:accessGroup]; } NSMutableDictionary *updateDictionary = [NSMutableDictionary dictionary]; @@ -373,7 +384,7 @@ + (BOOL)storeCredential:(AFOAuthCredential *)credential } OSStatus status; - BOOL exists = ([self retrieveCredentialWithIdentifier:identifier] != nil); + BOOL exists = ([self retrieveCredentialWithIdentifier:identifier accessGroup:accessGroup] != nil); if (exists) { status = SecItemUpdate((__bridge CFDictionaryRef)queryDictionary, (__bridge CFDictionaryRef)updateDictionary); @@ -385,16 +396,16 @@ + (BOOL)storeCredential:(AFOAuthCredential *)credential return (status == errSecSuccess); } -+ (BOOL)deleteCredentialWithIdentifier:(NSString *)identifier { - NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy]; ++ (BOOL)deleteCredentialWithIdentifier:(NSString *)identifier accessGroup:(NSString *)accessGroup { + NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier, accessGroup) mutableCopy]; OSStatus status = SecItemDelete((__bridge CFDictionaryRef)queryDictionary); return (status == errSecSuccess); } -+ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier { - NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy]; ++ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier accessGroup:(NSString *)accessGroup { + NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier, accessGroup) mutableCopy]; queryDictionary[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue; queryDictionary[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; From 7badf8e3d56fb1269a5a6a833ff13bb2ef238d87 Mon Sep 17 00:00:00 2001 From: Jan Gorman Date: Thu, 14 Jan 2016 14:19:11 -0800 Subject: [PATCH 2/2] Correct nullability --- AFOAuth2Manager/AFOAuth2Manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AFOAuth2Manager/AFOAuth2Manager.h b/AFOAuth2Manager/AFOAuth2Manager.h index e1f1426d..4c00caf1 100644 --- a/AFOAuth2Manager/AFOAuth2Manager.h +++ b/AFOAuth2Manager/AFOAuth2Manager.h @@ -285,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN @return The retrieved OAuth credential. */ -+ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier ++ (nullable AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier accessGroup:(nullable NSString *)accessGroup; /**