SPA best practices for authentication and session management
Asked Answered
F

3

390

When building SPA style applications using frameworks like Angular, Ember, React, etc. what do people believe to be some best practices for authentication and session management? I can think of a couple of ways of considering approaching the problem.

  1. Treat it no differently than authentication with a regular web application assuming the API and and UI have the same origin domain.

    This would likely involve having a session cookie, server side session storage and probably some session API endpoint that the authenticated web UI can hit to get current user information to help with personalization or possibly even determining roles/abilities on the client side. The server would still enforce rules protecting access to data of course, the UI would just use this information to customize the experience.

  2. Treat it like any third-party client using a public API and authenticate with some sort of token system similar to OAuth. This token mechanism would used by the client UI to authenticate each and every request made to the server API.

I'm not really much of an expert here but #1 seems to be completely sufficient for the vast majority of cases, but I'd really like to hear some more experienced opinions.

Flocky answered 7/1, 2014 at 3:13 Comment(1)
I perfer this way, https://mcmap.net/q/87961/-authentication-for-users-on-a-single-page-appGan
S
563

This question has been addressed, in a slightly different form, at length, here:

RESTful Authentication

But this addresses it from the server-side. Let's look at this from the client-side. Before we do that, though, there's an important prelude:

Javascript Crypto is Hopeless

Matasano's article on this is famous, but the lessons contained therein are pretty important:

https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

To summarize:

  • A man-in-the-middle attack can trivially replace your crypto code with <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • A man-in-the-middle attack is trivial against a page that serves any resource over a non-SSL connection.
  • Once you have SSL, you're using real crypto anyways.

And to add a corollary of my own:

  • A successful XSS attack can result in an attacker executing code on your client's browser, even if you're using SSL - so even if you've got every hatch battened down, your browser crypto can still fail if your attacker finds a way to execute any javascript code on someone else's browser.

This renders a lot of RESTful authentication schemes impossible or silly if you're intending to use a JavaScript client. Let's look!

HTTP Basic Auth

First and foremost, HTTP Basic Auth. The simplest of schemes: simply pass a name and password with every request.

This, of course, absolutely requires SSL, because you're passing a Base64 (reversibly) encoded name and password with every request. Anybody listening on the line could extract username and password trivially. Most of the "Basic Auth is insecure" arguments come from a place of "Basic Auth over HTTP" which is an awful idea.

The browser provides baked-in HTTP Basic Auth support, but it is ugly as sin and you probably shouldn't use it for your app. The alternative, though, is to stash username and password in JavaScript.

This is the most RESTful solution. The server requires no knowledge of state whatsoever and authenticates every individual interaction with the user. Some REST enthusiasts (mostly strawmen) insist that maintaining any sort of state is heresy and will froth at the mouth if you think of any other authentication method. There are theoretical benefits to this sort of standards-compliance - it's supported by Apache out of the box - you could store your objects as files in folders protected by .htaccess files if your heart desired!

The problem? You are caching on the client-side a username and password. This gives evil.ru a better crack at it - even the most basic of XSS vulnerabilities could result in the client beaming his username and password to an evil server. You could try to alleviate this risk by hashing and salting the password, but remember: JavaScript Crypto is Hopeless. You could alleviate this risk by leaving it up to the Browser's Basic Auth support, but.. ugly as sin, as mentioned earlier.

HTTP Digest Auth

Is Digest authentication possible with jQuery?

A more "secure" auth, this is a request/response hash challenge. Except JavaScript Crypto is Hopeless, so it only works over SSL and you still have to cache the username and password on the client side, making it more complicated than HTTP Basic Auth but no more secure.

Query Authentication with Additional Signature Parameters.

Another more "secure" auth, where you encrypt your parameters with nonce and timing data (to protect against repeat and timing attacks) and send the. One of the best examples of this is the OAuth 1.0 protocol, which is, as far as I know, a pretty stonking way to implement authentication on a REST server.

https://www.rfc-editor.org/rfc/rfc5849

Oh, but there aren't any OAuth 1.0 clients for JavaScript. Why?

JavaScript Crypto is Hopeless, remember. JavaScript can't participate in OAuth 1.0 without SSL, and you still have to store the client's username and password locally - which puts this in the same category as Digest Auth - it's more complicated than HTTP Basic Auth but it's no more secure.

