PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/Pods/NXOAuth2Client/Sources/OAuth2Client/NXOAuth2AccountStore.m

https://gitlab.com/duongbadu/Instagram-app-iOS-Coursera
Objective C | 746 lines | 587 code | 136 blank | 23 comment | 49 complexity | 51d73b05862d944cd506a30b3e8edd21 MD5 | raw file
  1. //
  2. // NXOAuth2AccountStore.m
  3. // OAuth2Client
  4. //
  5. // Created by Tobias Kräntzer on 12.07.11.
  6. //
  7. // Copyright 2011 nxtbgthng. All rights reserved.
  8. //
  9. // Licenced under the new BSD-licence.
  10. // See README.md in this repository for
  11. // the full licence.
  12. //
  13. #if TARGET_OS_IPHONE
  14. #import <UIKit/UIKit.h>
  15. #else
  16. #import <Cocoa/Cocoa.h>
  17. #endif
  18. #import "NXOAuth2Client.h"
  19. #import "NXOAuth2Connection.h"
  20. #import "NXOAuth2Account.h"
  21. #import "NXOAuth2Account+Private.h"
  22. #import "NXOAuth2AccountStore.h"
  23. #pragma mark Notifications
  24. NSString * const NXOAuth2AccountStoreDidFailToRequestAccessNotification = @"NXOAuth2AccountStoreDidFailToRequestAccessNotification";
  25. NSString * const NXOAuth2AccountStoreAccountsDidChangeNotification = @"NXOAuth2AccountStoreAccountsDidChangeNotification";
  26. NSString * const NXOAuth2AccountStoreNewAccountUserInfoKey = @"NXOAuth2AccountStoreNewAccountUserInfoKey";
  27. #pragma mark Configuration
  28. NSString * const kNXOAuth2AccountStoreConfigurationClientID = @"kNXOAuth2AccountStoreConfigurationClientID";
  29. NSString * const kNXOAuth2AccountStoreConfigurationSecret = @"kNXOAuth2AccountStoreConfigurationSecret";
  30. NSString * const kNXOAuth2AccountStoreConfigurationAuthorizeURL = @"kNXOAuth2AccountStoreConfigurationAuthorizeURL";
  31. NSString * const kNXOAuth2AccountStoreConfigurationTokenURL = @"kNXOAuth2AccountStoreConfigurationTokenURL";
  32. NSString * const kNXOAuth2AccountStoreConfigurationRedirectURL = @"kNXOAuth2AccountStoreConfigurationRedirectURL";
  33. NSString * const kNXOAuth2AccountStoreConfigurationScope = @"kNXOAuth2AccountStoreConfigurationScope";
  34. NSString * const kNXOAuth2AccountStoreConfigurationTokenType = @"kNXOAuth2AccountStoreConfigurationTokenType";
  35. NSString * const kNXOAuth2AccountStoreConfigurationTokenRequestHTTPMethod = @"kNXOAuth2AccountStoreConfigurationTokenRequestHTTPMethod";
  36. NSString * const kNXOAuth2AccountStoreConfigurationKeyChainGroup = @"kNXOAuth2AccountStoreConfigurationKeyChainGroup";
  37. NSString * const kNXOAuth2AccountStoreConfigurationAdditionalAuthenticationParameters = @"kNXOAuth2AccountStoreConfigurationAdditionalAuthenticationParameters";
  38. NSString * const kNXOAuth2AccountStoreConfigurationCustomHeaderFields = @"kNXOAuth2AccountStoreConfigurationCustomHeaderFields";
  39. #pragma mark Account Type
  40. NSString * const kNXOAuth2AccountStoreAccountType = @"kNXOAuth2AccountStoreAccountType";
  41. #pragma mark -
  42. @interface NXOAuth2AccountStore () <NXOAuth2ClientDelegate, NXOAuth2TrustDelegate>
  43. @property (nonatomic, strong, readwrite) NSMutableDictionary *pendingOAuthClients;
  44. @property (nonatomic, strong, readwrite) NSMutableDictionary *accountsDict;
  45. @property (nonatomic, strong, readwrite) NSMutableDictionary *configurations;
  46. @property (nonatomic, strong, readwrite) NSMutableDictionary *trustModeHandler;
  47. @property (nonatomic, strong, readwrite) NSMutableDictionary *trustedCertificatesHandler;
  48. #pragma mark OAuthClient to AccountType Relation
  49. - (NXOAuth2Client *)pendingOAuthClientForAccountType:(NSString *)accountType;
  50. - (NSString *)accountTypeOfPendingOAuthClient:(NXOAuth2Client *)oauthClient;
  51. #pragma mark Notification Handler
  52. - (void)accountDidChangeUserData:(NSNotification *)aNotification;
  53. - (void)accountDidChangeAccessToken:(NSNotification *)aNotification;
  54. - (void)accountDidLoseAccessToken:(NSNotification *)aNotification;
  55. #pragma mark Keychain Support
  56. + (NSString *)keychainServiceName;
  57. + (NSDictionary *)accountsFromDefaultKeychain;
  58. + (void)storeAccountsInDefaultKeychain:(NSDictionary *)accounts;
  59. + (void)removeFromDefaultKeychain;
  60. @end
  61. @implementation NXOAuth2AccountStore
  62. #pragma mark Lifecycle
  63. + (id)sharedStore;
  64. {
  65. static NXOAuth2AccountStore *shared;
  66. static dispatch_once_t onceToken;
  67. dispatch_once(&onceToken, ^{
  68. shared = [NXOAuth2AccountStore new];
  69. });
  70. return shared;
  71. }
  72. - (id)init;
  73. {
  74. self = [super init];
  75. if (self) {
  76. self.pendingOAuthClients = [NSMutableDictionary dictionary];
  77. self.accountsDict = [NSMutableDictionary dictionaryWithDictionary:[NXOAuth2AccountStore accountsFromDefaultKeychain]];
  78. self.configurations = [NSMutableDictionary dictionary];
  79. self.trustModeHandler = [NSMutableDictionary dictionary];
  80. self.trustedCertificatesHandler = [NSMutableDictionary dictionary];
  81. [[NSNotificationCenter defaultCenter] addObserver:self
  82. selector:@selector(accountDidChangeUserData:)
  83. name:NXOAuth2AccountDidChangeUserDataNotification
  84. object:nil];
  85. [[NSNotificationCenter defaultCenter] addObserver:self
  86. selector:@selector(accountDidChangeAccessToken:)
  87. name:NXOAuth2AccountDidChangeAccessTokenNotification
  88. object:nil];
  89. [[NSNotificationCenter defaultCenter] addObserver:self
  90. selector:@selector(accountDidLoseAccessToken:)
  91. name:NXOAuth2AccountDidLoseAccessTokenNotification
  92. object:nil];
  93. }
  94. return self;
  95. }
  96. - (void)dealloc;
  97. {
  98. [[NSNotificationCenter defaultCenter] removeObserver:self];
  99. }
  100. #pragma mark Accessors
  101. @synthesize pendingOAuthClients;
  102. @synthesize accountsDict;
  103. @synthesize configurations;
  104. @synthesize trustModeHandler;
  105. @synthesize trustedCertificatesHandler;
  106. - (NSArray *)accounts;
  107. {
  108. NSArray *result = nil;
  109. @synchronized (self.accountsDict) {
  110. result = [self.accountsDict allValues];
  111. }
  112. return result;
  113. }
  114. - (NSArray *)accountsWithAccountType:(NSString *)accountType;
  115. {
  116. NSMutableArray *result = [NSMutableArray array];
  117. for (NXOAuth2Account *account in self.accounts) {
  118. if ([account.accountType isEqualToString:accountType]) {
  119. [result addObject:account];
  120. }
  121. }
  122. return result;
  123. }
  124. - (NXOAuth2Account *)accountWithIdentifier:(NSString *)identifier;
  125. {
  126. NXOAuth2Account *result = nil;
  127. @synchronized (self.accountsDict) {
  128. result = [self.accountsDict objectForKey:identifier];
  129. }
  130. return result;
  131. }
  132. #pragma mark Manage Accounts
  133. - (void)requestAccessToAccountWithType:(NSString *)accountType;
  134. {
  135. NXOAuth2Client *client = [self pendingOAuthClientForAccountType:accountType];
  136. [client requestAccess];
  137. }
  138. - (void)requestAccessToAccountWithType:(NSString *)accountType
  139. withPreparedAuthorizationURLHandler:(NXOAuth2PreparedAuthorizationURLHandler)aPreparedAuthorizationURLHandler;
  140. {
  141. NSAssert(aPreparedAuthorizationURLHandler, @"Prepared Authorization Handler must not be nil.");
  142. NXOAuth2Client *client = [self pendingOAuthClientForAccountType:accountType];
  143. NSDictionary *configuration;
  144. @synchronized (self.configurations) {
  145. configuration = [self.configurations objectForKey:accountType];
  146. }
  147. NSURL *redirectURL = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationRedirectURL];
  148. NSURL *preparedURL = [client authorizationURLWithRedirectURL:redirectURL];
  149. aPreparedAuthorizationURLHandler(preparedURL);
  150. }
  151. - (void)requestAccessToAccountWithType:(NSString *)accountType username:(NSString *)username password:(NSString *)password;
  152. {
  153. NXOAuth2Client *client = [self pendingOAuthClientForAccountType:accountType];
  154. [client authenticateWithUsername:username password:password];
  155. }
  156. - (void)requestAccessToAccountWithType:(NSString *)accountType assertionType:(NSURL *)assertionType assertion:(NSString *)assertion;
  157. {
  158. NXOAuth2Client *client = [self pendingOAuthClientForAccountType:accountType];
  159. [client authenticateWithAssertionType:assertionType assertion:assertion];
  160. }
  161. - (void)requestClientCredentialsAccessWithType:(NSString *)accountType;
  162. {
  163. NXOAuth2Client *client = [self pendingOAuthClientForAccountType:accountType];
  164. [client authenticateWithClientCredentials];
  165. }
  166. - (void)removeAccount:(NXOAuth2Account *)account;
  167. {
  168. if (account) {
  169. @synchronized (self.accountsDict) {
  170. [self.accountsDict removeObjectForKey:account.identifier];
  171. [NXOAuth2AccountStore storeAccountsInDefaultKeychain:self.accountsDict];
  172. }
  173. [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2AccountStoreAccountsDidChangeNotification object:self];
  174. }
  175. }
  176. #pragma mark Configuration
  177. - (void)setClientID:(NSString *)aClientID
  178. secret:(NSString *)aSecret
  179. authorizationURL:(NSURL *)anAuthorizationURL
  180. tokenURL:(NSURL *)aTokenURL
  181. redirectURL:(NSURL *)aRedirectURL
  182. forAccountType:(NSString *)anAccountType;
  183. {
  184. [self setConfiguration:[NSDictionary dictionaryWithObjectsAndKeys:
  185. aClientID, kNXOAuth2AccountStoreConfigurationClientID,
  186. aSecret, kNXOAuth2AccountStoreConfigurationSecret,
  187. anAuthorizationURL, kNXOAuth2AccountStoreConfigurationAuthorizeURL,
  188. aTokenURL, kNXOAuth2AccountStoreConfigurationTokenURL,
  189. aRedirectURL, kNXOAuth2AccountStoreConfigurationRedirectURL, nil]
  190. forAccountType:anAccountType];
  191. }
  192. - (void)setClientID:(NSString *)aClientID
  193. secret:(NSString *)aSecret
  194. scope:(NSSet *)theScope
  195. authorizationURL:(NSURL *)anAuthorizationURL
  196. tokenURL:(NSURL *)aTokenURL
  197. redirectURL:(NSURL *)aRedirectURL
  198. keyChainGroup:(NSString *)aKeyChainGroup
  199. forAccountType:(NSString *)anAccountType;
  200. {
  201. [self setConfiguration:[NSDictionary dictionaryWithObjectsAndKeys:
  202. aClientID, kNXOAuth2AccountStoreConfigurationClientID,
  203. aSecret, kNXOAuth2AccountStoreConfigurationSecret,
  204. theScope, kNXOAuth2AccountStoreConfigurationScope,
  205. anAuthorizationURL, kNXOAuth2AccountStoreConfigurationAuthorizeURL,
  206. aTokenURL, kNXOAuth2AccountStoreConfigurationTokenURL,
  207. aKeyChainGroup, kNXOAuth2AccountStoreConfigurationKeyChainGroup,
  208. aRedirectURL, kNXOAuth2AccountStoreConfigurationRedirectURL, nil]
  209. forAccountType:anAccountType];
  210. }
  211. - (void)setClientID:(NSString *)aClientID
  212. secret:(NSString *)aSecret
  213. scope:(NSSet *)theScope
  214. authorizationURL:(NSURL *)anAuthorizationURL
  215. tokenURL:(NSURL *)aTokenURL
  216. redirectURL:(NSURL *)aRedirectURL
  217. keyChainGroup:(NSString *)aKeyChainGroup
  218. tokenType:(NSString *)aTokenType
  219. forAccountType:(NSString *)anAccountType;
  220. {
  221. [self setConfiguration:[NSDictionary dictionaryWithObjectsAndKeys:
  222. aClientID, kNXOAuth2AccountStoreConfigurationClientID,
  223. aSecret, kNXOAuth2AccountStoreConfigurationSecret,
  224. theScope, kNXOAuth2AccountStoreConfigurationScope,
  225. anAuthorizationURL, kNXOAuth2AccountStoreConfigurationAuthorizeURL,
  226. aTokenURL, kNXOAuth2AccountStoreConfigurationTokenURL,
  227. aTokenType, kNXOAuth2AccountStoreConfigurationTokenType,
  228. aKeyChainGroup, kNXOAuth2AccountStoreConfigurationKeyChainGroup,
  229. aRedirectURL, kNXOAuth2AccountStoreConfigurationRedirectURL, nil]
  230. forAccountType:anAccountType];
  231. }
  232. - (void)setConfiguration:(NSDictionary *)configuration
  233. forAccountType:(NSString *)accountType;
  234. {
  235. NSAssert1([configuration objectForKey:kNXOAuth2AccountStoreConfigurationClientID], @"Missing OAuth2 client ID for account type '%@'.", accountType);
  236. NSAssert1([configuration objectForKey:kNXOAuth2AccountStoreConfigurationSecret], @"Missing OAuth2 client secret for account type '%@'.", accountType);
  237. NSAssert1([configuration objectForKey:kNXOAuth2AccountStoreConfigurationAuthorizeURL], @"Missing OAuth2 authorize URL for account type '%@'.", accountType);
  238. NSAssert1([configuration objectForKey:kNXOAuth2AccountStoreConfigurationTokenURL], @"Missing OAuth2 token URL for account type '%@'.", accountType);
  239. @synchronized (self.configurations) {
  240. [self.configurations setObject:configuration forKey:accountType];
  241. }
  242. }
  243. - (NSDictionary *)configurationForAccountType:(NSString *)accountType;
  244. {
  245. NSDictionary *result = nil;
  246. @synchronized (self.configurations) {
  247. result = [self.configurations objectForKey:accountType];
  248. }
  249. return result;
  250. }
  251. #pragma mark Trust Mode Handler
  252. - (void)setTrustModeHandlerForAccountType:(NSString *)accountType
  253. block:(NXOAuth2TrustModeHandler)handler;
  254. {
  255. @synchronized (self.trustModeHandler) {
  256. [self.trustModeHandler setObject:[handler copy] forKey:accountType];
  257. }
  258. }
  259. - (void)setTrustedCertificatesHandlerForAccountType:(NSString *)accountType
  260. block:(NXOAuth2TrustedCertificatesHandler)handler;
  261. {
  262. @synchronized (self.trustedCertificatesHandler) {
  263. [self.trustedCertificatesHandler setObject:[handler copy] forKey:accountType];
  264. }
  265. }
  266. - (NXOAuth2TrustModeHandler)trustModeHandlerForAccountType:(NSString *)accountType;
  267. {
  268. return [self.trustModeHandler objectForKey:accountType];
  269. }
  270. - (NXOAuth2TrustedCertificatesHandler)trustedCertificatesHandlerForAccountType:(NSString *)accountType;
  271. {
  272. NXOAuth2TrustedCertificatesHandler handler = [self.trustedCertificatesHandler objectForKey:accountType];
  273. NSAssert(handler, @"You need to provied a NXOAuth2TrustedCertificatesHandler for account type '%@' because you are using 'NXOAuth2TrustModeSpecificCertificate' as trust mode for that account type.", accountType);
  274. return handler;
  275. }
  276. #pragma mark Handle OAuth Redirects
  277. - (BOOL)handleRedirectURL:(NSURL *)aURL;
  278. {
  279. __block NSURL *fixedRedirectURL = nil;
  280. NSSet *accountTypes;
  281. @synchronized (self.configurations) {
  282. accountTypes = [self.configurations keysOfEntriesPassingTest:^(id key, id obj, BOOL *stop) {
  283. NSDictionary *configuration = obj;
  284. NSURL *redirectURL = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationRedirectURL];
  285. if ( [[[aURL absoluteString] lowercaseString] hasPrefix:[[redirectURL absoluteString] lowercaseString]]) {
  286. // WORKAROUND: The URL which is passed to this method may be lower case also the scheme is registered in camel case. Therefor replace the prefix with the stored redirectURL.
  287. if (fixedRedirectURL == nil) {
  288. if ([aURL.scheme isEqualToString:redirectURL.scheme]) {
  289. fixedRedirectURL = aURL;
  290. } else {
  291. NSRange prefixRange;
  292. prefixRange.location = 0;
  293. prefixRange.length = [redirectURL.absoluteString length];
  294. fixedRedirectURL = [NSURL URLWithString:[aURL.absoluteString stringByReplacingCharactersInRange:prefixRange
  295. withString:redirectURL.absoluteString]];
  296. }
  297. }
  298. return YES;
  299. } else {
  300. return NO;
  301. }
  302. }];
  303. }
  304. for (NSString *accountType in accountTypes) {
  305. NXOAuth2Client *client = [self pendingOAuthClientForAccountType:accountType];
  306. if ([client openRedirectURL:fixedRedirectURL]) {
  307. return YES;
  308. }
  309. }
  310. return NO;
  311. }
  312. #pragma mark OAuthClient to AccountType Relation
  313. - (NXOAuth2Client *)pendingOAuthClientForAccountType:(NSString *)accountType;
  314. {
  315. NXOAuth2Client *client = nil;
  316. @synchronized (self.pendingOAuthClients) {
  317. client = [self.pendingOAuthClients objectForKey:accountType];
  318. if (!client) {
  319. NSDictionary *configuration;
  320. @synchronized (self.configurations) {
  321. configuration = [self.configurations objectForKey:accountType];
  322. }
  323. NSString *clientID = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationClientID];
  324. NSString *clientSecret = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationSecret];
  325. NSSet *scope = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationScope];
  326. NSURL *authorizeURL = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationAuthorizeURL];
  327. NSURL *tokenURL = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationTokenURL];
  328. NSString *tokenType = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationTokenType];
  329. NSString *tokenRequestHTTPMethod = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationTokenRequestHTTPMethod];
  330. NSString *keychainGroup = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationKeyChainGroup];
  331. NSDictionary *additionalAuthenticationParameters = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationAdditionalAuthenticationParameters];
  332. NSDictionary *customHeaderFields = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationCustomHeaderFields];
  333. client = [[NXOAuth2Client alloc] initWithClientID:clientID
  334. clientSecret:clientSecret
  335. authorizeURL:authorizeURL
  336. tokenURL:tokenURL
  337. accessToken:nil
  338. tokenType:tokenType
  339. keyChainGroup:keychainGroup
  340. persistent:YES
  341. delegate:self];
  342. client.persistent = NO;
  343. if (tokenRequestHTTPMethod != nil) {
  344. client.tokenRequestHTTPMethod = tokenRequestHTTPMethod;
  345. }
  346. if (additionalAuthenticationParameters != nil) {
  347. NSAssert([additionalAuthenticationParameters isKindOfClass:[NSDictionary class]], @"additionalAuthenticationParameters have to be a NSDictionary");
  348. client.additionalAuthenticationParameters = additionalAuthenticationParameters;
  349. }
  350. if (customHeaderFields) {
  351. client.customHeaderFields = customHeaderFields;
  352. }
  353. if (scope != nil) {
  354. client.desiredScope = scope;
  355. }
  356. [self.pendingOAuthClients setObject:client forKey:accountType];
  357. }
  358. }
  359. return client;
  360. }
  361. - (NSString *)accountTypeOfPendingOAuthClient:(NXOAuth2Client *)oauthClient;
  362. {
  363. NSString *result = nil;
  364. @synchronized (self.pendingOAuthClients) {
  365. NSSet *accountTypes = [self.pendingOAuthClients keysOfEntriesPassingTest:^(id key, id obj, BOOL *stop){
  366. if ([obj isEqual:oauthClient]) {
  367. *stop = YES;
  368. return YES;
  369. }
  370. return NO;
  371. }];
  372. result = [accountTypes anyObject];
  373. }
  374. return result;
  375. }
  376. #pragma mark NXOAuth2ClientDelegate
  377. - (void)oauthClientNeedsAuthentication:(NXOAuth2Client *)client;
  378. {
  379. NSString *accountType = [self accountTypeOfPendingOAuthClient:client];
  380. NSDictionary *configuration;
  381. @synchronized (self.configurations) {
  382. configuration = [self.configurations objectForKey:accountType];
  383. }
  384. NSURL *redirectURL = [configuration objectForKey:kNXOAuth2AccountStoreConfigurationRedirectURL];
  385. NSURL *preparedURL = [client authorizationURLWithRedirectURL:redirectURL];
  386. #if TARGET_OS_IPHONE
  387. [[UIApplication sharedApplication] openURL:preparedURL];
  388. #else
  389. [[NSWorkspace sharedWorkspace] openURL:preparedURL];
  390. #endif
  391. }
  392. - (void)oauthClientDidGetAccessToken:(NXOAuth2Client *)client;
  393. {
  394. NSString *accountType;
  395. @synchronized (self.pendingOAuthClients) {
  396. accountType = [self accountTypeOfPendingOAuthClient:client];
  397. [self.pendingOAuthClients removeObjectForKey:accountType];
  398. }
  399. NXOAuth2Account *account = [[NXOAuth2Account alloc] initAccountWithOAuthClient:client accountType:accountType];
  400. [self addAccount:account];
  401. }
  402. - (void)addAccount:(NXOAuth2Account *)account;
  403. {
  404. @synchronized (self.accountsDict) {
  405. [self.accountsDict setValue:account forKey:account.identifier];
  406. [NXOAuth2AccountStore storeAccountsInDefaultKeychain:self.accountsDict];
  407. }
  408. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:account
  409. forKey:NXOAuth2AccountStoreNewAccountUserInfoKey];
  410. [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2AccountStoreAccountsDidChangeNotification
  411. object:self
  412. userInfo:userInfo];
  413. }
  414. - (void)oauthClientDidLoseAccessToken:(NXOAuth2Client *)client;
  415. {
  416. // This delegate method should never be called because the account store
  417. // does not act as an delegate for established connections.
  418. // If there is one case that was overlooked, we will remove the oauth
  419. // client from the list of pending oauth clients as a precaution.
  420. NSString *accountType;
  421. @synchronized (self.pendingOAuthClients) {
  422. accountType = [self accountTypeOfPendingOAuthClient:client];
  423. if (accountType) {
  424. [self.pendingOAuthClients removeObjectForKey:accountType];
  425. }
  426. }
  427. }
  428. - (void)oauthClient:(NXOAuth2Client *)client didFailToGetAccessTokenWithError:(NSError *)error;
  429. {
  430. NSString *accountType;
  431. @synchronized (self.pendingOAuthClients) {
  432. accountType = [self accountTypeOfPendingOAuthClient:client];
  433. [self.pendingOAuthClients removeObjectForKey:accountType];
  434. }
  435. NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  436. accountType, kNXOAuth2AccountStoreAccountType,
  437. error, NXOAuth2AccountStoreErrorKey, nil];
  438. [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2AccountStoreDidFailToRequestAccessNotification
  439. object:self
  440. userInfo:userInfo];
  441. }
  442. #pragma mark NXOAuth2TrustDelegate
  443. -(NXOAuth2TrustMode)connection:(NXOAuth2Connection *)connection trustModeForHostname:(NSString *)hostname;
  444. {
  445. NSString *accountType = [self accountTypeOfPendingOAuthClient:connection.client];
  446. NXOAuth2TrustModeHandler handler = [self trustModeHandlerForAccountType:accountType];
  447. if (handler) {
  448. return handler(connection, hostname);
  449. } else {
  450. return NXOAuth2TrustModeSystem;
  451. }
  452. }
  453. -(NSArray *)connection:(NXOAuth2Connection *)connection trustedCertificatesForHostname:(NSString *)hostname;
  454. {
  455. NSString *accountType = [self accountTypeOfPendingOAuthClient:connection.client];
  456. NXOAuth2TrustedCertificatesHandler handler = [self trustedCertificatesHandlerForAccountType:accountType];
  457. return handler(hostname);
  458. }
  459. #pragma mark Notification Handler
  460. - (void)accountDidChangeUserData:(NSNotification *)aNotification;
  461. {
  462. @synchronized (self.accountsDict) {
  463. // The user data of an account has been changed.
  464. // Save all accounts in the default keychain.
  465. [NXOAuth2AccountStore storeAccountsInDefaultKeychain:self.accountsDict];
  466. }
  467. }
  468. - (void)accountDidChangeAccessToken:(NSNotification *)aNotification;
  469. {
  470. @synchronized (self.accountsDict) {
  471. // An access token of an account has been changed.
  472. // Save all accounts in the default keychain.
  473. [NXOAuth2AccountStore storeAccountsInDefaultKeychain:self.accountsDict];
  474. }
  475. }
  476. - (void)accountDidLoseAccessToken:(NSNotification *)aNotification;
  477. {
  478. NSLog(@"Removing account with id '%@' from account store because it lost its access token.", [aNotification.object identifier]);
  479. [self removeAccount:aNotification.object];
  480. }
  481. #pragma mark Keychain Support
  482. + (NSString *)keychainServiceName;
  483. {
  484. NSString *appName = [[NSBundle mainBundle] bundleIdentifier];
  485. return [NSString stringWithFormat:@"%@::NXOAuth2AccountStore", appName];
  486. }
  487. #if TARGET_OS_IPHONE
  488. + (NSDictionary *)accountsFromDefaultKeychain;
  489. {
  490. NSString *serviceName = [self keychainServiceName];
  491. NSDictionary *result = nil;
  492. NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
  493. (__bridge NSString *)kSecClassGenericPassword, kSecClass,
  494. serviceName, kSecAttrService,
  495. kCFBooleanTrue, kSecReturnAttributes,
  496. nil];
  497. CFTypeRef cfResult = nil;
  498. OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &cfResult);
  499. result = (__bridge_transfer NSDictionary *)cfResult;
  500. if (status != noErr) {
  501. NSAssert1(status == errSecItemNotFound, @"Unexpected error while fetching accounts from keychain: %zd", status);
  502. return nil;
  503. }
  504. return [NSKeyedUnarchiver unarchiveObjectWithData:[result objectForKey:(__bridge NSString *)kSecAttrGeneric]];
  505. }
  506. + (void)storeAccountsInDefaultKeychain:(NSDictionary *)accounts;
  507. {
  508. [self removeFromDefaultKeychain];
  509. NSString *serviceName = [self keychainServiceName];
  510. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:accounts];
  511. NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
  512. (__bridge NSString *)kSecClassGenericPassword, kSecClass,
  513. serviceName, kSecAttrService,
  514. @"OAuth 2 Account Store", kSecAttrLabel,
  515. data, kSecAttrGeneric,
  516. nil];
  517. OSStatus __attribute__((unused)) err = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
  518. NSAssert1(err == noErr, @"Error while adding token to keychain: %zd", err);
  519. }
  520. + (void)removeFromDefaultKeychain;
  521. {
  522. NSString *serviceName = [self keychainServiceName];
  523. NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
  524. (__bridge NSString *)kSecClassGenericPassword, kSecClass,
  525. serviceName, kSecAttrService,
  526. nil];
  527. OSStatus __attribute__((unused)) err = SecItemDelete((__bridge CFDictionaryRef)query);
  528. NSAssert1((err == noErr || err == errSecItemNotFound), @"Error while deleting token from keychain: %zd", err);
  529. }
  530. #else
  531. + (NSDictionary *)accountsFromDefaultKeychain;
  532. {
  533. NSString *serviceName = [self keychainServiceName];
  534. SecKeychainItemRef item = nil;
  535. OSStatus err = SecKeychainFindGenericPassword(NULL,
  536. strlen([serviceName UTF8String]),
  537. [serviceName UTF8String],
  538. 0,
  539. NULL,
  540. NULL,
  541. NULL,
  542. &item);
  543. if (err != noErr) {
  544. NSAssert1(err == errSecItemNotFound, @"Unexpected error while fetching accounts from keychain: %d", err);
  545. return nil;
  546. }
  547. // from Advanced Mac OS X Programming, ch. 16
  548. UInt32 length;
  549. char *password;
  550. NSData *result = nil;
  551. SecKeychainAttribute attributes[8];
  552. SecKeychainAttributeList list;
  553. attributes[0].tag = kSecAccountItemAttr;
  554. attributes[1].tag = kSecDescriptionItemAttr;
  555. attributes[2].tag = kSecLabelItemAttr;
  556. attributes[3].tag = kSecModDateItemAttr;
  557. list.count = 4;
  558. list.attr = attributes;
  559. err = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);
  560. if (err == noErr) {
  561. if (password != NULL) {
  562. result = [NSData dataWithBytes:password length:length];
  563. }
  564. SecKeychainItemFreeContent(&list, password);
  565. } else {
  566. // TODO find out why this always works in i386 and always fails on ppc
  567. NSLog(@"Error from SecKeychainItemCopyContent: %d", err);
  568. return nil;
  569. }
  570. CFRelease(item);
  571. return [NSKeyedUnarchiver unarchiveObjectWithData:result];
  572. }
  573. + (void)storeAccountsInDefaultKeychain:(NSDictionary *)accounts;
  574. {
  575. [self removeFromDefaultKeychain];
  576. NSString *serviceName = [self keychainServiceName];
  577. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:accounts];
  578. OSStatus __attribute__((unused))err = SecKeychainAddGenericPassword(NULL,
  579. strlen([serviceName UTF8String]),
  580. [serviceName UTF8String],
  581. 0,
  582. NULL,
  583. [data length],
  584. [data bytes],
  585. NULL);
  586. NSAssert1(err == noErr, @"Error while storing accounts in keychain: %d", err);
  587. }
  588. + (void)removeFromDefaultKeychain;
  589. {
  590. NSString *serviceName = [self keychainServiceName];
  591. SecKeychainItemRef item = nil;
  592. OSStatus err = SecKeychainFindGenericPassword(NULL,
  593. strlen([serviceName UTF8String]),
  594. [serviceName UTF8String],
  595. 0,
  596. NULL,
  597. NULL,
  598. NULL,
  599. &item);
  600. NSAssert1((err == noErr || err == errSecItemNotFound), @"Error while deleting accounts from keychain: %d", err);
  601. if (err == noErr) {
  602. err = SecKeychainItemDelete(item);
  603. }
  604. if (item) {
  605. CFRelease(item);
  606. }
  607. NSAssert1((err == noErr || err == errSecItemNotFound), @"Error while deleting accounts from keychain: %d", err);
  608. }
  609. #endif
  610. @end