diff --git a/.gitignore b/.gitignore index f587703b..e3dd0cba 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ sentry.properties .npmrc contentful.graphql api.graphql +ssl # Cypress diff --git a/README.md b/README.md index 85d530d2..a33be806 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,16 @@ To start developing start the next.js development script and open http://localho ```sh yarn dev ``` + +### Develop with SSL + +* Create a SSL certificate by executing the following command on the CLI in the project root which will generate a folder `ssl` with two files: + +```bash +mkdir ssl && openssl req -x509 -out ssl/localhost.crt -keyout ssl/localhost.key \ + -newkey rsa:2048 -nodes -sha256 \ + -subj '/CN=localhost' -extensions EXT -config <( \ + printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth") +``` + +* Start the dev-server: `yarn dev:ssl` diff --git a/package.json b/package.json index 1ad07943..e08842f4 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "cypress:run-local:chrome": "npx cypress@8.3.0 run --browser chrome", "cypress:open": "npx cypress@8.3.0 open", "dev": "next", + "dev:ssl": "PORT=3000 NODE_TLS_REJECT_UNAUTHORIZED=0 node ./server.js", "graphql:setup": "node prepare/graphql-setup.js", "graphql:types": "graphql-codegen --require dotenv/config --config codegen.yml", "lint": "yarn tsc:noEmit && yarn xo && yarn stylelint && next lint", diff --git a/server.js b/server.js new file mode 100644 index 00000000..f391aef9 --- /dev/null +++ b/server.js @@ -0,0 +1,32 @@ +/** + * A server that can be used for local SSL testing + */ +const https = require("https"); +const fs = require("fs"); +const { parse } = require("url"); + +const next = require("next"); +const port = parseInt(process.env.PORT) || 3001; +const dev = true; +const app = next({ + dev, + dir: __dirname, +}); +const handle = app.getRequestHandler(); + +var options = { + key: fs.readFileSync(`./ssl/localhost.key`), + cert: fs.readFileSync(`./ssl/localhost.crt`), +}; + +app.prepare().then(() => { + https + .createServer(options, (req, res) => { + const parsedUrl = parse(req.url, true); + handle(req, res, parsedUrl); + }) + .listen(port, err => { + if (err) throw err; + console.log(`> Ready on localhost:${port}`); + }); +}); diff --git a/src/ions/services/apollo/client.ts b/src/ions/services/apollo/client.ts index f23fab8e..3cc56cba 100644 --- a/src/ions/services/apollo/client.ts +++ b/src/ions/services/apollo/client.ts @@ -104,7 +104,10 @@ export const addApolloState = ( }; export const useApollo = (pageProps: PageProps) => - useMemo(() => initializeApollo(pageProps[APOLLO_STATE_PROP_NAME]), [pageProps]); + useMemo( + () => initializeApollo(pageProps[APOLLO_STATE_PROP_NAME], pageProps.cookie), + [pageProps] + ); export const useContentfulQuery = < TData extends Record = any, diff --git a/src/pages/api/auth/[...nextauth]/index.tsx b/src/pages/api/auth/[...nextauth]/index.tsx index 2c1d2bdf..c117756e 100644 --- a/src/pages/api/auth/[...nextauth]/index.tsx +++ b/src/pages/api/auth/[...nextauth]/index.tsx @@ -8,6 +8,10 @@ import Providers from "next-auth/providers"; import process from "process"; const prisma = new PrismaClient(); +const useSecureCookies = process.env.NEXTAUTH_URL.startsWith("https://"); +const cookiePrefix = useSecureCookies ? "__Secure-" : ""; +const hostName = new URL(process.env.NEXTAUTH_URL).hostname; +const domain = hostName === "localhost" ? hostName : "." + hostName; /* eslint-disable new-cap */ export default NextAuth({ @@ -34,6 +38,31 @@ export default NextAuth({ adapter: Adapters.Prisma.Adapter({ prisma }), secret: process.env.SECRET, + cookies: { + // Allow cookies on sub-domains (like api.dekk.app) by adding + // a . infront of the hostname (like .dekk.app) + sessionToken: { + name: `${cookiePrefix}next-auth.session-token`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + secure: useSecureCookies, + domain, + }, + }, + csrfToken: { + name: `${cookiePrefix}next-auth.csrf-token`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + secure: useSecureCookies, + domain, + }, + }, + }, + // @TODO: Use https://github.com/praveenweb/next-auth-hasura-example/blob/main/pages/api/auth/%5B...nextauth%5D.js // to make sure the JWT is send in the session diff --git a/src/pages/auth/error/index.tsx b/src/pages/auth/error/index.tsx index 446ad5ca..318cb116 100644 --- a/src/pages/auth/error/index.tsx +++ b/src/pages/auth/error/index.tsx @@ -31,6 +31,7 @@ export const getServerSideProps: GetServerSideProps = async context = session, locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/pages/auth/signin/index.tsx b/src/pages/auth/signin/index.tsx index d2a90418..c4c0ced0 100644 --- a/src/pages/auth/signin/index.tsx +++ b/src/pages/auth/signin/index.tsx @@ -31,6 +31,7 @@ export const getServerSideProps: GetServerSideProps = async context = session, locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/pages/auth/signout/index.tsx b/src/pages/auth/signout/index.tsx index 6aeaf83c..bba1c14e 100644 --- a/src/pages/auth/signout/index.tsx +++ b/src/pages/auth/signout/index.tsx @@ -31,6 +31,7 @@ export const getServerSideProps: GetServerSideProps = async context = session, locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/pages/auth/verify-request/index.tsx b/src/pages/auth/verify-request/index.tsx index e733fcae..9558d959 100644 --- a/src/pages/auth/verify-request/index.tsx +++ b/src/pages/auth/verify-request/index.tsx @@ -21,6 +21,7 @@ export const getServerSideProps: GetServerSideProps = async context = session: await getSession(context), locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 2cc92c16..ba430265 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -24,6 +24,7 @@ export const getServerSideProps: GetServerSideProps = async context = session: await getSession(context), locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/pages/legal/cookie-policy/index.tsx b/src/pages/legal/cookie-policy/index.tsx index b5735149..5fe97e91 100644 --- a/src/pages/legal/cookie-policy/index.tsx +++ b/src/pages/legal/cookie-policy/index.tsx @@ -133,6 +133,7 @@ export const getServerSideProps: GetServerSideProps = async context = providers: await getProviders(), locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/pages/wishlist/index.tsx b/src/pages/wishlist/index.tsx index 874cdfb2..c7e3a7ba 100644 --- a/src/pages/wishlist/index.tsx +++ b/src/pages/wishlist/index.tsx @@ -29,6 +29,7 @@ export const getServerSideProps: GetServerSideProps = async context = providers: await getProviders(), locale: context.locale, consent: getServerSideCookieConsent(context), + cookie: context.req.headers.cookie || null, }, }); }; diff --git a/src/types/index.ts b/src/types/index.ts index 624eac25..8132534c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -25,6 +25,7 @@ export interface PageProps { locale: string; [APOLLO_STATE_PROP_NAME]?: NormalizedCacheObject; consent: CookieConsent | null; + cookie: string; } export interface StaticPageProps { diff --git a/xo.config.js b/xo.config.js index cd8f3813..122ee452 100644 --- a/xo.config.js +++ b/xo.config.js @@ -8,6 +8,7 @@ module.exports = { "public", "migrations", "*.config.js", + "server.js", "next-env.d.ts", "types/*.d.ts", "src/types/contentful-api.ts",