6

I have built a simple test API for a CakePHP application that will let a user login from a mobile device (or any device for that matter) and get a JSON response. This API could be used for a mobile app built in PhoneGap.

The login method looks like so:

public function login()
{
    if($this->request->is('post'))
    {
        // Use custom method in Model to find record with password params
        $findUser = $this->User->findUser(
            $_POST['username_or_email'],
            AuthComponent::password($_POST['password'])
        );

        // If a user exists and matches params
        if($findUser)
        {                           
            $this->User->id = $findUser['User']['id'];

            $this->autoRender = false;
            $this->response->type('json');
            $this->response->body(json_encode(array('authenticated'=>true,'message'=>__('You have been logged in successfully'))));
        }
        else
        {
            $this->autoRender = false;
            $this->response->type('json');
            $this->response->body(json_encode(array('authenticated'=>false,'message'=>__('Username or password is incorrect'))));
        }

    }
    else
    {
        $this->autoRender = false;
        $this->response->type('json');
        $this->response->body(json_encode(array('message'=>'GET request not allowed!')));
    }
}

The mobile device (or any API user) can send their login details and then they get the request as JSON as true or false for authenticated. This boolean is NOT used to give the user access, it instead tells the mobile app if they can see certain screens and they ONLY get the data or can send data if the session exists!

As just stated, they are also actually logged into the API itself on the device so if they visit the website directly (from that device) they will have a session and see the same response for the JSON.

So essentially a user remains logged in for the duration of the session on the device they communicated with the server on. This is different to a token which would need to be passed for every request, where as in this example they have a session.

Now the questions...

  1. Is it bad practice for the user to be 'actually' logged into the API with a session like shown above? It seems like the most secure way to handle authentication for a device as it's using the same logic as the direct web root.

  2. I've seen some APIs use access tokens instead which I've also implemented (user gets their token returned instead of the boolean and no session is created). But from what I can tell, this seems like more work as then I need to check for the access token against a user record every time a request is made.

Cameron
  • 27,963
  • 100
  • 281
  • 483
  • This is a good REST API plugin for CakePHP. Maybe you can get some pointers from there https://github.com/kvz/cakephp-rest-plugin (it uses tokens in combination with the Auth Component) – Nick Zinger Feb 21 '13 at 13:10
  • Yeah I've seen that before but I was looking to develop something myself. BUT they also use `$this->Auth->login()` which actually logs a user into the system and creates a session. – Cameron Feb 21 '13 at 16:12
  • True. I'm using that, but overwriting some of it's features in the beforeFilter to make it Authorize in different ways with different tokens, while still keeping some of the automagick that the plugin and CakePHP give me. – Nick Zinger Feb 21 '13 at 18:32
  • Hi, I'm currently tryng to do the exact same thing but am struggling with ACL/Auth - did you manage to get this working? Would love to hear how! – Jimothey Jul 09 '13 at 16:14

4 Answers4

25

edit
For the sake of clarity, I am not a supporter of REST, I AM a supporter of RESTful/RESTlike services. If you look at all of the API's on the internet, very few actually stick to one standard. Whatever scheme you choose will depend on your specific problem-space. Just try to be secure and use intuitive design choices (ie dont name a service "cats" if it returns info about "dogs")
end edit

It is good practice in RESTful API's to manage some form of session/tokenizing scheme. Really the ideal (at least in my opinion, there are many schools of thought on this problem) setup involves rolling tokens.

If you are at all concerned with the security of your API, then permissions should be managed out of your database layer. Yes, this creates a bottleneck, BUT THAT IS ACTUALLY A GOOD THING. Needing to hit the database every single time to validate a client's token adds an extra step in the entire process. This slows down the API, which is actually desireable in a secure system. You don't want a malicious individual to be able to hit your API 3000 times a second, you want their requests to hang for a (somewhat) sizeable fraction of a second.

This is similar to MD5 hashing algorithms. Many of them recalculate the hash a few hundred times, with random pauses in between. This helps to keep a malicious client from attempting to brute force a password (by making it take more time to test each variation of the password string). The same applies to your API.

The other benefit, is that if you DO have a malicious user trying to log in over and over again, if you are managing them from the database layer, then you can red flag their IP Address/username/what-have-you and just drop their requests at step 1.

Anyway, for a suggested process (with rolling tokens, you can cut out parts of this if it seems overkill, but this is hella secure):

  1. User hits a 'login' service, this requires a username/password, and returns two tokens, a Private Access Token and a Public Request Token (the server stores these tokens in the db).
  2. The client stores these Tokens in a secure place
  3. User accesses another endpoint to push/pull some data
    • Request includes a timestamp
    • Request includes the Public Request Token
    • Request includes an Access Token=> This token should be a MD5 hash of the string resulting from concatenating the timestamp string to the end of the Private Access Token string
  4. The server takes the Public Request Token, uses that to lookup the Private Access Token that was stored
    • The server takes that Private Access Token, and concatenates on the Timestamp String, it then takes the MD5 of this string
    • If the new Access Token matches the one that the client sent the server, HURRAY, this client is validated, so push/pull the data
  5. (Optional) The server generates new tokens on every request, and returns them to the client. This way every transaction invalidates the old tokens, and if there was some kind of man-in-the-middle attack occurring, if the VALID user has already completed their request, the malicious user now has invalid tokens and can't start messing with your API. This scheme tries to ensure that a malicious user can not expect to intercept a single communication between the server and the client, and still gain access to the system. If they do, then the REAL user should immediately get invalidated tokens. Which should then trigger their API client to hit the 'login' service AGAIN, getting new valid tokens. This once again kicks the malicious user out of the system.

This scheme is not 100% secure, no user access system ever will be. It can be made more secure by adding expiration dates on tokens. This scheme also has the added benefit that you can assign specific permissions to users/tokens (ie Read-Only access, only certain End-Points can be seen, etc)

This is not the only way you can do things, I would look up other Authentication Schemes and take what you want from each of them (OAUTH is a good place to start, then I'd look at Facebook/Twitter/Instagram)

G. Shearer
  • 2,175
  • 17
  • 19
  • 1
    The process you are explained here is called HMAC. For further reading http://en.wikipedia.org/wiki/Hash-based_message_authentication_code – arulraj.net Apr 16 '15 at 17:21
3

Make your app login everytime, but not with login-pass pair as Swayok lastly suggested. When you login, server generates a token and returns it back to the client. Client then uses this token whenever it makes a request. On each request, server checks whether the token is valid and if so, executes the request.

This is very similar to how sessions work in that, server side frameworks manage it internally and these tokens expire from time to time. However, as Swayok rightuflly pointed out, you don't want session mainly because you're RESTful API should have no state. You get the same utility without storing any user specific data regarding user and logging user in with every request.

Here's a good article on this, or you can try the Facebook Graph API explorer to see it in action

Ege Akpinar
  • 3,266
  • 1
  • 23
  • 29
  • 1
    Note - when using tokens you will need to add them to each request via GET or POST and that adds some additional programming to each request. By using Basic Auth header - all you need is to make a method to add a header to any request and use that method for communication with server (that works even if user have not logged in yet). Actually you can pass a token as user login via Basic Auth header =) And sometimes you just don't need a tokens that can expire =) What way to choose - is up to you and your app's requirements =) – Swayok Feb 26 '13 at 12:26
2

Restful API restricts using sessions and saving system state at all. Each request must log-in user. Access tokes are great but also require additional handling.
The easiest way is to send authorisation data via HTTP Basic Auth ("Authorization" HTTP header)
http://www.httpwatch.com/httpgallery/authentication/
Mobile Applications can easily do that and it is easy to add this header for each request to API.
On server side:

$username = env('PHP_AUTH_USER');  
$password = env('PHP_AUTH_PW'); 

And process user log-in with this data in ApiAppController->beforeFilter()

