In order to authenticate Instapaper, which uses XAuth, I use AFXAuthClient which is an extension to AFNetworking 1.0 that adds XAuth support for authenticating.
It works really well, for 99% of my users. But for a few dozen, it's caused a TON of crashes (far more than any other crash in my app). My app uses Crashlytics, so I have information on each crash, but I can't quite figure out how to fix it, or even how to recreate it.
Crashlytics gives me this for the error message:
Fatal Exception: NSInvalidArgumentException * setObjectForKey: object cannot be nil (key: oauth_token)
And this for the logs:
Thread : Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x2e355f4b __exceptionPreprocess + 130
1 libobjc.A.dylib 0x386e56af objc_exception_throw + 38
2 CoreFoundation 0x2e291667 -[__NSDictionaryM setObject:forKey:] + 818
3 Syllable 0x0007511f -[AFXAuthClient authorizationHeaderWithRequest:parameters:] + 224 (AFXAuthClient.m:224)
4 Syllable 0x000752ad -[AFXAuthClient requestWithMethod:path:parameters:] + 239 (AFXAuthClient.m:239)
5 Syllable 0x00069377 -[AppDelegate loadInstapaperArticles] + 356 (AppDelegate.m:356)
6 Syllable 0x000680fb -[AppDelegate application:performFetchWithCompletionHandler:] + 137 (AppDelegate.m:137)
7 UIKit 0x30d469d1 -[UIApplication _handleOpportunisticFetchWithSequenceNumber:] + 448
8 UIKit 0x30b38fbb -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 2010
9 UIKit 0x30b33353 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 714
10 UIKit 0x30ace41f -[UIApplication handleEvent:withNewEvent:] + 3130
11 UIKit 0x30acd721 -[UIApplication sendEvent:] + 72
12 UIKit 0x30b32b3d _UIApplicationHandleEvent + 664
13 GraphicsServices 0x32f6970d _PurpleEventCallback + 608
14 GraphicsServices 0x32f692f7 PurpleEventCallback + 34
15 CoreFoundation 0x2e3209df __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 34
16 CoreFoundation 0x2e32097b __CFRunLoopDoSource1 + 346
17 CoreFoundation 0x2e31f14f __CFRunLoopRun + 1398
18 CoreFoundation 0x2e289c27 CFRunLoopRunSpecific + 522
19 CoreFoundation 0x2e289a0b CFRunLoopRunInMode + 106
20 UIKit 0x30b31dd9 -[UIApplication _run] + 760
21 UIKit 0x30b2d049 UIApplicationMain + 1136
22 Syllable 0x0003817f main + 16 (main.m:16)
23 libdyld.dylib 0x38bedab7 start + 2
Apparently the app is passing nil to setObjectForKey:
. Here's where it says it occurs (I've put an --> arrow by the line) in AFXAuthClient.m (the implementation file for the AFXAuthClient library):
- (NSMutableDictionary *)authorizationHeaderWithRequest:(NSURLRequest *)request parameters:(NSDictionary *)parameters
{
NSMutableDictionary *authorizationHeader = [[NSMutableDictionary alloc] initWithDictionary:@{@"oauth_nonce": _nonce,
@"oauth_signature_method": @"HMAC-SHA1",
@"oauth_timestamp": _timestamp,
@"oauth_consumer_key": self.consumerKey,
@"oauth_signature": AFHMACSHA1Signature([self baseStringWithRequest:request parameters:parameters], _consumerSecret, _token.secret),
@"oauth_version": @"1.0"}];
if (self.token)
--> [authorizationHeader setObject:RFC3986EscapedStringWithEncoding(self.token.key, NSUTF8StringEncoding) forKey:@"oauth_token"];
return authorizationHeader;
}
In the RFC3986EscapedStringWithEncoding()
function it calls, it states the following at the beginning:
// Escape per RFC 3986 standards as required by OAuth. Previously, not
// escaping asterisks (*) causes passwords with * to fail in
// Instapaper authentication
My users are indeed logging in with Instapaper so it seems like this library has had issues with Instapaper in the past. I'm not sure what is causing it in this case, or even how to reproduce it.
My only theory was that Instapaper allows you to create accounts without a password, so when a user is logging in without a password, maybe it's passing nil to setObjectForKey
? But nope, I tried with a password-less account and my app did not crash at all.
What could be causing this issue? How would I fix it? If there's more information I can provide from Crashlytics please just say so.
First, make sure you're using the latest version - I see a fix from 7 months ago for this issue.
Second, if you're up-to-date, I would take the approach of modifying AFXAuthClient like this:
if (self.token) {
NSString *escapedToken = RFC3986EscapedStringWithEncoding(self.token.key, NSUTF8StringEncoding);
if (escapedToken) {
/* guaranteed not to crash here */
[authorizationHeader setObject:escapedToken forKey:@"oauth_token"];
} else {
/* Log self.token you can inspect the types of tokens that are causing
invalid escapedTokens, perhaps using a service like Flurry. */
}
}
This will allow you to collect whatever data is causing RFC3986EscapedStringWithEncoding
to return nil
. Obviously, be careful how you store/transmit this data, since it is a user's auth token.
Plus, it should turn those crashes into failed authentication errors, which is (probably) better.