79

I am creating an social login page with an Access Management (AM) server. When user click on the login button then I make a fetch http post call to AM server. AM server generates a HTTP 301 redirect response with auth cookies to the social login page. I need to follow somehow this redirect response and show the new content in the web browser.

UI: ReactJS

Request:

POST /api/auth/socialauth/initiate HTTP/1.1
Host    example.com
User-Agent  Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0)
Accept  */*
Accept-Language en-US,en;q=0.5
Accept-Encoding gzip, deflate
origin  http://web.example.com:8080
Referer http://web.example.com:8080/myapp/login
Cookie  authId=...; NTID=...

Response

HTTP/1.1 307 Temporary Redirect
https://www.facebook.com/dialog/oauth?client_id=...&scope=public_profile%2Cemail&redirect_uri=http%3A%2F%2Fam.example.com%3A8083%2Fopenam%2Foauth2c%2FOAuthProxy.jsp&response_type=code&state=qtrwtidnwdpbft4ctj2e9mv3mjkifqo

React code:

initiateSocialLogin() {
    var url = "/api/auth/socialauth/initiate";

    fetch(url, { method: 'POST' })
        .then(response => {
            // HTTP 301 response
            // HOW CAN I FOLLOW THE HTTP REDIRECT RESPONSE?
        })
        .catch(function(err) {
            console.info(err + " url: " + url);
        });
}

How I can follow the redirect response and show the new content in the web browser?

Chris
  • 18,724
  • 6
  • 46
  • 80
zappee
  • 20,148
  • 14
  • 73
  • 129
  • 3
    you could have `fetch` automatically redirect, change `fetch(url, { method: 'POST' })` to `fetch(url, { method: 'POST', redirect: 'follow' })` – Derek Pollard Sep 27 '16 at 22:58
  • Plese have a look at [this answer](https://stackoverflow.com/a/75188418/17865804). – Chris Feb 05 '23 at 09:34

9 Answers9

77

Request.redirect could be "follow", "error" or "manual".

If it is "follow", fetch() API follows the redirect response (HTTP status code = 301,302,303,307,308).

If it is "error", fetch() API treats the redirect response as an error.

If it is "manual", fetch() API doesn't follow the redirect and returns an opaque-redirect filtered response which wraps the redirect response.

Since you want to redirect after a fetch just use it as

fetch(url, { method: 'POST', redirect: 'follow'})
    .then(response => {
        // HTTP 301 response
    })
    .catch(function(err) {
        console.info(err + " url: " + url);
    });
challet
  • 870
  • 9
  • 21
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 29
    The problem with redirect: 'follow' is that i need to show the page in the browser. So I need somehow to redirect the browser and show the new content. – zappee Sep 28 '16 at 19:56
  • 51
    the above code with cors set does not redirect automatically. But I do see the new URL in Network tab in Chrome Version 61.0.3163.100 (Official Build). Any idea why is the browser not rendering it. – j10 Sep 27 '17 at 13:29
  • How to achieve the same using axios – PCK Nov 29 '19 at 12:50
  • 5
    - The problem with redirect: 'follow' is that i need to show the page in the browser. In this case I think using `manual` and then setting the `location.href` or even `window.open` using the [Location header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location) of the returned response – Luke T O'Brien Nov 07 '20 at 09:37
  • 1
    is it possible to get the redirected url when using `manual`? – BenKoshy Aug 17 '21 at 05:39
  • 3
    @BenKoshy no it is not. @Luke T O'Brien `manual` returns an opaque response for service workers to replay responses. it is a zero byte, inaccessible, basically unusable response. Not what you are looking for. `error` may be more useful – Sampson Crowley Nov 04 '21 at 16:20
  • 7
    `error` is also useless because it only throws an error, but give no access to the redirect response – Sampson Crowley Nov 04 '21 at 16:26
  • 5
    This doesn't work at all, no redirect is performed. You can't even access the `location` header to manually redirect! Can't believe how difficult following a 302 is! – Joe Jul 22 '22 at 08:34
47

Have a look at properties url redirected of Response object: Doc says that this is

"Experimental. Expect behavior to change in the future"

The url read-only property of the Response interface contains the URL of the response. The value of the url property will be the final URL obtained after any redirects.

In my experiments, this 'url' property was exactly the same as the value of Location header in Chrome (Version 75.0.3770.100 (Official Build) (64-bit)) Network console.

The code to deal with redirecting link my look like this:

   fetch(url, { method: 'POST' })
    .then(response => {
        // HTTP 301 response
        // HOW CAN I FOLLOW THE HTTP REDIRECT RESPONSE?
        if (response.redirected) {
            window.location.href = response.url;
        }
    })
    .catch(function(err) {
        console.info(err + " url: " + url);
    });

I tested it working with react.js same-origin script with fetch AJAX call facing redirects 302 from server.

P.S. In SPA apps, redirect responses are unlikely, maybe this is the reason why ajax vendors apply little attention to this functionality. See also these discussions: here here

Andreas Gelever
  • 1,736
  • 3
  • 19
  • 25
31

It is not possible to follow a redirect to a new HTML page with javascript.

fetch(url, { method: 'POST', redirect: "follow" });

will simply perform another request to the redirected location which will be returned as data and not rendered by the browser. You might expect to be able to use { redirect : "manual" }, get the redirected location from the response and navigate to the page with Javascript, but unfortunately the redirected location is not returned, see https://github.com/whatwg/fetch/issues/763.

Max888
  • 3,089
  • 24
  • 55
5

I have a similar issue and I believe that the answer for fetch inside React is the same as it is for ajax inside JQuery - if you are able to detect that the response is a redirect, then update the window.location.href with the response.url

See for example: How to manage a redirect request after a jQuery Ajax call

Note that 'if you are able to detect that the response is a redirect' might be the tricky part. Fetch responses may contain a 'redirected' flag (see https://developer.mozilla.org/en-US/docs/Web/API/Response) but I've found that is not the case in Chrome. I also find in Chrome I get a 200 status response rather than a redirect status - but that could be something with our SSO implementation. If you are using a fetch polyfill with IE then you'll need to check whether response.url is included or not.

Community
  • 1
  • 1
vanappears
  • 147
  • 2
  • 3
  • 2
    As of Chrome Version 61.0.3163.100 (Official Build) --> it does return a "redirected" flag and a url and 200 status And inspite of setting the "redirect" to "follow" --> it does not redirect itself and I need to use window.location – j10 Sep 27 '17 at 13:31
  • window.location will make a new requests, giving a total of 2 @j10. idk what's the proper approach, but none of the answers here is correct – Minsky Dec 17 '20 at 13:32
  • Chrome now follows the redirect by default https://chromestatus.com/feature/4614142321229824 – sgu Mar 20 '22 at 01:50
1

My solution for this scenario was sent the url I want to be redirected as a parameter and then receiving it in body response, instead of using redirect from server

JMS_reTro
  • 11
  • 1
0

I tried using fetch to redirect to the url but this method didn't work, so I ended up using a different method to get the redirect to work. Inside your react component follow the following steps:

  • Step 1: create a state variable:
const [_id,setID]=useState('')
  • Step 2: create a method that updates the state:
function uidValue(event) {
        const endpoint = '/api/users/'+ event.target.value // this will be your url, so set it right.
        setID(endpoint)
    }
  • Step 3: in the return section of your component add an onChange listener to the username(id in my case):
<input id="uid" type="text" name=":_id" placeholder=":_id" onChange={uidValue} />
  • Step 4: make sure all your inputs are inside a form, and add an action attribute to the form tag that sets the URL you want it to navigate to using your state:
<form  action={_id} id="login info" method="post" >
v.kats
  • 21
  • 5
0

To make redirect work on fetch, this is how I do:

fetch(url, {redirect:'manual'})
.then(response => {
  if (response.type == "opaqueredirect"){  
  // To make sure the fetch is with redirect response return.  
    window.location.replace('.../redirectURL/'); // create one and only one request
    return;
  }
  ...
}

Another way to do it is as follows. However, this method would create two requests to the redirected url. The first request won't change the content. Only would the second request do.

fetch(url, {redirect:'follow'}) // 'follow' creates the first request
.then(response => {
  if (response.redirected) {
    window.location.replace(response.url); 
  // creates the second request, and change the content
    return;
  }
  ...
}

https://javascript.info/fetch-api#redirect

0

Use the "redirected" property inside the incoming data, then manually redirect(you need to send a 302 status code from the server first):

  await fetch('http://localhost:3333/signup', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    credentials: 'include',
    body: data,
  })
    .then(data => data.redirected && (document.location.href = '/foro'))
    .catch(err => console.error(err));
});
Pablo M
  • 1
  • 3
-1

Fetch is not able to get the redirect URL, but XMLHttpRequest can.

if you want to get the redirect URL, you can try this code:

const xhr = new XMLHttpRequest();

xhr.open("GET", "/api/auth/socialauth/initiate");

xhr.send();

xhr.onreadystatechange = function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseUR);
    this.abort();
  }
};
lmx
  • 1
  • 3