jaredpalmer / razzle

✨ Create server-rendered universal JavaScript applications with no configuration
https://razzlejs.org
MIT License
11.09k stars 870 forks source link

Example with http-only cookies #1407

Open rwehresmann opened 3 years ago

rwehresmann commented 3 years ago

❓Question

I'm migrating my SPA to razzle, and some changes were needed to make it work with SSR. I was saving the JWT for authentication in the localStorage, this's what I had:

const PrivateRoute = (props) => {
  const token = localStorage.getItem(BrowserStorageKeyEnum.Jwt);
  let isTokenExpired = false;

  if (token) {
    const decodedJwt = jwt.decode(token);
    const currentTimeInSeconds = moment(Math.floor(Date.now() / 1000));
    const expirationTimeInSeconds = decodedJwt.exp - currentTimeInSeconds;

    if (expirationTimeInSeconds <= 0) isTokenExpired = true;
  }

  if (token && !isTokenExpired) {
    return <Route {...props} />;
  } else {
    return (
      <Redirect
        to={{
          pathname: RouteEnum.Login,
          state: { from: props.location }
        }}
      />
    );
  }
};

export default PrivateRoute;

But this needs to change now. The alternative that I saw was to use http-only cookies, but I couldn't figure out how to implement this. If someone could give me some directions, I would appreciate it.

fivethreeo commented 3 years ago

You need to create a express route that checks if the token is expired. I would make two react providers that checks for expired token, one for server-side and one for client side. Server-side provider checks the token in the express view that renders the react app before rendering. Client-side provider checks the token by doing a request to the route that checks if the token is expired.

fivethreeo commented 3 years ago

https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/

fivethreeo commented 3 years ago

Would be cool to make a full featured example with keycloak and hasura.

fivethreeo commented 3 years ago

I may add it here later

https://github.com/fivethreeo/razzle-with-docker-nginx

rwehresmann commented 3 years ago

Thanks, it would be helpful! I'm having a hard time to figure it out how to implement the providers :sweat_smile:

fivethreeo commented 3 years ago

I have set up a deployment with keycloak now. https://github.com/fivethreeo/razzle-with-docker-nginx

But not quite sure how to do jwt with it.

fivethreeo commented 3 years ago

If you can make your spa work in this example I can do the changes to it to make it ssr capable. You may want to modify nginx/default_dev.conf.template and remove the hasura and keycloak parts so you can run just your razzle build.

fivethreeo commented 3 years ago

This is a good start:

https://github.com/jkasun/stack-abuse-express-jwt/blob/master/auth.js

Just make the refresh token a http-only cookie.

rwehresmann commented 3 years ago

Thanks @fivethreeo , I'll give it a try as soon as possible. I actually found a not so pretty workaround for my PrivateRoute for now: check if is the server rendering, and if it is, just return null and wait for the client rendering. Of course, the route in question will not work for SEO, but in this case, no worries for me, because the private routes don't aim this.

fivethreeo commented 2 years ago

Not done yet but I have a work in progress app here: https://github.com/fivethreeo/razzle-prisma-graphqlcodegen-urql-formik-jwt

fivethreeo commented 2 years ago

https://github.com/fivethreeo/razzle-prisma-graphqlcodegen-urql-formik-jwt

Is working with http only cookies.

GuyFawkesII commented 2 years ago

fivethreeo What configurations should I use to keep using javascript insted of typescript ? Thank you

fivethreeo commented 2 years ago

This example uses graphql codegen and prisma so not sure if it is possible.

GuyFawkesII commented 2 years ago

What options do you think I have , because 70% of the project has been done in JS .

fivethreeo commented 2 years ago

Just remove graphql-codgen typescript plugins. Make prisma output a js client instead of typescript. Remove types from code and rename ts(x) to js(x).