Token-based authentication systems are convenient and scalable. Unlike cookie-based authentication, tokens do not need to be managed on a central server because they are verified with a digital signature rather than a database lookup.
But it's not all good news. Token-based authentication systems introduce new security concerns.
Tokens are like cash. If you receive a valid token, you can only assume that the bearer is its rightful owner. You can't tell if the token is stolen. This is why we call them "bearer tokens", because all that is needed to gain access to a secure application is the presentation of a non-expired token.
It is this implicit trust that makes it so imperative that tokens be stored securely within an application.
Token-based authentication systems are actually less vulnerable to Cross-Site Request Forgery (XSRF) hacks that cookie-based ones. That's because browsers don't automatically attach token
Authorization headers to relevant HTTP requests, as they do with session cookies. But tokens are still susceptible to theft via Man-in-the-Middle (MITM) attacks, and the risks associated with Cross-Site Scripting (XSS) hacks are far greater with token-based authentication systems than with traditional session cookies.
Best practices for securing authentication tokens
Tokens should store only essential data that is required for the correct functioning of the application, and this data must be encrypted. Tokens should be transmitted over HTTPS and signed by their originating server with a strong private key. These features should be treated as mandatory.
Expiry and revocation
Tokens should have relatively short expiry times. You might consider using refresh tokens. This is where you have a long-lived token that triggers the regeneration of a shorter-lived token every so often. The trade-off for the extra demands on your authentication system is that users don't go so long without having their credentials re-verified.
You might also put in place a system that allows you to revoke tokens, in the event that you suspect a token to be compromised. This will require all generated tokens to be logged somewhere, which obviously puts an additional burden on your system.
The two best options for the secure storage of tokens are cookies and session/local storage.
Cookies are the preferred medium for the storage of tokens because of the extra defences they provide. Token cookies can be restricted to the domain and path of the web app and they can be restricted to communication over secure networks only.
For the ultimate in security, we can set token cookies to
Local storage is probably the most convenient medium for storage of tokens. Data is available to all browsing contexts (i.e. all browser tabs) and is not automatically discarded when a browsing context is closed. But local storage is also the least secure storage medium, for the same reasons that it is convenient.
Obviously, web applications that adopt token-based authentication systems must be immunised against XSS. That means validating and sanitising all user input. And it means escaping all text content that is injected into a web page.
It is also a good idea to ask your users to re-enter their passwords prior to undertaking any creative and destructive actions, such as making changes to their user profile or buying something. This process of re-authentication is much more important in token-based authentication systems than with cookie-based ones.
Developing and maintaining a proxy service for all remote API calls is, obviously, a lot of effort. And, while this approach reduces an application's susceptibility to XSS hacks, it increases exposure to XSRF exploits, because of the dependency on session cookies. The truth is that it is relatively easy to protect against XSS, while it is relatively difficult to protect against XSRF. For these reasons you don't tend to see client-side web applications being backed by same-origin APIs. It's just too costly and messy.
Token-based authentication systems provide a convenient and scalable system for cross-domain authentication in exchange for shifting more responsibility for application security onto application developers.
The optimum balance between security on the one hand and convenience and scalability on the other, will vary from application to application.
In most cases, advanced security measures such as API proxies add much complexity for little reward, and they rather negate the benefits of statelessness that token-based authentication provides in the first place. But if you follow best practices for token management and if you lock down your wider system architecture from common exploits, then you should be able to sleep reasonably peacefully.