2

I have a reactjs app which is using firebase to authenticate a user and also link with various auth provider accounts by using the firebase xxxWithRedirect() methods. For example, the following is invoked from the /users/settings page.

var user = this.props.fb.auth().currentUser;
if (user !== null) {
  var provider = new firebase.auth.GoogleAuthProvider();
  user.linkWithRedirect(provider).then(function () {
      console.log('Successfully linked with Google account');
    },
    function (error) {
      console.log('Failed to link with Google account:  code=' + error.code + ' message=' + error.message);
    });
}
else {
  console.log('user is null');
}

After the authentication is complete, the auth provider redirects back to the app page that originated the redirect. For example:

Navigated to http://localhost:3000/users/settings

In the app route handling, I would like to be able to determine if the initial page load was from a redirect or not. This is so that I can determine whether I should take the user back to the /users/settings page in the case of redirect from auth provider or the / page if the user has not yet been authenticated and is a not an auth redirect page load.

        <Switch>
          <Route exact path="/" component={Home}/>
          <Route exact path="/users/profile" render={() => (isLoggedIn() ? <UserProfile fb={fb}/> : <Redirect to="/"/>)}/>
          <Route exact path="/users/settings" render={() => (isLoggedIn() ? <UserSettings fb={fb}/> : <Redirect to="/"/>)}/>
          <Route component={NoMatch}/>
        </Switch>

Is there a way to tell if a reactjs matching route path is from a redirect?

user2800837
  • 321
  • 1
  • 3
  • 9
  • can't you tell firebase auth to redirect to a specific page? – André Werlang Dec 02 '17 at 17:17
  • @AndréWerlang - Firebase auth is actually doing what I want, it tries to redirect back to the /users/settings page which is where the auth request originated from. Unfortunately though, the navigation to this path happens before the user authentication has been completed, i.e. the firebase user has not been set yet as this happens asynchronously in firebase.auth().onAuthStateChanged(). I don't want to navigate to this /users/settings page unless a firebase user is logged in. So it's really a timing thing. I'd like to get them to /users/settings after the user is logged in. – user2800837 Dec 02 '17 at 17:31

3 Answers3

4

@AndreWerlang I was able to solve my problem by making use of the operationType provided in the firebase.auth().getRedirectResult() method. In this particular case I wanted to take the user back to the /user/settings page because that is where the user can link other auth provider accounts. By checking if the operationType was an attempt to "link" another account, I simply redirect again to the /user/settings page since the firebase user was not intact when the first redirect to /users/settings was received from the auth provider. This is the code.

componentDidMount = () => {
    firebase.auth().getRedirectResult().then(function(userCredential) {
        console.log('Successfully redirected');
        var operationType = userCredential.operationType;
        if (operationType) {
            console.log('Redirect operationType = ' + operationType);
            switch (operationType) {
                case 'signIn':
                   break;
                case 'link':
                   this.setState({redirectToUserSettings: true});
                   break;
                case 'reauthenticate':
                   break;
                default:
            }
        }
    }.bind(this), function(error) {}
}

render() {
    return (
        <div className="App">
            <Router history={history}>
                <div>
                    {this.state.redirectToUserSettings && <Redirect to="/users/settings" />}
                </div>
             </Router>
        </div>
    );
}
user2800837
  • 321
  • 1
  • 3
  • 9
1

If there's a timing issue, Firebase Auth returning to your page before a user object is populated, there needs to be a method to wait/retrieve this information when ready.

From https://firebase.google.com/docs/auth/web/google-signin#redirect-mode:

firebase.auth().getRedirectResult().then(function(result) {
  if (result.credential) {
    // This gives you a Google Access Token. You can use it to access the Google API.
    var token = result.credential.accessToken;
    // ...
  }
  // The signed-in user info.
  var user = result.user;
}).catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
  // The email of the user's account used.
  var email = error.email;
  // The firebase.auth.AuthCredential type that was used.
  var credential = error.credential;
  // ...
});

From https://firebase.google.com/docs/reference/js/firebase.auth.Auth#getRedirectResult:

If sign-in succeeded, returns the signed in user. If sign-in was unsuccessful, fails with an error. If no redirect operation was called, returns a UserCredential with a null User.

So at this point you would know when user is authenticated or not and deal with it.

André Werlang
  • 5,839
  • 1
  • 35
  • 49
  • Werlang - I am handling the redirect result just as you have described. The problem is that this async function is invoked after the browser has already been redirected and the reactjs router intercepts it and tries to handle it. This is all before the firebase user object is populated. Here is the sequence from the console. Navigated to http://localhost:3000/users/settings App.js:37 Is not logged in referrer = Successfully redirected App.js:276 Redirect operationType = link redirect url = / App.js:338 a user signed in: Dallas Clement uid=hjCA3w7DHAVkuuzp6S5tv1HQJT53 – user2800837 Dec 02 '17 at 18:16
  • can't react router wait until `firebase.auth().getRedirectResult()` resolves? – André Werlang Dec 02 '17 at 18:18
  • Werlang - I wish it could. That would solve everything. These things are being done in different threads. But if I could somehow determine that a navigation was from a redirect I might be able to remember that and after the firebase user is logged in, navigate the user to that page. – user2800837 Dec 02 '17 at 18:26
  • @user2800837 I'd say your initial inquiry can't be fulfilled in a positive way. You need to deal it with the router/Home component. – André Werlang Dec 02 '17 at 18:33
0

Either:1) find a way to provide a custom url to the thing that redirects (if possible), and put something like "?redirect=true" on it, or else 2) Use a server session variable to hold a flag that says it will be expecting to get a redirect.

  • These are good ideas. I'm not sure I can do either of these with the firebase sign in and link with redirect methods like the one I am using here - auth.currentUser.linkWithRedirect(provider); – user2800837 Dec 02 '17 at 18:12
  • Use Fiddler or some other tool, to check the HTTP headers coming in at the time of the redirect. Maybe there is some header properties/attributes that are put there already that you can detect. I didn't think of that before. Actually you could also use a Web Filter (servlet/java) to log the contents of the header attributes, and that's another way to check (monitor http) to see what you have there –  Dec 02 '17 at 18:18
  • @ClayFerguson how one checks request headers in a browser initiated redirect? – André Werlang Dec 02 '17 at 18:30
  • @AndréWerlang google this: "java servlet filter to print header values" https://stackoverflow.com/questions/25247218/servlet-filter-how-to-get-all-the-headers-from-servletrequest –  Dec 02 '17 at 23:03
  • @ClayFerguson okay, you're talking about a server side solution – André Werlang Dec 02 '17 at 23:10