Token

The user sends a username and password, and in exchange gets a token that can be used to authenticate requests.

This is marginally more secure than HTTP Basic Auth, because as soon as the username/password transaction is complete you can discard the sensitive data. It's also less RESTful, as tokens constitute "state" and make the server implementation more complicated.

SSL Still

The rub though, is that you still have to send that initial username and password to get a token. Sensitive information still touches your compromisable JavaScript.

To protect your user's credentials, you still need to keep attackers out of your JavaScript, and you still need to send a username and password over the wire. SSL Required.

Token Expiry

It's common to enforce token policies like "hey, when this token has been around too long, discard it and make the user authenticate again." or "I'm pretty sure that the only IP address allowed to use this token is XXX.XXX.XXX.XXX". Many of these policies are pretty good ideas.

Firesheeping

However, using a token Without SSL is still vulnerable to an attack called 'sidejacking': http://codebutler.github.io/firesheep/

The attacker doesn't get your user's credentials, but they can still pretend to be your user, which can be pretty bad.

tl;dr: Sending unencrypted tokens over the wire means that attackers can easily nab those tokens and pretend to be your user. FireSheep is a program that makes this very easy.

A Separate, More Secure Zone

The larger the application that you're running, the harder it is to absolutely ensure that they won't be able to inject some code that changes how you process sensitive data. Do you absolutely trust your CDN? Your advertisers? Your own code base?

Common for credit card details and less common for username and password - some implementers keep 'sensitive data entry' on a separate page from the rest of their application, a page that can be tightly controlled and locked down as best as possible, preferably one that is difficult to phish users with.

Cookie (just means Token)

It is possible (and common) to put the authentication token in a cookie. This doesn't change any of the properties of auth with the token, it's more of a convenience thing. All of the previous arguments still apply.

Session (still just means Token)

Session Auth is just Token authentication, but with a few differences that make it seem like a slightly different thing:

  • Users start with an unauthenticated token.
  • The backend maintains a 'state' object that is tied to a user's token.
  • The token is provided in a cookie.
  • The application environment abstracts the details away from you.

Aside from that, though, it's no different from Token Auth, really.

This wanders even further from a RESTful implementation - with state objects you're going further and further down the path of plain ol' RPC on a stateful server.

OAuth 2.0

OAuth 2.0 looks at the problem of "How does Software A give Software B access to User X's data without Software B having access to User X's login credentials."

The implementation is very much just a standard way for a user to get a token, and then for a third party service to go "yep, this user and this token match, and you can get some of their data from us now."

Fundamentally, though, OAuth 2.0 is just a token protocol. It exhibits the same properties as other token protocols - you still need SSL to protect those tokens - it just changes up how those tokens are generated.

There are two ways that OAuth 2.0 can help you:

  • Providing Authentication/Information to Others
  • Getting Authentication/Information from Others

But when it comes down to it, you're just... using tokens.

Back to your question

So, the question that you're asking is "should I store my token in a cookie and have my environment's automatic session management take care of the details, or should I store my token in Javascript and handle those details myself?"

And the answer is: do whatever makes you happy.

The thing about automatic session management, though, is that there's a lot of magic happening behind the scenes for you. Often it's nicer to be in control of those details yourself.

I am 21 so SSL is yes

The other answer is: Use https for everything or brigands will steal your users' passwords and tokens.

