http authentication in devise and rails 3 - iphone

I have an application which uses devise on rails 3. I would like to enable http authentication so that I can authenticate to my web app from an iPhone app. How can I authenticate from my iPhone app to devise?
Is this secure or should I be authenticating differently?

From the design point of view you have got 3 options:
1) Use basic http authentication: your IPhone app has a secret key -which is baked in your IPhone app code - that uses to authenticate each request with the web app.
Google search: "Devise basic http authentication"
2) You can use https by having a public certificates in your IPhone app and a private certificates on your web app. This is a lot of work to configure right, it is very secure since your IPhone app and the Rails server are exchanging messages over an encrypted channel. The security is also transparent to your rails code since authentication is done at the transport level.
3) The IPhone app connects to the web app using https, get an authentication token that it then uses to make calls to the web app over regular http. More secure than 1 since the key can expire, quite a bit of work to implement and very scalable.
(http://matteomelani.wordpress.com/2011/10/17/authentication-for-mobile-devices/)
Most of apps use solution 1.
Hope this help.
EDIT: to implement http authentication (either basic or digest) I suggest you look at:
http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Basic.html
and
https://github.com/plataformatec/devise/wiki/How-To:-Use-HTTP-Basic-Authentication
The precise steps will depends on your Rails server stack.
EDIT 2: I do not think Devise provide a way to get the auth_token. I can see you can try several solutions:
when the user logs in the server retrieves the authentication_token and puts it in the cookie. Not very secure unless you encrypt it with a shared secret key.
you can provide a https web service that your IPhone app uses to get a user token. Your IPhone app would make the request right after receiving the user request to sign in.
Sorry I cannot be of more help with some real code.

This largely depends on how you are implementing things on the server side, but we implemented this using Matteo's 3rd option. I have a rails 3.1 implementation using devise. The route to the login is /users/login.json . First build up the JSON body for login with code like this:
NSMutableDictionary *loginDictionary = [NSMutableDictionary dictionary];
NSMutableDictionary *usernamePasswordDictionary = [NSMutableDictionary dictionary];
[usernamePasswordDictionary setObject:username forKey:#"email"];
[usernamePasswordDictionary setObject:password forKey:#"password"];
[loginDictionary setObject:usernamePasswordDictionary forKey:#"user"];
NSData *data = [NSJSONSerialization dataWithJSONObject:loginDictionary options:0 error:&error];
which yields this JSON:
{"user":{"password":"blahblahblah","email":"admin#*****.com"}}
I send a POST url request with code like this:
NSString *postUrlString = [NSString stringWithFormat:#"%#users/login.json", kServerAPIBaseURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:postUrlString] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kTimeoutInterval];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody:data];
The response I get back contains JSON. We configured the server side to return the a session_auth_token:
{
admin = 1;
"created_at" = "2012-01-25T00:15:58Z";
"current_sign_in_at" = "2012-04-04T04:29:15Z";
"current_sign_in_ip" = "75.163.148.101";
email = "admin#******.com";
"encrypted_password" = "*****";
"failed_attempts" = 0;
id = 1;
"last_sign_in_at" = "2012-04-03T03:37:18Z";
"last_sign_in_ip" = "75.163.148.101";
"locked_at" = "<null>";
name = "Joe Smith";
"remember_created_at" = "2012-03-29T20:35:43Z";
"reset_password_sent_at" = "<null>";
"reset_password_token" = "<null>";
"session_auth_token" = "3FRgX6CYlzQJGC8tRWwqEjFaMMFKarQAYKTy3u84M0U=";
"sign_in_count" = 145;
status = 1;
"unlock_token" = "<null>";
"updated_at" = "2012-04-04T04:29:15Z";
}
We store that session_auth_token and then send it back with every request in a header, something like this:
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self postUrlString]]...
[postRequest setHTTPMethod:#"POST"];
[postRequest setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[postRequest setValue:[self sessionAuth] forHTTPHeaderField:#"X-CSRF-Token"];
[postRequest setHTTPBody:data];
That parameter [self sessionAuth] contains the session_auth_token.
Let me know if you need clarification.

Related

Changes coming in Version 1.1 of the Twitter API in iPhone

I am using twitter API version V1.0 for my new iphone application .I am successfully able to do this using twitters v1.0 of the API and all works perfectly. Simply making a request to http://api.twitter.com/1/statuses/user_timeline.json?screen_name=userid retrieves all the information that I require.
since v1.0 has been deprecated and V1.1 requires authentication for each request, I get a bad authorization error (HTTP response status: 400)using this API.
What are the Changes i need to do in my appication .how to generate OAuth request headers,Do i need register my application ?How can i get authentication for new version?
I hope the above makes sense and any help would be appreciated.
Thanks in advance.
Visit https://dev.twitter.com/docs/ios/making-api-requests-slrequest
https://dev.twitter.com/docs/oauth/xauth
u can get the authentication using following method:
NSString *base64 = #"Basic NFRLWGlZY3l1aHJ4OVJaUWI5RW5BOmdVMXlvcVV6YzBNZUhUQmFXdVRZU2NtZHlIWDFOZXhwZmxqRE16bm01aw==";
//oauth_consumer_secret,oauth_consumer_key
NSURL *urlAPI = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.twitter.com/oauth2/token"]];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:urlAPI];
[request setDelegate:self];
[request setRequestMethod:#"POST"];
[request addRequestHeader:#"Authorization" value:base64];
[request addRequestHeader:#"Content-Type" value:#"application/x-www-form-urlencoded;charset=UTF-8"];
[request setPostValue:#"client_credentials" forKey:#"grant_type"];
In response u will get the authtoken for your application like this
{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAMddRQAAAAAAXRP6Axur1RS%2Fv9YFtJ7TijyPAGo%3DpmNowwOGf3dZTHDiH2gnCcv7qNIyGZcyV2IW5YFTBs"}

Clearing credentials from ASIHTTPRequest

I'm working on an iPhone app that uses ASIHTTPRequest to interact with a web service using NTLM authentication. And the credentials should be stored in the keychain. It logs in fine, but I'd like to have a logout button that clears the credentials from the app, and I can't get that to work.
After I click the logout button, I expect that when I return to the view that queries the server that I'll get prompted to log back in again. However, that doesn't happen and the call to the server still authenticates.
The code that makes the request looks like this:
NSString *urlString = [NSString stringWithFormat:#"http://myserver.mydomain.com/myapp/items/%#", itemGroupId];
NSURL *url = [NSURL URLWithString:urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseKeychainPersistence:YES];
[request setShouldPresentAuthenticationDialog:YES];
[request setRequestMethod:#"POST"];
[request addRequestHeader:#"content-type" value:#"application/json;charset=utf-8"];
[request addRequestHeader:#"content-length" value:#"0"];
[request setDelegate:self];
[request startAsynchronous];
For the logout, I've tried calling:
[ASIHTTPRequest removeCredentialsForHost:#"myserver.mydomain.com" port:0 protocol:#"http" realm:nil];
But that doesn't work. The code inside that method doesn't find the NSURLCredential that was saved so that it can remove it, even though those arguments are what I've seen get passed to saveCredentials:forHost:port:protocol:realm: in the first place.
I've also tried calling clearSession, and I've tried disabling session persistence altogether when creating the request using setUseSessionPersistence, but no luck.
I also tried using code based on this example that loops through all of the credentials in the app's keychain and removes them all:
NSURLCredentialStorage *store = [NSURLCredentialStorage sharedCredentialStorage];
for (NSURLProtectionSpace *space in [store allCredentials]) {
NSDictionary *userCredentialMap = [store credentialsForProtectionSpace:space];
for (NSString *user in userCredentialMap) {
NSURLCredential *credential = [userCredentialMap objectForKey:user];
[store removeCredential:credential forProtectionSpace:space];
}
}
That sort of works, because the next time the app is launched it'll prompt for a login again. But it doesn't prompt for another login if the app continues to run.
Are you sure using port 0 and no realm is correct?
Just to make sure I create a NSURL from my connect-url-string (the same url I use for all ASIHTTPRequest), and retrieve the host, port and protocol from that. I only have to mention the realm by hand for now.
Using this code I am able to logout successfully in my app:
// clear keychain
NSURL *url = [NSURL URLWithString:kConnectorUrlString];
[ASIHTTPRequest removeCredentialsForHost:[url host] port:[[url port] intValue] protocol:[url scheme] realm:kConnectorRealm];
My app prompts me again when I let my app continue to run.
I am using the same arguments who get passed to saveCredentials too.

Difficulties signing in to Twitter from iPhone

I'm using MGTwitterEngine to add Twitter functionality to my app. It's very easy to simply prompt the user for a username and password and then start posting. Strangely, however, I've noticed other apps (eg Foursquare and Brightkite) require you to visit their website to associate your Twitter account with your foursquare/brightkite/whatever account.
Why do they do it this way?
Is there a reason why my app shouldn't prompt the user for a Twitter username and password, even though it would be so easy?
Thanks a bunch!
Tristan
This is because you're using Basic Auth, which is just a username/password. Most new Twitter apps use the more robust OAuth, which requires you to visit Twitter.com to allow access, but does not require a username/password. The Twitter API docs claim that support for Basic Auth will be dropped soon, so you should be using OAuth as well.
Twitter supports OAuth, which allows you to access their account without asking for their password directly.
The main concern is security. What happens if/when your database gets hacked? The attackers will have access to all of your user's twitter passwords.
On hte other hand, those sites don't store their users passwords; if they get hacked, twitter can easily disable the oauth credentials, locking out any attackers before they can do harm.
Use OAuth; it's a lot safer.
Just replace YOUR_TWITTER_USERNAME and YOUR_TWITTER_PASSWORD. The code below has to be included in your viewController.m
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL: [NSURL
URLWithString: #”http: //YOUR_TWITTER_USERNAME: YOUR_TWITTER_PASSWORD#twitter. com/
statuses/update. xml”]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval: 60. 0] ;
[theRequest setHTTPMethod: #”POST”] ;
[theRequest setHTTPBody: [[NSString stringWithFormat: #”status=%#”,
themessage] dataUsingEncoding: NSASCIIStringEncoding] ] ;
NSURLResponse* response;
NSError* error;
NSData* result = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse: &response error: &error] ;
NSLog( #”%#”, [[[ NSString alloc] initWithData: result
encoding: NSASCIIStringEncoding] autorelease] ) ;

iPhone - HTTPS connection to Server with Self-Signed Certificate [duplicate]

I have a cocoa class set up that I want to use to connect to a RESTful web service I'm building. I have decided to use HTTP Basic Authentication on my PHP backend like so…
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
//Stuff that users will see if they click 'Cancel'
exit;
}
else {
//Validation Code
echo "You entered info.";
}
?>
At this point I'm using a synchronous NSURLConnection, which I understand the Apple documentation states has less support for Authentication.
But is it even possible at all? I can do cookie authentication very easily sans NSURLProtectionSpaces or NSURLCredentials or any of the authentication classes. Also, are there any resources where I can read more about the Cocoa Authentication classes?
Thanks.
UPDATE: mikeabdullahuk
The code you supplied (the second example) is almost identical to what I had written. I have done some more investigating, and discovered that the NSURLConnection is returning an error…
Error Domain=NSURLErrorDomain Code=-1012 UserInfo=0x1a5170 "Operation could not be completed. (NSURLErrorDomain error -1012.)"
The code corresponds to NSURLErrorUserCancelledAuthentication. So apparently my code is not accessing the NSURLCredentialStorage and instead is canceling the authentication. Could this have anything to do with the PHP HTTP Authentication functions? I'm quite confused at this point.
A synchronous NSURLConnection will absolutely work with NSURLCredentialStorage. Here's how things usually work:
NSURLConnection requests the page from the server
The server replies with a 401 response
NSURLConnection looks to see what credentials it can glean from the URL
If the URL did not provide full credentials (username and password), NSURLConnection will also consult NSURLCredentialStorage to fill in the gaps
If full credentials have still not been determined, NSURLConnection will send the -connection:didReceiveAuthenticationChallenge: delegate method asking for credentials
If the NSURLConnection now finally has full credentials, it retries the original request including authorization data.
By using the synchronous connection method, you only lose out on step 5, the ability to provide custom authentication. So, you can either pre-provide authentication credentials in the URL, or place them in NSURLCredentialStorage before sending the request. e.g.
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://user:pass#example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
or:
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"user"
password:#"pass"
persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:#"example.com"
port:0
protocol:#"http"
realm:nil
authenticationMethod:nil];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
forProtectionSpace:protectionSpace];
[protectionSpace release];
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
In a situation where a 401 or other authentication challenge is unacceptable/impossible, I sometimes use a dummy CFHTTPMessage to generate the authetication line, then copy that back into the NSURLRequest:
// assume NSString *username and *password exist and NSURLRequest *urlRequest
// exists and is fully configured except for HTTP Basic Authentication..
CFHTTPMessageRef dummyRequest =
CFHTTPMessageCreateRequest(
kCFAllocatorDefault,
CFSTR("GET"),
(CFURLRef)[urlRequest URL],
kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
dummyRequest,
nil,
(CFStringRef)username,
(CFStringRef)password,
kCFHTTPAuthenticationSchemeBasic,
FALSE);
authorizationString =
(NSString *)CFHTTPMessageCopyHeaderFieldValue(
dummyRequest,
CFSTR("Authorization"));
CFRelease(dummyRequest);
[urlRequest setValue:authorizationString forHTTPHeaderField:#"Authorization"];
This may seem completely a bizarre way to do it but it is tolerant of situations where the username/password aren't URL clean and where NSURLRequest refuses to consult the NSURLCredentialStorage because the server isn't actually sending a HTTP 401 (for example it sends a regular page instead).
I would note mikeabdullahuk's answer is good but also if you use NSURLCredentialPersistencePermanent instead of per session it will store the credentials in the users keychain so next time you can check NSURLCredentialStorage for a non nil value for the default credentials for a protection space and if you get a non nil value you can just pass the credentials in. I am using this method right now for a delicious.com client I am writing and it works very well in my tests.
Set your credential as the default credential for the protectionspace:
// Permananent, session, whatever.
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence: NSURLCredentialPersistencePermanent];
// Make sure that if the server you are accessing presents a realm, you set it here.
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:#"blah.com" port:0 protocol:#"http" realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
// Store it
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
At this point, any subsequent NSURLConnection that is challenged using a protection space that matches what you set will use this credential

Unable to POST data from IPhone using google account authentication

I'm working on an IPhone application that works with a Google App Engine application. I manage to get logged by using a google account and I get the authentication token. I'm also able to GET data from the GAE service (I did it after reading another question written here) but now I need to POST data so I need to send the authentication token in the header of the POST request. I tried several options but none of them worked.
Here is the code I use to put that auth into the header:
NSString* urlStr = [NSString stringWithFormat:#"%#%#", HOST, url];
NSMutableURLRequest* urlPost = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
NSString* authStr = [NSString stringWithFormat:#"GoogleLogin auth=%#", token];
[urlPost addValue:authStr forHTTPHeaderField:#"Authorization"];
but it doesn't work.
Any help?
You need to use [request setHTTPMethod: #"POST"] and [request setHTTPBody: postdata] to properly configure the POST components. See the NSMutableURLRequest docs for more details.
Whenever I'm troubleshooting a problem related to HTTP, the first tool I'll grab is Charles HTTP Proxy. It will show you the entire request and response for closer examination.
If you're authenticating against an App Engine app, you need to obtain and send an authentication cookie, rather than using the GoogleLogin authentication. The source of appengine_rpc.py in the Python SDK demonstrates how.