2

I have search the web for an answer about this issue, but nothing is quite similar to the setup I have.

So I have a single page application and 3 services:

  1. Backend service - service A
  2. Service for serving static files of the SPA - service B
  3. Authentication service - service C

The flow is as follows:

  • A user visits the site by going to the / of service B gets redirected to the /login of service B.
  • The user enters the credentials and they are sent to service C to perform the authentication process and to get the permissions for the user, this data is sent in a JWT.
  • Service B then puts it in a cookie and returns it to the user's browser.
  • Then the user performs a task which requires that JWT, so I have to send that cookie to service A, but there is a problem, I can't do it, the cookie is only for service A.

https://auth0.com/docs/security/store-tokens - this link is an example of a source I found that is talking about the issue of where to store the tokens for SPA. It says there that I should use a cookie to store the JWT if :

  • If have my own backend
  • If the backend is in the same domain as the site itself.

The problem is that my backend has a different URL, it is a completely different service, so using cookies wont be the solution, or at least that is what it seems to me.

Then it says:

If your single-page app has a backend server at all, then tokens should be handled server-side using the Authorization Code Flow, Authorization Code Flow with Proof Key for Code Exchange (PKCE), or Hybrid Flow.

The problem here is that they don't even mention how and where to store the JWT so I can access it from multiple domains.

I have not found a clean way to save that JWT on the user's browser and send it in every request that I am doing to the backend.

The solution I need is to save the JWT in a secure way in the browser of the user, allowing me to send it to any backend service I need to.

Thanks for reading thus far and for helping!

Max
  • 907
  • 2
  • 13
  • 27

2 Answers2

1

One solution is to send the requests to the backend service with JWT in a query param.

You can then have a middleware in the backend service that converts it to an Authorization header so that libraries that look at it continue to work.

root
  • 5,528
  • 1
  • 7
  • 15
  • It still wont solve the problem of where to save the JWT when I receive it from service B to the user's browser. – Max Oct 08 '19 at 22:59
  • If all services were in the same domain, you'd store it in a cookie or in local storage of service B. Same applies here. – root Oct 08 '19 at 23:01
  • But the problem is that I dont want them to be in the same origin, I want the to scale independently. – Max Oct 09 '19 at 11:39
  • What I'm trying to say is, have service B put the JWT in local storage, and when it wants to send a request to service A, pull it from local storage and add it as a query param on the request to service A. Query params aren't limited the way cookies or local storage are. Also, I said "domain", not "origin". if you scale, that means you already have an http proxy, and if you have that, you don't need separate domains, you can do the redirection at the proxy. – root Oct 09 '19 at 12:07
0

So that is the way I chose to implement the solution, but first, few clarifications.

When a browser tries to visit the site, as stated in the question, will be redirected to the /login route to verify the user. But if the user is nonexistent, then I will let the user to see the site but with minimum permissions. So if the user is authenticated, the cookie with contain the jwt with the needed data. If the user is some sort of a guest, service B will still return a cookie, but will indicate that the user is a guest.

The way I chose to implement it is:

  • Because of the explanation above, we always get the cookie from service B, meaning we can always tell our site save it in the LocalStorage of the browser, so I decided to save the JWT in it.
  • For each fetch request that I make to service A, I will set a header named Authorization that the value of it will be Bearer <the jwt token> (insert the jwt inside <the jwt token>) as stated in this MDN article about authorization.

About security:

So because saving the JWT in a cookie that is related to service A is not possible (service B and A are from different domains), we are left with the LocalStorage option. LocalStorage is not the most secure way, it is by default vulnerable to XSS attacks, but, as stated in this answer, by mikejones1477, modern browsers have strong defense against XSS, and the LocalStorage is not vulnerable to CSRF. So, essentially, there should be extra care about XSS attacks, but that is the way its made possible to pass the token between services from different domains.

Max
  • 907
  • 2
  • 13
  • 27