Twitter OAuth and accessToken to GET statuses/user_timeline in Objective-C - objective-c

I'm trying to acquire the accessToken value from Twitter for using it in my app (I need to use API v1.1's Authentication Model for GET statuses/user_timeline); I have registered my app on api.twitter.com, imported the AFOAuth1Client classes in the project, and this is the simple code:
- (void)viewDidLoad
{
self.twitterClient = [[AFOAuth1Client alloc] initWithBaseURL:
[NSURL URLWithString:#"https://api.twitter.com/"] key:#"MYKEY" secret:#"MYSECRETKEY"];
AFOAuth1Token *accessToken;
[self.twitterClient acquireOAuthAccessTokenWithPath:#"oauth/request_token" requestToken:accessToken accessMethod:#"GET" success:^(AFOAuth1Token *accessToken) { // I have tried also accessMethod: #"POST"
NSLog(#"Success: %#", accessToken);
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Unluckly XCode give me this error:
Error: Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in (200-299), got 401" UserInfo=0x79a14f0 {NSLocalizedRecoverySuggestion=Failed to validate oauth signature and token, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x7956390>
What is wrong here? DO i need to REGISTER app on api.twitter.com? Is it the right way, or, what is the simpler way to Get statuses/user_timeline using API v1.1's Twitter Authentication Model in iOS? Thank you!
EDIT: possible waypoint?
1) register a new app on dev.twitter.com
2) in OAuth settings, read Consumer key and Consumer secret
3) set default app access type to read? or read/write? ask for access tokens? use this values in.... ?

It's been a few days since Twitter added an application only mode. In this mode, you can access specific API endpoints without user's context, ie. your users don't have to authenticate. Only the application does authenticate once in a while to get a "bearer token" which is then sent in every request.
The following code gets a bearer token and then retrieves the public timeline for #barackobama.
STTwitterAPI *twitter = [STTwitterAPI twitterAPIApplicationOnlyWithConsumerKey:#""
consumerSecret:#""];
[twitter verifyCredentialsWithSuccessBlock:^(NSString *bearerToken) {
NSLog(#"Access granted with %#", bearerToken);
[twitter getUserTimelineWithScreenName:#"barackobama" successBlock:^(NSArray *statuses) {
NSLog(#"-- statuses: %#", statuses);
} errorBlock:^(NSError *error) {
NSLog(#"-- error: %#", error);
}];
} errorBlock:^(NSError *error) {
NSLog(#"-- error %#", error);
}];
See STTwitter iOS demo project for a working example (use you own consumer key / secret).

If you have your own consumer tokens, you can use the STTwitter library I wrote.
STTwitterAPI *twitter =
[STTwitterAPI twitterAPIWithOAuthConsumerName:#""
consumerKey:#"your_key"
consumerSecret:#"your_secret"
username:#"username"
password:#"password"];
[twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
NSLog(#"Access granted for %#", username);
[twitter getUserTimelineWithScreenName:#"barackobama"
successBlock:^(NSArray *statuses) {
NSLog(#"-- statuses: %#", statuses);
} errorBlock:^(NSError *error) {
NSLog(#"-- error: %#", error);
}];
} errorBlock:^(NSError *error) {
NSLog(#"-- error %#", error);
}];
If your tokens are not 'xAuth' enabled, you'll have to use a PIN. STTwitter provides simple asynchronous block-based methods to ask the user. You can also choose to automate the PIN retrieval, see STTwitterDemo -[AppDelegate guessPIN:] for an automated process.

STTwitter is simple for use . Also I added example that you can get public twitter timeline

Related

Not getting the email from twitter using Fabric Twitter SDK

This seems to be age old question with no specific answers or solutions for it. I had my application whitelisted from twitter. Done all the things. Checked the request email from user box and used the following code
[[Twitter sharedInstance] startWithConsumerKey:#"FabricKey" consumerSecret:#"FabricSecretKey"];
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
But still it is returning null. Why is this?? I even created the application in twitter developer portal. Please help me. Is twitter trying to be more over secure?? It is just pissing me out......

How to do login in Twitter using STTwitter for OSX?

Hello,
I am developing a Twitter client for OSX using STTwitter library.
I use this code to do login:
- (void) loginWithUser:(NSString*) user
password:(NSString*) password {
twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:kOAuthConsumerKey
consumerSecret:kOAuthConsumerSecret
username:user
password:password];
[twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
_status = [NSString stringWithFormat:#"Access granted for %#", username];
_isConnected = YES;
} errorBlock:^(NSError *error) {
_isConnected = NO;
_status = [error localizedDescription];
NSLog(#"Status: %#",_status);
}];
}
I can read direct messages using my personal Twitter account and other development Twitter account but if I try to use other Twitter account from my beta testers I can not read the direct messages.
The error message is:
This application is not allowed to access or delete your direct messages.
I tried to use an OSX system account to do login using this code:
- (void) loginWithSystemAccount {
twitter = [STTwitterAPI twitterAPIOSWithFirstAccount];
[twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
_status = [NSString stringWithFormat:#"Access granted for %#", username];
_isConnected = YES;
} errorBlock:^(NSError *error) {
_isConnected = NO;
_status = [error localizedDescription];
NSLog(#"Status: %#",_status);
}];
}
But I can read direct messages using a system account.
I checked the OSX Demo in STTwitter git repository but I could not find a solution.
How can I do a right login in Twitter for all users to read direct messages?
Thanks in advance
At first, you should find out if it's a weird behaviour of Twitter API, or a bug in STTwitter.
To do so, I suggest that you try reading direct messages with twurl.
If you're not sure which endpoints you're calling, enable STTwitter networks logs with the STHTTPRequestShowCurlDescription argument.

Handle invalid accessToken with FBSession openActiveSessionWithReadPermissions in Facebook iOS 3.1.1 SDK

Before anything, I have read both this and this questions to solve the problem below and before asking.
My problem is that when the accessToken gets expired (either because the expiration date passes, or manually by deleting the app from my Facebook's App Center) the following code:
if ([[FBSession activeSession] isOpen]) {
//do something
}
else {
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if(FB_ISSESSIONOPENWITHSTATE(status)) {
//do something
}
}
}];
}
gets in the else block with FBSession.activeSession open but when the 'do something' is executed the accessToken is invalid so the request gets Error: HTTP status code: 400.
When I try to do the whole procedure twice immediately the FBSession asks for permission (either UIAlertView for iOS6 integrated facebook, Facebook App or Facebook website in Safari) and the rest runs smoothly.
My concern is why I have to do everything twice to work well and why Facebook SDK cannot detect in the first time that the activeSession and accessToken are invalid.
Thank you all in advance!
The questions you linked are relevant, especially Facebook SDK 3.1 - Error validating access token which explains the problem where the Facebook account on the device is out of sync with the server (I.e., if you deleted the app from App Center). As mentioned there, in 3.1.1 the SDK will call to renew the device token only when it gets the invalid response from the server. This is a trade off in convenience for less round-trips to the server.
Assuming your code block is executed on applicationDidFinishLaunching or something similar, it will go to the else block because the app starts with a new session. When it calls openActiveSessionWithReadPermissions, the iOS 6 device thinks the token is valid and will let the state go to Open, so then your "do something" gets executed. Only then does the SDK get the invalid response from the server and invalidate the device token. As a result, the next time the procedure is called, it will prompt the user appropriately to authorize again.
This is intentional. For now, you can consider a automatic retry in your application if the error code describes an invalid token. For example, see the Scrumptious sample postOpenGraph retry code. In your case, it may look closer to something like (I used requestForMe as the "do something" for demonstration purposes):
else {
[FBSessionopenActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if(FB_ISSESSIONOPENWITHSTATE(status)) {
//do something
[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
NSLog(#"success on first try");
} else if ([[error userInfo][FBErrorParsedJSONResponseKey][#"body"][#"error"][#"code"] compare:#190] == NSOrderedSame) {
//requestForMe failed due to error validating access token (code 190), so retry login
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if (!error){
//do something again, or consider recursive call with a max retry count.
NSLog(#"success on retry");
}
}];
}
}];
}
}];
}

Access Facebook Accounts on OS X Mountain Lion

As Mountain Lion now officialy has Facebook integrated I wanted to play a little bit with Accounts.framework and Social.framework of OS X.
I set up an App in the Facebook Developer App and used the following code to request access to the Facebook Accounts of the logged in user:
ACAccountStore *acStore = [[ACAccountStore alloc] init];
ACAccountType *accType = [acStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:FB_APP_ID, ACFacebookAppIdKey, #"read_stream, publish_stream", ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];
[acStore requestAccessToAccountsWithType:accType options:options completion:^(BOOL granted, NSError *error) {
if (granted) {
NSLog(#"Accounts: %#", acStore.accounts);
NSLog(#"Access granted");
} else {
NSLog(#"Access denied");
}
}];
Now I always get the error Error Domain=XPCObjectsErrorDomain Code=2 "The operation couldn’t be completed. (XPCObjectsErrorDomain error 2.)"
When I launched the App from Xcode the first 2-3 times, OS X displayed the confirmation dialog "XYZ wants to access your Facebook Accounts", I clicked ok and I got an error saying something like "The Application is not yet installed". This error now "disappeared" and I'm getting the error noted above.
My question is: Do I have to configure something special in my Xcode Project (Info.plist) or in the Facebook Developer App to make it work with OS X? Or am I missing something in the code? Samples and Tutorials regarding this seem to be very rare :-/
I already watched the WWDC 2012 Video about the Twitter/Facebook Integration but it uses almost exactly the code I used.
Any help is appreciated :-)
Cheers,
Björn
Update
I solved the issue, I just did not read the docs carefully enough ;-) I have to pass the permissions as an NSArray and not as a string. I've modified the options dictionary to look like this:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
FB_APP_ID, ACFacebookAppIdKey,
[NSArray arrayWithObjects:#"read_stream", #"publish_stream", #"email", #"user_likes", nil], ACFacebookPermissionsKey,
ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];
Now OS X shows an alert that asks me if I want to allow the App to post to Facebook on my behalf, I click OK and a new error appears resulting in no access to the Facebook accounts:
Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: The app must ask for a basic read permission at install time."
I already modified the permissions to be just email or just user_likes but it all resulted in the same error. If I only request access to read_stream the access gets granted but results in the OAuth error 190 with the message Error validating access token: User XXXXXX has not authorized application XXXXXX.
Update 2:
I now solved all my problems, see my answer below.
Alright, I finally solved it :-)
When you first request access to a users Facebook accounts, you may only request permissions from the user listed under "User and Friend permissions" in Facebook's Permission Reference.
Once the user granted access and with that also installed the application on Facebook, you may request any additional permissions required by your App. It's important that you may not ask for read and write permissions at the same time, one after the other! So requesting the permission read_stream and publish_stream in the same request will not work.
This is the basically the code I used:
self.store = [[ACAccountStore alloc] init];
ACAccountType *accType = [self.store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:
FB_APP_ID, ACFacebookAppIdKey,
[NSArray arrayWithObjects:#"email", #"user_about_me", #"user_likes", nil], ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];
[self.store requestAccessToAccountsWithType:accType options:options completion:^(BOOL granted, NSError *error) {
if (granted && error == nil) {
self.accounts = self.store.accounts;
[options setObject:[NSArray arrayWithObjects:#"read_stream, read_mailbox, read_requests", nil] forKey:ACFacebookPermissionsKey];
[self.store requestAccessToAccountsWithType:accType options:options completion:^(BOOL granted, NSError *error) {
if (granted && error == nil) {
completionHandler(granted, error);
} else {
NSLog(#"Access denied 2");
NSLog(#"%#", [error description]);
}
}];
} else {
NSLog(#"Error: %#", [error description]);
NSLog(#"Access denied");
}
}];
Hope this will help somebody :-)
I would like to add that there is an additional caveat in the Facebook docs:
// if a user has *never* logged into your app, you MUST include one of
// "email", "user_location", or "user_birthday". Other read
// permissions can also be included here.
Failure to do this leads to the same error.

How do I cancel a previous Facebook reauthorize call?

A quick question about the Facebook SDK for iOS. I'm trying to implement the functionality for a user to grant my app access to extended permissions.
While the code works fine when the user accepts the extended permission request (from the Facebook iOS app the user gets redirected to), I'm having trouble detecting when the user has returned to the app while having neither accepted the permissions, or clicking cancel then returning to the app.
If I click 'Cancel' and multitask back into the app, there is nothing logged and nothing shown on-screen. When I try to re-authorize again, the Facebook SDK throws an exception:
FBSession: It is not valid to reauthorize while a previous reauthorize call has not yet completed.
While I can catch the exception, it still doesn't help as I can't figure out how to stop the previous call in order to allow the user to try to re-authorize again.
Here's the code I'm using at the moment:
#try {
[[FBSession activeSession] reauthorizeWithPermissions:[self requiredPermissions]
behavior:FBSessionLoginBehaviorWithFallbackToWebView
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
[self fetchUserPermissionsWithCompletionHandler:^(BOOL extendedGranted) {
if (extendedGranted) {
[self setCanPostToActivityStream:YES];
}
}];
}
else {
NSLog(#"%#", [error localizedDescription]);
}
}];
}
#catch (NSException *exception) {
NSLog(#"%#", exception);
}
Now, the issue is not with the code above - the code works fine. The issue I'm having, again, is cancelling the previous re-authorize call when the user doesn't return to the app successfully after allowing permissions. The Facebook SDK as it is doesn't seem to alert the app of this situation in any way.
From your App's delegate, when - (void)applicationDidBecomeActive:(UIApplication*)application is invoked, you need to make a call to the active session's handleDidBecomeActive method. Once you do that, the completion handler associated with your reauthorizeWithPublishPermissions call will be invoked, and will provide the appropriate error.
- (void)applicationDidBecomeActive:(UIApplication*)application
{
[[FBSession activeSession] handleDidBecomeActive];
}
What you should see coming back from the SDK is an error from the reauthorize completion handler.
{
"com.facebook.sdk:ErrorLoginFailedReason" = "com.facebook.sdk:ErrorReauthorizeFailedReasonUserCancelled";
}
I tested something similar with the following setup: Xcode 4.5, iOS 5, Authorize against m.facebook.com. After an initial authorization I put code to hit reauthorize, then clicked Cancel and got this error.
[FBSession.activeSession
reauthorizeWithPublishPermissions:
[NSArray arrayWithObject:#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
// Success case
} else {
// Check the error, info
// [[error userInfo] objectForKey:#"com.facebook.sdk:ErrorLoginFailedReason"]
}
}];
If you are not seeing this then it is a bug and you may want to file it with Facebook.