Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

http authentication in devise and rails 3

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?

like image 568
ed1t Avatar asked Mar 27 '11 14:03

ed1t


2 Answers

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.

like image 177
Matteo Melani Avatar answered Nov 15 '22 10:11

Matteo Melani


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.

like image 41
Matt Long Avatar answered Nov 15 '22 10:11

Matt Long