From 6a0c77ae59ba9c36fdd65f1091ecb705eadd82ec Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Mon, 7 Nov 2022 17:38:49 +0100 Subject: [PATCH] Split server-only and client-only code in authenticated-container.tsx (#609) --- .../[projectId]/[targetId]/explorer.tsx | 3 +- .../[targetId]/explorer/[typename].tsx | 3 +- .../[projectId]/[targetId]/history.tsx | 3 +- .../[targetId]/history/[versionId].tsx | 2 +- .../[orgId]/[projectId]/[targetId]/index.tsx | 3 +- .../[projectId]/[targetId]/laboratory.tsx | 3 +- .../[projectId]/[targetId]/operations.tsx | 3 +- .../[projectId]/[targetId]/settings.tsx | 3 +- .../app/pages/[orgId]/[projectId]/alerts.tsx | 3 +- .../app/pages/[orgId]/[projectId]/index.tsx | 3 +- .../pages/[orgId]/[projectId]/settings.tsx | 3 +- packages/web/app/pages/[orgId]/index.tsx | 3 +- packages/web/app/pages/[orgId]/members.tsx | 3 +- packages/web/app/pages/[orgId]/settings.tsx | 3 +- .../app/pages/[orgId]/subscription/index.tsx | 3 +- .../app/pages/[orgId]/subscription/manage.tsx | 3 +- packages/web/app/pages/index.tsx | 8 +-- packages/web/app/pages/join/[inviteCode].tsx | 3 +- packages/web/app/pages/manage/index.tsx | 3 +- packages/web/app/pages/settings.tsx | 3 +- .../components/authenticated-container.tsx | 66 ------------------- .../api/extract-access-token-from-request.ts | 2 +- packages/web/app/src/lib/supertokens/guard.ts | 66 +++++++++++++++++++ ...party-email-password-node-okta-provider.ts | 2 +- 24 files changed, 108 insertions(+), 92 deletions(-) create mode 100644 packages/web/app/src/lib/supertokens/guard.ts diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer.tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer.tsx index 795f207ea..aaac225be 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer.tsx @@ -1,7 +1,8 @@ import { ReactElement } from 'react'; import { gql, useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { TargetLayout } from '@/components/layouts'; import { SchemaExplorerFilter } from '@/components/target/explorer/filter'; import { GraphQLObjectTypeComponent } from '@/components/target/explorer/object-type'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer/[typename].tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer/[typename].tsx index b784533ec..938c6cc1e 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer/[typename].tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/explorer/[typename].tsx @@ -1,7 +1,8 @@ import { ReactElement } from 'react'; import { DocumentType, gql, useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { TargetLayout } from '@/components/layouts'; import { GraphQLEnumTypeComponent, diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history.tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history.tsx index 6bf373665..5905a9c5b 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history.tsx @@ -5,7 +5,8 @@ import { VscBug, VscDiff, VscListFlat } from 'react-icons/vsc'; import reactStringReplace from 'react-string-replace'; import { useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { Label } from '@/components/common'; import { TargetLayout } from '@/components/layouts'; import { diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history/[versionId].tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history/[versionId].tsx index 990851c4b..ec3eaa49f 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history/[versionId].tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/history/[versionId].tsx @@ -1,4 +1,4 @@ -import { withSessionProtection } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import HistoryPage from '../history'; export const getServerSideProps = withSessionProtection(); diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/index.tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/index.tsx index 89ea0343e..6036184ad 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/index.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/index.tsx @@ -13,7 +13,8 @@ import { VscClose } from 'react-icons/vsc'; import { gql, useMutation, useQuery } from 'urql'; import { useDebouncedCallback } from 'use-debounce'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { TargetLayout } from '@/components/layouts'; import { MarkAsValid } from '@/components/target/history/MarkAsValid'; import { Button, DataWrapper, GraphQLBlock, noSchema, Title } from '@/components/v2'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/laboratory.tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/laboratory.tsx index a575ea74a..32ebfb618 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/laboratory.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/laboratory.tsx @@ -2,7 +2,8 @@ import { ReactElement, useCallback, useState } from 'react'; import { createGraphiQLFetcher } from '@graphiql/toolkit'; import { GraphiQL } from 'graphiql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { TargetLayout } from '@/components/layouts'; import { Button, Title } from '@/components/v2'; import { HiveLogo, Link2Icon } from '@/components/v2/icon'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/operations.tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/operations.tsx index 27cc26f6b..8ab8d67fd 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/operations.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/operations.tsx @@ -6,7 +6,8 @@ import { formatISO, subDays, subHours, subMinutes } from 'date-fns'; import { VscChevronDown } from 'react-icons/vsc'; import { useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { TargetLayout } from '@/components/layouts'; import { OperationsFilterTrigger } from '@/components/target/operations/Filters'; import { OperationsList } from '@/components/target/operations/List'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/settings.tsx b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/settings.tsx index 8e2781f4f..13bfaaab7 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/[targetId]/settings.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/[targetId]/settings.tsx @@ -6,7 +6,8 @@ import { useFormik } from 'formik'; import { gql, useMutation, useQuery } from 'urql'; import * as Yup from 'yup'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { TargetLayout } from '@/components/layouts'; import { SchemaEditor } from '@/components/schema-editor'; import { Button, Card, Checkbox, Heading, Input, Switch, Table, Tag, TimeAgo, Title } from '@/components/v2'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/alerts.tsx b/packages/web/app/pages/[orgId]/[projectId]/alerts.tsx index 05dc1b11a..aae111e10 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/alerts.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/alerts.tsx @@ -1,7 +1,8 @@ import { ReactElement, useCallback, useState } from 'react'; import { useMutation, useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { ProjectLayout } from '@/components/layouts'; import { Button, Card, Checkbox, Heading, Table, Tag, Title } from '@/components/v2'; import { CreateAlertModal, CreateChannelModal } from '@/components/v2/modals'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/index.tsx b/packages/web/app/pages/[orgId]/[projectId]/index.tsx index 6d6018c13..81835490c 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/index.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/index.tsx @@ -3,7 +3,8 @@ import NextLink from 'next/link'; import clsx from 'clsx'; import { useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { ProjectLayout } from '@/components/layouts'; import { Activities, Badge, Button, Card, DropdownMenu, EmptyList, Heading, TimeAgo, Title } from '@/components/v2'; import { LinkIcon, MoreIcon, SettingsIcon } from '@/components/v2/icon'; diff --git a/packages/web/app/pages/[orgId]/[projectId]/settings.tsx b/packages/web/app/pages/[orgId]/[projectId]/settings.tsx index da8460ef8..e6d62c54c 100644 --- a/packages/web/app/pages/[orgId]/[projectId]/settings.tsx +++ b/packages/web/app/pages/[orgId]/[projectId]/settings.tsx @@ -4,7 +4,8 @@ import { useFormik } from 'formik'; import { gql, useMutation, useQuery } from 'urql'; import * as Yup from 'yup'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { ProjectLayout } from '@/components/layouts'; import { ExternalCompositionSettings } from '@/components/project/settings/external-composition'; import { Button, Card, Heading, Input, Link, Select, Spinner, Tag, Title } from '@/components/v2'; diff --git a/packages/web/app/pages/[orgId]/index.tsx b/packages/web/app/pages/[orgId]/index.tsx index c6daf9d38..d1c92988a 100644 --- a/packages/web/app/pages/[orgId]/index.tsx +++ b/packages/web/app/pages/[orgId]/index.tsx @@ -3,7 +3,8 @@ import NextLink from 'next/link'; import { onlyText } from 'react-children-utilities'; import { useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { OrganizationLayout } from '@/components/layouts'; import { Activities, Button, Card, DropdownMenu, EmptyList, Heading, Skeleton, TimeAgo, Title } from '@/components/v2'; import { getActivity } from '@/components/v2/activities'; diff --git a/packages/web/app/pages/[orgId]/members.tsx b/packages/web/app/pages/[orgId]/members.tsx index c167c7b22..ec880818b 100644 --- a/packages/web/app/pages/[orgId]/members.tsx +++ b/packages/web/app/pages/[orgId]/members.tsx @@ -4,7 +4,8 @@ import { useFormik } from 'formik'; import { DocumentType, gql, useMutation, useQuery } from 'urql'; import * as Yup from 'yup'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { OrganizationLayout } from '@/components/layouts'; import { Avatar, Button, Card, Checkbox, DropdownMenu, Input, Title } from '@/components/v2'; import { CopyIcon, KeyIcon, MoreIcon, SettingsIcon, TrashIcon } from '@/components/v2/icon'; diff --git a/packages/web/app/pages/[orgId]/settings.tsx b/packages/web/app/pages/[orgId]/settings.tsx index 3fc57729c..7b2c9bfef 100644 --- a/packages/web/app/pages/[orgId]/settings.tsx +++ b/packages/web/app/pages/[orgId]/settings.tsx @@ -3,7 +3,8 @@ import { useFormik } from 'formik'; import { gql, useMutation, useQuery } from 'urql'; import * as Yup from 'yup'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { OrganizationLayout } from '@/components/layouts'; import { OIDCIntegrationSection } from '@/components/organization/settings/oidc-integration-section'; import { Button, Card, Heading, Input, Spinner, Tag, Title } from '@/components/v2'; diff --git a/packages/web/app/pages/[orgId]/subscription/index.tsx b/packages/web/app/pages/[orgId]/subscription/index.tsx index a7f55a74d..75987d453 100644 --- a/packages/web/app/pages/[orgId]/subscription/index.tsx +++ b/packages/web/app/pages/[orgId]/subscription/index.tsx @@ -3,7 +3,8 @@ import dynamic from 'next/dynamic'; import { Stat, StatHelpText, StatLabel, StatNumber } from '@chakra-ui/react'; import { endOfMonth, startOfMonth } from 'date-fns'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { OrganizationLayout } from '@/components/layouts'; import { BillingView } from '@/components/organization/billing/Billing'; import { CurrencyFormatter } from '@/components/organization/billing/helpers'; diff --git a/packages/web/app/pages/[orgId]/subscription/manage.tsx b/packages/web/app/pages/[orgId]/subscription/manage.tsx index 2f46d0ef5..b7be894e9 100644 --- a/packages/web/app/pages/[orgId]/subscription/manage.tsx +++ b/packages/web/app/pages/[orgId]/subscription/manage.tsx @@ -4,7 +4,8 @@ import { Stat, StatHelpText, StatLabel, StatNumber } from '@chakra-ui/react'; import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js'; import { useMutation, useQuery } from 'urql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { Section } from '@/components/common'; import { DataWrapper, QueryError } from '@/components/common/DataWrapper'; import { OrganizationLayout } from '@/components/layouts'; diff --git a/packages/web/app/pages/index.tsx b/packages/web/app/pages/index.tsx index 4cc8f1e41..41e169de7 100644 --- a/packages/web/app/pages/index.tsx +++ b/packages/web/app/pages/index.tsx @@ -6,7 +6,8 @@ import { DataWrapper } from '@/components/common/DataWrapper'; import { useRouteSelector } from '@/lib/hooks/use-route-selector'; import Cookies from 'cookies'; import { LAST_VISITED_ORG_KEY } from '@/constants'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; import { createTRPCClient } from '@trpc/client'; import type { InternalApi } from '@hive/server'; import { env } from '@/env/backend'; @@ -14,10 +15,7 @@ import { NextApiRequest, NextApiResponse } from 'next'; import Session from 'supertokens-node/recipe/session'; import { writeLastVisitedOrganization } from '@/lib/cookies'; -export async function getSuperTokensUserIdFromRequest( - req: NextApiRequest, - res: NextApiResponse -): Promise { +async function getSuperTokensUserIdFromRequest(req: NextApiRequest, res: NextApiResponse): Promise { const session = await Session.getSession(req, res, { sessionRequired: false }); return session?.getUserId() ?? null; } diff --git a/packages/web/app/pages/join/[inviteCode].tsx b/packages/web/app/pages/join/[inviteCode].tsx index b41ffc8b9..66f35e68c 100644 --- a/packages/web/app/pages/join/[inviteCode].tsx +++ b/packages/web/app/pages/join/[inviteCode].tsx @@ -7,7 +7,8 @@ import { Button } from '@chakra-ui/react'; import { Title } from '@/components/common'; import { DataWrapper } from '@/components/common/DataWrapper'; import { useNotifications } from '@/lib/hooks/use-notifications'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; const Center = tw.div`w-full h-full flex flex-row items-center justify-center`; diff --git a/packages/web/app/pages/manage/index.tsx b/packages/web/app/pages/manage/index.tsx index b3524e4c7..304d6fe3f 100644 --- a/packages/web/app/pages/manage/index.tsx +++ b/packages/web/app/pages/manage/index.tsx @@ -5,7 +5,8 @@ import { VscChevronDown } from 'react-icons/vsc'; import { AdminStats, Filters } from '@/components/admin/AdminStats'; import { Page } from '@/components/common'; import { DATE_RANGE_OPTIONS } from '@/components/common/TimeFilter'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; function Manage() { const [last, setLast] = React.useState(30); diff --git a/packages/web/app/pages/settings.tsx b/packages/web/app/pages/settings.tsx index eef70ea6e..0c8d0af13 100644 --- a/packages/web/app/pages/settings.tsx +++ b/packages/web/app/pages/settings.tsx @@ -4,7 +4,8 @@ import * as Yup from 'yup'; import { Avatar, Button, SubHeader, Heading, Input, Tabs, Title } from '@/components/v2'; import { MeDocument } from '@/graphql'; -import { authenticated, withSessionProtection } from '@/components/authenticated-container'; +import { authenticated } from '@/components/authenticated-container'; +import { withSessionProtection } from '@/lib/supertokens/guard'; const UpdateMeMutation = gql(/* GraphQL */ ` mutation updateMe($input: UpdateMeInput!) { diff --git a/packages/web/app/src/components/authenticated-container.tsx b/packages/web/app/src/components/authenticated-container.tsx index d20418e0c..7f684ba48 100644 --- a/packages/web/app/src/components/authenticated-container.tsx +++ b/packages/web/app/src/components/authenticated-container.tsx @@ -1,10 +1,7 @@ import React from 'react'; import type { ReactNode } from 'react'; -import { captureException } from '@sentry/nextjs'; import { Header } from './v2'; import { HiveStripeWrapper } from '@/lib/billing/stripe'; -import type { GetServerSideProps } from 'next'; -import { SessionContainerInterface } from 'supertokens-node/lib/build/recipe/session/types'; import Session, { SessionAuth } from 'supertokens-auth-react/recipe/session'; import { useRouter } from 'next/router'; @@ -57,66 +54,3 @@ export const authenticated = ); }; - -export const serverSidePropsSessionHandling = async (context: Parameters[0]) => { - const { backendConfig } = await import('@/config/supertokens/backend'); - const SupertokensNode = await import('supertokens-node'); - const Session = await import('supertokens-node/recipe/session'); - SupertokensNode.init(backendConfig()); - let session: SessionContainerInterface | undefined; - - try { - session = await Session.getSession(context.req, context.res, { sessionRequired: false }); - // TODO: better error decoding :) - } catch (err: any) { - // Check whether the email is already verified. - // If it is not then we need to redirect to the email verification page - which will trigger the email sending. - if (err.type === Session.Error.INVALID_CLAIMS) { - return { - redirect: { - destination: '/auth/verify-email', - permanent: false, - }, - }; - } - - if (err.type === Session.Error.TRY_REFRESH_TOKEN) { - return { props: { fromSupertokens: 'needs-refresh' } }; - } - captureException(err); - throw err; - } - - if (session === undefined) { - return { - redirect: { - destination: `/auth?redirectToPath=${encodeURIComponent(context.resolvedUrl)}`, - permanent: false, - }, - }; - } - - return null; -}; - -function defaultHandler() { - return Promise.resolve({ props: {} }); -} - -/** - * Utility for protecting a server side props function with session handling. - * Redirects user to the login page in case there is no session. - */ -export function withSessionProtection(handlerFn: GetServerSideProps = defaultHandler) { - const getServerSideProps: GetServerSideProps = async context => { - const result = await serverSidePropsSessionHandling(context); - - if (result) { - return result; - } - - return handlerFn(context); - }; - - return getServerSideProps; -} diff --git a/packages/web/app/src/lib/api/extract-access-token-from-request.ts b/packages/web/app/src/lib/api/extract-access-token-from-request.ts index a675cf3e7..3e6907ae9 100644 --- a/packages/web/app/src/lib/api/extract-access-token-from-request.ts +++ b/packages/web/app/src/lib/api/extract-access-token-from-request.ts @@ -3,7 +3,7 @@ import { superTokensNextWrapper } from 'supertokens-node/nextjs'; import { verifySession } from 'supertokens-node/recipe/session/framework/express'; import type { NextApiRequest, NextApiResponse } from 'next'; import { backendConfig } from '@/config/supertokens/backend'; -import { SessionContainerInterface } from 'supertokens-node/lib/build/recipe/session/types'; +import type { SessionContainerInterface } from 'supertokens-node/lib/build/recipe/session/types'; supertokens.init(backendConfig()); diff --git a/packages/web/app/src/lib/supertokens/guard.ts b/packages/web/app/src/lib/supertokens/guard.ts new file mode 100644 index 000000000..d975b8233 --- /dev/null +++ b/packages/web/app/src/lib/supertokens/guard.ts @@ -0,0 +1,66 @@ +import { captureException } from '@sentry/nextjs'; +import type { GetServerSideProps } from 'next'; +import type { SessionContainerInterface } from 'supertokens-node/lib/build/recipe/session/types'; + +export const serverSidePropsSessionHandling = async (context: Parameters[0]) => { + const { backendConfig } = await import('@/config/supertokens/backend'); + const SupertokensNode = await import('supertokens-node'); + const Session = await import('supertokens-node/recipe/session'); + SupertokensNode.init(backendConfig()); + let session: SessionContainerInterface | undefined; + + try { + session = await Session.getSession(context.req, context.res, { sessionRequired: false }); + // TODO: better error decoding :) + } catch (err: any) { + // Check whether the email is already verified. + // If it is not then we need to redirect to the email verification page - which will trigger the email sending. + if (err.type === Session.Error.INVALID_CLAIMS) { + return { + redirect: { + destination: '/auth/verify-email', + permanent: false, + }, + }; + } + + if (err.type === Session.Error.TRY_REFRESH_TOKEN) { + return { props: { fromSupertokens: 'needs-refresh' } }; + } + captureException(err); + throw err; + } + + if (session === undefined) { + return { + redirect: { + destination: `/auth?redirectToPath=${encodeURIComponent(context.resolvedUrl)}`, + permanent: false, + }, + }; + } + + return null; +}; + +function defaultHandler() { + return Promise.resolve({ props: {} }); +} + +/** + * Utility for protecting a server side props function with session handling. + * Redirects user to the login page in case there is no session. + */ +export function withSessionProtection(handlerFn: GetServerSideProps = defaultHandler) { + const getServerSideProps: GetServerSideProps = async context => { + const result = await serverSidePropsSessionHandling(context); + + if (result) { + return result; + } + + return handlerFn(context); + }; + + return getServerSideProps; +} diff --git a/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts b/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts index c27350905..e0480b34b 100644 --- a/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts +++ b/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts @@ -1,5 +1,5 @@ import { env } from '@/env/backend'; -import { TypeProvider } from 'supertokens-node/recipe/thirdpartyemailpassword'; +import type { TypeProvider } from 'supertokens-node/recipe/thirdpartyemailpassword'; import zod from 'zod'; type OktaConfig = Exclude;