Twitter oauth/access_token 401 Error - ios

I've been working on an all that uses the full OAuth app flow, and I have been running into an issue where I only get back a 401 = "Invalid or expired token" error. I've checked my request with the documentation, and everything looks correct, and I'm stumped. Below is the details of my request.
URL
https://api.twitter.com/1.1/oauth/access_token
HTTP Method
POST
Headers
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth oauth_consumer_key="CONSUMER_API_KEY", oauth_nonce="B4D43B0C-A348-4EB6-9C0B-8B0F4FE8", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1397724299", oauth_version="1.0", oauth_token="TOKEN_FROM_REQUEST_TOKEN_API", oauth_signature="ulYIzTacwC%2FeGUdoCPYsrFEqg4A%3D"
HTTP Body
oauth_verifier=OAUTH_VERIFIER_FROM_REQUEST_TOKEN_API
I have no issues getting the oauth/request_token API to work, and I have appropriately set the permissions in Twitter's app settings. Does anyone have any idea what's going on with this?
Thanks for any help you all may be able to offer.
-- UPDATE --
Another thing to note is that I'm using the STTwitter library to make the requests. It did not have a built-in method to handle the oauth/authorize or oath/authenticate API methods so I'm using the code below.
// get request token, and present login screen
STTwitterAPI *twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:twitterApiKey consumerSecret:twitterApiSecret];
[twitter postTokenRequest:^(NSURL *url, NSString *oauthToken)
{
authWebViewController = [[TWAuthWebViewController alloc] init];
authWebViewController.url = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.twitter.com/oauth/authorize?oauth_token=%#", oauthToken]];
authWebViewController.completion = ^(TWAuthWebViewController *authViewController, NSURL *oauthCallbackURL)
{
// get the request token and verifier from the URL
NSString *oauthToken = [TWLoginBusiness getOAuthTokenFromURL:oauthCallbackURL];
NSString *oauthVerifier = [TWLoginBusiness getOAuthVerifierFromURL:oauthCallbackURL];
// get user data with the oauth token
[twitter postResource:#"oauth/access_token"
baseURLString:#"https://api.twitter.com/1.1"
parameters:#{#"oauth_verifier" : oauthVerifier}
uploadProgressBlock:nil
downloadProgressBlock:nil
successBlock:^(NSDictionary *rateLimits, id response)
{
NSLog(#"Reponse: %#", response);
completion(nil, nil);
} errorBlock:^(NSError *error)
{
NSLog(#"Error: %#", error);
completion(nil, error);
}];
};
presentAuthController(authWebViewController);
} oauthCallback:twitterApiCallback errorBlock:^(NSError *error)
{
completion(nil, error);
}];
One last note. The part that actually displays the web view controller is not listed here. I wanted to keep the code snippet here focused on the actual API methods, and not the UI logic. Just be assured that the line right after authWebViewController.url.... displays the web view, and then the completion block is called after the user completes the authentication on the Twitter web page. Also the two methods getOauthTokenFromURL and getOauthVerifierFromUrl do in fact return the correct token and verifier. The STTwitter library actually saves out the token it's self, so that is why it's not manually passed into the logic below. The logic generates the request above.
Thanks

The full OAuth flow is already implemented in STTwitter library.
Check out iOS demo for a working example of web-based authentication through Safari.
What you are trying to do here is a web-based authentication inside the application.
Although it is totally feasible, I did not include this workflow in the demo project because it is considered a bad practice.
Indeed, the point of OAuth is that the user does not want to enter her Twitter credentials in your own application, but asks Twitter to send your app dedicated OAuth tokens instead.
Let me know if you need more help with STTwitter.

Related

iOS ADAL-Make silent call using refresh token

I am using iOS ADAL library version 2.2.6 and receiving refresh token upon successful login. Now I want to make a silent call by using this refresh token. I tried with following method but it fails to return the access token.
ADAuthenticationContext *authContext;
[authContext acquireTokenSilentWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
userId:strUserID //loggedIn userID
completionBlock:^(ADAuthenticationResult *result){
// It alway throws an error //Please call the non-silent acquireTokenWithResource methods.
if(result.error){
ADAuthenticationError *error = nil;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:inputData.authority error:&error];
[authContext acquireTokenWithResource:inputData.ResourceID
clientId:inputData.ClientId // Comes from App Portal
redirectUri:inputData.RedirectUri // Comes from App Portal
completionBlock:^(ADAuthenticationResult *result)
{
if (AD_SUCCEEDED != result.status){
// Show alert with error description
}
else{
//Handle Success token
}
}];
}else{
//Handle Success token
}
}];
But it always throws an error saying "The user credentials are needed to obtain access token. Please call the non-silent acquireTokenWithResource methods."
Is there any way to make a silent call using refresh token? please help me on it. Thanks in advance.
When you use Microsoft's authentication libraries, you should always first check to see if there is a user in the cache that can be used for your resource before prompting the user to sign in. This allows us to check if the user had previously signed in to your app or if there are other apps that share state with your app that may have already asked the user to sign in elsewhere.
If the user is found, we will try to acquire a token without interrupting the user at all. Sometimes a user will have changed their password or done some other action that will require them to sign in again even if they have signed in to your app previously. This is what you are seeing. The library is telling you that for the user you are trying to acquire a token for, they need to sign in again to make something right.
In order to handle all these cases elegantly, we recommend that you use the pseudocode pattern of:
acquireTokenSilent()
(if error InteractiveAuthenticationRequired) {
acquireTokenInteractively() }
The pattern first checks if a user you specify is available in the token cache. If it is, we then call the Azure Active Directory service to see if the Refresh token for that user is valid. If both of these are true, then the user is signed in silently. If the user isn't found or the server rejects the Refresh Token, then an error is sent from the library that indicates the user needs to sign in interactively.
In the above, you are doing this first part, but you aren't handling the case where the user needs to sign in if there is a problem.
The best way is to catch the error with a ADErrorCode of AD_ERROR_USER_INPUT_NEEDED
Here is a code sample on how to do this pattern.
// Here we try to get a token from the stored user information we would have from a successful authentication
[authContext acquireTokenSilentWithResource:data.resourceId
clientId:data.clientId
redirectUri:redirectUri
userId:data.userItem.userInformation.userId
completionBlock:^(ADAuthenticationResult *result) {
if (!result.error)
{
completionBlock(result.tokenCacheStoreItem.userInformation, nil);
} else {
if ([result.error.domain isEqual:ADAuthenticationErrorDomain] && result.error.code == AD_ERROR_USER_INPUT_NEEDED) {
// Here we know that input is required because we couldn't get a token from the cache
[authContext acquireTokenWithResource:data.resourceId
clientId:data.clientId
redirectUri:redirectUri
userId:data.userItem.userInformation.userId
completionBlock:^(ADAuthenticationResult *result) {
if (result.status != AD_SUCCEEDED)
{
completionBlock(nil, result.error);
}
else
{
data.userItem = result.tokenCacheStoreItem;
completionBlock(result.tokenCacheStoreItem.userInformation, nil);
}
}];
} else {
completionBlock(nil, result.error);
}
}
}];
Keep in mind this code is very verbose. You will most likely want to have acquireTokenWithResource: a separate method that you could call with [self acquireTokenWithResource]

Do I need to refresh the access token when using Microsoft Graph and How to do it?

I'm using Microsoft Graph SDK for my iOS Application.
Do I need to manually refresh the access token when it expired?
The access token I'm talking about is:
NXOAuth2AccountStore.sharedStore().accounts[0].accessToken
I have tested that I can still query even the accessToken expired. At the time I first logged in, the expired time is 3600 secs. So, I waited 2 hours, test to get user info, events again and still can get it.
I have dump "accessToken.hasExpired" and "accessToken.expiresAt" to make sure access token is expired
Thanks
* More Details *
I follow the sample here:
https://github.com/microsoftgraph/ios-swift-connect-sample
I cannot find any documents about refresh access token on Microsoft Graph:
https://graph.microsoft.io/en-us/code-samples-and-sdks
Yes, you need to refresh tokens periodically when using Graph in your application. More detailed documentation is available through Azure AD's site: https://learn.microsoft.com/en-us/azure/active-directory/active-directory-authentication-scenarios
The suggested auth library you are using contains a method for refreshing this token:
#implementation NXOAuth2AuthenticatorRefreshCallback
If I haven't answered your question, could you be more specific about what you are trying to accomplish? Are you able to use an expired token or are you unable to refresh your old one?
Use this code whenever you need to refresh the access token.
This will act as a patch to predefined code provided in graph sdk and you can extract the token from the method :
+(id)tokenWithResponseBody:(NSString *)theResponseBody tokenType:(NSString *)tokenType;
[MSGraphClient setAuthenticationProvider:AppDel.authentication.authProvider];
_graphClient = [MSGraphClient client];
NSMutableURLRequest * sampleReq = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://graph.microsoft.com/v1.0/me"]];
[_graphClient.authenticationProvider appendAuthenticationHeaders:sampleReq completion:^(NSMutableURLRequest *request, NSError *error){
if(error == nil)
{
}
else
{
[self showToast:#"" message:#"Failure in refresh 0365 token"];
}
}];

ADALiOS seems to be redirecting my login to login.live.com

I'm trying to use ADALiOS framework v1.2.4 to acquire a token with a specific resource and clientId. I'm using a custom authority hosted on 'microsoftonline.com'.
I create an authentication context from within a VC thusly:
ADAuthenticationError *error = nil;
ADAuthenticationContext context = [[ADAuthenticationContext alloc] initWithAuthority:#"https://login.microsoftonline.com/somethingcustom/oauth2/v2.0/authorize?p=some_signin_policy"
validateAuthority:YES
tokenCacheStore:nil
error:&error];
context.parentController = self;
And then I proceed to acquire a token thusly:
[context acquireTokenWithResource:#"someResource"
clientId:#"someClientId"
redirectUri:[NSURL URLWithString:#"someAppScheme://moreStuff"]
completionBlock:^(ADAuthenticationResult *result) {
NSLog(#"Was here!");
}];
What I would expect to happen is that a UIWebView would open and redirect me to my custom authority sign-in page where I can enter my credentials. To confirm, that's what I see when I type in my custom authority URL into any old browser.
However, instead what I see happening is that the code redirects me to login.live.com that (A) looks different and (B) doesn't recognize my login credentials.
When I turn verbose logging on, I see the following line in the logs:
VERBOSE: HTTP Protocol. Additional Information: HTTPProtocol::connection:willSendRequest:. Redirect response: https://login.microsoftonline.com/somethingcustom/oauth2/authorize?response_type=code&client_id=someClientID&resource=someResourceID&redirect_uri=something%3A%2F%2Fsomethingelse&state=bunchOfStuff&x-client-CPU=64&x-client-DM=iPhone&x-client-Ver=1.2.4&x-client-SKU=iOS&x-client-OS=9.2.1&client-request-id=70E51F25-FD3D-4F98-8EDF-04CD19320A98. New request:https://login.live.com/login.srf?wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=estsredirect%3d2%26estsrequest%3drQIIAWVQTWsTUQD0ZVfQXFo8iKUHPUQQ4W33vX2bbAqCxdhtpEnoJhF3b-9ru5u-l63Z3TTJj_BQL6U_wWNPpf9ByFG8CIIg_gIvgokHETzMMMwMc5jqbWRhizw1kIV2a9gWji0wgq5w65Aw2oBNwWLYWAVe3LQxJfXJverm44ffTp_9uui9-2J-3MLVi2sAlgB8B-C8cn_Yfnm0ZzvIUwI9P9Y0VRbP9LICPlc2entlkeA1ZZN0Ib_-55wbGxldO9aaeSbkpfEehAOUiYPgjM-Hi6P-cB6kK-jZNMKqPNToNHReTTluauE3S-arMuq7I4Zt3TmJ8uhNgrgTFEzvz8K-O2X_9l_86Wk-QjpqJarrd87Cvk26g_bscBCchIuw6LX2VWeOkq7fJr1WMIr81yoctT8YtVh4knCbQs9dPUYEr0MPSQdSwjyPcCElw1fGtpKzMi_0mL6lVjbWKZ9kERYX61NujFodIe46jEFkYxsSKuRqJKaQNmKXUSFiD6FPBlia4IdZvQM2Kw_MR7eegJ8muLm7lWdaFkk6Pt7d2fmrpcrlbw2&id=. ErrorCode: 0.
I'm guessing I'm doing something very wrong, but I can't figure out what it is. Any suggestions?
For building iOS apps with Azure AD B2C, I'd recommend starting with this tutorial: https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-ios/. The version of ADAL you are using doesn't support B2C.
For automatically redirecting to custom authority sign-in page instead of common Microsoft landing page, I use extraQueryParameters while getting the token as:
[context acquireTokenWithResource:#"someResource"
clientId:#"someClientId"
redirectUri:[NSURL URLWithString:#"someAppScheme://moreStuff"]
extraQueryParameters:#"domain_hint=AzureADDomain.co.uk"
completionBlock:^(ADAuthenticationResult *result) {
NSLog(#"Was here!");
}];
Hope this helps someone.

How to post a pic to Tumblr using TMTumblrSDK in iOS

I am trying to integrate Tumblr into my app for simply posting of pics into tumblr.
I am confused in following the authentication process of tumblr.
I registered the app.
I have the consumer key, and the consumer secret key.
I have requested for the token and the token secret key by using OAuthConsumer.
However I am failing to do the next step which is posting a photo .
This is the code I'm using to authenticate:
[[TMAPIClient sharedInstance]authenticate:#"myappURLScheme" callback:^(NSError *err) {
NSLog(#"authentication failed %#",err);
}];
And this takes me to the safari tumblr page, to do the Allow/ Not allow process.After I click on Allow, it takes me to the tumblr app. But the pic is not posted.
Here is what I tried to post the pic:
[[TMAPIClient sharedInstance] photo:#"Sample Upload"
filePathArray:#[[_imagesPath stringByAppendingPathComponent:
#"tm.png" ]]
contentTypeArray:#[#"image/png"]
fileNameArray:#[#"tm.png"]
parameters:#{#"caption" : #"Caption"}
callback:^(id response, NSError *error) {
if (error)
NSLog(#"Error posting to Tumblr %#",error);
else
NSLog(#"Posted to Tumblr %#",error);
}];
HEre I get the error:
Error posting to Tumblr Error Domain=Request failed Code=404 "(null)"
The response parameter is also null.
I believe the authentication is a success,I am also able to get the user info from the Tumblr. but I'm not sure why the pic is not posted .
Is Sample Upload your blog? I have to admit Tumblr's naming conventions are really bad, but if you check the code, you can see you need to pass the blog name there.
- (void)photo:(NSString *)blogName filePathArray:(NSArray *)filePathArrayOrNil contentTypeArray:(NSArray *)contentTypeArrayOrNil
fileNameArray:(NSArray *)fileNameArrayOrNil parameters:(NSDictionary *)parameters callback:(TMAPICallback)callback;
Otherwise make sure you set the OAuthConsumerKey, OAuthConsumerSecret, OAuthToken and OAuthTokenSecret before sending the request.
Your code looks fine otherwise, about the same worked for me.

Best solution to refresh token automatically with AFNetworking?

Once your user is logged in, you get a token (digest or oauth) which you set into your HTTP Authorization header and which gives you the authorization to access your web service.
If you store your user's name, password and this token somewhere on the phone (in user defaults, or preferably in the keychain), then your the user is automatically logged in each time the application restarts.
But what if your token expires? Then you "simply" need to ask for a new token and if the user did not change his password, then he should be logged in once again automatically.
One way to implement this token refreshing operation is to subclass AFHTTPRequestOperation and take care of 401 Unauthorized HTTP Status code in order to ask a new token. When the new token is issued, you can call once again the failed operation which should now succeeds.
Then you must register this class so that each AFNetworking request (getPath, postPath, ...) now uses this class.
[httpClient registerHTTPOperationClass:[RetryRequestOperation class]]
Here is an exemple of such a class:
static NSInteger const kHTTPStatusCodeUnauthorized = 401;
#interface RetryRequestOperation ()
#property (nonatomic, assign) BOOL isRetrying;
#end
#implementation RetryRequestOperation
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *, id))success
failure:(void (^)(AFHTTPRequestOperation *, NSError *))failure
{
__unsafe_unretained RetryRequestOperation *weakSelf = self;
[super setCompletionBlockWithSuccess:success failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// In case of a 401 error, an authentification with email/password is tried just once to renew the token.
// If it succeeds, then the opration is sent again.
// If it fails, then the failure operation block is called.
if(([operation.response statusCode] == kHTTPStatusCodeUnauthorized)
&& ![weakSelf isAuthenticateURL:operation.request.URL]
&& !weakSelf.isRetrying)
{
NSString *email;
NSString *password;
email = [SessionManager currentUserEmail];
password = [SessionManager currentUserPassword];
// Trying to authenticate again before relaunching unauthorized request.
[ServiceManager authenticateWithEmail:email password:password completion:^(NSError *logError) {
if (logError == nil) {
RetryRequestOperation *retryOperation;
// We are now authenticated again, the same request can be launched again.
retryOperation = [operation copy];
// Tell this is a retry. This ensures not to retry indefinitely if there is still an unauthorized error.
retryOperation.isRetrying = YES;
[retryOperation setCompletionBlockWithSuccess:success failure:failure];
// Enqueue the operation.
[ServiceManager enqueueObjectRequestOperation:retryOperation];
}
else
{
failure(operation, logError);
if([self httpCodeFromError:logError] == kHTTPStatusCodeUnauthorized)
{
// The authentication returns also an unauthorized error, user really seems not to be authorized anymore.
// Maybe his password has changed?
// Then user is definitely logged out to be redirected to the login view.
[SessionManager logout];
}
}
}];
}
else
{
failure(operation, error);
}
}];
}
- (BOOL)isAuthenticateURL:(NSURL *)url
{
// The path depends on your implementation, can be "auth", "oauth/token", ...
return [url.path hasSuffix:kAuthenticatePath];
}
- (NSInteger)httpCodeFromError:(NSError *)error
{
// How you get the HTTP status code depends on your implementation.
return error.userInfo[kHTTPStatusCodeKey];
}
Please, be aware that this code does not work as is, as it relies on external code that depends on your web API, the kind of authorization (digest, oath, ...) and also which kind of framework you use over AFNetworking (RestKit for example).
This is quite efficient and has proved to work well with both digest and oauth authorization using RestKit tied to CoreData (in that case RetryRequestOperation is a subclass of RKManagedObjectRequestOperation).
My question now is: is this the best way to refresh a token?
I am actually wondering if NSURLAuthenticationChallenge could be used to solve this situation in a more elegant manner.
Your current solution works and you have the code for it, there might be a reasonable amount of code to achieve it but the approach has merits.
Using an NSURLAuthenticationChallenge based approach means subclassing at a different level and augmenting each created operation with setWillSendRequestForAuthenticationChallengeBlock:. In general this would be a better approach as a single operation would be used to perform the whole operation rather than having to copy it and update details, and the operation auth support would be doing the auth task instead of the operation completion handler. This should be less code to maintain, but that code will likely be understood by less people (or take longer to understand by most) so the maintenance side of things probably balances out over all.
If you're looking for elegance, then consider changing, but given that you already have a working solution there is little gain otherwise.
I was searching an answer for this problem and "Matt", the creator of AFNetworking, suggest this:
the best solution I've found for dealing with this is to use dependent
NSOperations to check for a valid, un-expired token before any
outgoing request is allowed to go through. At that point, it's up to
the developer to determine the best course of action for refreshing
the token, or acquiring a new one in the first place.

Resources