Reputation: 31
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
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
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