Simple RestKit login without using JSON - ios

It's been two days that I'm looking for a simple code for RestKit API for logging into a website without using JSON. Here is the code that I wrote so far:
- (void)login
{
[RKClient clientWithBaseURLString:#"http://Mywebsite.com/login.php"];
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObject:#"user" forKey:#"username"];
[params setObject:#"pass" forKey:#"password"];
[params setObject:#"login" forKey:#"type"];
[[RKClient sharedClient] post:#"/login" params:params delegate:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
NSLog(#"HTTP status code: %d", response.statusCode);
NSLog(#"HTTP status message: %#", [response localizedStatusCodeString]);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
NSRange range = [[error localizedDescription] rangeOfString:#"-1012"];
if (range.length > 0){
//Do whatever here to handle authentication failures
}
RKLogError(#"Hit error: %#", error);
}
Here is the log that I receive:
2012-09-08 21:44:14.633 RestKitTest[889:fb03] I restkit:RKLog.m:33 RestKit initialized...
2012-09-08 21:44:15.010 RestKitTest[889:fb03] I restkit.network.reachability:RKReachabilityObserver.m:369 Network availability has been determined for reachability observer <RKReachabilityObserver: 0x6c5a560 host=0.0.0.0 isReachabilityDetermined=YES isMonitoringLocalWiFi=NO reachabilityFlags=-R -----l->
2012-09-08 21:44:21.021 RestKitTest[889:fb03] I restkit.network:RKRequest.m:676 Status Code: 404
2012-09-08 21:44:21.036 RestKitTest[889:fb03] HTTP status code: 404
2012-09-08 21:44:21.090 RestKitTest[889:fb03] HTTP status message: not found
Any idea how I can fix this issue would be appreciated.

I'd say it 404s because you set baseURL as http://Mywebsite.com/login.php yet you POST as post:#"/login". Do you really want to access http://Mywebsite.com/login.php/login?
The baseURL should be set to a common prefix to all your backend calls, in your case http://Mywebsite.com, the resource itself should be posted as post:#"/login.php". The resource name and baseURL are concatenated before the request is sent.

You'll need to provide info on what login.php is doing. I don't see why it should 404 if the script url resolves correctly.
Also, if that PHP script does a redirect on success, you should use the RKClient method post:usingBlock: and inside that block do request.followRedirect = NO;. That way you'll get the correct status code, if you are returning one.
If may also help to log more of the response:
NSLog(#"Header fields: %#", response.allHeaderFields);
NSLog(#"Body: %#", response.bodyAsString);

Related

Post text and URLs with TwitterKit 2.7 iOS SDK

I just upgrade TwitterKit in my APP to 2.7 and could not post a tweet like before. Instead, I got an error after calling method sendTwitterRequest:completion:
"Error Domain=TwitterAPIErrorDomain Code=32 "Request failed: unauthorized (401)" UserInfo={NSLocalizedFailureReason=Twitter API error : Could not authenticate you. (code 32), TWTRNetworkingStatusCode=401, NSErrorFailingURLKey=https://api.twitter.com/1.1/statuses/update.json, NSLocalizedDescription=Request failed: unauthorized (401)}"
I did some research but got no luck. However, I accidentally found out that if I delete the ">>" in my "status" content string, everything is fine! Anyone has a clue why this happened? What if I need insert ">" in my tweet? Any clues would be appreciated!
NSDictionary *message = #{#"status": #"My tweet >> https://www.google.com"};
NSError *updateRequestError = nil;
NSURLRequest *updateRequest = [myAPIClient URLRequestWithMethod:#"POST" URL:#"https://api.twitter.com/1.1/statuses/update.json" parameters:message error:&updateRequestError];
if (updateRequestError) {
return;
}
[myAPIClient sendTwitterRequest:updateRequest completion:^(NSURLResponse* response, NSData* data, NSError* updateConnectionError){
if (!updateConnectionError) {
} else {
NSLog(#"error = %#", updateRequestError");
}
}];

Google OAuth Login Error: Invalid credentials

I have an iPad application which allows users to login to their Gmail account(s) using OAuth2. Thus far, the login process and email fetching is successful. However, when the app is closed and then re-opened after a (long) period of time, an error is produced "invalid credentials,' even though previous logins with the same credentials were successful.
Login Flow:
1) User logs in to gmail using OAuth 2.
2) User email address and oAuthToken provided by the GTMOAuth2Authentication object are saved to core data for future logins.
3) IMAP Session is created using saved email address and OAuthToken.
Here is the relevant code
Google Login
- (void)gmailOAuthLogin
{
NSDictionary *googleSettings = [[EmailServicesInfo emailServicesInfoDict] objectForKey:Gmail];
GTMOAuth2ViewControllerTouch *googleSignInController =
[[GTMOAuth2ViewControllerTouch alloc] initWithScope:GmailScope clientID:GmailAppClientID clientSecret:GmailClientSecret keychainItemName:KeychainItemName completionHandler:^(GTMOAuth2ViewControllerTouch *googleSignInController, GTMOAuth2Authentication *auth, NSError *error){
if (error != nil) {
//handle error
} else {
[[ModelManager sharedInstance] authenticateWithEmailAddress:[auth userEmail]
oAuthToken:[auth accessToken] imapHostname:[googleSettings objectForKey:IMAPHostName] imapPort:[[googleSettings objectForKey:IMAPPort]integerValue] smtpHostname:[googleSettings objectForKey:SMTPHostName] smtpPort:[[googleSettings objectForKey:SMTPPort]integerValue] type:EmailProtocolTypeImapAndSmtpGMail success:^(Account *account) {
//create IMAP session using above arguments
} failure:^(NSError *error) {
//handle error
}];
}
}];
[self presentGoogleSignInController:googleSignInController];
}
Create IMAP Session Using MailCore2
- (void)authenticateWithEmailAddress:(NSString *)emailAddress password:(NSString *)password oAuthToken:(NSString *)oAuthToken imapHostname:(NSString *)imapHostname imapPort:(NSInteger)imapPort smtpHostname:(NSString *)smtpHostname smtpPort:(NSInteger)smtpPort success:(void (^)())success failure:(void (^)(NSError *))failure
{
self.imapSession = [[MCOIMAPSession alloc] init];
self.imapSession.hostname = imapHostname;
self.imapSession.port = imapPort;
self.imapSession.username = emailAddress;
self.imapSession.connectionType = MCOConnectionTypeTLS;
self.imapSession.password = nil;
self.imapSession.OAuth2Token = oAuthToken;
self.imapSession.authType = nil != oAuthToken ? MCOAuthTypeXOAuth2 :
self.imapSession.authType;
[self.imapSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type,
NSData * data){
NSLog(#"MCOIMAPSession: [%i] %#", type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
self.smtpSession = [[MCOSMTPSession alloc] init];
self.smtpSession.hostname = smtpHostname;
self.smtpSession.port = smtpPort;
self.smtpSession.username = emailAddress;
self.smtpSession.connectionType = MCOConnectionTypeTLS;
self.smtpSession.password = nil;
self.smtpSession.OAuth2Token = oAuthToken;
self.smtpSession.authType = nil != oAuthToken ? MCOAuthTypeXOAuth2 :
self.smtpSession.authType;
[self.smtpSession setConnectionLogger:^(void * connectionID, MCOConnectionLogType type, NSData * data){
NSLog(#"MCOSMTPSession: [%i] %#", type, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[[self.imapSession checkAccountOperation] start:^(NSError *error) {
if (nil == error) {
success();
} else {
failure(error); //FAILS WITH INVALID CREDENTIALS ERROR
}
}];
}
Once again, the above code works fine, unless the application has not been used in some time. I was not sure if I needed to refresh the OAuthToken or not, so I tried doing the following on launch of the application:
GTMOAuth2Authentication *auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:KeychainItemName clientID:GmailAppClientID clientSecret:GmailClientSecret];
BOOL canAuthorize = [auth canAuthorize]; //returns YES
NSDictionary *googleSettings = [[EmailServicesInfo emailServicesInfoDict] objectForKey:Gmail];
[[ModelManager sharedDefaultInstance] authenticateWithEmailAddress:[auth userEmail] oAuthToken:[auth refreshToken] imapHostname:[googleSettings objectForKey:IMAPHostName] imapPort:[[googleSettings objectForKey:IMAPPort]integerValue] smtpHostname:[googleSettings objectForKey:SMTPHostName] smtpPort:[[googleSettings objectForKey:SMTPPort]integerValue] type:EmailProtocolTypeImapAndSmtpGMail success:^(Account *account) {
//create IMAP session
} failure:^(NSError *error) {
NSLog(#"failure %#", error);
}];
But I still get the same error. I have no idea why the OAuth token stops working or how to resolve this. Since the user is able to save multiple accounts, I am wondering if I need to save the refresh token for each account in core data and use that if the access token stops working?
(Disclaimer - I don't know iOS or the gtm-oauth2 libraries, but I do know Google's OAuth implementation.)
Conceptually you do need to persist the refresh token for the user. The refresh token is a long-lived credential which is used (along with your client secret) to get a short-lived access token that is used for actual API calls.
If you anticipate making multiple calls in a short period of time then your app will normally actually persist both the refresh token and access token (currently access tokens will last 1 hour).
That all said, it looks like the gtm-oauth2 library should be taking care of persisting these already (looks like authForGoogleFromKeychainForName does this).
What I think you need help with is getting an up-to-date access token that you can use to initiate your IMAP session.
The gtm-oauth2 library does contain an authorizeRequest method. It takes information about an HTTP request you intend to make and adds the appropriate authorization headers. It looks like this code will examine the state of the access token, and refresh it if necessary.
While I know you aren't able to make an HTTP request (you need to speak IMAP), my suggestion is to use this method with a dummy NSMutableURLRequest - and then, once it's finished, don't actually send the HTTP request, instead examine the headers it added and pull the access token from there.
See:
https://code.google.com/p/gtm-oauth2/wiki/Introduction#Using_the_Authentication_Tokens
Hope that helps - I don't have an iOS environment to test it on.

stackmob ios datastore http 401 error

I'm using the iOS DataStore API to upload data to StackMob. I get this error when I try to use my smclient initialized with my public key.
HTTP Code=401 "The operation couldn’t be completed. (HTTP error 401.)" UserInfo=0xa14dac0 {error=Insufficient authorization}
Sample code
[[self.smclient dataStore] createObject:eventDictObj
inSchema:#"EventSchema"
onSuccess:^(NSDictionary *object, NSString *schema)
{
NSLog(#"Created online event : %#", object);
successBlock();
}
onFailure:^(NSError *error, NSDictionary* object, NSString *schema)
{
failedBlock(error);
}];
And smclient is initialized as follows
self.smclient = [[SMClient alloc] initWithAPIVersion:#"0" publicKey:#"xxxxxxxxxx"];
For this use case I don't need to use the logged in user credentials to create this entry in StackMob
Make sure that the permissions are set to Open on your stack mob database.

AFHTTPClient putpath method

I have problem with my below funct:
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
sportid, #"sport",
country, SC_PAIS,
team, SC_TEAM,
token, SC_TOKEN,nil];
[[SCHTTPClientServer sharedClient] setParameterEncoding:AFJSONParameterEncoding];
[[SCHTTPClientServer sharedClient] putPath:#"calendarelemfilters/teams" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"RESPonsee %#",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"ERROR %#",[error localizedDescription]);
}];
this is my code. I have below error- How can i fix it?
Thanks
"""RESPONSE STRING: <>Apache Tomcat/7.0.26 - Error report HTTP Status 400 - Required String parameter 'token' is not presenttype Status reportmessage Required String parameter 'token' is not presentdescription The request sent by the client was syntactically incorrect (Required String parameter 'token' is not present).Apache Tomcat/7.0.26
2013-09-19 13:10:24.254 ISportsCal[834:907] ERROR Expected status code in (200-299), got 400"""
It's all there in the error message. The server is expecting a parameter called token and you aren't passing one in the expected format.
What is SC_TOKEN? Possibly a capitalization error?
Are you sure the server is expecting JSON?
Is the server expecting any additional headers?
You may want to download https://github.com/AFNetworking/AFHTTPRequestOperationLogger to take a look at your requests. You can download a tool like Postman REST Client, get the put request working there, and then make sure your AFNetworking request matches this.
I have fixed it with postPath. and also i setted "setparameter encoding" as below.
[[SCHTTPClientServer sharedClient] setParameterEncoding:AFFormURLParameterEncoding];
Thanks

iOS HTTP request - getting the error response message

This is my bit of code doing a GET request to a REST api.
Im not sure how to get back the message if I get an error:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSURL *URL = [NSURL URLWithString:urlString];
[request setURL:URL];
[request setHTTPMethod:#"GET"];
NSError *err = nil;
NSHTTPURLResponse *res = nil;
NSData *retData = [NSURLConnection sendSynchronousRequest:request returningResponse:&res error:&err];
if (err) // This part is never called.
{
NSLog(#"Error: %#", err);
}
else
{
if (res.statusCode != 200)
{
// show the user the status message
NSLog(#"Error: %#", res); // This part is called
}
else
{
}
}
I want to get the error message if it was not successful. But the if (err) block is never called. err is still null, although the statuscode is 400.
And if successful I will get back a json response.
In the code above I get back a statusCode of 400
The error block is not called because the error object is created only if a system level error occurs. This does not happen because the request is sent correctly and the server sends a response. If you are in control of the server, you should probably make it return status code 200 and include an app level status code in the response, that would tell your app that the entered credentials are incorrect.
Edit:
To get status message you can use
+ (NSString *)localizedStringForStatusCode:(NSInteger)statusCode
This is a class method of the NSHTTPURLResponse class.
if (res.statusCode != 200)
{
// show the user the status message
NSLog(#"Error: %#", [NSHTTPURLResponse localizedStringForStatusCode: res.statusCode]); // This part is called
}
Take a look at the NSError class reference:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/Reference/Reference.html
You can try to log the error message from the localizedDescription.
you are receiving this status code because- The Web server (running the Web site) thinks that the data stream sent by the client (e.g. your Web browser or our CheckUpDown robot) was 'malformed' i.e. did not respect the HTTP protocol completely. So the Web server was unable to understand the request and process it
to log above problem in respect to ios visit this link
If you read the documentation of sendSynchronousRequest...
error
Out parameter used if an error occurs while processing the request. May be NULL.
this mean that erro will be a valid NSError object in case there is a problem to resolve the request, like a malformed URL.
If the request can be resolved error will be NULL and according with HTTP protocol and depending to the server that you are trying to connect, the NSHTTPURLResponse object will contain all the information about the request.
In general is an error think that every status code different than 200 is an error, for example for a REST based API 204 mean empty data, and in this case the request is finished successfully but the requested resource is just empty data, and this is not an error.
So about your question, is absolutely fine that error is NULL most of the time, if is not mean that there is an issue before reach the target server, in general you have to consider both, error and according to the server that you are trying to talk the status code maps, in most of cases the REST pattern

Resources