grpc / grpc-web

gRPC for Web Clients
https://grpc.io
Apache License 2.0
8.51k stars 766 forks source link

Not redirecting on response 302 which includes a Location location header #1234

Open ghost opened 2 years ago

ghost commented 2 years ago

I am trying to use the OAuth2 framework on our Angular web application which uses grpc-web to communicate with our backend services proxied with Envoy (using oauth2 http filter). The redirect (to the SSO login page) is correctly sent by the SSO server but the grpcwebclientreadablestream.js expects the Content-Type response header to have one of the allowed values (application/grpc-web-text, application/grpc).

I see the error 'Unknown Content-type received.' in the console but I also see the expected HTTP requests but they are not reflected on the browser. They are just background AJAX requests.

POST gRPC Request => GET HTTP Auth Request => GET HTTP Auth HTML Page

Could a browser redirection take place?

document.location.href = response.headers["Location"]

Is there a way to achieve this? I tried Interceptors but it seems that the failure on the Content-Type fails the promise altogether even if you have implemented then, catch, finally.

So the only solution I have in mind is to somehow override the events handler in grpcwebclientreadablestream.js :

events.listen(this.xhr_, EventType.READY_STATE_CHANGE, function(e) {
  // check if there is a redirection and change the document.location.href

Is there another way or an elegant way to override this (events.listen(this.xhr_, EventType.READY_STATE_CHANGE, function(e))?

ghost commented 2 years ago

OK I have managed to make it work by overriding XMLHttpRequest.prototype.open

(function() {
    var open = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
        var oldReady;
        if (async) {
            oldReady = this.onreadystatechange; // this could be null
            // override onreadystatechange
            this.onreadystatechange = function() {
                if (this.readyState === 4) {
                   var location = this.getResponseHeader("Location");
                   if(location) {
                       document.location.href = location;
                   }
                } else if(oldReady != null) {
                    return oldReady.apply(this, arguments);
                }
            }
        }
        // call original open method
        return open.apply(this, arguments);
    }
})();

If there is a different, more elegant way to do this, please let me know. Thank you!