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:
- Receive POST on /login API endpoint with username and password
- 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:
- Extract token from Authorization header
- the user id might be included in the request too, to be decided
- Hash token and compare it to each of the user's hashes in the db
- 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:
- Send POST request to /login API endpoint with username and password
- 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:
- 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?
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.