Jesse Davda
Jesse Davda

Reputation: 31

How do I handle external redirects to a client side react app?

I'm building a web app that uses the True Layer open banking API. The app has a React front end using react router and an Express and Nodejs backend. Currently I am using the react build script to serve the static files:

const app = express();

app.use(cors());
app.use(express.json());

app.use(express.static('../client/build'));
app.get('*', (req, res) => {
    res.sendFile('index.html', {root: path.join(__dirname, '../client/build/')});
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
    console.log("Server listening on port: ", PORT);
});

// The API routes are imported below

This solution works well but when the user is redirected to True Layer's authentication page an OAuth code is generated and passed back to a route on the client as a url query. The issue is that when True Layer redirect the user back to my app the url is interpreted by the server and not the react router/browser so it returns cannot GET /myAccounts. I've looked into using server side rendering for the React client with a library like Next.js but I wanted to know if there was a way to do it without having to significantly refactor my code.

My React Router setup:

class App extends Component {
    render() {
        return (
            <Router>
                <Route name="Landing" path="/" exact component={Login} />
                <Route name="Login" path="/login" exact component={Login} />
                <Route name="MyAccounts" path="/myAccounts" exact component={Home} />
            </Router>
        );
    }
}

The /myAccounts route renders the Home component/page where the code parameter is extracted: qs.parse(props.location.search)['?code'], and sent to my server to complete the OAuth process.

Any help would be greatly appreciated.

Upvotes: 3

Views: 7586

Answers (2)

winwiz1
winwiz1

Reputation: 3164

P.S.
To cater for login the logic needs to be modified.
The preferred (and more logically simple/clear) option in this case is to have 2 SPAs: on for the login, another for the rest of the application. But I understand it would mean more changes to the existing app and that is why you prefer to change Express side only. This can be done but the logic gets more complex.

Client side
In your only entrypoint for the only SPA you can have:

<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/login" component={Login} />
  <Route path="/myaccount" component={either Login or maybe MyAccount} />
  <Route component={Home} />
</Switch>

The Home component should verify the client is logged in (for example by checking the cookie set by Express or external cloud server after successful login) and if not redirect internally (history.push) to /login. In the same manner Login component should check if the client has been authenticated and if so redirect internally to'/' e.g. to Home component.

Express side logic

  • / - serve index.html (the only .html file produced by React build since you have one SPA) which eventually goes to Router which in turn will render Home component due to the 1st <Route>

  • /index – serve index.html. Router will render Home component due to the 4th <Route>.

  • /login – serve index.html. This is a fallback (for example already logged in user can manually type this path). Router will render Login component due to the 2nd <Route>

  • /myaccount – serve index.html. The Router will render Login component due to the 3rd <Route>. The component should be able to get the ?code=123bac...

  • unknown page that doesn't look malicious. It's probably some internal path to some internal page of your SPA retyped by a user. Fallback to '/' to show Home component.

Upvotes: 0

winwiz1
winwiz1

Reputation: 3164

The issue is that when True Layer redirect the user back to my app the url is interpreted by the server and not the react router/browser so it returns cannot GET /myAccounts

What happens is:
The path /myAccounts is the internal path of your React SPA which displays the internal SPA page rendered by MyAccounts component. The webserver is not aware of this page and returns "cannot GET /myAccounts". This webserver behaviour is incorrect not because the webserver doesn't support True Layer/OAuth but rather because the webserver doesn't support SPA correctly.

What should happen:
The webserver should support SPA by implementing the fallback behaviour which simply means the server redirects requests for unknown pages to the SPA landing page. For example, this behaviour is enabled in webpack-dev-server using the historyApiFallback which exists specifically to support SPAs.

The fallback behavior is required for any SPA because the user can see the path to any internal page in the navigation bar and can either retype it manually and press Enter or refresh the browser. In both cases the webserver gets hit with a request for the internal SPA page it is not aware of and responding with 404 error doesn't look good for the user.

Once you implement fallback in Express, you can add a piece of data to the Request being redirected to the landing page. So that your Landing component will see this optional piece and redirect internally (webserver won't know) to MyAccounts component.

Upvotes: 2

Related Questions