Swayok
  • 436
  • 3
  • 12
  • Not sure what you mean by 'restricts' as I can do all the requests and send data fine once the session exists for the mobile device. So if a user has logged in, they will now have a session and be able to access the methods across the API like expected without having to authenticate on each request. So not seeing the advantage of using Basic Auth and Access Tokens unless I am mistaken? – Cameron Feb 20 '13 at 17:47
  • Ofcourse there are no PHP restrictions in using sessions in API or elsewhere. But according to widely-spread API recommendations called "Restful API" - API must be stateless which means that it does not store anything between any 2 requests. Here is the question about it - http://stackoverflow.com/questions/8711044/is-it-good-to-implement-rest-api-using-sessions – Swayok Feb 20 '13 at 20:58
  • Here is about REST - http://en.wikipedia.org/wiki/Representational_state_transfer. I'd recommend you to stick to that recommendations – Swayok Feb 20 '13 at 21:01
  • So what about mobile applications like Facebook for example where you are actually logging in and out and therefore you WOULD have a state. As stated above I am building a mobile app from this API for PhoneGap so it would behave in a similar fashion. – Cameron Feb 20 '13 at 21:48
  • You can store everything on mobile phone, but not on server. Facebook, Instagram, Twitter - they all use REST API. OAUTH tokens are similar to login and password pair but more complicated and have more possibilities that you won't use until you developing own social network. Note that when you call facebook API you must attach access_token for each request and if do not do that - Facebook will forbid you using API. They do not use sessions in API. – Swayok Feb 20 '13 at 22:49
  • In your case you have: mobile app and API on server. Interaction between them is like: Step 1: app::login -> server::login::response -> app::save_auth_info_on_success (auth data never saved on server). Step 2: app::get_some_data_or_smth_else::attached_auth_data_from_step1 -> server::login::on_success_response -> app::do_what_needed. This workflow will let you avoid session expire for the whole time app is used without any risk to loose any data sent to server. For free-for-all data you can avoid login action – Swayok Feb 20 '13 at 22:56
  • If they are using OAuth though, that would mean that when you see the login page on the mobile app you are actually seeing another website right? As OAuth works by making you sign in on the actual provider website and then you get sent back but with a token. BUT how would you get redirected back if you're on a mobile app? As there is no url to be redirected to if you're an actual app and not a website. – Cameron Feb 23 '13 at 10:26
  • You're right - on login you're redirected to facebook. But redirect initiated by your server because you need to provide app's credentials for facebook. After process is done - facebook sends token to 'callback_url'. This url must reside on your server and when it receives token - it creates a json response that contains token for your app. On app side you save that token and attach it whenever it is needed. As you can see - server works like proxy between app and social network or database – Swayok Feb 25 '13 at 10:36
  • Okay following all that. BUT from what I have seen on Foursquare and Facebook apps the login form is actually in the app itself and doesn't seem to be loading an external webpage. – Cameron Feb 25 '13 at 14:04
  • I think there should be a way to avoid server interaction in case of Facebook or other social networks. But in that case you won't be able to link user of your app with a facebook user. The exact solution depends on your requrements. If you need own users - you will need a server interaction, but if you are making a client for a social network - you can work without a server – Swayok Feb 25 '13 at 15:03
  • As I understood - you need to login users into your app (without any social networks for now). In this case you build login form in app and send login requests to your server using Basic Auth which I recommended earlier. Server responds with user information. On app's side you remember user info and his login-pass pair. Next requests to your server should be signed with login-pass pair. Quite easy interaction actually. You just need to stick to the rules - "api should not save current state" and "each request must be signed by user credentials" – Swayok Feb 25 '13 at 15:12
0

To answer your questions

  1. Its not a bad practice as long as you close their session on app close and recreate it when needed. it is same as if they were logged in on a browser they would know and have facility to log out however the same should be available on the app as well otherwise they might have closed the app but not actually ended their session. You can handle this in many ways by asking them to log out automatic checking when they close app
  2. Tokens are an enhanced way of doing the above however you have to consider how secure the token is when transmitted and server need to verify the token on each request. You have said that it seems like more work so yes its more work and if you have time or money constrains and looking for an answer to say if the session style would harm your application in future it wont as long as you are in control of session and not leaving user without ending the session. If you have time then implement tokens and you would like that.
Farrukh Subhani
  • 2,018
  • 1
  • 17
  • 25