Stegall answered 16/1, 2014 at 23:27 Comment(12)
Great answer. I appreciate the equivalence between token auth systems and basic cookie auth (which is often built into the web framework). That's sort of what I was looking for. I appreciate you covering so many potential issues for consideration too. Cheers!Flocky
I know it's been a while but I'm wondering if this should be expanded to include JWT? auth0.com/blog/2014/01/07/…Flocky
finally found a person that really understand security, by reading the way he explain it, well done Curtis.Kriegspiel
Token It's also less RESTful, as tokens constitute "state and make the server implementation more complicated." (1) REST requires the server to be stateless. A token stored client-side doesn't represent state in any meaningful way for the server. (2) Marginally more complicated server-side code has nothing to do with RESTfulness.Susi
@ChrisNicola JWT is basically one implementation of the Token method above. Same implications.Susi
lol_nope_send_it_to_me_instead I loved this function's name :DRundown
An answer on this topic should to be approached with SSL as a given. Give a brief list of reasons SSL is a must and then move on. Nothing is secure without it. There are plenty of deeper topics on securing services like "What are the security problems with passing a token (via SSL) via GET vs. via POST?" Your answer seems to invalidate all approaches except SSL, yet most of the approaches have been proposed with SSL as a given.Mallissa
One thing you appear to overlook: Cookies are XSS safe when marked httpOnly, and can be locked down further with secure and samesite. And cookie handling has been around much longer === more battle hardened. Relying on JS and local storage to handle token security is a fools game.Lepp
This wanders even further from a RESTful implementation - with state objects you're going further and further down the path of plain ol' RPC on a stateful server. - this is a slippery slope argument and is fairly absurd. It's also less RESTful, as tokens constitute "state" and make the server implementation more complicated. Actually, one of the major purposes of JWT is that it's stateless. If you want to argue RESTfulness, perhaps we should address HATEOAS (or lackthereof in many 'RESTful' APIs)?Calvinism
@Curtis, this is an excellent answer! To add to this, auth tokens are of many types. And what you do with them also impacts your overall security. For example, you have to choose between JWTs or Opaque tokens, you can configure their expiry time, you can also choose if you want to use refresh tokens or not.. To read more on the different session protocols you can use, please see this blog post I wrote (I hope it helps you): medium.com/@supertokens.io/ee5245e6bdadSchramm
If you use an HTTP COOKIE to store your token then javascript cannot go anywhere near it so enhancing security. But that only works as long as your API and site pages are from the same domain :-)Gorgonzola
It is always understood that if the web app is running without using SSL, any form of authentication is going to be susceptible to MITM attack... I am not able to understand what it has to do with the JavaScript crypto...Alcohol
A
69

You can increase security in authentication process by using JWT (JSON Web Tokens) and SSL/HTTPS.

The Basic Auth / Session ID can be stolen via:

  • MITM attack (Man-In-The-Middle) - without SSL/HTTPS
  • An intruder gaining access to a user's computer
  • XSS

By using JWT you're encrypting the user's authentication details and storing in the client, and sending it along with every request to the API, where the server/API validates the token. It can't be decrypted/read without the private key (which the server/API stores secretly) Read update.

The new (more secure) flow would be:

Login

  • User logs in and sends login credentials to API (over SSL/HTTPS)
  • API receives login credentials
  • If valid:
  • Register a new session in the database Read update
  • Encrypt User ID, Session ID, IP address, timestamp, etc. in a JWT with a private key.
  • API sends the JWT token back to the client (over SSL/HTTPS)
  • Client receives the JWT token and stores in localStorage/cookie

Every request to API

  • User sends a HTTP request to API (over SSL/HTTPS) with the stored JWT token in the HTTP header
  • API reads HTTP header and decrypts JWT token with its private key
  • API validates the JWT token, matches the IP address from the HTTP request with the one in the JWT token and checks if session has expired
  • If valid:
  • Return response with requested content
  • If invalid:
  • Throw exception (403 / 401)
  • Flag intrusion in the system
  • Send a warning email to the user.

Updated 30.07.15:

JWT payload/claims can actually be read without the private key (secret) and it's not secure to store it in localStorage. I'm sorry about these false statements. However they seem to be working on a JWE standard (JSON Web Encryption).

I implemented this by storing claims (userID, exp) in a JWT, signed it with a private key (secret) the API/backend only knows about and stored it as a secure HttpOnly cookie on the client. That way it cannot be read via XSS and cannot be manipulated, otherwise the JWT fails signature verification. Also by using a secure HttpOnly cookie, you're making sure that the cookie is sent only via HTTP requests (not accessible to script) and only sent via secure connection (HTTPS).

Updated 17.07.16:

JWTs are by nature stateless. That means they invalidate/expire themselves. By adding the SessionID in the token's claims you're making it stateful, because its validity doesn't now only depend on signature verification and expiry date, it also depends on the session state on the server. However the upside is you can invalidate tokens/sessions easily, which you couldn't before with stateless JWTs.

