Javascript apps are considered Public Clients. It is not possible to have
a client_secret
then.
Uses the PKCE extension flow.
Tokens are available in the browser. Browser is vulnerable to a lot of attacks like XSS. The attacker is able to run code within your application. Use a Strong Security Policy to protect XSS. It tells the browser which domains are allowed to load JS from. But you are still vulnerable.
There's no single secure storage API available.
Because the browser is so vulnerable, the refresh tokens may have different policies. Refresh tokens may be disabled completely or they might be one time use.
Token lifetimes should be kept short.
Storage mechanisms are unsafe
- LocalStorage
- shared across tabs
- vulnerable
- SessionStorage
- as long as a window is open
- not shared across tabs
- vulnerable
- Cookies:
- vulnerable
- Keep them in memory
- harder for an attacker
- Service Worker
- storage is isolated from the main browser window
- XSS vulnerabilities won't be able to access tokens
- complicated to build
- doesn't work on IE11
- whole app uses as a little oauth client
- JS can't make API calls itself anymore, needs to tell the service worker to make API call
- JS never sees the access_token itself
- WebCrypto API
- not available on IE
- it is a published standard
- provides ability for JS to generate and use a private key while preventing that private key from being extracted
- you could generate a private key and then encrypt things that you want to store with that key.
- not a perfect solution
- an attacker could use that key to decrypt, but it's an specific targeted attack
You are always at risk of cross-site scripting attacks, with every storage.
Move oauth flow and token storage into a dynamic backend server, also known as backend-for-frontend, like NextJS or NuxtJS server.
Proxy all your API requests through that backend.
This backend can use an HTTPOnly session cookie to the browser.
That way the cookie can't be stolen from JS.
Much safer option than actually handling the access tokens directly in JS.