Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authorisation #100

Closed
zorgick opened this issue Jan 10, 2020 · 1 comment
Closed

Authorisation #100

zorgick opened this issue Jan 10, 2020 · 1 comment

Comments

@zorgick
Copy link

zorgick commented Jan 10, 2020

So I'm really into your solution for ssr! For me it is an eye opener on many aspects of a great code design, I found a lot of brilliant ideas in your code. But what bothers me is the authorisation process. Exactly, how to implement it and to benefit from ssr?

The problem is that if we request any url, the requested layout will be rendered (the one in the serverRender.tsx file). The react-router works great without authorisation, building the markup for a necessary url. But as for authorisation the markup is pretty simple, and personally I won't profit from server rendering the view of the login page.

So my question is how to do it properly?
Let's imagine a user that makes a request to my home page. The server sees that he has no auth data in cookies, so a PrivateRouter redirects to Login page and the server finishes the render process by sending pre-rendered html login page with js bundled code. Next on the client after authorisation user proceeds to the home page (main heavy js chuck), making additional requests for all other content (js chunks, styles, svgs), that I ideally would like to pre-render on the server, but the react-router will handle the routing because it is what SPAs do. Maybe there is a solution you know, how to do it better, because now I can't come up with the best option.

@manuelbieh
Copy link
Owner

manuelbieh commented Jan 10, 2020

Thanks for the compliment! 🙌

Authorization is a really complex topic. Probably too complex to explain it here in all details. Last time I built a React app with authentication and SSR I cheated a little bit. I created a cookieAuthentication middleware in Express that has read the accessToken of a user from a cookie, validated it against a validation endpoint and wrote the accessToken to the Redux store on the server side. That looked like this:

import { ClientAuthenticatedRequest } from 'shared/services/api';
import { accessTokenSuccess, accessTokenInvalidate } from 'shared/store/auth/actions';

export default async (req, res, next) => {
    try {
        const { accessToken } = req.cookies;
        if (accessToken) {
            const { data: isValid } = await ClientAuthenticatedRequest().get(
                `/oauth2/validate/${accessToken}`
            );

            if (isValid) {
                req.store.dispatch(accessTokenSuccess(accessToken));
                return next();
            }

            req.store.dispatch(accessTokenInvalidate());
        }
    } catch (err) {}

    return next();
};

My App.js then looked similar to this:

return props.accessToken 
  ? <Private /> 
  : <Public />;

Here I also cheated and didn't achieve a "100% universal JS rate" but instead I used Express middlewares which used the accessToken from the cookie and pre-populated the Redux store with the values from server side API calls along with a timestamp of when the data was received. On the client I have then checked if the data is stale (> 30 seconds old) and only made another requests if the server side data was outdated. The Redux actions were universal and worked similar on both client and server side though.

For example, the Express middleware when hitting /users looked like this:

export default async (req, res, next) => {
    const { accessToken } = req.cookies;
    if (!accessToken) {
        return next();
    }

    await req.store.dispatch(fetchUsers());

    return next();
};

fetchUsers was a universal function implemented with Axios. It fetched all the users from the API, pre-populated the Redux store before React rendered the <Userlist /> component. The component was connected to Redux where all the necessary data was already present at render time.

I assume server side rendering with asynchronous and probably also authenticated requests will be much easier once Suspense is fully implemented. Currently the server side part is not fully implemented yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants