Thanks again for your insight. In our case it seems the best we can do is hold the preview application responsible - we are after all allowing all users unfettered access to the application.
Our SPA uses the implicit flow, and passes the accessToken to various services. One option to solve this could be to introduce a “proxy server” in between (it’s a fair bit of refactor to do this) - like you mention, there can be a session between the SPA and this proxy server, and the proxy server can in-turn make API requests on behalf of the SPA. Should this proxy server then use client-credentials? The token is itself then hidden from the SPA with this approach, but the API can still be misused. However, the misuse will be restricted to whatever the proxy server exposes, and the registered (preview) application can be held responsible. Like was mentioned earlier, we are after all allowing all users unfettered access to this preview application - so we are accepting the possibility that the API (exposed by this proxy server) can be misused.
However, does this option sound all that much better than just “leaking” the access token? In both cases, we can hold the application responsible - just that in the first case, we also limit the possibility of misuse to the API exposed by the proxy server.
Is there a better pattern for solving this preview use case?