// // NYTAuthentication.m // Notifications for YouTube // // Created by Kim Wittenburg on 21.07.13. // Copyright (c) 2013 Kim Wittenburg. All rights reserved. // #import "NYTAuthentication.h" // API Imports #import "OAuth2.h" // Core Imports #import "NYTUser.h" #import "NYTUtil.h" #import "NYTUpdateManager.h" // Google Access Constants NSString * const DeveloperKey = @"key=AI39si6ubNmTeFzFTFz27hFn6r-51knz0_CDWvqgyL-nVKBcFWu5C_c46TWQcHTwwK9bOBlrhdlKatyEqjEnJhZd2dMB7UgFhQ"; NSString * const DeveloperKeyHTTPHeaderField = @"X-GData-Key"; NSString * const ClientID = @"908431532405.apps.googleusercontent.com"; NSString * const ClientSecret = @"hKcyoNAzEIuHZaz0pdTUBMBv"; NSString * const KeychainItemName = @"Notifications for YouTube"; @interface NYTAuthentication () @property (readwrite) GTMOAuth2Authentication *authentication; @property (readwrite) NYTUser *loggedInUser; @end @implementation NYTAuthentication #pragma mark *** Initialization *** static NYTAuthentication *sharedAuthentication = nil; + (NYTAuthentication *)sharedAuthentication { if (!sharedAuthentication) { sharedAuthentication = [[self alloc] init]; } return sharedAuthentication; } - (id)init { // Allow exactly one instance at all (for any way of initialization) if (sharedAuthentication) { return sharedAuthentication; } self = [super init]; if (self) { sharedAuthentication = self; } return self; } #pragma mark *** KVO/KVC Additions to Properties *** + (NSSet *)keyPathsForValuesAffectingIsLoggedIn { return [NSSet setWithObjects:@"authentication", nil]; } #pragma mark *** Login *** - (void)tryLoginFromKeychainWithCompletionHandler:(void(^)(void))handler { // Get keychain data GTMOAuth2Authentication *auth = [GTMOAuth2WindowController authForGoogleFromKeychainForName:KeychainItemName clientID:ClientID clientSecret:ClientSecret]; if (auth.canAuthorize) { // Possible success (keychain) // Login (to refresh tokens/validate values) in background dispatch_queue_t prevQueue = dispatch_get_current_queue(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Try logging in NSError *error = [self completeLoginWithAuthentication:auth]; dispatch_async(prevQueue, ^{ if (!error) { // We suceeded so update the authentication object properly self.authentication = auth; // Refresh the feeds without notifications to set the 'known' state [[NYTUpdateManager sharedManager] refreshFeedsWithErrorHandler:NULL notify:NO]; } if (handler) { handler(); } }); }); } else { // Failed (keychain) if (handler) { handler(); } } } - (void)beginLoginSheetForWindow:(NSWindow *)window completionHandler:(void (^)(NSError *))handler { [[NYTUpdateManager sharedManager] pauseAutoRefreshing]; // Show the login window GTMOAuth2WindowController *windowController = [[GTMOAuth2WindowController alloc] initWithScope:@"https://gdata.youtube.com" clientID:ClientID clientSecret:ClientSecret keychainItemName:KeychainItemName resourceBundle:nil]; [windowController signInSheetModalForWindow:window completionHandler:^(GTMOAuth2Authentication *auth, NSError *error) { if (error) { // If an error occured just return if (handler) { handler(error); } [[NYTUpdateManager sharedManager] resumeAutoRefreshing]; } else { // Complete the login asynchronously (because we are very likely currently operating on the main thread) dispatch_queue_t prevQueue = dispatch_get_current_queue(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Complete the login NSError *error = [self completeLoginWithAuthentication:auth]; // Handle the error on the caller's queue dispatch_async(prevQueue, ^{ if (!error) { self.authentication = auth; [[NYTUpdateManager sharedManager] refreshFeedsWithErrorHandler:NULL notify:NO]; } if (handler) { handler(error); } [[NYTUpdateManager sharedManager] resumeAutoRefreshing]; }); }); } }]; } - (NSError *)completeLoginWithAuthentication:(GTMOAuth2Authentication *)auth { // Get the user's data NSError *error; static NSString *userPageURL = @"https://gdata.youtube.com/feeds/api/users/default"; NSXMLDocument *document = LoadXMLDocumentSynchronous(userPageURL, auth, &error); if (!document) { return error; } // Parse the data and store them app-friendly NYTUser *user = [[NYTUser alloc] initWithUserProfilePage:document]; dispatch_async(dispatch_get_main_queue(), ^{ // Set on the main queue due to bindings relations self.loggedInUser = user; }); return nil; } #pragma mark *** Logout *** - (void)discardLogin { // Also remove from keychain [GTMOAuth2WindowController removeAuthFromKeychainForName:KeychainItemName]; self.authentication = nil; self.loggedInUser = nil; } #pragma mark *** Login Information *** - (BOOL)isLoggedIn { return self.authentication != nil && self.authentication.canAuthorize; } @end