6

I'm building a REST API app in Symfony2 and I'm trying to find a good way to handle user authentication and authorization. There are many solutions out there, but so far none of them addressed all my requirements/concerns.

The API will primarily be used by an AngularJS SPA where users log in with a username and password combination. It's important to make the GUI users able to:

  • log in with username and password
  • use "remember me" checkbox to be logged in "forever" (probably a week or something like that)
  • use "remember me" across different browsers at the same time (like facebook)
  • delete cookies from a browser to log the user out

On the server side I want to remain strictly RESTful. I came up with a solution which seems to be what I need, but I want to be sure I'm not missing anything.

The workflow of a successful login attempt from the server's point of view:

  1. Receive POST on /login API endpoint with username and password
  2. Generate a new token and return it to client. Save a hash of that token in db.
    • each token db entry must have a predefined lifetime (set in config)
      • expiration datetime must be stored in db
      • old token hashes must be deleted from db when they expire (on login attempt or by cron)
    • tokens are many to one with user - a user can have multiple active tokens at any given time

The workflow of authorization of a logged in user from the server's point of view:

  1. Extract token from Authorization header
    • the user id might be included in the request too, to be decided
  2. Hash token and compare it to each of the user's hashes in the db
  3. If the one of the db hashes matches the request hash and is not expired, update hash expiry and authorize user

The workflow of a successful login attempt from the client's (GUI) point of view:

  1. Send POST request to /login API endpoint with username and password
  2. Receive token generated by server. Store it in a cookie in the browser (using js, the server doesn't set the cookie)

The workflow of authorization of a logged in user from the client's (GUI) point of view:

  1. Read token from cookie and set Authorization header in the request, for example Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
    • always read the token from the cookie, because the user might want to delete it to log himself out in the browser.

In case of login failure (cookie deleted or 401 response) discard all form data, delete cookie and redirect to login screen.

Obviously the cookie stored on client side will be passed to the server on each request, because that's what cookies do, but the server will ignore it. The cookie is just for the client. The added bandwidth overhead is considered negligible.

Does this workflow seem ok? Am I missing anything?

tomasp
  • 71
  • 3
  • Have you considered using local storage instead of Cookies. Is there any specific reason why you have chosen cookies? – Wayne Ellery Jan 06 '15 at 10:32
  • If your not sending the cookies to the server then why use them? Can't you use local storage or some client side method to store this. We had a pen test done on one of our systems and one thing that was flagged was cookies that could be modified by client side code. – Lee Willis Jan 06 '15 at 10:36
  • By "use "remember me" across different browsers at the same time (like facebook)" do you mean a that the remember me is shared - ie I ticked it in Firefox so Chrome should use it? If so I don't think that is possible. Cookies, cache, local storage, is all local to each browser. – Lee Willis Jan 06 '15 at 10:38
  • And cookies limit you to browser based clients - no good if you want to provide an API in the future (for native apps). – Lee Willis Jan 06 '15 at 10:40
  • 1
    Also make sure you add CSRF protection – Wayne Ellery Jan 06 '15 at 10:55
  • I need to use cookies, because "delete cookies from a browser to log the user out" is a requirement. Why is it bad when the client can modify cookies? I can modify any cookie in firebug manually, I don't think that's a security concern? Cookies don't limit me to browser based clients. The authorization header is used. Cookies are just client side. I could use local storage instead, but cookies are widely used and sort of expected by end users (though I might reconsider this). CSRF protection isn't necessary because the server doesn't read the cookie. – tomasp Jan 06 '15 at 11:40

1 Answers1

1

I have done a project called WebApiSecure that uses Json Web Tokens(JWT) for authorization - Basic and Bearer. I believe it does most of the things you describe, like allowing for token expiration and adding user id or roles. It might assist you in what you want to do.

It is built using Web Api 2 and the front end can be AngularJS or JQuery based.

Atomic Star
  • 5,427
  • 4
  • 39
  • 48