I have the following steps completed in the OAuth process:
1) Request token
2) Authorize token
3) Receive PIN number
What I can't get to work is getting the access token with the PIN I receive. It always hits my accessTokenTicket:didFailWithError selector.
Here's the URL that's getting passed to it:
http://www.formspring.me/oauth/access_token?oauth_token=TOKEN_KEY_HERE&oauth_verifier=PIN_HERE
And here's the code that's being called:
- (void)successfulAuthorizationWithPin:(NSString *)pin {
NSLog(#"successfulAuthorizationWithPin:%#", pin);<br>
OAMutableURLRequest *request;<br>
OADataFetcher *fetcher;
NSURL *url = [NSURL URLWithString:kOAuthAccessTokenURL];
request = [[[OAMutableURLRequest alloc] initWithURL:url
consumer:self.consumer
token:self.accessToken
realm:nil
signatureProvider:nil] autorelease];
OARequestParameter *p0 = [[OARequestParameter alloc] initWithName:#"oauth_token" value:self.accessToken.key];
OARequestParameter *p1 = [[OARequestParameter alloc] initWithName:#"oauth_verifier"
value:pin];
NSArray *params = [NSArray arrayWithObjects:p0, p1, nil];
[request setParameters:params];
[request prepare];
NSLog(#"%#", request.URL);
fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request
delegate:self
didFinishSelector:#selector(accessTokenTicket:didFinishWithData:)
didFailSelector:#selector(accessTokenTicket:didFailWithError:)];
[p0 release];
[p1 release];
}
And the didFail method (just an NSLog of the error) produces this error:
Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x6160c20 {NSErrorFailingURLKey=http://www.formspring.me/oauth/access_token?oauth_token=TOKEN_KEY_HERE&oauth_verifier=PIN_HERE, NSErrorFailingURLStringKey=http://www.formspring.me/oauth/access_token?oauth_token=TOKEN_KEY_HERE&oauth_verifier=PIN_HERE, NSUnderlyingError=0x61321f0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1012.)"}
Am I formatting the URL wrong or supplying wrong or not enough parameters?
Thanks!
John
Did you check if the oauth token generated was correct? I would suggest starting there. Here's a good resource to check your OAuth tokens.
The message comes up if there is an authentication error. Generate the URL via the Netflix Oauth verification and run it on a browser. See if it loads then.
Related
I'm totally new to RestKit and I'm trying to use a POST request to login to my system. I'm using RestKit version 0.20.3 and this is how I did:
- (IBAction)login:(id)sender {
NSString *email = [self.emailTextField text];
NSString *password = [self.passwordTextField text];
NSURL *url = [[NSURL alloc] initWithString:#"http://myhost.com/api.php"];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:url];
NSDictionary *tmp = #{#"rquest":#"user",
#"tag":#"login",
#"email":email,
#"password":password};
[manager postObject:tmp path:#"" parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSDictionary *result = [mappingResult dictionary];
if([[result objectForKey:#"success"] isEqualToNumber:[NSNumber numberWithInt:1]]){
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
[def setBool:YES forKey:#"isLoggedIn"];
// set user details...
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}];
}
As you can see, since I don't really need to map the response into an object, I tried to access the response data with NSDictionary. I'm not sure if this is the problem, but when I try to run the above code, I get the error:
013-10-06 11:24:51.897 Eateries[1182:454b] E restkit.network:RKResponseMapperOperation.m:304 Failed to parse response data: Loaded an unprocessable response (404) with content type 'application/json'
2013-10-06 11:24:51.902 Eateries[1182:1003] E restkit.network:RKObjectRequestOperation.m:243 POST 'http://myhost.com/api.php' (404 Not Found / 0 objects) [request=0.1479s mapping=0.0000s total=0.1648s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1017 "Loaded an unprocessable response (404) with content type 'application/json'" UserInfo=0x8ee81e0 {NSErrorFailingURLKey=http://myhost.com/api.php, NSUnderlyingError=0x8ef4ea0 "The operation couldn’t be completed. (Cocoa error 3840.)", NSLocalizedDescription=Loaded an unprocessable response (404) with content type 'application/json'}
2013-10-06 11:24:51.978 Eateries[1182:a0b] Hit error: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1017 "Loaded an unprocessable response (404) with content type 'application/json'" UserInfo=0x8ee81e0 {NSErrorFailingURLKey=http://myhost.com/api.php, NSUnderlyingError=0x8ef4ea0 "The operation couldn’t be completed. (Cocoa error 3840.)", NSLocalizedDescription=Loaded an unprocessable response (404) with content type 'application/json'}
I'm really confused because I don't really know what I did wrong here. If you have any suggestion, please kindly let me know. Thank you.
P.s: I changed the name of my host just for my personal purpose, but my server really responses ok with the request when I try to test it from other platforms.
You are using the wrong API.
postObject:... is meant for posting an object to be mapped, meaning that the object parameter - if not nil - will be used as target to map the response.
If you don't want to map the response, just use the underlying AFHTTPClient to perform a plain POST request.
[[RKObjectManager sharedManager].HTTPClient postPath:#"" parameters:tmp success:^(AFHTTPRequestOperation *operation, id responseObject) {
// ...
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// ...
}
I am trying to make a PUT request using AFNetworking. There is a JSON on a remote location, which I want to update with the request.
I have managed to serialize my object into JSON, and I have put it an NSDictionary object:
NSDictionary *container = [NSDictionary dictionaryWithObject:[self notifications] forKey:#"notifications"];
This way when I print out the container using NSLog, I get precisely the JSON I want to send.
Then I try to use AFNetworking for my request:
AFHTTPClient *putClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://creativemind.appspot.com"]];
[putClient setParameterEncoding:AFJSONParameterEncoding];
NSMutableURLRequest *putRequest = [putClient requestWithMethod:#"PUT"
path:#"/notifications"
parameters:container];
AFHTTPRequestOperation *putOperation = [[AFHTTPRequestOperation alloc] initWithRequest:putRequest];
[putClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[putOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[putOperation start];
When I try this solution, I get the following error message:
2013-08-10 16:10:40.741 NotificationReader[1132:c07] Error: Error
Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in
(200-299), got 400" UserInfo=0x75c89e0
{NSLocalizedRecoverySuggestion={"cause":null,"class":"java.lang.NumberFormatException","localizedMessage":"null","message":"null"},
AFNetworkingOperationFailingURLRequestErrorKey=http://creativemind.appspot.com/notifications>,
NSErrorFailingURLKey=http://creativemind.appspot.com/notifications,
NSLocalizedDescription=Expected status code in (200-299), got 400,
AFNetworkingOperationFailingURLResponseErrorKey=}
I think it's important to note that when I try the same request with nil parameters, I get te same error message. I also tried to perform a GET request on the same JSON to check if I can reach it and it works perfectly. I really have no idea what I could do. Any help would be appreciated
I have the following code and it always gives me a crash when performing the request, any idea?
NSString *responseBody = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"Response body is %#", responseBody);
NSDictionary *accessTokenRequestParams = [[NSMutableDictionary alloc] init];
[accessTokenRequestParams setValue:CONSUMER_KEY forKey:#"x_reverse_auth_target"];
[accessTokenRequestParams setValue:responseBody forKey:#"x_reverse_auth_parameters"];
NSURL *url2 = [NSURL URLWithString:#"https://api.twitter.com/oauth/access_token"];
TWRequest * accessTokenRequest = [[TWRequest alloc] initWithURL:url2 parameters:accessTokenRequestParams requestMethod:TWRequestMethodPOST];
if (selectionIndex != -1)
[accessTokenRequest setAccount:[self.twitterACAccounts objectAtIndex:selectionIndex]];
// execute the request
[accessTokenRequest performRequestWithHandler:
^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *responseStr =
[[NSString alloc] initWithData:responseData
encoding:NSUTF8StringEncoding];
NSLog(#"The user's info for your server:\n%#", responseStr);
}];
UPDATE:
Turning on NSZombieEnabled gives me
*** -[ACAccountStore typeForAccount:]: message sent to deallocated instance 0x7199c70
this is no where to be found
Your error looks completely different than your question. You have error in ACAccountStore class. Check your retain count when you accessing, manipulating, and store accounts (ACAccountStore). I think you are deallocated memory first and you are using some where.
Somewhere you call ACAccountStore typeForAccount. But the ACAccountStore is gone. Looking at the AcAccount docs, there are no special initializers, so likely in your code you have something like:
static ACAccountStore* accountStore = [[[ACAccountStore alloc] init] autorelease];
then in the completion for the request, the object has been cleaned away by the OS, but your accountStore still points to the old, now dangling pointer. The pointer may be 'static' or 'global' or a member of some other static or global object.
Look for ACAccountStore in your code.
I'm trying to implement a custom login view to Twitter (I don't want that UIWebView).
I've downloaded many classes, and I'm so far having a nightmare with this. Now I'm trying to make Twitter + oAuth work. Here's the demo code (that works):
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
[_engine requestRequestToken];
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: _engine delegate: self];
if (controller)
[self presentModalViewController: controller animated: YES];
else
[_engine sendUpdate: [NSString stringWithFormat: #"Already Updated. %#", [NSDate date]]];
Now what I wanna do is replace that SA_OAuthTwitterController with custom UITextFields. So I'm trying this:
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;
[_engine requestRequestToken];
[_engine requestAccessToken];
[_engine setUsername:#"username" password:#"password"];
[_engine sendUpdate:#"tweet"];
But I keep getting error 401. I'm probably missing a step. Anyone?
The view controller you are eliminating loads a webview that prompts the user to authenticate your application with twitter. If you don't ever have the user authenticate, you won't be able to make authenticated twitter calls. There is a way to do what you are wanting, however, you have to go through all of the OAuth steps yourself. Here are the steps as specified from a different web service (Vimeo), but the same rules apply:
Your application sends a request
with your consumer key, and signed
with your consumer secret, for a
something called a request token. If
we verify your application
correctly, we'll send you back a
request token and a token secret.
You'll then create a link for the user to click on with the request token.
When the user gets to Vimeo, they'll be prompted to allow your
application access to their account.
If they click yes, we'll send them
back to your application, along with
a verifier.
You'll then use the request token, verifier, and token secret to make
another call to us to get an access
token. The access token is what
you'll use to access the user's
information on Vimeo.
OAuth is a real pain in the rump, so good luck. ;-)
I think the bit you're missing is here, SA_OAuthTwitterEngine.m:103:
//This generates a URL request that can be passed to a UIWebView. It will open a page in which the user must enter their Twitter creds to validate
- (NSURLRequest *) authorizeURLRequest {
if (!_requestToken.key && _requestToken.secret) return nil; // we need a valid request token to generate the URL
OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL: self.authorizeURL consumer: nil token: _requestToken realm: nil signatureProvider: nil] autorelease];
[request setParameters: [NSArray arrayWithObject: [[[OARequestParameter alloc] initWithName: #"oauth_token" value: _requestToken.key] autorelease]]];
return request;
}
You will have to "fake" this user login out yourself, I believe by sending twitter the login credentials as a separate request. It appears that the setUsername method you're calling is actually called as a post operation once a valid access token has been received. See SA_OAuthTwitterEngine.m:185
//
// access token callback
// when twitter sends us an access token this callback will fire
// we store it in our ivar as well as writing it to the keychain
//
- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
if (!ticket.didSucceed || !data) return;
NSString *dataString = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
if (!dataString) return;
if (self.pin.length && [dataString rangeOfString: #"oauth_verifier"].location == NSNotFound) dataString = [dataString stringByAppendingFormat: #"&oauth_verifier=%#", self.pin];
NSString *username = [self extractUsernameFromHTTPBody:dataString];
if (username.length > 0) {
[[self class] setUsername: username password: nil];
if ([_delegate respondsToSelector: #selector(storeCachedTwitterOAuthData:forUsername:)]) [(id) _delegate storeCachedTwitterOAuthData: dataString forUsername: username];
}
[_accessToken release];
_accessToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
}
So the steps are as follows:
Request request token
Fake user login to twitter and get the pin value
Set the pin on the engine
Request access token
You can see where SA_OAuthTwitterController parses the pin out of the webview content in SA_OAuthTwitterController.m:156
#pragma mark Webview Delegate stuff
- (void) webViewDidFinishLoad: (UIWebView *) webView {
NSError *error;
NSString *path = [[NSBundle mainBundle] pathForResource: #"jQueryInject" ofType: #"txt"];
NSString *dataSource = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
if (dataSource == nil) {
NSLog(#"An error occured while processing the jQueryInject file");
}
[_webView stringByEvaluatingJavaScriptFromString:dataSource]; //This line injects the jQuery to make it look better
NSString *authPin = [[_webView stringByEvaluatingJavaScriptFromString: #"document.getElementById('oauth_pin').innerHTML"] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (authPin.length == 0) authPin = [[_webView stringByEvaluatingJavaScriptFromString: #"document.getElementById('oauth_pin').getElementsByTagName('a')[0].innerHTML"] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[_activityIndicator stopAnimating];
if (authPin.length) {
[self gotPin: authPin];
}
if ([_webView isLoading] || authPin.length) {
[_webView setHidden:YES];
} else {
[_webView setHidden:NO];
}
}
hope this helps.
You have to use xAuth for custom user/pass controls. But it requires the permission from Twitter.
This is a weird one...
With the help of people here, I've got my iPhone app posting to TwitPic successfully - and when I first got it working, I could see an XML result being returned too...
But for some reason over the past two days, the API call seems to succeed - the pic appears on TwitPic - but... the response seems to be empty...
Anyone have any ideas? Seen anything similar? The code I use to invoke the API call is:
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
[request setData:twitpicImage forKey:#"media"];
[request setPostValue:username forKey:#"username"];
[request setPostValue:password forKey:#"password"];
// Initiate the WebService request
[request start];
// Need to find out how I can access the result from this call...
/* Result structure should be:
<?xml version="1.0" encoding="UTF-8"?>
<rsp stat="ok">
<mediaid>abc123</mediaid>
<mediaurl>http://twitpic.com/abc123</mediaurl>
</rsp>
*/
// Check for errors
if ([[request responseHeaders] objectForKey:#"stat"] != #"ok"){
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"TwitPic Submission"
message:[[request responseHeaders] objectForKey:#"mediaurl"]
delegate:nil
cancelButtonTitle:#"OK!"
otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
}
NSString *twitpicURL = [[request responseHeaders] objectForKey:#"mediaurl"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"TwitPic Submission"
message:twitpicURL
delegate:nil
cancelButtonTitle:#"OK!"
otherButtonTitles:nil];
I tried just dumping out [request responseString]... that's empty now also. That WAS showing a response, for sure.
As always, any help gratefully received. I'll give back once I'm able!
Cheers,
Jamie.
OK, problem solved :-)
Check out this website: link text
Simply add these lines to your request:
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
and the following methods:
- (void)requestDone:(ASIHTTPRequest *)request { NSString *response = [request responseString];}
- (void)requestWentWrong:(ASIHTTPRequest *)request { NSError *error = [request error];}
NSString *response holds all the important feedback from twitpic.
Enjoy :-)
AH! I think I've found the problem. Seems to be a problem with TwitPic - 2 days ago I changed my password to a mixed-case one with numbers and letters. I just reset it back to an all lowercase one and... it all works fine.
I've sent a message to twitpic support - hopefully they'll look in to it!
Jamie.