Adonai answered 1/2, 2015 at 3:36 Comment(20)
In the end a JWT is still 'just a token' from a security standpoint I think. The server could still associate the user id, IP address, timestamp etc. with an opaque session token and it would be no more or less secure than a JWT. However, the stateless nature of JWT does make the implementation easier.Zitazitah
@Zitazitah the JWT has the advantage of being verifiable and capable of carrying key details. This is pretty useful for various API scenarios, like where cross-domain auth is required. Something a session won't be as good for. It is also a defined (or at least in progress) spec, which is useful for implementations. That isn't to say it's any better than any other good token implementation, but it is well defined and convenient.Flocky
@Chris Yes I agree with all of your points. However, the flow described in the answer above is not inherently a more secure flow as claimed due to the use of a JWT. Furthermore, the JWT is not revocable in the scheme described above unless you associate an identifier with JWT and store state on the server. Otherwise you either need to regularly get a new JWT by requesting username/password (poor user experience) or issue a JWT with a very long expiry time (bad if the token gets stolen).Zitazitah
OP and @ChrisNicola - I love JWT but only find it particularly useful when the token is only signed/encoded (not encrypted) so that the client actually gains some value from using it and not using a backed session database. What would be less secure about taking the verifiable details that we want to keep private on the client (IP Address, etc.), encrypting them, and storing the hashed value in a claim on the token. Then on the server undergo the standard JWT validation on each request with an additional check that decrypts the private details claim to test if it matches the request details?Fer
My answer is not 100% correct, because JWT can actually be decrypted/read without the private key (secret) and it's not secure to store it in localStorage. I implemented this by storing claims (userID, exp) in a JWT, signed it with a private key (secret) the API/backend only knows about and stored it as a HttpOnly cookie on the client. That way it cannot be read by XSS. But you have to use HTTPS because the token could be stolen with MITM attack. I will update my answer to reflect on this.Adonai
As a rule I would suggest never communicating anything remotely comprising in a JWT token. Usually just a user ID and the token details. Perhaps some of the permissions or roles allowed for by the token if you are using it in some sort of federated system.Flocky
@James, yes I wouldn't call JWT "more" secure than other well implemented token strategies. It is however both simple and well defined by it it's RFC, which I do think are very useful properties.Flocky
Sounds a bit like "OpenID Connect" what you're describing.Recension
@Gaui, but if you store JWT in HttpOnly cookie, then how do you plan to use it in SPA which implies accessing server API from JS?Lagging
@Lagging The cookie is sent with each request from the client. You don't access the cookie from the JS, it's tied with each HTTP request from the client to the API.Adonai
@Gaui, oops, you're right. I supposed that XHRs don't contain httponly cookies, and I was wrong.Lagging
What about before the user is authenticated? Or even has a login. Like maybe an anonymous checkout that needs state but no user exists yet.Recur
@Recur The cookie is just for backend authentication. You keep the client state in a local storage.Adonai
@Adonai Apologies for this being so old, but I'm trying to learn the best approach to this. What's to stop a XSS attacker just sending a fetch request with the cookie?Del
@Del Secure HttpOnly cookie. 😉Adonai
Thanks Gaui, but doesnt that just stop it being stolen, rather than prevent it being used in a request from the client?Del
@gaui if JWT is stored in a cookie does a mobile client extract the token from cookie and store it locally? is this secure? Also if we a user changes password do we invalidate all tokens or rather delete all tokens assigned to that user?Datnow
@Datnow The cookie is set in the client's browser automatically and is transferred with every HTTP request to the server. This is safe if it's a HttpOnly cookie, because those can't be read with a script. I would revoke all tokens if the user changes his password. Because he will have to authenticate again.Adonai
This is not true: "By using JWT you're encrypting the user's authentication details"Ordnance
One flaw which is worth considering is encrypting the token with a private key. I guess the author misunderstood the asymmetric encryption system. The token is encrypted with a public key and verified with the private key. The public key and the payload are decryptable. However, tampered data will be invalidated against the private key. Public private keys are generated and used in complementary fashion.Jadotville
N
8

I would go for the second, the token system.

Did you know about ember-auth or ember-simple-auth? They both use the token based system, like ember-simple-auth states:

A lightweight and unobtrusive library for implementing token based authentication in Ember.js applications. http://ember-simple-auth.simplabs.com

They have session management, and are easy to plug into existing projects too.

There is also an Ember App Kit example version of ember-simple-auth: Working example of ember-app-kit using ember-simple-auth for OAuth2 authentication.

Nancee answered 7/1, 2014 at 11:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.