Any mechanism you come up with, that does not involve the user entering a secret for every request will be vulnerable to XSS. The solution is to try and eliminate XSS from your domain, not to have a solution that is secure in the presence of XSS vulnerabilities. A strong CSP policy will help you in browsers that support it.
HttpOnly, but you don't need it to as the cookies will be sent automatically with every request. So cookies are going to be more secure then LocalStorage as long as you mark them
So I would go for Scenario 2, using cookies. If it needs to be stateless the cookie should be a signed and HMAC'ed token with a time expiry in addition to the cookie timeout, such that the servers stop accepting these tokens after the timeout. It will be hard to securely implement the logout functionality without storing state on the server, you can instruct the client to throw away the token, but without a blacklist or whitelist maintained on the server-side you will be vulnerable to the token being used after the user has logged out.
Response to Comments
CSP will need to be served on the top level page (The url the user sees in the address bar), this is A in your case. You should be able to configure you static web server to add headers to every response. Doco. You could also do it at the load balancer but that seems more complex then is required.
If I add CSP on requests going between A and B, ...
CSP is added between A and the browser, not between A and B. In the CSP policy you can configure the browser to be able to communicate with B.
A and B are running on different domains, B adds a CORS filter to only allow JSON POST requests from a specific domain for A, should the CSP also be added alongside the CORS headers?
Since the authentication token is generated, validated and invalidated in the Authentication Service, would HMAC really be needed
In most situations a HMAC makes tokens more secure. If you need to remain stateless you will need a HMAC. If you Authentication Service is state-full, that is it has a list of valid, current tokens, then HMACs are less important as long as your token id's are not guessable. If this is the case consider GUID's for your tokens.
The cookie should obviously be created at A as that is what the browser is interacting with.
My understanding is the browser is also interacting with B, via AJAX, hence the need for the CORS policy. As it is B that is managing the login state and requiring the authentication token the cookie should be set by B. Also as A is just serving static content it would be more elegant if A did not require to set cookies, a dynamic process.
Timeline - As I would see it.
- User navigates to https://a/index.html
- A responds with a HTML page
including CSP headers, the HTML includes a
- The browser fetches https://a/code.js
- code.js puts up a login box, user enters username and password and instructs the browser to send an AJAX POST request to https://b/login
- The browser sends a HEAD request to https://b/login to ensure it can make posts.
- B responds with the CORS headers allowing POSTs from https://a
- The browser sends a POST request to https://b/login containing the username and password
- B checks the password and if successful returns a success message and sets a cookie which is
Secure. This cookie contains a token such as a random GUID. This token is stored in B's state.
- The user clicks a button
- A request is sent to https://b/customer/5/update containing the cookie
- B validates the cookie value and proceeds to do the action requested e.g. updates the customer.
- The user click logout
- The browser sends an AJAX request to https://b/logout
- B invalidates the token contained in the cookie in it's state, and send a message back to the client clearing the cookie there also.