Permalink
Browse files

Improved the authentication process: It should no longer be necessary…

… to log in every time Spotify is relaunched (I was storing the temporary authentication token instead of the permanent session key in the keychain - oops)
  • Loading branch information...
1 parent 94b87ed commit 94af59af9ef89071b24cf8df32de2036f9c6d492 @georgebrock committed Mar 31, 2009
View
@@ -126,7 +126,7 @@ - (IBAction)auth:(id)sender
defaultButton:@"Continue"
alternateButton:@"Cancel"
otherButton:nil
- informativeTextWithFormat:@"If you click OK the Last.fm website will open so you can authorise Lastify"];
+ informativeTextWithFormat:@"If you click 'Continue' the Last.fm website will open so you can authorise Lastify"];
if([authAlert runModal] == NSAlertAlternateReturn)
return;
@@ -145,7 +145,7 @@ - (IBAction)auth:(id)sender
if([authCompleteAlert runModal] == NSAlertAlternateReturn)
return;
- [lastfm startNewSession:FALSE];
+ [lastfm startNewSession];
}
}
View
@@ -35,7 +35,7 @@
- (void)authenticate;
- (void)authenticateQuietly;
-- (void)startNewSession:(BOOL)quietly;
+- (void)startNewSession;
- (BOOL)loveTrack:(NSString*)trackName byArtist:(NSString*)artistName;
- (BOOL)banTrack:(NSString*)trackName byArtist:(NSString*)artistName;
View
@@ -13,9 +13,8 @@
@interface LastifyLastfmClient (Private)
- (NSString*)requestAuthToken;
- (NSString*)callMethod:(NSString*)methodName withParams:(NSDictionary*)params usingPost:(BOOL)post error:(NSError**)error;
-- (NSString*)loadAuthTokenFromKeychain;
-- (void)storeAuthTokenInKeychain:(NSString*)newAuthToken;
-- (void)removeAuthTokenFromKeychain;
+- (void)loadSessionKey;
+- (void)storeSessionKey;
@end
@implementation LastifyLastfmClient
@@ -54,152 +53,43 @@ - (void)dealloc
[super dealloc];
}
-- (void)authenticateQuietly
+- (void)loadSessionKey
{
- NSLog(@"LASTIFY Authenticate quiety");
-
- NSString *loadedAuthToken = [self loadAuthTokenFromKeychain];
- if(!loadedAuthToken)
- return;
-
- NSLog(@"LASTIFY Loaded an auth token: %@", loadedAuthToken);
-
- self.authToken = loadedAuthToken;
- [self startNewSession:TRUE];
-}
-
-- (void)authenticate
-{
- NSString *loadedAuthToken = [self loadAuthTokenFromKeychain];
-
- if(loadedAuthToken)
- {
- self.authToken = loadedAuthToken;
- [self startNewSession:FALSE];
- return;
- }
-
- // Get a new token
- NSString *newAuthToken = [self requestAuthToken];
-
- if(!newAuthToken)
- {
- //TODO: Handle this
- return;
- }
-
- // Store the auth token
- self.authToken = newAuthToken;
- [self storeAuthTokenInKeychain:newAuthToken];
-
- NSLog(@"LASTIFY About to send the user to the Last.fm site to log in");
-
- // Get the user to authorise the token
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.last.fm/api/auth/?api_key=%@&token=%@", self.APIKey, newAuthToken ]]];
- self.waitingForUserAuth = TRUE;
-
- // The authentication will resume with completeUserAuth when the user has logged in ...
- return;
-}
-
-- (void)startNewSession:(BOOL)quietly
-{
- NSError *err = nil;
- NSString *response = [self callMethod:@"auth.getSession" withParams:[NSDictionary dictionaryWithObjectsAndKeys:self.authToken, @"token", nil] usingPost:FALSE error:&err];
-
- if(err || !response)
- {
- switch([err code])
- {
- case 15: // This token has expired
- case 4: // Invalid authentication token supplied
- case 14: // This token has not been authorized
-
- [self removeAuthTokenFromKeychain];
- if(!quietly)
- [self authenticate];
-
- break;
-
- case 2: // Invalid service -This service does not exist
- case 3: // Invalid Method - No method with that name in this package
- case 5: // Invalid format - This service doesn't exist in that format
- case 6: // Invalid parameters - Your request is missing a required parameter
- case 7: // Invalid resource specified
- case 9: // Invalid session key - Please re-authenticate
- case 10: // Invalid API key - You must be granted a valid key by last.fm
- case 11: // Service Offline - This service is temporarily offline. Try again later.
- case 12: // Subscribers Only - This service is only available to paid last.fm subscribers
- default:
- break;
- }
-
- return;
- }
-
- // Extract the username and the session key
- NSString *newSessionKey;
- NSString *newUsername;
- NSScanner *scanner = [NSScanner scannerWithString:response];
-
- [scanner scanUpToString:@"<name>" intoString:NULL];
- [scanner scanString:@"<name>" intoString:NULL];
- [scanner scanUpToString:@"</name>" intoString:&newUsername];
-
- [scanner scanUpToString:@"<key>" intoString:NULL];
- [scanner scanString:@"<key>" intoString:NULL];
- [scanner scanUpToString:@"</key>" intoString:&newSessionKey];
-
- if(!newSessionKey)
- {
- //TODO: Handle this (must be an unexpected error if not caught sooner)
- return;
- }
-
- self.sessionKey = newSessionKey;
- self.username = newUsername;
- self.waitingForUserAuth = FALSE;
- self.sessionReady = TRUE;
-}
-
-- (NSString*)loadAuthTokenFromKeychain
-{
- NSString *loadedAuthToken = nil;
SecKeychainSearchRef search;
SecKeychainItemRef item;
SecKeychainAttributeList list;
SecKeychainAttribute attributes[3];
OSErr result;
-
+
attributes[0].tag = kSecAccountItemAttr;
attributes[0].data = (void*)[self.APIKey UTF8String];
attributes[0].length = [self.APIKey length];
- NSString *itemDescription = @"Lastify Last.fm access token";
+ NSString *itemDescription = @"Lastify Last.fm session information";
attributes[1].tag = kSecDescriptionItemAttr;
attributes[1].data = (void*)[itemDescription UTF8String];
attributes[1].length = [itemDescription length];
- NSString *itemLabel = @"Lastify Last.fm access token";
+ NSString *itemLabel = @"Lastify Last.fm session information";
attributes[2].tag = kSecLabelItemAttr;
attributes[2].data = (void*)[itemLabel UTF8String];
attributes[2].length = [itemLabel length];
-
+
list.count = 3;
list.attr = (SecKeychainAttribute*)&attributes;
-
+
result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search);
-
+
if(result != noErr)
- return nil;
+ return;
if(SecKeychainSearchCopyNext(search, &item) == noErr)
{
UInt32 length;
char *password;
OSStatus status;
-
+
status = SecKeychainItemCopyContent(item, NULL, NULL, &length, (void **)&password);
if(status == noErr)
@@ -210,21 +100,30 @@ - (NSString*)loadAuthTokenFromKeychain
strncpy(passwordBuffer, password, length);
passwordBuffer[length] = '\0';
- loadedAuthToken = [NSString stringWithUTF8String:passwordBuffer];
+ NSString *rawLoadedPassword = [NSString stringWithUTF8String:passwordBuffer];
+ NSArray *parts = [rawLoadedPassword componentsSeparatedByString:@" / "];
+
+ if(parts && [parts count] == 2)
+ {
+ self.sessionKey = [parts objectAtIndex:0];
+ self.username = [parts objectAtIndex:1];
+ self.waitingForUserAuth = FALSE;
+ self.sessionReady = TRUE;
+ }
}
-
+
SecKeychainItemFreeContent(NULL, password);
}
-
+
CFRelease(item);
CFRelease (search);
}
-
- return loadedAuthToken;
}
-- (void)storeAuthTokenInKeychain:(NSString*)newAuthToken
+- (void)storeSessionKey
{
+ NSLog(@"In storeSessionKey");
+
// Create attributes array
SecKeychainAttribute attributes[3];
@@ -234,64 +133,110 @@ - (void)storeAuthTokenInKeychain:(NSString*)newAuthToken
attributes[0].length = [self.APIKey length];
// Set the description
- NSString *itemDescription = @"Lastify Last.fm access token";
+ NSString *itemDescription = @"Lastify Last.fm session information";
attributes[1].tag = kSecDescriptionItemAttr;
attributes[1].data = (void*)[itemDescription UTF8String];
attributes[1].length = [itemDescription length];
// Label the item
- NSString *itemLabel = @"Lastify Last.fm access token";
+ NSString *itemLabel = @"Lastify Last.fm session information";
attributes[2].tag = kSecLabelItemAttr;
attributes[2].data = (void*)[itemLabel UTF8String];
attributes[2].length = [itemLabel length];
-
+
// Create list from attributes array
SecKeychainAttributeList list;
list.count = 3;
list.attr = attributes;
-
+
// Store the password
- SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &list, [self.authToken length], [self.authToken UTF8String], NULL,NULL,NULL);
+ NSString *password = [NSString stringWithFormat:@"%@ / %@", self.sessionKey, self.username];
+ SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &list, [password length], [password UTF8String], NULL,NULL,NULL);
}
-- (void)removeAuthTokenFromKeychain
+- (void)authenticateQuietly
{
- SecKeychainSearchRef search;
- SecKeychainItemRef item;
- SecKeychainAttributeList list;
- SecKeychainAttribute attributes[3];
- OSErr result;
+ [self loadSessionKey];
+}
- attributes[0].tag = kSecAccountItemAttr;
- attributes[0].data = (void*)[self.APIKey UTF8String];
- attributes[0].length = [self.APIKey length];
-
- NSString *itemDescription = @"Lastify Last.fm access token";
- attributes[1].tag = kSecDescriptionItemAttr;
- attributes[1].data = (void*)[itemDescription UTF8String];
- attributes[1].length = [itemDescription length];
+- (void)authenticate
+{
+ // Attempt to authenticate without user interaction
+ [self authenticateQuietly];
+ if(self.sessionKey)
+ return;
- NSString *itemLabel = @"Lastify Last.fm access token";
- attributes[2].tag = kSecLabelItemAttr;
- attributes[2].data = (void*)[itemLabel UTF8String];
- attributes[2].length = [itemLabel length];
-
- list.count = 3;
- list.attr = (SecKeychainAttribute*)&attributes;
+ // We need the user to authenticate
+ NSString *newAuthToken = [self requestAuthToken];
+
+ if(!newAuthToken)
+ {
+ //TODO: Present an error to the user
+ return;
+ }
+
+ self.authToken = newAuthToken;
+
+ // Get the user to authorise the token
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.last.fm/api/auth/?api_key=%@&token=%@", self.APIKey, newAuthToken]]];
+ self.waitingForUserAuth = TRUE;
+
+ // The authentication will resume with startNewSession when the user has logged in ...
+}
- result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search);
+- (void)startNewSession
+{
+ NSError *err = nil;
+ NSString *response = [self callMethod:@"auth.getSession" withParams:[NSDictionary dictionaryWithObjectsAndKeys:self.authToken, @"token", nil] usingPost:FALSE error:&err];
- if(result != noErr)
+ if(err || !response)
+ {
+ switch([err code])
+ {
+ case 15: // This token has expired
+ case 4: // Invalid authentication token supplied
+ case 14: // This token has not been authorized
+ case 2: // Invalid service -This service does not exist
+ case 3: // Invalid Method - No method with that name in this package
+ case 5: // Invalid format - This service doesn't exist in that format
+ case 6: // Invalid parameters - Your request is missing a required parameter
+ case 7: // Invalid resource specified
+ case 9: // Invalid session key - Please re-authenticate
+ case 10: // Invalid API key - You must be granted a valid key by last.fm
+ case 11: // Service Offline - This service is temporarily offline. Try again later.
+ case 12: // Subscribers Only - This service is only available to paid last.fm subscribers
+ default:
+ break;
+ }
+
return;
+ }
+
+ // Extract the username and the session key
+ NSString *newSessionKey;
+ NSString *newUsername;
+ NSScanner *scanner = [NSScanner scannerWithString:response];
- if(SecKeychainSearchCopyNext(search, &item) == noErr)
+ [scanner scanUpToString:@"<name>" intoString:NULL];
+ [scanner scanString:@"<name>" intoString:NULL];
+ [scanner scanUpToString:@"</name>" intoString:&newUsername];
+
+ [scanner scanUpToString:@"<key>" intoString:NULL];
+ [scanner scanString:@"<key>" intoString:NULL];
+ [scanner scanUpToString:@"</key>" intoString:&newSessionKey];
+
+ if(!newSessionKey)
{
- SecKeychainItemDelete(item);
- CFRelease(item);
+ //TODO: Handle this (must be an unexpected error if not caught sooner)
+ return;
}
- CFRelease(search);
-
+ self.sessionKey = newSessionKey;
+ self.username = newUsername;
+ self.waitingForUserAuth = FALSE;
+ self.sessionReady = TRUE;
+
+ [self storeSessionKey];
}
- (NSString*)requestAuthToken
@@ -19,9 +19,9 @@
<key>dSYM_UUID</key>
<dict>
<key>ppc</key>
- <string>43692AFC-D363-3F52-939D-CD466FCDE35A</string>
+ <string>6FF53252-B1CD-E29E-A754-FD448F768E77</string>
<key>i386</key>
- <string>0D43B2D7-EDB1-E937-C103-AB228663323E</string>
+ <string>FAA52792-F30B-0B85-033C-2A7AF85ACC37</string>
</dict>
</dict>
</plist>
Binary file not shown.

0 comments on commit 94af59a

Please sign in to comment.