I'm developing an app in React and Spring Boot + Spring Security. Right now, I've added HTTP Basic Auth for user authentication. The user inputs username and password in a login form, and I send them (of course over HTTPS) to my backend. If the user is authenticated, I send back success, and the frontend saves the credentials into local storage for future requests (i.e. no session server-side). Then whenever I navigate to a "secured" route in React, I check the local storage, and then use the credentials for my secured requests. When the user needs to logout, the app removes the credentials from local storage.
It's a very trivial security implementation, but unless I have XSS vulnerability, or there is downgrade to HTTP (for my domain, not possible - HSTS preloading), I don't think it's vulnerable. But the main problem I see with it is that I can't for example logout users remotely, which might come in handy at some point. Have I found myself in a dead-end? Is HTTP Basic Auth just not meant for stateless authentication with SPAs?
First, I would suggest not doing that. As you said, a downgrade to HTTP will expose credentials and a XSS vulnerability will make it possible for the attacker to steal the credentials of your users.
However, if you insist to store credentials in localStorage then you should ensure several things:
This way, a downgrade to HTTP will expose the traffic but the credentials are transferred after encryption.
The localStorage is only accessible via scripts coming in through your own domain, so you're safe as long as the only frontend code running is your own. But if any other code is executed, via injection or if you share the domain with someone else, they will be able to access the localStorage data.
For the second part of your question, logging out users remotely. You can implement it in a different way:
Upon a successful login, generate a completely random string unrelated to user credentials and store that in the database, along with an expiry date. Then, pass that string to be stored in localStorage.
From then on, so long as that local storage credential matches the database one and the timeout has not expired, you automatically consider them logged in.
This way, there is no risk concerning the exposure of the user's credentials from localStorage. However, with this temporary unique string essentially functioning as a sessionID, you will still to need to be aware of and take precautions against the risks associated with session hijacking.
Now, to end a user's session remotely, you can update the expiry date of that entry in the DB.