From 386c85546308b3f7bb5c5af855506b88fe0364ba Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 12 Oct 2023 17:57:35 +0530 Subject: [PATCH 01/27] Add next app dir docs for thirdpartyemailpassword --- .../nextjs/app-directory/init.mdx | 437 ++++++++++++++++++ .../nextjs/app-directory/next-steps.mdx | 35 ++ .../nextjs/app-directory/protecting-route.mdx | 152 ++++++ .../app-directory/session-verification.mdx | 255 ++++++++++ .../app-directory/setting-up-backend.mdx | 80 ++++ .../app-directory/setting-up-frontend.mdx | 72 +++ v2/thirdpartyemailpassword/sidebars.js | 36 +- 7 files changed, 1058 insertions(+), 9 deletions(-) create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx new file mode 100644 index 000000000..81dbbfa05 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -0,0 +1,437 @@ +--- +id: init +title: 1. Configuration +hide_title: true +show_ui_switcher: true +--- + + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import {Question, Answer}from "/src/components/question" +import AppInfoForm from "/src/components/appInfoForm" +import CoreInjector from "/src/components/coreInjector" +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + +# 1. Configuration + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + websiteDomain: "^{form_websiteDomain}", + apiBasePath: "^{form_apiBasePath}", + websiteBasePath: "^{form_websiteBasePath}" +} + +``` + + + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + apiBasePath: "^{form_apiBasePath}", +} + +``` + + + + + + + + + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import ThirdPartyEmailPasswordReact from 'supertokens-auth-react/recipe/thirdpartyemailpassword' +import SessionReact from 'supertokens-auth-react/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyEmailPasswordReact.init({ + signInAndUpFeature: { + providers: [ + ThirdPartyEmailPasswordReact.Google.init(), + ThirdPartyEmailPasswordReact.Facebook.init(), + ThirdPartyEmailPasswordReact.Github.init(), + ThirdPartyEmailPasswordReact.Apple.init(), + ], + }, + }), + SessionReact.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import ThirdPartyEmailPasswordWebJs from 'supertokens-web-js/recipe/thirdpartyemailpassword' +import SessionWebJs from 'supertokens-web-js/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyEmailPasswordWebJs.init(), + SessionWebJs.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 5) Create a backend config function + + + + + +```tsx title="/config/backendConfig.ts" +import SuperTokens from "supertokens-node"; +import ThirdPartyEmailPasswordNode from 'supertokens-node/recipe/thirdpartyemailpassword' +import SessionNode from 'supertokens-node/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import { TypeInput } from "supertokens-node/types"; + +export const backendConfig = (): TypeInput => { + return { + framework: "express", + supertokens: { + ^{coreInjector_connection_uri_comment} + connectionURI: ^{coreInjector_uri} + ^{coreInjector_api_key_commented}apiKey: ^{coreInjector_api_key}, + }, + appInfo, + recipeList: [ + ThirdPartyEmailPasswordNode.init({ + // We have provided you with development keys which you can use for testing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + providers: [{ + config: { + thirdPartyId: "google", + clients: [{ + clientId: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + clientSecret: "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" + }] + } + }, { + config: { + thirdPartyId: "github", + clients: [{ + clientId: "467101b197249757c71f", + clientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd" + }] + } + }, { + config: { + thirdPartyId: "apple", + clients: [{ + clientId: "4398792-io.supertokens.example.service", + additionalConfig: { + keyId: "7M48Y4RYDL", + privateKey: + "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + teamId: "YWQCXGJRJL", + } + }] + } + }], + }), + SessionNode.init(), + ], + isInServerlessEnv: true, + } +} + +let initialized = false; +// This function is used in your APIs to make sure SuperTokens is initialised +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} +``` + +`ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. + +**When you want to generate your own keys**, please refer to the corresponding documentation to get your client ids and client secrets for each of the below providers: + +
+Google + +- Generate your client ID and secret by following the [docs here](https://support.google.com/cloud/answer/6158849?hl=en) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/google` + +
+ +
+Github + +- Generate your client ID and secret by following the [docs here](https://docs.github.com/en/developers/apps/creating-an-oauth-app) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/github` + +
+ +
+Facebook + +- Generate your client ID and secret by following the [docs here](https://developers.facebook.com/docs/development/create-an-app) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/facebook` + +:::info Note +Make sure to enable `https` to be able to use the test users of the Facebook app. On `http://localhost`, the login flow can be verified only with the app's admin user. +::: + +
+ +
+Apple + +- Generate your client ID and secret by following [this article](https://medium.com/identity-beyond-borders/how-to-configure-sign-in-with-apple-77c61e336003) +- Set the authorisation callback URL to `^{form_apiDomain}^{form_apiBasePathForCallbacks}/callback/apple`. Note that Apple doesn't allow `localhost` in the URL. So if you are in dev mode, you can use the dev keys we have provided above. + +
+ +
+ +
+ + + + + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component + +- Create a client component `/app/components/supertokensProvider.tsx`. This file will initialise SuperTokens and wrap its children with the `SuperTokensWrapper` component +- Modify the `/app/layout.tsx` file to use the `SuperTokensProvider` component. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/layout.tsx) + +```tsx title="/app/components/supertokensProvider.tsx" +'use client'; +import React from 'react'; +import { SuperTokensWrapper } from 'supertokens-auth-react'; +import SuperTokensReact from 'supertokens-auth-react'; +import { frontendConfig, setRouter } from '../config/frontend'; +import { usePathname, useRouter } from 'next/navigation'; + +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + SuperTokensReact.init(frontendConfig()); +} + +export const SuperTokensProvider: React.FC> = ({ + children, +}) => { + setRouter(useRouter(), usePathname() || window.location.pathname); + + return {children}; +}; +``` + +```tsx title="/app/layout.tsx" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { SuperTokensProvider } from './components/supertokensProvider' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions + +- Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). + + +```tsx title="/pages/_app.ts" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import SuperTokensWebJs from 'supertokens-web-js' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) + } + + return ( + + {children} + + ) +} +``` + + + + + + \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx new file mode 100644 index 000000000..25c925bd5 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx @@ -0,0 +1,35 @@ +--- +id: next-steps +title: 6. Next steps +hide_title: true +--- + + + + +import {Question, Answer}from "/src/components/question" + +# 6. Next steps + +## Setting up the core and database + { + return ( + Are you using https://try.supertokens.com as the connection URI in the init function? + ) + }}> + + +You need to now setup an instance of the SuperTokens core for your app (that your backend should connect to). You have two options: +- [Managed service](../quick-setup/core/saas-setup) +- Self hosted with your own database ([With Docker](../quick-setup/core/with-docker) or [Without Docker](../quick-setup/core/without-docker)) + + + + +:::success +You have successfully completed the quick setup! Head over to the "Post login operations" or "Common customizations" section. +::: + + + diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx new file mode 100644 index 000000000..9e2d5d607 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -0,0 +1,152 @@ +--- +id: protecting-route +title: 4. Protecting a website route +hide_title: true +--- + + + + +# 4. Protecting a website route + +Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + +First we will create a utility function to get the session information in the SSR safe way: + +```tsx +import { cookies } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} +``` + +`getSSRSession` will try to fetch session information from the request or from existing cookies. We can use this function to ensure that a session exists, this function can be called in both client or server components. + +Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + return ( +
+
+

+ Hello world +

+
+
+ ); +} +``` + +The `TryRefreshComponent` is a client component that checks if a session exists and tryies to refresh it if it is expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +'use client'; + +import { useEffect } from 'react'; +import { useRouter, redirect } from 'next/navigation'; +import Session from 'supertokens-auth-react/recipe/session'; +import SuperTokens from 'supertokens-auth-react'; + +export const TryRefreshComponent = () => { + const router = useRouter(); + useEffect(() => { + void Session.attemptRefreshingSession() + .then(() => { + router.refresh(); + }) + .catch(console.error); + }, []); + + return
Loading...
; +}; +``` + +And then we can modify the `/app/page.tsx` file to use our server component + +```tsx title="app/page.tsx" +import { HomePage } from './components/home' +import styles from './page.module.css' + +export default function Home() { + return ( +
+ +
+ ) +} + +``` + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: + +:::important +An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). +::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx new file mode 100644 index 000000000..29787d933 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx @@ -0,0 +1,255 @@ +--- +id: session-verification +title: 5. Session verification in an API call +hide_title: true +--- + + + + +# 5. Session verification in an API call + +:::note +This is applicable for when the frontend calls an API in the `/app/api` folder. +::: + +For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. + +## 1) Create new helper functions + +We will create some functions so that we can use them to check for sessions in our APIs + +```ts +import { cookies, headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; +import { ensureSuperTokensInit } from './config/backend'; + +ensureSuperTokensInit(); + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} + +export function updateSessionInResponse( + session: SessionContainer, + response?: NextResponse, + ) { + let tokens = session.getAllSessionTokensDangerously(); + if (tokens.accessAndFrontTokenUpdated) { + const accessTokenCookie = { + name: 'sAccessToken', + value: tokens.accessToken, + httpOnly: true, + path: '/', + expires: Date.now() + 3153600000000, + }; + + if (response) { + response.cookies.set(accessTokenCookie); + response.headers.set('front-token', tokens.frontToken); + } else { + cookies().set(accessTokenCookie); + headers().set('front-token', tokens.frontToken); + } + } + } + +export async function withSession( + request: NextRequest, + handler: (session: SessionContainer | undefined) => Promise, + ) { + let { session, resp: stResponse } = await getSSRSession(request); + if (stResponse) { + return stResponse; + } + let userResponse = await handler(session); + + if (session) { + updateSessionInResponse(session, userResponse); + } + return userResponse; +} +``` + +The `getSSRSession` is a function we created in a previous step. The `withSession` function will be used in our APIs to check if a session exists, it will call the callback which is where our API logic will be written. `updateSessionInResponse` helps with attaching session tokens to the response. + +## 2) Create a new file `/app/api/user/route.ts` +- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). + +```ts +import { NextResponse, NextRequest } from 'next/server'; +import { withSession } from '../../sessionUtils'; +import SuperTokens from 'supertokens-node'; + +export function GET(request: NextRequest) { + return withSession(request, async (session) => { + if (!session) { + return new NextResponse('Authentication required', { status: 401 }); + } + + const userId = session.getUserId(); + const user = await SuperTokens.getUser(userId); + + if (user === undefined) { + return NextResponse.json({ + email: "not found", + }) + } + + return NextResponse.json({ + email: user.emails[0], + }); + }) +} +``` + +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created above, the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. + +## 3) Calling the API from the frontend + +Lets modify the Home page we made in a previous step to make a call to this API + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' +import { SignOut } from './signOut'; + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + const userEmailResponse = await fetch('http://localhost:3000/api/user', { + headers: { + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + let email = ""; + + if (userEmailResponse.status !== 200) { + email = "error with status " + userEmailResponse.status; + } else { + email = (await userEmailResponse.json()).email; + } + + return ( +
+
+

+ Server side component got userId: {session.getUserId()}
+ Server side component got email: {email} +

+
+ +
+ ); +} +``` + +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. + +## (OPTIONAL) Using the Next.js middleware + +:::warning +We do not recommend this approach because information set by the middleware will not change based on changes in the user session +::: + +```tsx title="middleware.tsx" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import { SessionContainer } from 'supertokens-node/recipe/session' +import { withSession } from './app/sessionUtils'; + + +export async function middleware( + request: NextRequest & { session?: SessionContainer } +) { + if (request.headers.has("x-user-id")) { + console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); + request.headers.delete("x-user-id"); + } + + if (request.nextUrl.pathname.startsWith('/api/auth')) { + // this hits our pages/api/auth/* endpoints + return NextResponse.next() + } + + return withSession(request, async (session) => { + if (session === undefined) { + return NextResponse.next() + } + return NextResponse.next({ + headers: { + 'x-user-id': session.getUserId(), + }, + }) + }) +} + +export const config = { + matcher: '/api/:path*', +} +``` + +In the middleware we check if a session exists using the `withSession` helper function and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +You cannot attach the full request object to the request +::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx new file mode 100644 index 000000000..fc352d470 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -0,0 +1,80 @@ +--- +id: setting-up-backend +title: 3. Adding auth APIs +hide_title: true +--- + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import AppInfoForm from "/src/components/appInfoForm" + +# 3. Adding auth APIs + +We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. + +## 1) Create the `app/api/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app/api/` folder. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). + +## 2) Expose the SuperTokens APIs + + + +```tsx title="pages/api/auth/[[...path]].ts" +import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; +import { NextRequest, NextResponse } from 'next/server'; +import { ensureSuperTokensInit } from '../../../config/backend'; + +ensureSuperTokensInit(); + +const handleCall = getAppDirRequestHandler(NextResponse); + +export async function GET(request: NextRequest) { + const res = await handleCall(request); + if (!res.headers.has('Cache-Control')) { + // This is needed for production deployments with Vercel + res.headers.set( + 'Cache-Control', + 'no-cache, no-store, max-age=0, must-revalidate' + ) + } + return res; +} + +export async function POST(request: NextRequest) { + return handleCall(request); +} + +export async function DELETE(request: NextRequest) { + return handleCall(request); +} + +export async function PUT(request: NextRequest) { + return handleCall(request); +} + +export async function PATCH(request: NextRequest) { + return handleCall(request); +} + +export async function HEAD(request: NextRequest) { + return handleCall(request); +} +``` + +:::note +In the snippet above we add the `Cache-Control` header to the responses for all auth APIs. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. +::: + + + +## 3) Use the login widget +If you are now able to sign in or sign up, this means the backend setup is done correctly! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx new file mode 100644 index 000000000..6d2d488b9 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx @@ -0,0 +1,72 @@ +--- +id: setting-up-frontend +title: 2. Showing the Login UI +hide_title: true +show_ui_switcher: true +--- + + + + +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import AppInfoForm from "/src/components/appInfoForm"; + +# 2. Showing the Login UI + + + + + +## 1) Create the `app/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app` folder. +- `[[...path]].tsx` will contain the component for showing SuperTokens UI +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). + +## 2) Create the `Auth` component: + +```tsx title="app/auth/[[...path]].tsx" +'use client'; + +import { useEffect } from 'react'; +import { redirectToAuth } from 'supertokens-auth-react'; +import SuperTokens from 'supertokens-auth-react/ui'; +import { ^{recipePreBuiltUINameCapitalLetters} } from "supertokens-auth-react/recipe/^{codeImportRecipeName}/prebuiltui"; + +export default function Auth() { + // if the user visits a page that is not handled by us (like /auth/random), then we redirect them back to the auth page. + useEffect(() => { + if ( + SuperTokens.canHandleRoute([^{recipePreBuiltUINameCapitalLetters}]) === false + ) { + redirectToAuth({ redirectBack: false }); + } + }, []); + + if (typeof window !== 'undefined') { + return SuperTokens.getRoutingComponent([^{recipePreBuiltUINameCapitalLetters}]); + } + + return null; +} +``` + +## 3) Visit `/auth` page on your website + +If you see a login UI, then you have successfully completed this step! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) + + + + + +You need to build your own UI. Please follow the docs after the "Initialisation" section in the "Using your own UI" section for how to build out the various auth flows. + + + + diff --git a/v2/thirdpartyemailpassword/sidebars.js b/v2/thirdpartyemailpassword/sidebars.js index 40f724017..22ac96252 100644 --- a/v2/thirdpartyemailpassword/sidebars.js +++ b/v2/thirdpartyemailpassword/sidebars.js @@ -179,20 +179,38 @@ module.exports = { logoUrl: '/img/logos/next-logo.png' }, items: [ - "nextjs/about", - "nextjs/init", - "nextjs/setting-up-frontend", - "nextjs/setting-up-backend", - "nextjs/protecting-route", { type: 'category', - label: '5. Session verification', + label: 'Using the App directory', items: [ - "nextjs/session-verification/in-api", - "nextjs/session-verification/in-ssr" + "nextjs/app-directory/init", + "nextjs/app-directory/setting-up-frontend", + "nextjs/app-directory/setting-up-backend", + "nextjs/app-directory/protecting-route", + "nextjs/app-directory/session-verification", + "nextjs/app-directory/next-steps" + ], + }, + { + type: 'category', + label: 'Using the Pages directory', + items: [ + "nextjs/about", + "nextjs/init", + "nextjs/setting-up-frontend", + "nextjs/setting-up-backend", + "nextjs/protecting-route", + { + type: 'category', + label: '5. Session verification', + items: [ + "nextjs/session-verification/in-api", + "nextjs/session-verification/in-ssr" + ], + }, + "nextjs/next-steps" ], }, - "nextjs/next-steps" ], }, { From 55abb52b656bf3265d4c85d4f2e1ac69b5cb1864 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 13 Oct 2023 14:55:05 +0530 Subject: [PATCH 02/27] Refactor based on PR comments --- .../nextjs/app-directory/next-steps.mdx | 2 +- .../nextjs/app-directory/protecting-route.mdx | 148 +++++----- .../server-components-requests.mdx | 59 ++++ .../nextjs/app-directory/session-helpers.mdx | 125 +++++++++ .../session-verification-middleware.mdx | 57 ++++ .../session-verification-session-guard.mdx | 52 ++++ .../app-directory/session-verification.mdx | 255 ------------------ .../app-directory/setting-up-backend.mdx | 2 +- v2/thirdpartyemailpassword/sidebars.js | 11 +- 9 files changed, 388 insertions(+), 323 deletions(-) create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx delete mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx index 25c925bd5..b26f14fad 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/next-steps.mdx @@ -1,6 +1,6 @@ --- id: next-steps -title: 6. Next steps +title: 8. Next steps hide_title: true --- diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 9e2d5d607..35d8292b0 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -1,77 +1,17 @@ --- id: protecting-route -title: 4. Protecting a website route +title: 5. Checking for sessions in frontend routes hide_title: true --- -# 4. Protecting a website route +# 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. -First we will create a utility function to get the session information in the SSR safe way: - -```tsx -import { cookies } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; - -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; -}> { - let token; - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, - }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} -``` - -`getSSRSession` will try to fetch session information from the request or from existing cookies. We can use this function to ensure that a session exists, this function can be called in both client or server components. +## Sessions with Server Components Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: @@ -84,6 +24,15 @@ import { redirect } from 'next/navigation' export async function HomePage() { const { session, hasToken } = await getSSRSession(); + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + */ if (!session) { if (!hasToken) { redirect('/auth'); @@ -103,7 +52,7 @@ export async function HomePage() { } ``` -The `TryRefreshComponent` is a client component that checks if a session exists and tryies to refresh it if it is expired. +`getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" 'use client'; @@ -140,7 +89,6 @@ export default function Home() { ) } - ``` :::tip Test by navigating to `/` @@ -149,4 +97,74 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). +::: + +## Sessions with Client Components + +Lets create a client component for the `/` route of our website. + +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. + +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. + +:::info +`useSessionContext` can be used along with the `SessionAuth` wrapper component. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx new file mode 100644 index 000000000..5e13c87c4 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx @@ -0,0 +1,59 @@ +--- +id: server-components-requests +title: 7. Making requests from Server Components +hide_title: true +--- + + + + +# 7. Making requests from Server Components + +Lets modify the Home page we made in a previous step to make a call to this API + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' +import { SignOut } from './signOut'; + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + const userEmailResponse = await fetch('http://localhost:3000/api/user', { + headers: { + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + let email = ""; + + if (userEmailResponse.status !== 200) { + email = "error with status " + userEmailResponse.status; + } else { + email = (await userEmailResponse.json()).email; + } + + return ( +
+
+

+ Server side component got userId: {session.getUserId()}
+ Server side component got email: {email} +

+
+ +
+ ); +} +``` + +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx new file mode 100644 index 000000000..e701e369a --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -0,0 +1,125 @@ +--- +id: session-helpers +title: 4. Add helper functions for sessions +hide_title: true +--- + + + + +# 4. Add helper functions for sessions + +To make it easy to access session information and protect our API routes we will create some helper functions: + +```ts title="app/sessionUtils.ts" +import { cookies, headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; +import { ensureSuperTokensInit } from './config/backend'; + +ensureSuperTokensInit(); + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + /** + * We try reading the access token from the incoming request if it exists + * + * If the request does not exist or it does not contain an access token we try to read it + * from the existing cookies stored for the website + */ + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} + +export function updateSessionInResponse( + session: SessionContainer, + response?: NextResponse, + ) { + let tokens = session.getAllSessionTokensDangerously(); + if (tokens.accessAndFrontTokenUpdated) { + const accessTokenCookie = { + name: 'sAccessToken', + value: tokens.accessToken, + httpOnly: true, + path: '/', + expires: Date.now() + 3153600000000, + }; + + if (response) { + response.cookies.set(accessTokenCookie); + response.headers.set('front-token', tokens.frontToken); + } else { + cookies().set(accessTokenCookie); + headers().set('front-token', tokens.frontToken); + } + } + } + +export async function withSession( + request: NextRequest, + handler: (session: SessionContainer | undefined) => Promise, + ) { + let { session, resp: stResponse } = await getSSRSession(request); + if (stResponse) { + return stResponse; + } + let userResponse = await handler(session); + + if (session) { + updateSessionInResponse(session, userResponse); + } + return userResponse; +} +``` + +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. + +- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx new file mode 100644 index 000000000..0bf314643 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -0,0 +1,57 @@ +--- +id: session-verification-middleware +title: Using the Next.js middleware +hide_title: true +--- + + + + +# Using the Next.js middleware + +```tsx title="middleware.tsx" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import { SessionContainer } from 'supertokens-node/recipe/session' +import { withSession } from './app/sessionUtils'; + + +export async function middleware( + request: NextRequest & { session?: SessionContainer } +) { + if (request.headers.has("x-user-id")) { + console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); + request.headers.delete("x-user-id"); + } + + if (request.nextUrl.pathname.startsWith('/api/auth')) { + /** + * /api/auth/* endpoints are exposed by the SuperTokens SDK, + * we do not want to modify the request for these routes + */ + return NextResponse.next() + } + + return withSession(request, async (session) => { + if (session === undefined) { + return NextResponse.next() + } + return NextResponse.next({ + headers: { + // You cannot attach the full session object here + 'x-user-id': session.getUserId(), + }, + }) + }) +} + +export const config = { + matcher: '/api/:path*', +} +``` + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) +::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx new file mode 100644 index 000000000..e6b86f8d2 --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -0,0 +1,52 @@ +--- +id: session-verification-session-guard +title: Adding a session guard to all API routes +hide_title: true +--- + + + + +# Adding a session guard to all API routes + +:::note +This is applicable for when the frontend calls an API in the `/app/api` folder. +::: + +For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. + +Create a new file `/app/api/user/route.ts` + +- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). + +```ts title="app/api/user/route.ts" +import { NextResponse, NextRequest } from 'next/server'; +import { withSession } from '../../sessionUtils'; +import SuperTokens from 'supertokens-node'; + +export function GET(request: NextRequest) { + return withSession(request, async (session) => { + // Session will be undefined if it does not exist + if (!session) { + // The frontend sdk will try to refresh the session if it recieves a 401 status code + return new NextResponse('Authentication required', { status: 401 }); + } + + // The session object exposes utility functions to fetch information such as the user id + const userId = session.getUserId(); + const user = await SuperTokens.getUser(userId); + + if (user === undefined) { + return NextResponse.json({ + email: "not found", + }) + } + + return NextResponse.json({ + email: user.emails[0], + }); + }) +} +``` + +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx deleted file mode 100644 index 29787d933..000000000 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification.mdx +++ /dev/null @@ -1,255 +0,0 @@ ---- -id: session-verification -title: 5. Session verification in an API call -hide_title: true ---- - - - - -# 5. Session verification in an API call - -:::note -This is applicable for when the frontend calls an API in the `/app/api` folder. -::: - -For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. - -## 1) Create new helper functions - -We will create some functions so that we can use them to check for sessions in our APIs - -```ts -import { cookies, headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; -import { ensureSuperTokensInit } from './config/backend'; - -ensureSuperTokensInit(); - -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; -}> { - let token; - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, - }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} - -export function updateSessionInResponse( - session: SessionContainer, - response?: NextResponse, - ) { - let tokens = session.getAllSessionTokensDangerously(); - if (tokens.accessAndFrontTokenUpdated) { - const accessTokenCookie = { - name: 'sAccessToken', - value: tokens.accessToken, - httpOnly: true, - path: '/', - expires: Date.now() + 3153600000000, - }; - - if (response) { - response.cookies.set(accessTokenCookie); - response.headers.set('front-token', tokens.frontToken); - } else { - cookies().set(accessTokenCookie); - headers().set('front-token', tokens.frontToken); - } - } - } - -export async function withSession( - request: NextRequest, - handler: (session: SessionContainer | undefined) => Promise, - ) { - let { session, resp: stResponse } = await getSSRSession(request); - if (stResponse) { - return stResponse; - } - let userResponse = await handler(session); - - if (session) { - updateSessionInResponse(session, userResponse); - } - return userResponse; -} -``` - -The `getSSRSession` is a function we created in a previous step. The `withSession` function will be used in our APIs to check if a session exists, it will call the callback which is where our API logic will be written. `updateSessionInResponse` helps with attaching session tokens to the response. - -## 2) Create a new file `/app/api/user/route.ts` -- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). - -```ts -import { NextResponse, NextRequest } from 'next/server'; -import { withSession } from '../../sessionUtils'; -import SuperTokens from 'supertokens-node'; - -export function GET(request: NextRequest) { - return withSession(request, async (session) => { - if (!session) { - return new NextResponse('Authentication required', { status: 401 }); - } - - const userId = session.getUserId(); - const user = await SuperTokens.getUser(userId); - - if (user === undefined) { - return NextResponse.json({ - email: "not found", - }) - } - - return NextResponse.json({ - email: user.emails[0], - }); - }) -} -``` - -In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created above, the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. - -## 3) Calling the API from the frontend - -Lets modify the Home page we made in a previous step to make a call to this API - -```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' -import { SignOut } from './signOut'; - -export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - if (!session) { - if (!hasToken) { - redirect('/auth'); - } - return ; - } - - const userEmailResponse = await fetch('http://localhost:3000/api/user', { - headers: { - Authorization: 'Bearer ' + session.getAccessToken(), - }, - }); - - let email = ""; - - if (userEmailResponse.status !== 200) { - email = "error with status " + userEmailResponse.status; - } else { - email = (await userEmailResponse.json()).email; - } - - return ( -
-
-

- Server side component got userId: {session.getUserId()}
- Server side component got email: {email} -

-
- -
- ); -} -``` - -We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. - -## (OPTIONAL) Using the Next.js middleware - -:::warning -We do not recommend this approach because information set by the middleware will not change based on changes in the user session -::: - -```tsx title="middleware.tsx" -import { NextResponse } from 'next/server' -import type { NextRequest } from 'next/server' -import { SessionContainer } from 'supertokens-node/recipe/session' -import { withSession } from './app/sessionUtils'; - - -export async function middleware( - request: NextRequest & { session?: SessionContainer } -) { - if (request.headers.has("x-user-id")) { - console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); - request.headers.delete("x-user-id"); - } - - if (request.nextUrl.pathname.startsWith('/api/auth')) { - // this hits our pages/api/auth/* endpoints - return NextResponse.next() - } - - return withSession(request, async (session) => { - if (session === undefined) { - return NextResponse.next() - } - return NextResponse.next({ - headers: { - 'x-user-id': session.getUserId(), - }, - }) - }) -} - -export const config = { - matcher: '/api/:path*', -} -``` - -In the middleware we check if a session exists using the `withSession` helper function and set the user's user id to the request headers using the session object. You can set other information in the same way. - -:::note -You cannot attach the full request object to the request -::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx index fc352d470..02dcd2361 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -71,7 +71,7 @@ export async function HEAD(request: NextRequest) { ``` :::note -In the snippet above we add the `Cache-Control` header to the responses for all auth APIs. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. +In the snippet above we add the `Cache-Control` header to the responses for all auth APIs with the `GET` method. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. ::: diff --git a/v2/thirdpartyemailpassword/sidebars.js b/v2/thirdpartyemailpassword/sidebars.js index 22ac96252..013804ac0 100644 --- a/v2/thirdpartyemailpassword/sidebars.js +++ b/v2/thirdpartyemailpassword/sidebars.js @@ -186,8 +186,17 @@ module.exports = { "nextjs/app-directory/init", "nextjs/app-directory/setting-up-frontend", "nextjs/app-directory/setting-up-backend", + "nextjs/app-directory/session-helpers", "nextjs/app-directory/protecting-route", - "nextjs/app-directory/session-verification", + { + type: "category", + label: "6. Checking for sessions in API routes", + items: [ + "nextjs/app-directory/session-verification-session-guard", + "nextjs/app-directory/session-verification-middleware", + ], + }, + "nextjs/app-directory/server-components-requests", "nextjs/app-directory/next-steps" ], }, From 274de47b5f233d024ffbdfb3c4cc8a671d691049 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 13 Oct 2023 15:13:18 +0530 Subject: [PATCH 03/27] Add docs for email password --- .../nextjs/app-directory/init.mdx | 395 ++++++++++++++++++ .../nextjs/app-directory/next-steps.mdx | 35 ++ .../nextjs/app-directory/protecting-route.mdx | 170 ++++++++ .../server-components-requests.mdx | 59 +++ .../nextjs/app-directory/session-helpers.mdx | 125 ++++++ .../session-verification-middleware.mdx | 57 +++ .../session-verification-session-guard.mdx | 52 +++ .../app-directory/setting-up-backend.mdx | 80 ++++ .../app-directory/setting-up-frontend.mdx | 72 ++++ v2/emailpassword/sidebars.js | 45 +- 10 files changed, 1081 insertions(+), 9 deletions(-) create mode 100644 v2/emailpassword/nextjs/app-directory/init.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/next-steps.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/protecting-route.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/server-components-requests.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/session-helpers.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx create mode 100644 v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx new file mode 100644 index 000000000..c1f4ea4c9 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -0,0 +1,395 @@ +--- +id: init +title: 1. Configuration +hide_title: true +show_ui_switcher: true +--- + + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import {Question, Answer}from "/src/components/question" +import AppInfoForm from "/src/components/appInfoForm" +import CoreInjector from "/src/components/coreInjector" +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + +# 1. Configuration + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + websiteDomain: "^{form_websiteDomain}", + apiBasePath: "^{form_apiBasePath}", + websiteBasePath: "^{form_websiteBasePath}" +} + +``` + + + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + apiBasePath: "^{form_apiBasePath}", +} + +``` + + + + + + + + + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import EmailPasswordReact from 'supertokens-auth-react/recipe/emailpassword' +import SessionReact from 'supertokens-auth-react/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + EmailPasswordReact.init(), + SessionReact.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import EmailPasswordWebJs from 'supertokens-web-js/recipe/emailpassword' +import SessionWebJs from 'supertokens-web-js/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + EmailPasswordWebJs.init(), + SessionWebJs.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 5) Create a backend config function + + + + + +```tsx title="/config/backendConfig.ts" +import SuperTokens from "supertokens-node"; +import EmailPasswordNode from 'supertokens-node/recipe/emailpassword' +import SessionNode from 'supertokens-node/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import { TypeInput } from "supertokens-node/types"; + +export const backendConfig = (): TypeInput => { + return { + framework: "express", + supertokens: { + ^{coreInjector_connection_uri_comment} + connectionURI: ^{coreInjector_uri} + ^{coreInjector_api_key_commented}apiKey: ^{coreInjector_api_key}, + }, + appInfo, + recipeList: [ + EmailPasswordNode.init(), + SessionNode.init(), + ], + isInServerlessEnv: true, + } +} + +let initialized = false; +// This function is used in your APIs to make sure SuperTokens is initialised +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} +``` + +`ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. + +**When you want to generate your own keys**, please refer to the corresponding documentation to get your client ids and client secrets for each of the below providers: + +
+Google + +- Generate your client ID and secret by following the [docs here](https://support.google.com/cloud/answer/6158849?hl=en) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/google` + +
+ +
+Github + +- Generate your client ID and secret by following the [docs here](https://docs.github.com/en/developers/apps/creating-an-oauth-app) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/github` + +
+ +
+Facebook + +- Generate your client ID and secret by following the [docs here](https://developers.facebook.com/docs/development/create-an-app) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/facebook` + +:::info Note +Make sure to enable `https` to be able to use the test users of the Facebook app. On `http://localhost`, the login flow can be verified only with the app's admin user. +::: + +
+ +
+Apple + +- Generate your client ID and secret by following [this article](https://medium.com/identity-beyond-borders/how-to-configure-sign-in-with-apple-77c61e336003) +- Set the authorisation callback URL to `^{form_apiDomain}^{form_apiBasePathForCallbacks}/callback/apple`. Note that Apple doesn't allow `localhost` in the URL. So if you are in dev mode, you can use the dev keys we have provided above. + +
+ +
+ +
+ + + + + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component + +- Create a client component `/app/components/supertokensProvider.tsx`. This file will initialise SuperTokens and wrap its children with the `SuperTokensWrapper` component +- Modify the `/app/layout.tsx` file to use the `SuperTokensProvider` component. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/layout.tsx) + +```tsx title="/app/components/supertokensProvider.tsx" +'use client'; +import React from 'react'; +import { SuperTokensWrapper } from 'supertokens-auth-react'; +import SuperTokensReact from 'supertokens-auth-react'; +import { frontendConfig, setRouter } from '../config/frontend'; +import { usePathname, useRouter } from 'next/navigation'; + +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + SuperTokensReact.init(frontendConfig()); +} + +export const SuperTokensProvider: React.FC> = ({ + children, +}) => { + setRouter(useRouter(), usePathname() || window.location.pathname); + + return {children}; +}; +``` + +```tsx title="/app/layout.tsx" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { SuperTokensProvider } from './components/supertokensProvider' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions + +- Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). + + +```tsx title="/pages/_app.ts" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import SuperTokensWebJs from 'supertokens-web-js' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) + } + + return ( + + {children} + + ) +} +``` + + + + + + \ No newline at end of file diff --git a/v2/emailpassword/nextjs/app-directory/next-steps.mdx b/v2/emailpassword/nextjs/app-directory/next-steps.mdx new file mode 100644 index 000000000..b26f14fad --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/next-steps.mdx @@ -0,0 +1,35 @@ +--- +id: next-steps +title: 8. Next steps +hide_title: true +--- + + + + +import {Question, Answer}from "/src/components/question" + +# 6. Next steps + +## Setting up the core and database + { + return ( + Are you using https://try.supertokens.com as the connection URI in the init function? + ) + }}> + + +You need to now setup an instance of the SuperTokens core for your app (that your backend should connect to). You have two options: +- [Managed service](../quick-setup/core/saas-setup) +- Self hosted with your own database ([With Docker](../quick-setup/core/with-docker) or [Without Docker](../quick-setup/core/without-docker)) + + + + +:::success +You have successfully completed the quick setup! Head over to the "Post login operations" or "Common customizations" section. +::: + + + diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx new file mode 100644 index 000000000..35d8292b0 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -0,0 +1,170 @@ +--- +id: protecting-route +title: 5. Checking for sessions in frontend routes +hide_title: true +--- + + + + +# 5. Checking for sessions in frontend routes + +Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + +## Sessions with Server Components + +Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + */ + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + return ( +
+
+

+ Hello world +

+
+
+ ); +} +``` + +`getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +'use client'; + +import { useEffect } from 'react'; +import { useRouter, redirect } from 'next/navigation'; +import Session from 'supertokens-auth-react/recipe/session'; +import SuperTokens from 'supertokens-auth-react'; + +export const TryRefreshComponent = () => { + const router = useRouter(); + useEffect(() => { + void Session.attemptRefreshingSession() + .then(() => { + router.refresh(); + }) + .catch(console.error); + }, []); + + return
Loading...
; +}; +``` + +And then we can modify the `/app/page.tsx` file to use our server component + +```tsx title="app/page.tsx" +import { HomePage } from './components/home' +import styles from './page.module.css' + +export default function Home() { + return ( +
+ +
+ ) +} +``` + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: + +:::important +An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). +::: + +## Sessions with Client Components + +Lets create a client component for the `/` route of our website. + +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. + +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. + +:::info +`useSessionContext` can be used along with the `SessionAuth` wrapper component. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: \ No newline at end of file diff --git a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx new file mode 100644 index 000000000..5e13c87c4 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx @@ -0,0 +1,59 @@ +--- +id: server-components-requests +title: 7. Making requests from Server Components +hide_title: true +--- + + + + +# 7. Making requests from Server Components + +Lets modify the Home page we made in a previous step to make a call to this API + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' +import { SignOut } from './signOut'; + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + const userEmailResponse = await fetch('http://localhost:3000/api/user', { + headers: { + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + let email = ""; + + if (userEmailResponse.status !== 200) { + email = "error with status " + userEmailResponse.status; + } else { + email = (await userEmailResponse.json()).email; + } + + return ( +
+
+

+ Server side component got userId: {session.getUserId()}
+ Server side component got email: {email} +

+
+ +
+ ); +} +``` + +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx new file mode 100644 index 000000000..e701e369a --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -0,0 +1,125 @@ +--- +id: session-helpers +title: 4. Add helper functions for sessions +hide_title: true +--- + + + + +# 4. Add helper functions for sessions + +To make it easy to access session information and protect our API routes we will create some helper functions: + +```ts title="app/sessionUtils.ts" +import { cookies, headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; +import { ensureSuperTokensInit } from './config/backend'; + +ensureSuperTokensInit(); + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + /** + * We try reading the access token from the incoming request if it exists + * + * If the request does not exist or it does not contain an access token we try to read it + * from the existing cookies stored for the website + */ + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} + +export function updateSessionInResponse( + session: SessionContainer, + response?: NextResponse, + ) { + let tokens = session.getAllSessionTokensDangerously(); + if (tokens.accessAndFrontTokenUpdated) { + const accessTokenCookie = { + name: 'sAccessToken', + value: tokens.accessToken, + httpOnly: true, + path: '/', + expires: Date.now() + 3153600000000, + }; + + if (response) { + response.cookies.set(accessTokenCookie); + response.headers.set('front-token', tokens.frontToken); + } else { + cookies().set(accessTokenCookie); + headers().set('front-token', tokens.frontToken); + } + } + } + +export async function withSession( + request: NextRequest, + handler: (session: SessionContainer | undefined) => Promise, + ) { + let { session, resp: stResponse } = await getSSRSession(request); + if (stResponse) { + return stResponse; + } + let userResponse = await handler(session); + + if (session) { + updateSessionInResponse(session, userResponse); + } + return userResponse; +} +``` + +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. + +- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx new file mode 100644 index 000000000..0bf314643 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -0,0 +1,57 @@ +--- +id: session-verification-middleware +title: Using the Next.js middleware +hide_title: true +--- + + + + +# Using the Next.js middleware + +```tsx title="middleware.tsx" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import { SessionContainer } from 'supertokens-node/recipe/session' +import { withSession } from './app/sessionUtils'; + + +export async function middleware( + request: NextRequest & { session?: SessionContainer } +) { + if (request.headers.has("x-user-id")) { + console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); + request.headers.delete("x-user-id"); + } + + if (request.nextUrl.pathname.startsWith('/api/auth')) { + /** + * /api/auth/* endpoints are exposed by the SuperTokens SDK, + * we do not want to modify the request for these routes + */ + return NextResponse.next() + } + + return withSession(request, async (session) => { + if (session === undefined) { + return NextResponse.next() + } + return NextResponse.next({ + headers: { + // You cannot attach the full session object here + 'x-user-id': session.getUserId(), + }, + }) + }) +} + +export const config = { + matcher: '/api/:path*', +} +``` + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) +::: \ No newline at end of file diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx new file mode 100644 index 000000000..e6b86f8d2 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -0,0 +1,52 @@ +--- +id: session-verification-session-guard +title: Adding a session guard to all API routes +hide_title: true +--- + + + + +# Adding a session guard to all API routes + +:::note +This is applicable for when the frontend calls an API in the `/app/api` folder. +::: + +For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. + +Create a new file `/app/api/user/route.ts` + +- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). + +```ts title="app/api/user/route.ts" +import { NextResponse, NextRequest } from 'next/server'; +import { withSession } from '../../sessionUtils'; +import SuperTokens from 'supertokens-node'; + +export function GET(request: NextRequest) { + return withSession(request, async (session) => { + // Session will be undefined if it does not exist + if (!session) { + // The frontend sdk will try to refresh the session if it recieves a 401 status code + return new NextResponse('Authentication required', { status: 401 }); + } + + // The session object exposes utility functions to fetch information such as the user id + const userId = session.getUserId(); + const user = await SuperTokens.getUser(userId); + + if (user === undefined) { + return NextResponse.json({ + email: "not found", + }) + } + + return NextResponse.json({ + email: user.emails[0], + }); + }) +} +``` + +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx new file mode 100644 index 000000000..02dcd2361 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -0,0 +1,80 @@ +--- +id: setting-up-backend +title: 3. Adding auth APIs +hide_title: true +--- + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import AppInfoForm from "/src/components/appInfoForm" + +# 3. Adding auth APIs + +We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. + +## 1) Create the `app/api/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app/api/` folder. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). + +## 2) Expose the SuperTokens APIs + + + +```tsx title="pages/api/auth/[[...path]].ts" +import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; +import { NextRequest, NextResponse } from 'next/server'; +import { ensureSuperTokensInit } from '../../../config/backend'; + +ensureSuperTokensInit(); + +const handleCall = getAppDirRequestHandler(NextResponse); + +export async function GET(request: NextRequest) { + const res = await handleCall(request); + if (!res.headers.has('Cache-Control')) { + // This is needed for production deployments with Vercel + res.headers.set( + 'Cache-Control', + 'no-cache, no-store, max-age=0, must-revalidate' + ) + } + return res; +} + +export async function POST(request: NextRequest) { + return handleCall(request); +} + +export async function DELETE(request: NextRequest) { + return handleCall(request); +} + +export async function PUT(request: NextRequest) { + return handleCall(request); +} + +export async function PATCH(request: NextRequest) { + return handleCall(request); +} + +export async function HEAD(request: NextRequest) { + return handleCall(request); +} +``` + +:::note +In the snippet above we add the `Cache-Control` header to the responses for all auth APIs with the `GET` method. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. +::: + + + +## 3) Use the login widget +If you are now able to sign in or sign up, this means the backend setup is done correctly! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx new file mode 100644 index 000000000..6d2d488b9 --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx @@ -0,0 +1,72 @@ +--- +id: setting-up-frontend +title: 2. Showing the Login UI +hide_title: true +show_ui_switcher: true +--- + + + + +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import AppInfoForm from "/src/components/appInfoForm"; + +# 2. Showing the Login UI + + + + + +## 1) Create the `app/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app` folder. +- `[[...path]].tsx` will contain the component for showing SuperTokens UI +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). + +## 2) Create the `Auth` component: + +```tsx title="app/auth/[[...path]].tsx" +'use client'; + +import { useEffect } from 'react'; +import { redirectToAuth } from 'supertokens-auth-react'; +import SuperTokens from 'supertokens-auth-react/ui'; +import { ^{recipePreBuiltUINameCapitalLetters} } from "supertokens-auth-react/recipe/^{codeImportRecipeName}/prebuiltui"; + +export default function Auth() { + // if the user visits a page that is not handled by us (like /auth/random), then we redirect them back to the auth page. + useEffect(() => { + if ( + SuperTokens.canHandleRoute([^{recipePreBuiltUINameCapitalLetters}]) === false + ) { + redirectToAuth({ redirectBack: false }); + } + }, []); + + if (typeof window !== 'undefined') { + return SuperTokens.getRoutingComponent([^{recipePreBuiltUINameCapitalLetters}]); + } + + return null; +} +``` + +## 3) Visit `/auth` page on your website + +If you see a login UI, then you have successfully completed this step! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) + + + + + +You need to build your own UI. Please follow the docs after the "Initialisation" section in the "Using your own UI" section for how to build out the various auth flows. + + + + diff --git a/v2/emailpassword/sidebars.js b/v2/emailpassword/sidebars.js index 956c32813..aef8631c4 100644 --- a/v2/emailpassword/sidebars.js +++ b/v2/emailpassword/sidebars.js @@ -176,20 +176,47 @@ module.exports = { logoUrl: '/img/logos/next-logo.png' }, items: [ - "nextjs/about", - "nextjs/init", - "nextjs/setting-up-frontend", - "nextjs/setting-up-backend", - "nextjs/protecting-route", { type: 'category', - label: '5. Session verification', + label: 'Using the App directory', items: [ - "nextjs/session-verification/in-api", - "nextjs/session-verification/in-ssr" + "nextjs/app-directory/init", + "nextjs/app-directory/setting-up-frontend", + "nextjs/app-directory/setting-up-backend", + "nextjs/app-directory/session-helpers", + "nextjs/app-directory/protecting-route", + { + type: "category", + label: "6. Checking for sessions in API routes", + items: [ + "nextjs/app-directory/session-verification-session-guard", + "nextjs/app-directory/session-verification-middleware", + ], + }, + "nextjs/app-directory/server-components-requests", + "nextjs/app-directory/next-steps" + ], + }, + { + type: 'category', + label: 'Using the Pages directory', + items: [ + "nextjs/about", + "nextjs/init", + "nextjs/setting-up-frontend", + "nextjs/setting-up-backend", + "nextjs/protecting-route", + { + type: 'category', + label: '5. Session verification', + items: [ + "nextjs/session-verification/in-api", + "nextjs/session-verification/in-ssr" + ], + }, + "nextjs/next-steps" ], }, - "nextjs/next-steps" ], }, { From 504c39334f77de28558000d70aa2efdeee970ce2 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 13 Oct 2023 15:32:36 +0530 Subject: [PATCH 04/27] Add docs for passwordless --- .../nextjs/app-directory/init.mdx | 38 -- v2/passwordless/nextjs/app-directory/init.mdx | 364 ++++++++++++++++++ .../nextjs/app-directory/next-steps.mdx | 35 ++ .../nextjs/app-directory/protecting-route.mdx | 170 ++++++++ .../server-components-requests.mdx | 59 +++ .../nextjs/app-directory/session-helpers.mdx | 125 ++++++ .../session-verification-middleware.mdx | 57 +++ .../session-verification-session-guard.mdx | 52 +++ .../app-directory/setting-up-backend.mdx | 80 ++++ .../app-directory/setting-up-frontend.mdx | 72 ++++ v2/passwordless/sidebars.js | 45 ++- 11 files changed, 1050 insertions(+), 47 deletions(-) create mode 100644 v2/passwordless/nextjs/app-directory/init.mdx create mode 100644 v2/passwordless/nextjs/app-directory/next-steps.mdx create mode 100644 v2/passwordless/nextjs/app-directory/protecting-route.mdx create mode 100644 v2/passwordless/nextjs/app-directory/server-components-requests.mdx create mode 100644 v2/passwordless/nextjs/app-directory/session-helpers.mdx create mode 100644 v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx create mode 100644 v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx create mode 100644 v2/passwordless/nextjs/app-directory/setting-up-backend.mdx create mode 100644 v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index c1f4ea4c9..c72bb7c98 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -241,44 +241,6 @@ export function ensureSuperTokensInit() { `ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. -**When you want to generate your own keys**, please refer to the corresponding documentation to get your client ids and client secrets for each of the below providers: - -
-Google - -- Generate your client ID and secret by following the [docs here](https://support.google.com/cloud/answer/6158849?hl=en) -- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/google` - -
- -
-Github - -- Generate your client ID and secret by following the [docs here](https://docs.github.com/en/developers/apps/creating-an-oauth-app) -- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/github` - -
- -
-Facebook - -- Generate your client ID and secret by following the [docs here](https://developers.facebook.com/docs/development/create-an-app) -- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/facebook` - -:::info Note -Make sure to enable `https` to be able to use the test users of the Facebook app. On `http://localhost`, the login flow can be verified only with the app's admin user. -::: - -
- -
-Apple - -- Generate your client ID and secret by following [this article](https://medium.com/identity-beyond-borders/how-to-configure-sign-in-with-apple-77c61e336003) -- Set the authorisation callback URL to `^{form_apiDomain}^{form_apiBasePathForCallbacks}/callback/apple`. Note that Apple doesn't allow `localhost` in the URL. So if you are in dev mode, you can use the dev keys we have provided above. - -
- diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx new file mode 100644 index 000000000..16475f93c --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -0,0 +1,364 @@ +--- +id: init +title: 1. Configuration +hide_title: true +show_ui_switcher: true +--- + + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import {Question, Answer}from "/src/components/question" +import AppInfoForm from "/src/components/appInfoForm" +import CoreInjector from "/src/components/coreInjector" +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + +# 1. Configuration + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + websiteDomain: "^{form_websiteDomain}", + apiBasePath: "^{form_apiBasePath}", + websiteBasePath: "^{form_websiteBasePath}" +} + +``` + + + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + apiBasePath: "^{form_apiBasePath}", +} + +``` + + + + + + + + + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import PasswordlessReact from 'supertokens-auth-react/recipe/passwordless' +import SessionReact from 'supertokens-auth-react/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + PasswordlessReact.init({ + contactMethod: "^{form_contactMethod}" + }), + SessionReact.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import PasswordlessWebJs from 'supertokens-web-js/recipe/passwordless' +import SessionWebJs from 'supertokens-web-js/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + PasswordlessWebJs.init(), + SessionWebJs.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 5) Create a backend config function + + + + + + +```tsx title="/config/backendConfig.ts" +import SuperTokens from "supertokens-node"; +import PasswordlessNode from 'supertokens-node/recipe/passwordless' +import SessionNode from 'supertokens-node/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import { TypeInput } from "supertokens-node/types"; + +export const backendConfig = (): TypeInput => { + return { + framework: "express", + supertokens: { + ^{coreInjector_connection_uri_comment} + connectionURI: ^{coreInjector_uri} + ^{coreInjector_api_key_commented}apiKey: ^{coreInjector_api_key}, + }, + appInfo, + recipeList: [ + PasswordlessNode.init({ + flowType: "^{form_flowType}", + contactMethod: "^{form_contactMethod}" + }), + SessionNode.init(), + ], + isInServerlessEnv: true, + } +} + +let initialized = false; +// This function is used in your APIs to make sure SuperTokens is initialised +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} +``` + +`ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. + + + + + + + + + + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component + +- Create a client component `/app/components/supertokensProvider.tsx`. This file will initialise SuperTokens and wrap its children with the `SuperTokensWrapper` component +- Modify the `/app/layout.tsx` file to use the `SuperTokensProvider` component. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/layout.tsx) + +```tsx title="/app/components/supertokensProvider.tsx" +'use client'; +import React from 'react'; +import { SuperTokensWrapper } from 'supertokens-auth-react'; +import SuperTokensReact from 'supertokens-auth-react'; +import { frontendConfig, setRouter } from '../config/frontend'; +import { usePathname, useRouter } from 'next/navigation'; + +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + SuperTokensReact.init(frontendConfig()); +} + +export const SuperTokensProvider: React.FC> = ({ + children, +}) => { + setRouter(useRouter(), usePathname() || window.location.pathname); + + return {children}; +}; +``` + +```tsx title="/app/layout.tsx" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { SuperTokensProvider } from './components/supertokensProvider' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions + +- Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). + + +```tsx title="/pages/_app.ts" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import SuperTokensWebJs from 'supertokens-web-js' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) + } + + return ( + + {children} + + ) +} +``` + + + + + + \ No newline at end of file diff --git a/v2/passwordless/nextjs/app-directory/next-steps.mdx b/v2/passwordless/nextjs/app-directory/next-steps.mdx new file mode 100644 index 000000000..b26f14fad --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/next-steps.mdx @@ -0,0 +1,35 @@ +--- +id: next-steps +title: 8. Next steps +hide_title: true +--- + + + + +import {Question, Answer}from "/src/components/question" + +# 6. Next steps + +## Setting up the core and database + { + return ( + Are you using https://try.supertokens.com as the connection URI in the init function? + ) + }}> + + +You need to now setup an instance of the SuperTokens core for your app (that your backend should connect to). You have two options: +- [Managed service](../quick-setup/core/saas-setup) +- Self hosted with your own database ([With Docker](../quick-setup/core/with-docker) or [Without Docker](../quick-setup/core/without-docker)) + + + + +:::success +You have successfully completed the quick setup! Head over to the "Post login operations" or "Common customizations" section. +::: + + + diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx new file mode 100644 index 000000000..35d8292b0 --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -0,0 +1,170 @@ +--- +id: protecting-route +title: 5. Checking for sessions in frontend routes +hide_title: true +--- + + + + +# 5. Checking for sessions in frontend routes + +Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + +## Sessions with Server Components + +Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + */ + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + return ( +
+
+

+ Hello world +

+
+
+ ); +} +``` + +`getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +'use client'; + +import { useEffect } from 'react'; +import { useRouter, redirect } from 'next/navigation'; +import Session from 'supertokens-auth-react/recipe/session'; +import SuperTokens from 'supertokens-auth-react'; + +export const TryRefreshComponent = () => { + const router = useRouter(); + useEffect(() => { + void Session.attemptRefreshingSession() + .then(() => { + router.refresh(); + }) + .catch(console.error); + }, []); + + return
Loading...
; +}; +``` + +And then we can modify the `/app/page.tsx` file to use our server component + +```tsx title="app/page.tsx" +import { HomePage } from './components/home' +import styles from './page.module.css' + +export default function Home() { + return ( +
+ +
+ ) +} +``` + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: + +:::important +An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). +::: + +## Sessions with Client Components + +Lets create a client component for the `/` route of our website. + +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. + +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. + +:::info +`useSessionContext` can be used along with the `SessionAuth` wrapper component. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: \ No newline at end of file diff --git a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx new file mode 100644 index 000000000..5e13c87c4 --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx @@ -0,0 +1,59 @@ +--- +id: server-components-requests +title: 7. Making requests from Server Components +hide_title: true +--- + + + + +# 7. Making requests from Server Components + +Lets modify the Home page we made in a previous step to make a call to this API + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' +import { SignOut } from './signOut'; + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + const userEmailResponse = await fetch('http://localhost:3000/api/user', { + headers: { + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + let email = ""; + + if (userEmailResponse.status !== 200) { + email = "error with status " + userEmailResponse.status; + } else { + email = (await userEmailResponse.json()).email; + } + + return ( +
+
+

+ Server side component got userId: {session.getUserId()}
+ Server side component got email: {email} +

+
+ +
+ ); +} +``` + +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx new file mode 100644 index 000000000..e701e369a --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -0,0 +1,125 @@ +--- +id: session-helpers +title: 4. Add helper functions for sessions +hide_title: true +--- + + + + +# 4. Add helper functions for sessions + +To make it easy to access session information and protect our API routes we will create some helper functions: + +```ts title="app/sessionUtils.ts" +import { cookies, headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; +import { ensureSuperTokensInit } from './config/backend'; + +ensureSuperTokensInit(); + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + /** + * We try reading the access token from the incoming request if it exists + * + * If the request does not exist or it does not contain an access token we try to read it + * from the existing cookies stored for the website + */ + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} + +export function updateSessionInResponse( + session: SessionContainer, + response?: NextResponse, + ) { + let tokens = session.getAllSessionTokensDangerously(); + if (tokens.accessAndFrontTokenUpdated) { + const accessTokenCookie = { + name: 'sAccessToken', + value: tokens.accessToken, + httpOnly: true, + path: '/', + expires: Date.now() + 3153600000000, + }; + + if (response) { + response.cookies.set(accessTokenCookie); + response.headers.set('front-token', tokens.frontToken); + } else { + cookies().set(accessTokenCookie); + headers().set('front-token', tokens.frontToken); + } + } + } + +export async function withSession( + request: NextRequest, + handler: (session: SessionContainer | undefined) => Promise, + ) { + let { session, resp: stResponse } = await getSSRSession(request); + if (stResponse) { + return stResponse; + } + let userResponse = await handler(session); + + if (session) { + updateSessionInResponse(session, userResponse); + } + return userResponse; +} +``` + +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. + +- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file diff --git a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx new file mode 100644 index 000000000..0bf314643 --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx @@ -0,0 +1,57 @@ +--- +id: session-verification-middleware +title: Using the Next.js middleware +hide_title: true +--- + + + + +# Using the Next.js middleware + +```tsx title="middleware.tsx" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import { SessionContainer } from 'supertokens-node/recipe/session' +import { withSession } from './app/sessionUtils'; + + +export async function middleware( + request: NextRequest & { session?: SessionContainer } +) { + if (request.headers.has("x-user-id")) { + console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); + request.headers.delete("x-user-id"); + } + + if (request.nextUrl.pathname.startsWith('/api/auth')) { + /** + * /api/auth/* endpoints are exposed by the SuperTokens SDK, + * we do not want to modify the request for these routes + */ + return NextResponse.next() + } + + return withSession(request, async (session) => { + if (session === undefined) { + return NextResponse.next() + } + return NextResponse.next({ + headers: { + // You cannot attach the full session object here + 'x-user-id': session.getUserId(), + }, + }) + }) +} + +export const config = { + matcher: '/api/:path*', +} +``` + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) +::: \ No newline at end of file diff --git a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx new file mode 100644 index 000000000..e6b86f8d2 --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -0,0 +1,52 @@ +--- +id: session-verification-session-guard +title: Adding a session guard to all API routes +hide_title: true +--- + + + + +# Adding a session guard to all API routes + +:::note +This is applicable for when the frontend calls an API in the `/app/api` folder. +::: + +For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. + +Create a new file `/app/api/user/route.ts` + +- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). + +```ts title="app/api/user/route.ts" +import { NextResponse, NextRequest } from 'next/server'; +import { withSession } from '../../sessionUtils'; +import SuperTokens from 'supertokens-node'; + +export function GET(request: NextRequest) { + return withSession(request, async (session) => { + // Session will be undefined if it does not exist + if (!session) { + // The frontend sdk will try to refresh the session if it recieves a 401 status code + return new NextResponse('Authentication required', { status: 401 }); + } + + // The session object exposes utility functions to fetch information such as the user id + const userId = session.getUserId(); + const user = await SuperTokens.getUser(userId); + + if (user === undefined) { + return NextResponse.json({ + email: "not found", + }) + } + + return NextResponse.json({ + email: user.emails[0], + }); + }) +} +``` + +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file diff --git a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx new file mode 100644 index 000000000..02dcd2361 --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx @@ -0,0 +1,80 @@ +--- +id: setting-up-backend +title: 3. Adding auth APIs +hide_title: true +--- + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import AppInfoForm from "/src/components/appInfoForm" + +# 3. Adding auth APIs + +We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. + +## 1) Create the `app/api/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app/api/` folder. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). + +## 2) Expose the SuperTokens APIs + + + +```tsx title="pages/api/auth/[[...path]].ts" +import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; +import { NextRequest, NextResponse } from 'next/server'; +import { ensureSuperTokensInit } from '../../../config/backend'; + +ensureSuperTokensInit(); + +const handleCall = getAppDirRequestHandler(NextResponse); + +export async function GET(request: NextRequest) { + const res = await handleCall(request); + if (!res.headers.has('Cache-Control')) { + // This is needed for production deployments with Vercel + res.headers.set( + 'Cache-Control', + 'no-cache, no-store, max-age=0, must-revalidate' + ) + } + return res; +} + +export async function POST(request: NextRequest) { + return handleCall(request); +} + +export async function DELETE(request: NextRequest) { + return handleCall(request); +} + +export async function PUT(request: NextRequest) { + return handleCall(request); +} + +export async function PATCH(request: NextRequest) { + return handleCall(request); +} + +export async function HEAD(request: NextRequest) { + return handleCall(request); +} +``` + +:::note +In the snippet above we add the `Cache-Control` header to the responses for all auth APIs with the `GET` method. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. +::: + + + +## 3) Use the login widget +If you are now able to sign in or sign up, this means the backend setup is done correctly! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) diff --git a/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx new file mode 100644 index 000000000..6d2d488b9 --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx @@ -0,0 +1,72 @@ +--- +id: setting-up-frontend +title: 2. Showing the Login UI +hide_title: true +show_ui_switcher: true +--- + + + + +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import AppInfoForm from "/src/components/appInfoForm"; + +# 2. Showing the Login UI + + + + + +## 1) Create the `app/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app` folder. +- `[[...path]].tsx` will contain the component for showing SuperTokens UI +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). + +## 2) Create the `Auth` component: + +```tsx title="app/auth/[[...path]].tsx" +'use client'; + +import { useEffect } from 'react'; +import { redirectToAuth } from 'supertokens-auth-react'; +import SuperTokens from 'supertokens-auth-react/ui'; +import { ^{recipePreBuiltUINameCapitalLetters} } from "supertokens-auth-react/recipe/^{codeImportRecipeName}/prebuiltui"; + +export default function Auth() { + // if the user visits a page that is not handled by us (like /auth/random), then we redirect them back to the auth page. + useEffect(() => { + if ( + SuperTokens.canHandleRoute([^{recipePreBuiltUINameCapitalLetters}]) === false + ) { + redirectToAuth({ redirectBack: false }); + } + }, []); + + if (typeof window !== 'undefined') { + return SuperTokens.getRoutingComponent([^{recipePreBuiltUINameCapitalLetters}]); + } + + return null; +} +``` + +## 3) Visit `/auth` page on your website + +If you see a login UI, then you have successfully completed this step! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) + + + + + +You need to build your own UI. Please follow the docs after the "Initialisation" section in the "Using your own UI" section for how to build out the various auth flows. + + + + diff --git a/v2/passwordless/sidebars.js b/v2/passwordless/sidebars.js index 62765bf04..4a6622449 100644 --- a/v2/passwordless/sidebars.js +++ b/v2/passwordless/sidebars.js @@ -175,20 +175,47 @@ module.exports = { logoUrl: '/img/logos/next-logo.png' }, items: [ - "nextjs/about", - "nextjs/init", - "nextjs/setting-up-frontend", - "nextjs/setting-up-backend", - "nextjs/protecting-route", { type: 'category', - label: '5. Session verification', + label: 'Using the App directory', items: [ - "nextjs/session-verification/in-api", - "nextjs/session-verification/in-ssr" + "nextjs/app-directory/init", + "nextjs/app-directory/setting-up-frontend", + "nextjs/app-directory/setting-up-backend", + "nextjs/app-directory/session-helpers", + "nextjs/app-directory/protecting-route", + { + type: "category", + label: "6. Checking for sessions in API routes", + items: [ + "nextjs/app-directory/session-verification-session-guard", + "nextjs/app-directory/session-verification-middleware", + ], + }, + "nextjs/app-directory/server-components-requests", + "nextjs/app-directory/next-steps" + ], + }, + { + type: 'category', + label: 'Using the Pages directory', + items: [ + "nextjs/about", + "nextjs/init", + "nextjs/setting-up-frontend", + "nextjs/setting-up-backend", + "nextjs/protecting-route", + { + type: 'category', + label: '5. Session verification', + items: [ + "nextjs/session-verification/in-api", + "nextjs/session-verification/in-ssr" + ], + }, + "nextjs/next-steps" ], }, - "nextjs/next-steps" ], }, { From 4497e383177abc404ed5e616e7f0c1103007d136 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 13 Oct 2023 15:48:34 +0530 Subject: [PATCH 05/27] Add docs for thirdparty --- v2/thirdparty/nextjs/app-directory/init.mdx | 439 ++++++++++++++++++ .../nextjs/app-directory/next-steps.mdx | 35 ++ .../nextjs/app-directory/protecting-route.mdx | 170 +++++++ .../server-components-requests.mdx | 59 +++ .../nextjs/app-directory/session-helpers.mdx | 125 +++++ .../session-verification-middleware.mdx | 57 +++ .../session-verification-session-guard.mdx | 52 +++ .../app-directory/setting-up-backend.mdx | 80 ++++ .../app-directory/setting-up-frontend.mdx | 72 +++ v2/thirdparty/sidebars.js | 45 +- 10 files changed, 1125 insertions(+), 9 deletions(-) create mode 100644 v2/thirdparty/nextjs/app-directory/init.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/next-steps.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/protecting-route.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/server-components-requests.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/session-helpers.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx new file mode 100644 index 000000000..a85f6bf5b --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -0,0 +1,439 @@ +--- +id: init +title: 1. Configuration +hide_title: true +show_ui_switcher: true +--- + + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import {Question, Answer}from "/src/components/question" +import AppInfoForm from "/src/components/appInfoForm" +import CoreInjector from "/src/components/coreInjector" +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + +# 1. Configuration + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + websiteDomain: "^{form_websiteDomain}", + apiBasePath: "^{form_apiBasePath}", + websiteBasePath: "^{form_websiteBasePath}" +} + +``` + + + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + apiBasePath: "^{form_apiBasePath}", +} + +``` + + + + + + + + + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import ThirdPartyReact, {Google, Facebook} from 'supertokens-auth-react/recipe/thirdparty' +import SessionReact from 'supertokens-auth-react/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyReact.init({ + signInAndUpFeature: { + providers: [ + ThirdPartyReact.Google.init(), + ThirdPartyReact.Facebook.init(), + ThirdPartyReact.Apple.init(), + ThirdPartyReact.Github.init(), + ], + }, + }), + SessionReact.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import ThirdPartyWebJs from 'supertokens-web-js/recipe/thirdparty' +import SessionWebJs from 'supertokens-web-js/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyWebJs.init(), + SessionWebJs.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 5) Create a backend config function + + + + + +```tsx title="/config/backendConfig.ts" +import SuperTokens from "supertokens-node"; +import ThirdPartyNode from 'supertokens-node/recipe/thirdparty' +import SessionNode from 'supertokens-node/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import { TypeInput } from "supertokens-node/types"; + +export const backendConfig = (): TypeInput => { + return { + framework: "express", + supertokens: { + ^{coreInjector_connection_uri_comment} + connectionURI: ^{coreInjector_uri} + ^{coreInjector_api_key_commented}apiKey: ^{coreInjector_api_key}, + }, + appInfo, + recipeList: [ + ThirdPartyNode.init({ + signInAndUpFeature: { + // We have provided you with development keys which you can use for testing. + // IMPORTANT: Please replace them with your own OAuth keys for production use. + providers: [{ + config: { + thirdPartyId: "google", + clients: [{ + clientId: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + clientSecret: "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" + }] + } + }, { + config: { + thirdPartyId: "github", + clients: [{ + clientId: "467101b197249757c71f", + clientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd" + }] + } + }, { + config: { + thirdPartyId: "apple", + clients: [{ + clientId: "4398792-io.supertokens.example.service", + additionalConfig: { + keyId: "7M48Y4RYDL", + privateKey: + "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + teamId: "YWQCXGJRJL", + } + }] + } + }], + } + }), + SessionNode.init(), + ], + isInServerlessEnv: true, + } +} + +let initialized = false; +// This function is used in your APIs to make sure SuperTokens is initialised +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} +``` + +`ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. + +**When you want to generate your own keys**, please refer to the corresponding documentation to get your client ids and client secrets for each of the below providers: + +
+Google + +- Generate your client ID and secret by following the [docs here](https://support.google.com/cloud/answer/6158849?hl=en) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/google` + +
+ +
+Github + +- Generate your client ID and secret by following the [docs here](https://docs.github.com/en/developers/apps/creating-an-oauth-app) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/github` + +
+ +
+Facebook + +- Generate your client ID and secret by following the [docs here](https://developers.facebook.com/docs/development/create-an-app) +- Set the authorisation callback URL to `^{form_websiteDomain}^{form_websiteBasePathForCallbacks}/callback/facebook` + +:::info Note +Make sure to enable `https` to be able to use the test users of the Facebook app. On `http://localhost`, the login flow can be verified only with the app's admin user. +::: + +
+ +
+Apple + +- Generate your client ID and secret by following [this article](https://medium.com/identity-beyond-borders/how-to-configure-sign-in-with-apple-77c61e336003) +- Set the authorisation callback URL to `^{form_apiDomain}^{form_apiBasePathForCallbacks}/callback/apple`. Note that Apple doesn't allow `localhost` in the URL. So if you are in dev mode, you can use the dev keys we have provided above. + +
+ +
+ +
+ + + + + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component + +- Create a client component `/app/components/supertokensProvider.tsx`. This file will initialise SuperTokens and wrap its children with the `SuperTokensWrapper` component +- Modify the `/app/layout.tsx` file to use the `SuperTokensProvider` component. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/layout.tsx) + +```tsx title="/app/components/supertokensProvider.tsx" +'use client'; +import React from 'react'; +import { SuperTokensWrapper } from 'supertokens-auth-react'; +import SuperTokensReact from 'supertokens-auth-react'; +import { frontendConfig, setRouter } from '../config/frontend'; +import { usePathname, useRouter } from 'next/navigation'; + +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + SuperTokensReact.init(frontendConfig()); +} + +export const SuperTokensProvider: React.FC> = ({ + children, +}) => { + setRouter(useRouter(), usePathname() || window.location.pathname); + + return {children}; +}; +``` + +```tsx title="/app/layout.tsx" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { SuperTokensProvider } from './components/supertokensProvider' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions + +- Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). + + +```tsx title="/pages/_app.ts" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import SuperTokensWebJs from 'supertokens-web-js' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) + } + + return ( + + {children} + + ) +} +``` + + + + + + \ No newline at end of file diff --git a/v2/thirdparty/nextjs/app-directory/next-steps.mdx b/v2/thirdparty/nextjs/app-directory/next-steps.mdx new file mode 100644 index 000000000..b26f14fad --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/next-steps.mdx @@ -0,0 +1,35 @@ +--- +id: next-steps +title: 8. Next steps +hide_title: true +--- + + + + +import {Question, Answer}from "/src/components/question" + +# 6. Next steps + +## Setting up the core and database + { + return ( + Are you using https://try.supertokens.com as the connection URI in the init function? + ) + }}> + + +You need to now setup an instance of the SuperTokens core for your app (that your backend should connect to). You have two options: +- [Managed service](../quick-setup/core/saas-setup) +- Self hosted with your own database ([With Docker](../quick-setup/core/with-docker) or [Without Docker](../quick-setup/core/without-docker)) + + + + +:::success +You have successfully completed the quick setup! Head over to the "Post login operations" or "Common customizations" section. +::: + + + diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx new file mode 100644 index 000000000..35d8292b0 --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -0,0 +1,170 @@ +--- +id: protecting-route +title: 5. Checking for sessions in frontend routes +hide_title: true +--- + + + + +# 5. Checking for sessions in frontend routes + +Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + +## Sessions with Server Components + +Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + */ + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + return ( +
+
+

+ Hello world +

+
+
+ ); +} +``` + +`getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +'use client'; + +import { useEffect } from 'react'; +import { useRouter, redirect } from 'next/navigation'; +import Session from 'supertokens-auth-react/recipe/session'; +import SuperTokens from 'supertokens-auth-react'; + +export const TryRefreshComponent = () => { + const router = useRouter(); + useEffect(() => { + void Session.attemptRefreshingSession() + .then(() => { + router.refresh(); + }) + .catch(console.error); + }, []); + + return
Loading...
; +}; +``` + +And then we can modify the `/app/page.tsx` file to use our server component + +```tsx title="app/page.tsx" +import { HomePage } from './components/home' +import styles from './page.module.css' + +export default function Home() { + return ( +
+ +
+ ) +} +``` + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: + +:::important +An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). +::: + +## Sessions with Client Components + +Lets create a client component for the `/` route of our website. + +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. + +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. + +:::info +`useSessionContext` can be used along with the `SessionAuth` wrapper component. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: \ No newline at end of file diff --git a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx new file mode 100644 index 000000000..5e13c87c4 --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx @@ -0,0 +1,59 @@ +--- +id: server-components-requests +title: 7. Making requests from Server Components +hide_title: true +--- + + + + +# 7. Making requests from Server Components + +Lets modify the Home page we made in a previous step to make a call to this API + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' +import { SignOut } from './signOut'; + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + const userEmailResponse = await fetch('http://localhost:3000/api/user', { + headers: { + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + let email = ""; + + if (userEmailResponse.status !== 200) { + email = "error with status " + userEmailResponse.status; + } else { + email = (await userEmailResponse.json()).email; + } + + return ( +
+
+

+ Server side component got userId: {session.getUserId()}
+ Server side component got email: {email} +

+
+ +
+ ); +} +``` + +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx new file mode 100644 index 000000000..e701e369a --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -0,0 +1,125 @@ +--- +id: session-helpers +title: 4. Add helper functions for sessions +hide_title: true +--- + + + + +# 4. Add helper functions for sessions + +To make it easy to access session information and protect our API routes we will create some helper functions: + +```ts title="app/sessionUtils.ts" +import { cookies, headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; +import { ensureSuperTokensInit } from './config/backend'; + +ensureSuperTokensInit(); + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + /** + * We try reading the access token from the incoming request if it exists + * + * If the request does not exist or it does not contain an access token we try to read it + * from the existing cookies stored for the website + */ + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} + +export function updateSessionInResponse( + session: SessionContainer, + response?: NextResponse, + ) { + let tokens = session.getAllSessionTokensDangerously(); + if (tokens.accessAndFrontTokenUpdated) { + const accessTokenCookie = { + name: 'sAccessToken', + value: tokens.accessToken, + httpOnly: true, + path: '/', + expires: Date.now() + 3153600000000, + }; + + if (response) { + response.cookies.set(accessTokenCookie); + response.headers.set('front-token', tokens.frontToken); + } else { + cookies().set(accessTokenCookie); + headers().set('front-token', tokens.frontToken); + } + } + } + +export async function withSession( + request: NextRequest, + handler: (session: SessionContainer | undefined) => Promise, + ) { + let { session, resp: stResponse } = await getSSRSession(request); + if (stResponse) { + return stResponse; + } + let userResponse = await handler(session); + + if (session) { + updateSessionInResponse(session, userResponse); + } + return userResponse; +} +``` + +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. + +- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx new file mode 100644 index 000000000..0bf314643 --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx @@ -0,0 +1,57 @@ +--- +id: session-verification-middleware +title: Using the Next.js middleware +hide_title: true +--- + + + + +# Using the Next.js middleware + +```tsx title="middleware.tsx" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import { SessionContainer } from 'supertokens-node/recipe/session' +import { withSession } from './app/sessionUtils'; + + +export async function middleware( + request: NextRequest & { session?: SessionContainer } +) { + if (request.headers.has("x-user-id")) { + console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); + request.headers.delete("x-user-id"); + } + + if (request.nextUrl.pathname.startsWith('/api/auth')) { + /** + * /api/auth/* endpoints are exposed by the SuperTokens SDK, + * we do not want to modify the request for these routes + */ + return NextResponse.next() + } + + return withSession(request, async (session) => { + if (session === undefined) { + return NextResponse.next() + } + return NextResponse.next({ + headers: { + // You cannot attach the full session object here + 'x-user-id': session.getUserId(), + }, + }) + }) +} + +export const config = { + matcher: '/api/:path*', +} +``` + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) +::: \ No newline at end of file diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx new file mode 100644 index 000000000..e6b86f8d2 --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx @@ -0,0 +1,52 @@ +--- +id: session-verification-session-guard +title: Adding a session guard to all API routes +hide_title: true +--- + + + + +# Adding a session guard to all API routes + +:::note +This is applicable for when the frontend calls an API in the `/app/api` folder. +::: + +For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. + +Create a new file `/app/api/user/route.ts` + +- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). + +```ts title="app/api/user/route.ts" +import { NextResponse, NextRequest } from 'next/server'; +import { withSession } from '../../sessionUtils'; +import SuperTokens from 'supertokens-node'; + +export function GET(request: NextRequest) { + return withSession(request, async (session) => { + // Session will be undefined if it does not exist + if (!session) { + // The frontend sdk will try to refresh the session if it recieves a 401 status code + return new NextResponse('Authentication required', { status: 401 }); + } + + // The session object exposes utility functions to fetch information such as the user id + const userId = session.getUserId(); + const user = await SuperTokens.getUser(userId); + + if (user === undefined) { + return NextResponse.json({ + email: "not found", + }) + } + + return NextResponse.json({ + email: user.emails[0], + }); + }) +} +``` + +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx new file mode 100644 index 000000000..02dcd2361 --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx @@ -0,0 +1,80 @@ +--- +id: setting-up-backend +title: 3. Adding auth APIs +hide_title: true +--- + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import AppInfoForm from "/src/components/appInfoForm" + +# 3. Adding auth APIs + +We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. + +## 1) Create the `app/api/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app/api/` folder. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). + +## 2) Expose the SuperTokens APIs + + + +```tsx title="pages/api/auth/[[...path]].ts" +import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; +import { NextRequest, NextResponse } from 'next/server'; +import { ensureSuperTokensInit } from '../../../config/backend'; + +ensureSuperTokensInit(); + +const handleCall = getAppDirRequestHandler(NextResponse); + +export async function GET(request: NextRequest) { + const res = await handleCall(request); + if (!res.headers.has('Cache-Control')) { + // This is needed for production deployments with Vercel + res.headers.set( + 'Cache-Control', + 'no-cache, no-store, max-age=0, must-revalidate' + ) + } + return res; +} + +export async function POST(request: NextRequest) { + return handleCall(request); +} + +export async function DELETE(request: NextRequest) { + return handleCall(request); +} + +export async function PUT(request: NextRequest) { + return handleCall(request); +} + +export async function PATCH(request: NextRequest) { + return handleCall(request); +} + +export async function HEAD(request: NextRequest) { + return handleCall(request); +} +``` + +:::note +In the snippet above we add the `Cache-Control` header to the responses for all auth APIs with the `GET` method. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. +::: + + + +## 3) Use the login widget +If you are now able to sign in or sign up, this means the backend setup is done correctly! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx new file mode 100644 index 000000000..6d2d488b9 --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx @@ -0,0 +1,72 @@ +--- +id: setting-up-frontend +title: 2. Showing the Login UI +hide_title: true +show_ui_switcher: true +--- + + + + +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import AppInfoForm from "/src/components/appInfoForm"; + +# 2. Showing the Login UI + + + + + +## 1) Create the `app/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app` folder. +- `[[...path]].tsx` will contain the component for showing SuperTokens UI +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). + +## 2) Create the `Auth` component: + +```tsx title="app/auth/[[...path]].tsx" +'use client'; + +import { useEffect } from 'react'; +import { redirectToAuth } from 'supertokens-auth-react'; +import SuperTokens from 'supertokens-auth-react/ui'; +import { ^{recipePreBuiltUINameCapitalLetters} } from "supertokens-auth-react/recipe/^{codeImportRecipeName}/prebuiltui"; + +export default function Auth() { + // if the user visits a page that is not handled by us (like /auth/random), then we redirect them back to the auth page. + useEffect(() => { + if ( + SuperTokens.canHandleRoute([^{recipePreBuiltUINameCapitalLetters}]) === false + ) { + redirectToAuth({ redirectBack: false }); + } + }, []); + + if (typeof window !== 'undefined') { + return SuperTokens.getRoutingComponent([^{recipePreBuiltUINameCapitalLetters}]); + } + + return null; +} +``` + +## 3) Visit `/auth` page on your website + +If you see a login UI, then you have successfully completed this step! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) + + + + + +You need to build your own UI. Please follow the docs after the "Initialisation" section in the "Using your own UI" section for how to build out the various auth flows. + + + + diff --git a/v2/thirdparty/sidebars.js b/v2/thirdparty/sidebars.js index 327297b1f..386a2c723 100644 --- a/v2/thirdparty/sidebars.js +++ b/v2/thirdparty/sidebars.js @@ -174,20 +174,47 @@ module.exports = { logoUrl: '/img/logos/next-logo.png' }, items: [ - "nextjs/about", - "nextjs/init", - "nextjs/setting-up-frontend", - "nextjs/setting-up-backend", - "nextjs/protecting-route", { type: 'category', - label: '5. Session verification', + label: 'Using the App directory', items: [ - "nextjs/session-verification/in-api", - "nextjs/session-verification/in-ssr" + "nextjs/app-directory/init", + "nextjs/app-directory/setting-up-frontend", + "nextjs/app-directory/setting-up-backend", + "nextjs/app-directory/session-helpers", + "nextjs/app-directory/protecting-route", + { + type: "category", + label: "6. Checking for sessions in API routes", + items: [ + "nextjs/app-directory/session-verification-session-guard", + "nextjs/app-directory/session-verification-middleware", + ], + }, + "nextjs/app-directory/server-components-requests", + "nextjs/app-directory/next-steps" + ], + }, + { + type: 'category', + label: 'Using the Pages directory', + items: [ + "nextjs/about", + "nextjs/init", + "nextjs/setting-up-frontend", + "nextjs/setting-up-backend", + "nextjs/protecting-route", + { + type: 'category', + label: '5. Session verification', + items: [ + "nextjs/session-verification/in-api", + "nextjs/session-verification/in-ssr" + ], + }, + "nextjs/next-steps" ], }, - "nextjs/next-steps" ], }, { From 3a20decb4f4d075196e5a7ee88cb5568b4218acb Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 13 Oct 2023 15:51:17 +0530 Subject: [PATCH 06/27] Add docs for thirdparty --- .../nextjs/app-directory/init.mdx | 362 ++++++++++++++++++ .../nextjs/app-directory/next-steps.mdx | 35 ++ .../nextjs/app-directory/protecting-route.mdx | 170 ++++++++ .../server-components-requests.mdx | 59 +++ .../nextjs/app-directory/session-helpers.mdx | 125 ++++++ .../session-verification-middleware.mdx | 57 +++ .../session-verification-session-guard.mdx | 52 +++ .../app-directory/setting-up-backend.mdx | 80 ++++ .../app-directory/setting-up-frontend.mdx | 72 ++++ v2/thirdpartypasswordless/sidebars.js | 45 ++- 10 files changed, 1048 insertions(+), 9 deletions(-) create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/init.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/next-steps.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx new file mode 100644 index 000000000..aa8199771 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -0,0 +1,362 @@ +--- +id: init +title: 1. Configuration +hide_title: true +show_ui_switcher: true +--- + + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import {Question, Answer}from "/src/components/question" +import AppInfoForm from "/src/components/appInfoForm" +import CoreInjector from "/src/components/coreInjector" +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + +# 1. Configuration + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + websiteDomain: "^{form_websiteDomain}", + apiBasePath: "^{form_apiBasePath}", + websiteBasePath: "^{form_websiteBasePath}" +} + +``` + + + + + + + +## 1) Install supertokens package +```bash +yarn add supertokens-node supertokens-web-js nextjs-cors +``` + +## 2) Create configuration files +- Create a `config` folder in the root directory of your project +- Create an `appInfo.ts` inside the `config` folder. +- Create a `backendConfig.ts` inside the `config` folder. +- Create a `frontendConfig.ts` inside the `config` folder. + + +## 3) Create the `appInfo` configuration. + + + +```tsx title="/config/appInfo.ts" + +export const appInfo = { + // learn more about this on https://supertokens.com/docs/thirdpartyemailpassword/appinfo + appName: "^{form_appName}", + apiDomain: "^{form_apiDomain}", + apiBasePath: "^{form_apiBasePath}", +} + +``` + + + + + + + + + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import ThirdPartyPasswordlessReact from 'supertokens-auth-react/recipe/thirdpartypasswordless' +import SessionReact from 'supertokens-auth-react/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyPasswordlessReact.init({ + contactMethod: "^{form_contactMethod}" + }), + SessionReact.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 4) Create a frontend config function + +```tsx title="/config/frontendConfig.ts" +import ThirdPartyPasswordlessWebJs from 'supertokens-web-js/recipe/thirdpartypasswordless' +import SessionWebJs from 'supertokens-web-js/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import Router from 'next/navigation' + +const routerInfo: { router?: ReturnType; pathName?: string } = + {}; + +export function setRouter( + router: ReturnType, + pathName: string, +) { + routerInfo.router = router; + routerInfo.pathName = pathName; +} + +export const frontendConfig = () => { + return { + appInfo, + recipeList: [ + ThirdPartyPasswordlessWebJs.init(), + SessionWebJs.init(), + ], + windowHandler: (original) => ({ + ...original, + location: { + ...original.location, + getPathName: () => routerInfo.pathName!, + assign: (url) => routerInfo.router!.push(url.toString()), + setHref: (url) => routerInfo.router!.push(url.toString()), + }, + }), + } +} +``` + + + + + +## 5) Create a backend config function + + + + + +```tsx title="/config/backendConfig.ts" +import SuperTokens from "supertokens-node"; +import ThirdPartyPasswordlessNode from 'supertokens-node/recipe/thirdpartypasswordless' +import SessionNode from 'supertokens-node/recipe/session' +// @ts-ignore +import { appInfo } from './appInfo' +import { TypeInput } from "supertokens-node/types"; + +export const backendConfig = (): TypeInput => { + return { + framework: "express", + supertokens: { + ^{coreInjector_connection_uri_comment} + connectionURI: ^{coreInjector_uri} + ^{coreInjector_api_key_commented}apiKey: ^{coreInjector_api_key}, + }, + appInfo, + recipeList: [ + ThirdPartyPasswordlessNode.init({ + flowType: "^{form_flowType}", + contactMethod: "^{form_contactMethod}" + }), + SessionNode.init(), + ], + isInServerlessEnv: true, + } +} + +let initialized = false; +// This function is used in your APIs to make sure SuperTokens is initialised +export function ensureSuperTokensInit() { + if (!initialized) { + SuperTokens.init(backendConfig()); + initialized = true; + } +} +``` + +`ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. + + + + + + + + + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component + +- Create a client component `/app/components/supertokensProvider.tsx`. This file will initialise SuperTokens and wrap its children with the `SuperTokensWrapper` component +- Modify the `/app/layout.tsx` file to use the `SuperTokensProvider` component. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/layout.tsx) + +```tsx title="/app/components/supertokensProvider.tsx" +'use client'; +import React from 'react'; +import { SuperTokensWrapper } from 'supertokens-auth-react'; +import SuperTokensReact from 'supertokens-auth-react'; +import { frontendConfig, setRouter } from '../config/frontend'; +import { usePathname, useRouter } from 'next/navigation'; + +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + SuperTokensReact.init(frontendConfig()); +} + +export const SuperTokensProvider: React.FC> = ({ + children, +}) => { + setRouter(useRouter(), usePathname() || window.location.pathname); + + return {children}; +}; +``` + +```tsx title="/app/layout.tsx" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { SuperTokensProvider } from './components/supertokensProvider' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + + + + + +## ^{nextjsinitlastnumber}) Call the frontend `init` functions + +- Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). + + +```tsx title="/pages/_app.ts" +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import SuperTokensWebJs from 'supertokens-web-js' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) + } + + return ( + + {children} + + ) +} +``` + + + + + + \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/next-steps.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/next-steps.mdx new file mode 100644 index 000000000..b26f14fad --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/next-steps.mdx @@ -0,0 +1,35 @@ +--- +id: next-steps +title: 8. Next steps +hide_title: true +--- + + + + +import {Question, Answer}from "/src/components/question" + +# 6. Next steps + +## Setting up the core and database + { + return ( + Are you using https://try.supertokens.com as the connection URI in the init function? + ) + }}> + + +You need to now setup an instance of the SuperTokens core for your app (that your backend should connect to). You have two options: +- [Managed service](../quick-setup/core/saas-setup) +- Self hosted with your own database ([With Docker](../quick-setup/core/with-docker) or [Without Docker](../quick-setup/core/without-docker)) + + + + +:::success +You have successfully completed the quick setup! Head over to the "Post login operations" or "Common customizations" section. +::: + + + diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx new file mode 100644 index 000000000..35d8292b0 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -0,0 +1,170 @@ +--- +id: protecting-route +title: 5. Checking for sessions in frontend routes +hide_title: true +--- + + + + +# 5. Checking for sessions in frontend routes + +Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + +## Sessions with Server Components + +Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + */ + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + return ( +
+
+

+ Hello world +

+
+
+ ); +} +``` + +`getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +'use client'; + +import { useEffect } from 'react'; +import { useRouter, redirect } from 'next/navigation'; +import Session from 'supertokens-auth-react/recipe/session'; +import SuperTokens from 'supertokens-auth-react'; + +export const TryRefreshComponent = () => { + const router = useRouter(); + useEffect(() => { + void Session.attemptRefreshingSession() + .then(() => { + router.refresh(); + }) + .catch(console.error); + }, []); + + return
Loading...
; +}; +``` + +And then we can modify the `/app/page.tsx` file to use our server component + +```tsx title="app/page.tsx" +import { HomePage } from './components/home' +import styles from './page.module.css' + +export default function Home() { + return ( +
+ +
+ ) +} +``` + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: + +:::important +An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). +::: + +## Sessions with Client Components + +Lets create a client component for the `/` route of our website. + +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. + +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. + +:::info +`useSessionContext` can be used along with the `SessionAuth` wrapper component. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. +::: \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx new file mode 100644 index 000000000..5e13c87c4 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx @@ -0,0 +1,59 @@ +--- +id: server-components-requests +title: 7. Making requests from Server Components +hide_title: true +--- + + + + +# 7. Making requests from Server Components + +Lets modify the Home page we made in a previous step to make a call to this API + +```tsx title="app/components/home.tsx" +import { getSSRSession } from '../sessionUtils'; +import { TryRefreshComponent } from './tryRefreshClientComponent'; +import styles from '../page.module.css'; +import { redirect } from 'next/navigation' +import { SignOut } from './signOut'; + +export async function HomePage() { + const { session, hasToken } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + redirect('/auth'); + } + return ; + } + + const userEmailResponse = await fetch('http://localhost:3000/api/user', { + headers: { + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + let email = ""; + + if (userEmailResponse.status !== 200) { + email = "error with status " + userEmailResponse.status; + } else { + email = (await userEmailResponse.json()).email; + } + + return ( +
+
+

+ Server side component got userId: {session.getUserId()}
+ Server side component got email: {email} +

+
+ +
+ ); +} +``` + +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx new file mode 100644 index 000000000..e701e369a --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -0,0 +1,125 @@ +--- +id: session-helpers +title: 4. Add helper functions for sessions +hide_title: true +--- + + + + +# 4. Add helper functions for sessions + +To make it easy to access session information and protect our API routes we will create some helper functions: + +```ts title="app/sessionUtils.ts" +import { cookies, headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import Session, { SessionContainer } from 'supertokens-node/recipe/session'; +import { ensureSuperTokensInit } from './config/backend'; + +ensureSuperTokensInit(); + +export async function getSSRSession(req?: NextRequest): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + resp?: Response; +}> { + let token; + /** + * We try reading the access token from the incoming request if it exists + * + * If the request does not exist or it does not contain an access token we try to read it + * from the existing cookies stored for the website + */ + if (req?.cookies) { + token = req.cookies.get('sAccessToken')?.value; + } else { + token = cookies().get('sAccessToken')?.value; + } + + if (req?.headers.get("Authorization")) { + token = req.headers.get("Authorization")!; + // We remove the "Bearer" from the token + if (token.includes("Bearer")) { + token = token.replace("Bearer ", ""); + } + } + + if (token === undefined) { + return { + session: undefined, + hasToken: false, + resp: new NextResponse('Authentication required', { status: 401 }), + }; + } + + let session; + let resp; + + try { + session = await Session.getSessionWithoutRequestResponse(token, undefined, { + sessionRequired: false, + }); + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + resp = new NextResponse('Authentication required', { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }); + } else { + throw err; + } + } + + return { + session, + hasToken: true, + resp, + }; +} + +export function updateSessionInResponse( + session: SessionContainer, + response?: NextResponse, + ) { + let tokens = session.getAllSessionTokensDangerously(); + if (tokens.accessAndFrontTokenUpdated) { + const accessTokenCookie = { + name: 'sAccessToken', + value: tokens.accessToken, + httpOnly: true, + path: '/', + expires: Date.now() + 3153600000000, + }; + + if (response) { + response.cookies.set(accessTokenCookie); + response.headers.set('front-token', tokens.frontToken); + } else { + cookies().set(accessTokenCookie); + headers().set('front-token', tokens.frontToken); + } + } + } + +export async function withSession( + request: NextRequest, + handler: (session: SessionContainer | undefined) => Promise, + ) { + let { session, resp: stResponse } = await getSSRSession(request); + if (stResponse) { + return stResponse; + } + let userResponse = await handler(session); + + if (session) { + updateSessionInResponse(session, userResponse); + } + return userResponse; +} +``` + +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. + +- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx new file mode 100644 index 000000000..0bf314643 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx @@ -0,0 +1,57 @@ +--- +id: session-verification-middleware +title: Using the Next.js middleware +hide_title: true +--- + + + + +# Using the Next.js middleware + +```tsx title="middleware.tsx" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import { SessionContainer } from 'supertokens-node/recipe/session' +import { withSession } from './app/sessionUtils'; + + +export async function middleware( + request: NextRequest & { session?: SessionContainer } +) { + if (request.headers.has("x-user-id")) { + console.warn("The FE tried to pass x-user-id, which is only supposed to be a backend internal header. Ignoring."); + request.headers.delete("x-user-id"); + } + + if (request.nextUrl.pathname.startsWith('/api/auth')) { + /** + * /api/auth/* endpoints are exposed by the SuperTokens SDK, + * we do not want to modify the request for these routes + */ + return NextResponse.next() + } + + return withSession(request, async (session) => { + if (session === undefined) { + return NextResponse.next() + } + return NextResponse.next({ + headers: { + // You cannot attach the full session object here + 'x-user-id': session.getUserId(), + }, + }) + }) +} + +export const config = { + matcher: '/api/:path*', +} +``` + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) +::: \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx new file mode 100644 index 000000000..e6b86f8d2 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -0,0 +1,52 @@ +--- +id: session-verification-session-guard +title: Adding a session guard to all API routes +hide_title: true +--- + + + + +# Adding a session guard to all API routes + +:::note +This is applicable for when the frontend calls an API in the `/app/api` folder. +::: + +For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. + +Create a new file `/app/api/user/route.ts` + +- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). + +```ts title="app/api/user/route.ts" +import { NextResponse, NextRequest } from 'next/server'; +import { withSession } from '../../sessionUtils'; +import SuperTokens from 'supertokens-node'; + +export function GET(request: NextRequest) { + return withSession(request, async (session) => { + // Session will be undefined if it does not exist + if (!session) { + // The frontend sdk will try to refresh the session if it recieves a 401 status code + return new NextResponse('Authentication required', { status: 401 }); + } + + // The session object exposes utility functions to fetch information such as the user id + const userId = session.getUserId(); + const user = await SuperTokens.getUser(userId); + + if (user === undefined) { + return NextResponse.json({ + email: "not found", + }) + } + + return NextResponse.json({ + email: user.emails[0], + }); + }) +} +``` + +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx new file mode 100644 index 000000000..02dcd2361 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx @@ -0,0 +1,80 @@ +--- +id: setting-up-backend +title: 3. Adding auth APIs +hide_title: true +--- + + + + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import AppInfoForm from "/src/components/appInfoForm" + +# 3. Adding auth APIs + +We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. + +## 1) Create the `app/api/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app/api/` folder. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). + +## 2) Expose the SuperTokens APIs + + + +```tsx title="pages/api/auth/[[...path]].ts" +import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; +import { NextRequest, NextResponse } from 'next/server'; +import { ensureSuperTokensInit } from '../../../config/backend'; + +ensureSuperTokensInit(); + +const handleCall = getAppDirRequestHandler(NextResponse); + +export async function GET(request: NextRequest) { + const res = await handleCall(request); + if (!res.headers.has('Cache-Control')) { + // This is needed for production deployments with Vercel + res.headers.set( + 'Cache-Control', + 'no-cache, no-store, max-age=0, must-revalidate' + ) + } + return res; +} + +export async function POST(request: NextRequest) { + return handleCall(request); +} + +export async function DELETE(request: NextRequest) { + return handleCall(request); +} + +export async function PUT(request: NextRequest) { + return handleCall(request); +} + +export async function PATCH(request: NextRequest) { + return handleCall(request); +} + +export async function HEAD(request: NextRequest) { + return handleCall(request); +} +``` + +:::note +In the snippet above we add the `Cache-Control` header to the responses for all auth APIs with the `GET` method. This is required if you are deploying your app with Vercel because API responses are automatically cached for production deployments. This results in problems because APIs such as `/session/refresh` return older session tokens resulting in infinite calls to refresh if an API returns unauthorised status. Setting the header ensures that Vercel does not cache any of the auth API responses. +::: + + + +## 3) Use the login widget +If you are now able to sign in or sign up, this means the backend setup is done correctly! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx new file mode 100644 index 000000000..6d2d488b9 --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx @@ -0,0 +1,72 @@ +--- +id: setting-up-frontend +title: 2. Showing the Login UI +hide_title: true +show_ui_switcher: true +--- + + + + +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import AppInfoForm from "/src/components/appInfoForm"; + +# 2. Showing the Login UI + + + + + +## 1) Create the `app/auth/[[...path]].tsx` page +- Be sure to create the `auth` folder in the `app` folder. +- `[[...path]].tsx` will contain the component for showing SuperTokens UI +- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). + +## 2) Create the `Auth` component: + +```tsx title="app/auth/[[...path]].tsx" +'use client'; + +import { useEffect } from 'react'; +import { redirectToAuth } from 'supertokens-auth-react'; +import SuperTokens from 'supertokens-auth-react/ui'; +import { ^{recipePreBuiltUINameCapitalLetters} } from "supertokens-auth-react/recipe/^{codeImportRecipeName}/prebuiltui"; + +export default function Auth() { + // if the user visits a page that is not handled by us (like /auth/random), then we redirect them back to the auth page. + useEffect(() => { + if ( + SuperTokens.canHandleRoute([^{recipePreBuiltUINameCapitalLetters}]) === false + ) { + redirectToAuth({ redirectBack: false }); + } + }, []); + + if (typeof window !== 'undefined') { + return SuperTokens.getRoutingComponent([^{recipePreBuiltUINameCapitalLetters}]); + } + + return null; +} +``` + +## 3) Visit `/auth` page on your website + +If you see a login UI, then you have successfully completed this step! If not, please feel free to ask questions on [Discord](https://supertokens.com/discord) + + + + + +You need to build your own UI. Please follow the docs after the "Initialisation" section in the "Using your own UI" section for how to build out the various auth flows. + + + + diff --git a/v2/thirdpartypasswordless/sidebars.js b/v2/thirdpartypasswordless/sidebars.js index 62a2c49ef..e6197b1cf 100644 --- a/v2/thirdpartypasswordless/sidebars.js +++ b/v2/thirdpartypasswordless/sidebars.js @@ -177,20 +177,47 @@ module.exports = { logoUrl: '/img/logos/next-logo.png' }, items: [ - "nextjs/about", - "nextjs/init", - "nextjs/setting-up-frontend", - "nextjs/setting-up-backend", - "nextjs/protecting-route", { type: 'category', - label: '5. Session verification', + label: 'Using the App directory', items: [ - "nextjs/session-verification/in-api", - "nextjs/session-verification/in-ssr" + "nextjs/app-directory/init", + "nextjs/app-directory/setting-up-frontend", + "nextjs/app-directory/setting-up-backend", + "nextjs/app-directory/session-helpers", + "nextjs/app-directory/protecting-route", + { + type: "category", + label: "6. Checking for sessions in API routes", + items: [ + "nextjs/app-directory/session-verification-session-guard", + "nextjs/app-directory/session-verification-middleware", + ], + }, + "nextjs/app-directory/server-components-requests", + "nextjs/app-directory/next-steps" + ], + }, + { + type: 'category', + label: 'Using the Pages directory', + items: [ + "nextjs/about", + "nextjs/init", + "nextjs/setting-up-frontend", + "nextjs/setting-up-backend", + "nextjs/protecting-route", + { + type: 'category', + label: '5. Session verification', + items: [ + "nextjs/session-verification/in-api", + "nextjs/session-verification/in-ssr" + ], + }, + "nextjs/next-steps" ], }, - "nextjs/next-steps" ], }, { From 17d032809dea467419b1f8eb4dd2bb744b89b1c4 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 19 Oct 2023 10:02:06 +0530 Subject: [PATCH 07/27] Refactor session utils --- .../nextjs/app-directory/session-helpers.mdx | 215 ++++++++++-------- .../nextjs/app-directory/session-helpers.mdx | 215 ++++++++++-------- .../nextjs/app-directory/session-helpers.mdx | 215 ++++++++++-------- .../nextjs/app-directory/session-helpers.mdx | 215 ++++++++++-------- .../nextjs/app-directory/session-helpers.mdx | 215 ++++++++++-------- 5 files changed, 620 insertions(+), 455 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index e701e369a..75c484581 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -12,114 +12,147 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" -import { cookies, headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; -import { ensureSuperTokensInit } from './config/backend'; +import { serialize } from "cookie"; +import { cookies, headers } from "next/headers"; +import { NextRequest, NextResponse } from "next/server"; +import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; +import { ensureSuperTokensInit } from "./config/backend"; +import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; +import { HTTPMethod } from "supertokens-node/types"; ensureSuperTokensInit(); -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; +export async function getSSRSession( + req?: NextRequest, + options?: VerifySessionOptions +): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + hasInvalidClaims: boolean; + baseResponse: CollectingResponse; + nextResponse?: NextResponse; }> { - let token; - /** - * We try reading the access token from the incoming request if it exists - * - * If the request does not exist or it does not contain an access token we try to read it - * from the existing cookies stored for the website - */ - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, + const query = req !== undefined ? Object.fromEntries(new URL(req.url).searchParams.entries()) : {}; + const parsedCookies: Record = Object.fromEntries( + (req !== undefined ? req.cookies : cookies()).getAll().map((cookie) => [cookie.name, cookie.value]) + ); + + /** + * Pre parsed request is a wrapper exposed by SuperTokens. It is used as a helper to detect if the + * original request contains session tokens. We then use this pre parsed request to call `getSession` + * to check if there is a valid session. + */ + let baseRequest = new PreParsedRequest({ + method: req !== undefined ? (req.method as HTTPMethod) : "get", + url: req !== undefined ? req.url : "", + query: query, + headers: req !== undefined ? req.headers : headers(), + cookies: parsedCookies, + getFormBody: () => req!.formData(), + getJSONBody: () => req!.json(), }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} -export function updateSessionInResponse( - session: SessionContainer, - response?: NextResponse, - ) { - let tokens = session.getAllSessionTokensDangerously(); - if (tokens.accessAndFrontTokenUpdated) { - const accessTokenCookie = { - name: 'sAccessToken', - value: tokens.accessToken, - httpOnly: true, - path: '/', - expires: Date.now() + 3153600000000, - }; - - if (response) { - response.cookies.set(accessTokenCookie); - response.headers.set('front-token', tokens.frontToken); - } else { - cookies().set(accessTokenCookie); - headers().set('front-token', tokens.frontToken); - } + /** + * Collecting response is a wrapper exposed by SuperTokens. In this case we are using an empty + * CollectingResponse when calling `getSession`. If the request contains valid session tokens + * the SuperTokens SDK will attach all the relevant tokens to the collecting response object which + * we can then use to return those session tokens in the final result (refer to `withSession` in this file) + */ + let baseResponse = new CollectingResponse(); + + try { + /** + * `getSession` will throw if session is required and there is no valid session. You can use + * `options` to configure whether or not you want to require sessions when calling `getSSRSession` + */ + let session = await Session.getSession(baseRequest, baseResponse, options); + return { + session, + hasInvalidClaims: false, + hasToken: session !== undefined, + baseResponse, + }; + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + return { + hasToken: err.type !== Session.Error.UNAUTHORISED, + /** + * This allows us to protect our routes based on the current session claims. For example + * this will be true if email verification is required but the user has not verified their + * email. + */ + hasInvalidClaims: err.type === Session.Error.INVALID_CLAIMS, + session: undefined, + baseResponse, + nextResponse: new NextResponse("Authentication required", { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }), + }; + } else { + throw err; + } } - } +} export async function withSession( request: NextRequest, handler: (session: SessionContainer | undefined) => Promise, - ) { - let { session, resp: stResponse } = await getSSRSession(request); - if (stResponse) { - return stResponse; + options?: VerifySessionOptions +) { + let { session, nextResponse, baseResponse } = await getSSRSession(request, options); + if (nextResponse) { + return nextResponse; } + let userResponse = await handler(session); - if (session) { - updateSessionInResponse(session, userResponse); + let didAddCookies = false; + let didAddHeaders = false; + + /** + * Base response is the response from SuperTokens that contains all the session tokens. + * We add all cookies and headers in the base response to the final response from the + * API to make sure sessions work correctly. + */ + for (const respCookie of baseResponse.cookies) { + didAddCookies = true; + userResponse.headers.append( + "Set-Cookie", + serialize(respCookie.key, respCookie.value, { + domain: respCookie.domain, + expires: new Date(respCookie.expires), + httpOnly: respCookie.httpOnly, + path: respCookie.path, + sameSite: respCookie.sameSite, + secure: respCookie.secure, + }) + ); } + + baseResponse.headers.forEach((value, key) => { + didAddHeaders = true; + userResponse.headers.set(key, value); + }); + + /** + * For some deployment services (Vercel for example) production builds can return cached results for + * APIs with older header values. In this case if the session tokens have changed (because of refreshing + * for example) the cached result would still contain the older tokens and sessions would stop working. + * + * As a result, if we add cookies or headers from base response we also set the Cache-Control header + * to make sure that the final result is not a cached version. + */ + if (didAddCookies || didAddHeaders) { + if (userResponse.headers.has("Cache-Control")) { + // This is needed for production deployments with Vercel + userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + } + } + return userResponse; } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. -- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index e701e369a..75c484581 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -12,114 +12,147 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" -import { cookies, headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; -import { ensureSuperTokensInit } from './config/backend'; +import { serialize } from "cookie"; +import { cookies, headers } from "next/headers"; +import { NextRequest, NextResponse } from "next/server"; +import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; +import { ensureSuperTokensInit } from "./config/backend"; +import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; +import { HTTPMethod } from "supertokens-node/types"; ensureSuperTokensInit(); -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; +export async function getSSRSession( + req?: NextRequest, + options?: VerifySessionOptions +): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + hasInvalidClaims: boolean; + baseResponse: CollectingResponse; + nextResponse?: NextResponse; }> { - let token; - /** - * We try reading the access token from the incoming request if it exists - * - * If the request does not exist or it does not contain an access token we try to read it - * from the existing cookies stored for the website - */ - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, + const query = req !== undefined ? Object.fromEntries(new URL(req.url).searchParams.entries()) : {}; + const parsedCookies: Record = Object.fromEntries( + (req !== undefined ? req.cookies : cookies()).getAll().map((cookie) => [cookie.name, cookie.value]) + ); + + /** + * Pre parsed request is a wrapper exposed by SuperTokens. It is used as a helper to detect if the + * original request contains session tokens. We then use this pre parsed request to call `getSession` + * to check if there is a valid session. + */ + let baseRequest = new PreParsedRequest({ + method: req !== undefined ? (req.method as HTTPMethod) : "get", + url: req !== undefined ? req.url : "", + query: query, + headers: req !== undefined ? req.headers : headers(), + cookies: parsedCookies, + getFormBody: () => req!.formData(), + getJSONBody: () => req!.json(), }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} -export function updateSessionInResponse( - session: SessionContainer, - response?: NextResponse, - ) { - let tokens = session.getAllSessionTokensDangerously(); - if (tokens.accessAndFrontTokenUpdated) { - const accessTokenCookie = { - name: 'sAccessToken', - value: tokens.accessToken, - httpOnly: true, - path: '/', - expires: Date.now() + 3153600000000, - }; - - if (response) { - response.cookies.set(accessTokenCookie); - response.headers.set('front-token', tokens.frontToken); - } else { - cookies().set(accessTokenCookie); - headers().set('front-token', tokens.frontToken); - } + /** + * Collecting response is a wrapper exposed by SuperTokens. In this case we are using an empty + * CollectingResponse when calling `getSession`. If the request contains valid session tokens + * the SuperTokens SDK will attach all the relevant tokens to the collecting response object which + * we can then use to return those session tokens in the final result (refer to `withSession` in this file) + */ + let baseResponse = new CollectingResponse(); + + try { + /** + * `getSession` will throw if session is required and there is no valid session. You can use + * `options` to configure whether or not you want to require sessions when calling `getSSRSession` + */ + let session = await Session.getSession(baseRequest, baseResponse, options); + return { + session, + hasInvalidClaims: false, + hasToken: session !== undefined, + baseResponse, + }; + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + return { + hasToken: err.type !== Session.Error.UNAUTHORISED, + /** + * This allows us to protect our routes based on the current session claims. For example + * this will be true if email verification is required but the user has not verified their + * email. + */ + hasInvalidClaims: err.type === Session.Error.INVALID_CLAIMS, + session: undefined, + baseResponse, + nextResponse: new NextResponse("Authentication required", { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }), + }; + } else { + throw err; + } } - } +} export async function withSession( request: NextRequest, handler: (session: SessionContainer | undefined) => Promise, - ) { - let { session, resp: stResponse } = await getSSRSession(request); - if (stResponse) { - return stResponse; + options?: VerifySessionOptions +) { + let { session, nextResponse, baseResponse } = await getSSRSession(request, options); + if (nextResponse) { + return nextResponse; } + let userResponse = await handler(session); - if (session) { - updateSessionInResponse(session, userResponse); + let didAddCookies = false; + let didAddHeaders = false; + + /** + * Base response is the response from SuperTokens that contains all the session tokens. + * We add all cookies and headers in the base response to the final response from the + * API to make sure sessions work correctly. + */ + for (const respCookie of baseResponse.cookies) { + didAddCookies = true; + userResponse.headers.append( + "Set-Cookie", + serialize(respCookie.key, respCookie.value, { + domain: respCookie.domain, + expires: new Date(respCookie.expires), + httpOnly: respCookie.httpOnly, + path: respCookie.path, + sameSite: respCookie.sameSite, + secure: respCookie.secure, + }) + ); } + + baseResponse.headers.forEach((value, key) => { + didAddHeaders = true; + userResponse.headers.set(key, value); + }); + + /** + * For some deployment services (Vercel for example) production builds can return cached results for + * APIs with older header values. In this case if the session tokens have changed (because of refreshing + * for example) the cached result would still contain the older tokens and sessions would stop working. + * + * As a result, if we add cookies or headers from base response we also set the Cache-Control header + * to make sure that the final result is not a cached version. + */ + if (didAddCookies || didAddHeaders) { + if (userResponse.headers.has("Cache-Control")) { + // This is needed for production deployments with Vercel + userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + } + } + return userResponse; } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. -- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index e701e369a..75c484581 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -12,114 +12,147 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" -import { cookies, headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; -import { ensureSuperTokensInit } from './config/backend'; +import { serialize } from "cookie"; +import { cookies, headers } from "next/headers"; +import { NextRequest, NextResponse } from "next/server"; +import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; +import { ensureSuperTokensInit } from "./config/backend"; +import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; +import { HTTPMethod } from "supertokens-node/types"; ensureSuperTokensInit(); -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; +export async function getSSRSession( + req?: NextRequest, + options?: VerifySessionOptions +): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + hasInvalidClaims: boolean; + baseResponse: CollectingResponse; + nextResponse?: NextResponse; }> { - let token; - /** - * We try reading the access token from the incoming request if it exists - * - * If the request does not exist or it does not contain an access token we try to read it - * from the existing cookies stored for the website - */ - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, + const query = req !== undefined ? Object.fromEntries(new URL(req.url).searchParams.entries()) : {}; + const parsedCookies: Record = Object.fromEntries( + (req !== undefined ? req.cookies : cookies()).getAll().map((cookie) => [cookie.name, cookie.value]) + ); + + /** + * Pre parsed request is a wrapper exposed by SuperTokens. It is used as a helper to detect if the + * original request contains session tokens. We then use this pre parsed request to call `getSession` + * to check if there is a valid session. + */ + let baseRequest = new PreParsedRequest({ + method: req !== undefined ? (req.method as HTTPMethod) : "get", + url: req !== undefined ? req.url : "", + query: query, + headers: req !== undefined ? req.headers : headers(), + cookies: parsedCookies, + getFormBody: () => req!.formData(), + getJSONBody: () => req!.json(), }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} -export function updateSessionInResponse( - session: SessionContainer, - response?: NextResponse, - ) { - let tokens = session.getAllSessionTokensDangerously(); - if (tokens.accessAndFrontTokenUpdated) { - const accessTokenCookie = { - name: 'sAccessToken', - value: tokens.accessToken, - httpOnly: true, - path: '/', - expires: Date.now() + 3153600000000, - }; - - if (response) { - response.cookies.set(accessTokenCookie); - response.headers.set('front-token', tokens.frontToken); - } else { - cookies().set(accessTokenCookie); - headers().set('front-token', tokens.frontToken); - } + /** + * Collecting response is a wrapper exposed by SuperTokens. In this case we are using an empty + * CollectingResponse when calling `getSession`. If the request contains valid session tokens + * the SuperTokens SDK will attach all the relevant tokens to the collecting response object which + * we can then use to return those session tokens in the final result (refer to `withSession` in this file) + */ + let baseResponse = new CollectingResponse(); + + try { + /** + * `getSession` will throw if session is required and there is no valid session. You can use + * `options` to configure whether or not you want to require sessions when calling `getSSRSession` + */ + let session = await Session.getSession(baseRequest, baseResponse, options); + return { + session, + hasInvalidClaims: false, + hasToken: session !== undefined, + baseResponse, + }; + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + return { + hasToken: err.type !== Session.Error.UNAUTHORISED, + /** + * This allows us to protect our routes based on the current session claims. For example + * this will be true if email verification is required but the user has not verified their + * email. + */ + hasInvalidClaims: err.type === Session.Error.INVALID_CLAIMS, + session: undefined, + baseResponse, + nextResponse: new NextResponse("Authentication required", { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }), + }; + } else { + throw err; + } } - } +} export async function withSession( request: NextRequest, handler: (session: SessionContainer | undefined) => Promise, - ) { - let { session, resp: stResponse } = await getSSRSession(request); - if (stResponse) { - return stResponse; + options?: VerifySessionOptions +) { + let { session, nextResponse, baseResponse } = await getSSRSession(request, options); + if (nextResponse) { + return nextResponse; } + let userResponse = await handler(session); - if (session) { - updateSessionInResponse(session, userResponse); + let didAddCookies = false; + let didAddHeaders = false; + + /** + * Base response is the response from SuperTokens that contains all the session tokens. + * We add all cookies and headers in the base response to the final response from the + * API to make sure sessions work correctly. + */ + for (const respCookie of baseResponse.cookies) { + didAddCookies = true; + userResponse.headers.append( + "Set-Cookie", + serialize(respCookie.key, respCookie.value, { + domain: respCookie.domain, + expires: new Date(respCookie.expires), + httpOnly: respCookie.httpOnly, + path: respCookie.path, + sameSite: respCookie.sameSite, + secure: respCookie.secure, + }) + ); } + + baseResponse.headers.forEach((value, key) => { + didAddHeaders = true; + userResponse.headers.set(key, value); + }); + + /** + * For some deployment services (Vercel for example) production builds can return cached results for + * APIs with older header values. In this case if the session tokens have changed (because of refreshing + * for example) the cached result would still contain the older tokens and sessions would stop working. + * + * As a result, if we add cookies or headers from base response we also set the Cache-Control header + * to make sure that the final result is not a cached version. + */ + if (didAddCookies || didAddHeaders) { + if (userResponse.headers.has("Cache-Control")) { + // This is needed for production deployments with Vercel + userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + } + } + return userResponse; } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. -- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index e701e369a..6806140c2 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -12,114 +12,147 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" -import { cookies, headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; -import { ensureSuperTokensInit } from './config/backend'; +import { serialize } from "cookie"; +import { cookies, headers } from "next/headers"; +import { NextRequest, NextResponse } from "next/server"; +import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; +import { ensureSuperTokensInit } from "./config/backend"; +import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; +import { HTTPMethod } from "supertokens-node/types"; ensureSuperTokensInit(); -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; +export async function getSSRSession( + req?: NextRequest, + options?: VerifySessionOptions +): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + hasInvalidClaims: boolean; + baseResponse: CollectingResponse; + nextResponse?: NextResponse; }> { - let token; - /** - * We try reading the access token from the incoming request if it exists - * - * If the request does not exist or it does not contain an access token we try to read it - * from the existing cookies stored for the website - */ - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, + const query = req !== undefined ? Object.fromEntries(new URL(req.url).searchParams.entries()) : {}; + const parsedCookies: Record = Object.fromEntries( + (req !== undefined ? req.cookies : cookies()).getAll().map((cookie) => [cookie.name, cookie.value]) + ); + + /** + * Pre parsed request is a wrapper exposed by SuperTokens. It is used as a helper to detect if the + * original request contains session tokens. We then use this pre parsed request to call `getSession` + * to check if there is a valid session. + */ + let baseRequest = new PreParsedRequest({ + method: req !== undefined ? (req.method as HTTPMethod) : "get", + url: req !== undefined ? req.url : "", + query: query, + headers: req !== undefined ? req.headers : headers(), + cookies: parsedCookies, + getFormBody: () => req!.formData(), + getJSONBody: () => req!.json(), }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} -export function updateSessionInResponse( - session: SessionContainer, - response?: NextResponse, - ) { - let tokens = session.getAllSessionTokensDangerously(); - if (tokens.accessAndFrontTokenUpdated) { - const accessTokenCookie = { - name: 'sAccessToken', - value: tokens.accessToken, - httpOnly: true, - path: '/', - expires: Date.now() + 3153600000000, - }; - - if (response) { - response.cookies.set(accessTokenCookie); - response.headers.set('front-token', tokens.frontToken); - } else { - cookies().set(accessTokenCookie); - headers().set('front-token', tokens.frontToken); - } + /** + * Collecting response is a wrapper exposed by SuperTokens. In this case we are using an empty + * CollectingResponse when calling `getSession`. If the request contains valid session tokens + * the SuperTokens SDK will attach all the relevant tokens to the collecting response object which + * we can then use to return those session tokens in the final result (refer to `withSession` in this file) + */ + let baseResponse = new CollectingResponse(); + + try { + /** + * `getSession` will throw if session is required and there is no valid session. You can use + * `options` to configure whether or not you want to require sessions when calling `getSSRSession` + */ + let session = await Session.getSession(baseRequest, baseResponse, options); + return { + session, + hasInvalidClaims: false, + hasToken: session !== undefined, + baseResponse, + }; + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + return { + hasToken: err.type !== Session.Error.UNAUTHORISED, + /** + * This allows us to protect our routes based on the current session claims. For example + * this will be true if email verification is required but the user has not verified their + * email. + */ + hasInvalidClaims: err.type === Session.Error.INVALID_CLAIMS, + session: undefined, + baseResponse, + nextResponse: new NextResponse("Authentication required", { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }), + }; + } else { + throw err; + } } - } +} export async function withSession( request: NextRequest, handler: (session: SessionContainer | undefined) => Promise, - ) { - let { session, resp: stResponse } = await getSSRSession(request); - if (stResponse) { - return stResponse; + options?: VerifySessionOptions +) { + let { session, nextResponse, baseResponse } = await getSSRSession(request, options); + if (nextResponse) { + return nextResponse; } + let userResponse = await handler(session); - if (session) { - updateSessionInResponse(session, userResponse); + let didAddCookies = false; + let didAddHeaders = false; + + /** + * Base response is the response from SuperTokens that contains all the session tokens. + * We add all cookies and headers in the base response to the final response from the + * API to make sure sessions work correctly. + */ + for (const respCookie of baseResponse.cookies) { + didAddCookies = true; + userResponse.headers.append( + "Set-Cookie", + serialize(respCookie.key, respCookie.value, { + domain: respCookie.domain, + expires: new Date(respCookie.expires), + httpOnly: respCookie.httpOnly, + path: respCookie.path, + sameSite: respCookie.sameSite, + secure: respCookie.secure, + }) + ); } + + baseResponse.headers.forEach((value, key) => { + didAddHeaders = true; + userResponse.headers.set(key, value); + }); + + /** + * For some deployment services (Vercel for example) production builds can return cached results for + * APIs with older header values. In this case if the session tokens have changed (because of refreshing + * for example) the cached result would still contain the older tokens and sessions would stop working. + * + * As a result, if we add cookies or headers from base response we also set the Cache-Control header + * to make sure that the final result is not a cached version. + */ + if (didAddCookies || didAddHeaders) { + if (userResponse.headers.has("Cache-Control")) { + // This is needed for production deployments with Vercel + userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + } + } + return userResponse; } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. -- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index e701e369a..75c484581 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -12,114 +12,147 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" -import { cookies, headers } from 'next/headers'; -import { NextRequest, NextResponse } from 'next/server'; -import Session, { SessionContainer } from 'supertokens-node/recipe/session'; -import { ensureSuperTokensInit } from './config/backend'; +import { serialize } from "cookie"; +import { cookies, headers } from "next/headers"; +import { NextRequest, NextResponse } from "next/server"; +import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; +import { ensureSuperTokensInit } from "./config/backend"; +import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; +import { HTTPMethod } from "supertokens-node/types"; ensureSuperTokensInit(); -export async function getSSRSession(req?: NextRequest): Promise<{ - session: SessionContainer | undefined; - hasToken: boolean; - resp?: Response; +export async function getSSRSession( + req?: NextRequest, + options?: VerifySessionOptions +): Promise<{ + session: SessionContainer | undefined; + hasToken: boolean; + hasInvalidClaims: boolean; + baseResponse: CollectingResponse; + nextResponse?: NextResponse; }> { - let token; - /** - * We try reading the access token from the incoming request if it exists - * - * If the request does not exist or it does not contain an access token we try to read it - * from the existing cookies stored for the website - */ - if (req?.cookies) { - token = req.cookies.get('sAccessToken')?.value; - } else { - token = cookies().get('sAccessToken')?.value; - } - - if (req?.headers.get("Authorization")) { - token = req.headers.get("Authorization")!; - // We remove the "Bearer" from the token - if (token.includes("Bearer")) { - token = token.replace("Bearer ", ""); - } - } - - if (token === undefined) { - return { - session: undefined, - hasToken: false, - resp: new NextResponse('Authentication required', { status: 401 }), - }; - } - - let session; - let resp; - - try { - session = await Session.getSessionWithoutRequestResponse(token, undefined, { - sessionRequired: false, + const query = req !== undefined ? Object.fromEntries(new URL(req.url).searchParams.entries()) : {}; + const parsedCookies: Record = Object.fromEntries( + (req !== undefined ? req.cookies : cookies()).getAll().map((cookie) => [cookie.name, cookie.value]) + ); + + /** + * Pre parsed request is a wrapper exposed by SuperTokens. It is used as a helper to detect if the + * original request contains session tokens. We then use this pre parsed request to call `getSession` + * to check if there is a valid session. + */ + let baseRequest = new PreParsedRequest({ + method: req !== undefined ? (req.method as HTTPMethod) : "get", + url: req !== undefined ? req.url : "", + query: query, + headers: req !== undefined ? req.headers : headers(), + cookies: parsedCookies, + getFormBody: () => req!.formData(), + getJSONBody: () => req!.json(), }); - } catch (err) { - if (Session.Error.isErrorFromSuperTokens(err)) { - resp = new NextResponse('Authentication required', { - status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, - }); - } else { - throw err; - } - } - - return { - session, - hasToken: true, - resp, - }; -} -export function updateSessionInResponse( - session: SessionContainer, - response?: NextResponse, - ) { - let tokens = session.getAllSessionTokensDangerously(); - if (tokens.accessAndFrontTokenUpdated) { - const accessTokenCookie = { - name: 'sAccessToken', - value: tokens.accessToken, - httpOnly: true, - path: '/', - expires: Date.now() + 3153600000000, - }; - - if (response) { - response.cookies.set(accessTokenCookie); - response.headers.set('front-token', tokens.frontToken); - } else { - cookies().set(accessTokenCookie); - headers().set('front-token', tokens.frontToken); - } + /** + * Collecting response is a wrapper exposed by SuperTokens. In this case we are using an empty + * CollectingResponse when calling `getSession`. If the request contains valid session tokens + * the SuperTokens SDK will attach all the relevant tokens to the collecting response object which + * we can then use to return those session tokens in the final result (refer to `withSession` in this file) + */ + let baseResponse = new CollectingResponse(); + + try { + /** + * `getSession` will throw if session is required and there is no valid session. You can use + * `options` to configure whether or not you want to require sessions when calling `getSSRSession` + */ + let session = await Session.getSession(baseRequest, baseResponse, options); + return { + session, + hasInvalidClaims: false, + hasToken: session !== undefined, + baseResponse, + }; + } catch (err) { + if (Session.Error.isErrorFromSuperTokens(err)) { + return { + hasToken: err.type !== Session.Error.UNAUTHORISED, + /** + * This allows us to protect our routes based on the current session claims. For example + * this will be true if email verification is required but the user has not verified their + * email. + */ + hasInvalidClaims: err.type === Session.Error.INVALID_CLAIMS, + session: undefined, + baseResponse, + nextResponse: new NextResponse("Authentication required", { + status: err.type === Session.Error.INVALID_CLAIMS ? 403 : 401, + }), + }; + } else { + throw err; + } } - } +} export async function withSession( request: NextRequest, handler: (session: SessionContainer | undefined) => Promise, - ) { - let { session, resp: stResponse } = await getSSRSession(request); - if (stResponse) { - return stResponse; + options?: VerifySessionOptions +) { + let { session, nextResponse, baseResponse } = await getSSRSession(request, options); + if (nextResponse) { + return nextResponse; } + let userResponse = await handler(session); - if (session) { - updateSessionInResponse(session, userResponse); + let didAddCookies = false; + let didAddHeaders = false; + + /** + * Base response is the response from SuperTokens that contains all the session tokens. + * We add all cookies and headers in the base response to the final response from the + * API to make sure sessions work correctly. + */ + for (const respCookie of baseResponse.cookies) { + didAddCookies = true; + userResponse.headers.append( + "Set-Cookie", + serialize(respCookie.key, respCookie.value, { + domain: respCookie.domain, + expires: new Date(respCookie.expires), + httpOnly: respCookie.httpOnly, + path: respCookie.path, + sameSite: respCookie.sameSite, + secure: respCookie.secure, + }) + ); } + + baseResponse.headers.forEach((value, key) => { + didAddHeaders = true; + userResponse.headers.set(key, value); + }); + + /** + * For some deployment services (Vercel for example) production builds can return cached results for + * APIs with older header values. In this case if the session tokens have changed (because of refreshing + * for example) the cached result would still contain the older tokens and sessions would stop working. + * + * As a result, if we add cookies or headers from base response we also set the Cache-Control header + * to make sure that the final result is not a cached version. + */ + if (didAddCookies || didAddHeaders) { + if (userResponse.headers.has("Cache-Control")) { + // This is needed for production deployments with Vercel + userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + } + } + return userResponse; } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request cookies and headers, or from any existing cookies for the website. The `hasToken` property is useful when deciding to refresh the session because if the session does not exist and `hasToken` is `false` we can skip trying to refresh the session and redirect the user to the login page. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. -- `updateSessionInResponse` is a utility function that attaches session tokens to the response or existing cookies for the website. \ No newline at end of file +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. From 28a45fcda3b4e21e63c36e6aac710cd51f98e614 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 19 Oct 2023 10:04:13 +0530 Subject: [PATCH 08/27] Refactor snippet titles --- v2/emailpassword/nextjs/app-directory/init.mdx | 14 +++++++------- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- v2/passwordless/nextjs/app-directory/init.mdx | 14 +++++++------- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- v2/thirdparty/nextjs/app-directory/init.mdx | 14 +++++++------- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../nextjs/app-directory/init.mdx | 14 +++++++------- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../nextjs/app-directory/init.mdx | 14 +++++++------- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- 10 files changed, 40 insertions(+), 40 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index c72bb7c98..20f11805d 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -318,7 +318,7 @@ export default function RootLayout({ - Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). -```tsx title="/pages/_app.ts" +```tsx title="/app/layout.tsx" import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' @@ -331,17 +331,17 @@ export const metadata: Metadata = { description: 'Generated by create next app', } +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) +} + export default function RootLayout({ children, }: { children: React.ReactNode }) { - if (typeof window !== 'undefined') { - // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' - // highlight-next-line - SuperTokensWebJs.init(frontendConfig()) - } - return ( {children} diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx index 02dcd2361..3fa371b7e 100644 --- a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -28,7 +28,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by showNextJSAPIRouteCheckbox > -```tsx title="pages/api/auth/[[...path]].ts" +```tsx title="app/api/auth/[[...path]].ts" import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; import { ensureSuperTokensInit } from '../../../config/backend'; diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index 16475f93c..540a0d142 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -325,7 +325,7 @@ export default function RootLayout({ - Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). -```tsx title="/pages/_app.ts" +```tsx title="/app/layout.tsx" import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' @@ -338,17 +338,17 @@ export const metadata: Metadata = { description: 'Generated by create next app', } +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) +} + export default function RootLayout({ children, }: { children: React.ReactNode }) { - if (typeof window !== 'undefined') { - // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' - // highlight-next-line - SuperTokensWebJs.init(frontendConfig()) - } - return ( {children} diff --git a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx index 02dcd2361..3fa371b7e 100644 --- a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx @@ -28,7 +28,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by showNextJSAPIRouteCheckbox > -```tsx title="pages/api/auth/[[...path]].ts" +```tsx title="app/api/auth/[[...path]].ts" import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; import { ensureSuperTokensInit } from '../../../config/backend'; diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index a85f6bf5b..a8c9cab4f 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -400,7 +400,7 @@ export default function RootLayout({ - Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). -```tsx title="/pages/_app.ts" +```tsx title="/app/layout.tsx" import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' @@ -413,17 +413,17 @@ export const metadata: Metadata = { description: 'Generated by create next app', } +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) +} + export default function RootLayout({ children, }: { children: React.ReactNode }) { - if (typeof window !== 'undefined') { - // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' - // highlight-next-line - SuperTokensWebJs.init(frontendConfig()) - } - return ( {children} diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx index 02dcd2361..3fa371b7e 100644 --- a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx @@ -28,7 +28,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by showNextJSAPIRouteCheckbox > -```tsx title="pages/api/auth/[[...path]].ts" +```tsx title="app/api/auth/[[...path]].ts" import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; import { ensureSuperTokensInit } from '../../../config/backend'; diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx index 81dbbfa05..3d880a624 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -398,7 +398,7 @@ export default function RootLayout({ - Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). -```tsx title="/pages/_app.ts" +```tsx title="/app/layout.tsx" import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' @@ -411,17 +411,17 @@ export const metadata: Metadata = { description: 'Generated by create next app', } +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) +} + export default function RootLayout({ children, }: { children: React.ReactNode }) { - if (typeof window !== 'undefined') { - // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' - // highlight-next-line - SuperTokensWebJs.init(frontendConfig()) - } - return ( {children} diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx index 02dcd2361..3fa371b7e 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -28,7 +28,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by showNextJSAPIRouteCheckbox > -```tsx title="pages/api/auth/[[...path]].ts" +```tsx title="app/api/auth/[[...path]].ts" import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; import { ensureSuperTokensInit } from '../../../config/backend'; diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index aa8199771..566a46b46 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -323,7 +323,7 @@ export default function RootLayout({ - Modify the `/app/layout.tsx` file to initialise the SuperTokens SDK. You can learn more about this file [here](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). -```tsx title="/pages/_app.ts" +```tsx title="/app/layout.tsx" import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' @@ -336,17 +336,17 @@ export const metadata: Metadata = { description: 'Generated by create next app', } +if (typeof window !== 'undefined') { + // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' + // highlight-next-line + SuperTokensWebJs.init(frontendConfig()) +} + export default function RootLayout({ children, }: { children: React.ReactNode }) { - if (typeof window !== 'undefined') { - // we only want to call this init function on the frontend, so we check typeof window !== 'undefined' - // highlight-next-line - SuperTokensWebJs.init(frontendConfig()) - } - return ( {children} diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx index 02dcd2361..3fa371b7e 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx @@ -28,7 +28,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by showNextJSAPIRouteCheckbox > -```tsx title="pages/api/auth/[[...path]].ts" +```tsx title="app/api/auth/[[...path]].ts" import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; import { ensureSuperTokensInit } from '../../../config/backend'; From 37521d0c362d787faa69a296f7b5bc346a608723 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 19 Oct 2023 10:28:22 +0530 Subject: [PATCH 09/27] Refactor snippets based on latest example app --- .../nextjs/app-directory/protecting-route.mdx | 166 +++++++++++++----- .../server-components-requests.mdx | 79 ++++----- .../session-verification-session-guard.mdx | 39 ++-- .../app-directory/setting-up-backend.mdx | 4 +- .../nextjs/app-directory/protecting-route.mdx | 166 +++++++++++++----- .../server-components-requests.mdx | 79 ++++----- .../session-verification-session-guard.mdx | 39 ++-- .../app-directory/setting-up-backend.mdx | 4 +- .../nextjs/app-directory/protecting-route.mdx | 166 +++++++++++++----- .../server-components-requests.mdx | 79 ++++----- .../session-verification-session-guard.mdx | 39 ++-- .../app-directory/setting-up-backend.mdx | 4 +- .../nextjs/app-directory/protecting-route.mdx | 164 ++++++++++++----- .../server-components-requests.mdx | 77 ++++---- .../session-verification-session-guard.mdx | 37 ++-- .../app-directory/setting-up-backend.mdx | 4 +- .../nextjs/app-directory/protecting-route.mdx | 166 +++++++++++++----- .../server-components-requests.mdx | 79 ++++----- .../session-verification-session-guard.mdx | 39 ++-- .../app-directory/setting-up-backend.mdx | 4 +- 20 files changed, 882 insertions(+), 552 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 35d8292b0..4ee4da0b0 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -13,66 +13,142 @@ Protecting a website route means that it cannot be accessed unless a user is sig ## Sessions with Server Components -Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + +:::info +This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +::: + +```tsx title="app/components/sessionAuthForNextJS.tsx" +"use client"; + +import { SessionAuth } from "supertokens-auth-react/recipe/session"; + +type Props = Parameters[0] & { + children?: React.ReactNode | undefined; +}; + +export const SessionAuthForNextJS = (props: Props) => { + if (typeof window === "undefined") { + return props.children; + } + + return {props.children}; +}; +``` + +This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. + +### Using `SessionAuthForNextJS` and checking for sessions + +We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - */ - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + * + * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email + * verification is required but the user has not verified their email. + */ + if (!session) { + if (!hasToken) { + /** + * This means that the user is not logged in. If you want to display some other UI in this + * case, you can do so here. + */ + return redirect("/auth"); + } + + if (hasInvalidClaims) { + /** + * This will make sure that the user is redirected based on their session claims. For example they + * will be redirected to the email verification screen if needed. + * + * We pass in no children in this case to prevent hydration issues and still be able to redirect the + * user. + */ + return ; + } else { + return ; + } } - return ; - } - return ( -
-
-

+ /** + * SessionAuthForNextJS will handle proper redirection for the user based on the different session states. + * It will redirect to the login page if the session does not exist etc. + */ + return ( + +

Hello world -

-
-
- ); +
+
+ ); } ``` `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" -'use client'; +"use client"; -import { useEffect } from 'react'; -import { useRouter, redirect } from 'next/navigation'; -import Session from 'supertokens-auth-react/recipe/session'; -import SuperTokens from 'supertokens-auth-react'; +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; export const TryRefreshComponent = () => { - const router = useRouter(); - useEffect(() => { - void Session.attemptRefreshingSession() - .then(() => { - router.refresh(); - }) - .catch(console.error); - }, []); - - return
Loading...
; + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; }; ``` @@ -167,4 +243,4 @@ export const HomeClientComponent = () => { :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: \ No newline at end of file +::: diff --git a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx index 5e13c87c4..94724ecfa 100644 --- a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx @@ -9,51 +9,50 @@ hide_title: true # 7. Making requests from Server Components -Lets modify the Home page we made in a previous step to make a call to this API +Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' -import { SignOut } from './signOut'; +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + return redirect("/auth"); + } + + if (hasInvalidClaims) { + return ; + } else { + return ; + } } - return ; - } - - const userEmailResponse = await fetch('http://localhost:3000/api/user', { - headers: { - Authorization: 'Bearer ' + session.getAccessToken(), - }, - }); - - let email = ""; - - if (userEmailResponse.status !== 200) { - email = "error with status " + userEmailResponse.status; - } else { - email = (await userEmailResponse.json()).email; - } - - return ( -
-
-

- Server side component got userId: {session.getUserId()}
- Server side component got email: {email} -

-
- -
- ); + + const userInfoResponse = await fetch('http://localhost:3000/api/user', { + headers: { + /** + * We read the access token from the session and use that as a Bearer token when + * making network requests. + */ + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + // You can use `userInfoResponse` to read the users session information + + return ( + +
+ Hello world +
+
+ ); } ``` -We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx index e6b86f8d2..5c79c374d 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,33 +20,24 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" -import { NextResponse, NextRequest } from 'next/server'; -import { withSession } from '../../sessionUtils'; -import SuperTokens from 'supertokens-node'; +import { NextResponse, NextRequest } from "next/server"; +import { withSession } from "../../sessionUtils"; +import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { return withSession(request, async (session) => { - // Session will be undefined if it does not exist - if (!session) { - // The frontend sdk will try to refresh the session if it recieves a 401 status code - return new NextResponse('Authentication required', { status: 401 }); - } - - // The session object exposes utility functions to fetch information such as the user id - const userId = session.getUserId(); - const user = await SuperTokens.getUser(userId); - - if (user === undefined) { - return NextResponse.json({ - email: "not found", - }) - } - - return NextResponse.json({ - email: user.emails[0], - }); - }) + if (!session) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + note: "Fetch any data from your application for authenticated user after using verifySession middleware", + userId: session.getUserId(), + sessionHandle: session.getHandle(), + accessTokenPayload: session.getAccessTokenPayload(), + }); + }); } ``` -In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx index 3fa371b7e..de6044e47 100644 --- a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -15,9 +15,9 @@ import AppInfoForm from "/src/components/appInfoForm" We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. -## 1) Create the `app/api/auth/[[...path]].tsx` page +## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. -- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). - An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 35d8292b0..4ee4da0b0 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -13,66 +13,142 @@ Protecting a website route means that it cannot be accessed unless a user is sig ## Sessions with Server Components -Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + +:::info +This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +::: + +```tsx title="app/components/sessionAuthForNextJS.tsx" +"use client"; + +import { SessionAuth } from "supertokens-auth-react/recipe/session"; + +type Props = Parameters[0] & { + children?: React.ReactNode | undefined; +}; + +export const SessionAuthForNextJS = (props: Props) => { + if (typeof window === "undefined") { + return props.children; + } + + return {props.children}; +}; +``` + +This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. + +### Using `SessionAuthForNextJS` and checking for sessions + +We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - */ - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + * + * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email + * verification is required but the user has not verified their email. + */ + if (!session) { + if (!hasToken) { + /** + * This means that the user is not logged in. If you want to display some other UI in this + * case, you can do so here. + */ + return redirect("/auth"); + } + + if (hasInvalidClaims) { + /** + * This will make sure that the user is redirected based on their session claims. For example they + * will be redirected to the email verification screen if needed. + * + * We pass in no children in this case to prevent hydration issues and still be able to redirect the + * user. + */ + return ; + } else { + return ; + } } - return ; - } - return ( -
-
-

+ /** + * SessionAuthForNextJS will handle proper redirection for the user based on the different session states. + * It will redirect to the login page if the session does not exist etc. + */ + return ( + +

Hello world -

-
-
- ); +
+
+ ); } ``` `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" -'use client'; +"use client"; -import { useEffect } from 'react'; -import { useRouter, redirect } from 'next/navigation'; -import Session from 'supertokens-auth-react/recipe/session'; -import SuperTokens from 'supertokens-auth-react'; +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; export const TryRefreshComponent = () => { - const router = useRouter(); - useEffect(() => { - void Session.attemptRefreshingSession() - .then(() => { - router.refresh(); - }) - .catch(console.error); - }, []); - - return
Loading...
; + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; }; ``` @@ -167,4 +243,4 @@ export const HomeClientComponent = () => { :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: \ No newline at end of file +::: diff --git a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx index 5e13c87c4..94724ecfa 100644 --- a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx @@ -9,51 +9,50 @@ hide_title: true # 7. Making requests from Server Components -Lets modify the Home page we made in a previous step to make a call to this API +Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' -import { SignOut } from './signOut'; +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + return redirect("/auth"); + } + + if (hasInvalidClaims) { + return ; + } else { + return ; + } } - return ; - } - - const userEmailResponse = await fetch('http://localhost:3000/api/user', { - headers: { - Authorization: 'Bearer ' + session.getAccessToken(), - }, - }); - - let email = ""; - - if (userEmailResponse.status !== 200) { - email = "error with status " + userEmailResponse.status; - } else { - email = (await userEmailResponse.json()).email; - } - - return ( -
-
-

- Server side component got userId: {session.getUserId()}
- Server side component got email: {email} -

-
- -
- ); + + const userInfoResponse = await fetch('http://localhost:3000/api/user', { + headers: { + /** + * We read the access token from the session and use that as a Bearer token when + * making network requests. + */ + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + // You can use `userInfoResponse` to read the users session information + + return ( + +
+ Hello world +
+
+ ); } ``` -We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. diff --git a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx index e6b86f8d2..5c79c374d 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,33 +20,24 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" -import { NextResponse, NextRequest } from 'next/server'; -import { withSession } from '../../sessionUtils'; -import SuperTokens from 'supertokens-node'; +import { NextResponse, NextRequest } from "next/server"; +import { withSession } from "../../sessionUtils"; +import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { return withSession(request, async (session) => { - // Session will be undefined if it does not exist - if (!session) { - // The frontend sdk will try to refresh the session if it recieves a 401 status code - return new NextResponse('Authentication required', { status: 401 }); - } - - // The session object exposes utility functions to fetch information such as the user id - const userId = session.getUserId(); - const user = await SuperTokens.getUser(userId); - - if (user === undefined) { - return NextResponse.json({ - email: "not found", - }) - } - - return NextResponse.json({ - email: user.emails[0], - }); - }) + if (!session) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + note: "Fetch any data from your application for authenticated user after using verifySession middleware", + userId: session.getUserId(), + sessionHandle: session.getHandle(), + accessTokenPayload: session.getAccessTokenPayload(), + }); + }); } ``` -In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. diff --git a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx index 3fa371b7e..de6044e47 100644 --- a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx @@ -15,9 +15,9 @@ import AppInfoForm from "/src/components/appInfoForm" We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. -## 1) Create the `app/api/auth/[[...path]].tsx` page +## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. -- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). - An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 35d8292b0..4ee4da0b0 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -13,66 +13,142 @@ Protecting a website route means that it cannot be accessed unless a user is sig ## Sessions with Server Components -Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + +:::info +This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +::: + +```tsx title="app/components/sessionAuthForNextJS.tsx" +"use client"; + +import { SessionAuth } from "supertokens-auth-react/recipe/session"; + +type Props = Parameters[0] & { + children?: React.ReactNode | undefined; +}; + +export const SessionAuthForNextJS = (props: Props) => { + if (typeof window === "undefined") { + return props.children; + } + + return {props.children}; +}; +``` + +This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. + +### Using `SessionAuthForNextJS` and checking for sessions + +We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - */ - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + * + * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email + * verification is required but the user has not verified their email. + */ + if (!session) { + if (!hasToken) { + /** + * This means that the user is not logged in. If you want to display some other UI in this + * case, you can do so here. + */ + return redirect("/auth"); + } + + if (hasInvalidClaims) { + /** + * This will make sure that the user is redirected based on their session claims. For example they + * will be redirected to the email verification screen if needed. + * + * We pass in no children in this case to prevent hydration issues and still be able to redirect the + * user. + */ + return ; + } else { + return ; + } } - return ; - } - return ( -
-
-

+ /** + * SessionAuthForNextJS will handle proper redirection for the user based on the different session states. + * It will redirect to the login page if the session does not exist etc. + */ + return ( + +

Hello world -

-
-
- ); +
+
+ ); } ``` `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" -'use client'; +"use client"; -import { useEffect } from 'react'; -import { useRouter, redirect } from 'next/navigation'; -import Session from 'supertokens-auth-react/recipe/session'; -import SuperTokens from 'supertokens-auth-react'; +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; export const TryRefreshComponent = () => { - const router = useRouter(); - useEffect(() => { - void Session.attemptRefreshingSession() - .then(() => { - router.refresh(); - }) - .catch(console.error); - }, []); - - return
Loading...
; + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; }; ``` @@ -167,4 +243,4 @@ export const HomeClientComponent = () => { :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: \ No newline at end of file +::: diff --git a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx index 5e13c87c4..94724ecfa 100644 --- a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx @@ -9,51 +9,50 @@ hide_title: true # 7. Making requests from Server Components -Lets modify the Home page we made in a previous step to make a call to this API +Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' -import { SignOut } from './signOut'; +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + return redirect("/auth"); + } + + if (hasInvalidClaims) { + return ; + } else { + return ; + } } - return ; - } - - const userEmailResponse = await fetch('http://localhost:3000/api/user', { - headers: { - Authorization: 'Bearer ' + session.getAccessToken(), - }, - }); - - let email = ""; - - if (userEmailResponse.status !== 200) { - email = "error with status " + userEmailResponse.status; - } else { - email = (await userEmailResponse.json()).email; - } - - return ( -
-
-

- Server side component got userId: {session.getUserId()}
- Server side component got email: {email} -

-
- -
- ); + + const userInfoResponse = await fetch('http://localhost:3000/api/user', { + headers: { + /** + * We read the access token from the session and use that as a Bearer token when + * making network requests. + */ + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + // You can use `userInfoResponse` to read the users session information + + return ( + +
+ Hello world +
+
+ ); } ``` -We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx index e6b86f8d2..5c79c374d 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,33 +20,24 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" -import { NextResponse, NextRequest } from 'next/server'; -import { withSession } from '../../sessionUtils'; -import SuperTokens from 'supertokens-node'; +import { NextResponse, NextRequest } from "next/server"; +import { withSession } from "../../sessionUtils"; +import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { return withSession(request, async (session) => { - // Session will be undefined if it does not exist - if (!session) { - // The frontend sdk will try to refresh the session if it recieves a 401 status code - return new NextResponse('Authentication required', { status: 401 }); - } - - // The session object exposes utility functions to fetch information such as the user id - const userId = session.getUserId(); - const user = await SuperTokens.getUser(userId); - - if (user === undefined) { - return NextResponse.json({ - email: "not found", - }) - } - - return NextResponse.json({ - email: user.emails[0], - }); - }) + if (!session) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + note: "Fetch any data from your application for authenticated user after using verifySession middleware", + userId: session.getUserId(), + sessionHandle: session.getHandle(), + accessTokenPayload: session.getAccessTokenPayload(), + }); + }); } ``` -In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx index 3fa371b7e..de6044e47 100644 --- a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx @@ -15,9 +15,9 @@ import AppInfoForm from "/src/components/appInfoForm" We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. -## 1) Create the `app/api/auth/[[...path]].tsx` page +## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. -- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). - An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 35d8292b0..b4cd52794 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -13,66 +13,142 @@ Protecting a website route means that it cannot be accessed unless a user is sig ## Sessions with Server Components -Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + +:::info +This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +::: + +```tsx title="app/components/sessionAuthForNextJS.tsx" +"use client"; + +import { SessionAuth } from "supertokens-auth-react/recipe/session"; + +type Props = Parameters[0] & { + children?: React.ReactNode | undefined; +}; + +export const SessionAuthForNextJS = (props: Props) => { + if (typeof window === "undefined") { + return props.children; + } + + return {props.children}; +}; +``` + +This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. + +### Using `SessionAuthForNextJS` and checking for sessions + +We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - */ - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + * + * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email + * verification is required but the user has not verified their email. + */ + if (!session) { + if (!hasToken) { + /** + * This means that the user is not logged in. If you want to display some other UI in this + * case, you can do so here. + */ + return redirect("/auth"); + } + + if (hasInvalidClaims) { + /** + * This will make sure that the user is redirected based on their session claims. For example they + * will be redirected to the email verification screen if needed. + * + * We pass in no children in this case to prevent hydration issues and still be able to redirect the + * user. + */ + return ; + } else { + return ; + } } - return ; - } - return ( -
-
-

+ /** + * SessionAuthForNextJS will handle proper redirection for the user based on the different session states. + * It will redirect to the login page if the session does not exist etc. + */ + return ( + +

Hello world -

-
-
- ); +
+
+ ); } ``` `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" -'use client'; +"use client"; -import { useEffect } from 'react'; -import { useRouter, redirect } from 'next/navigation'; -import Session from 'supertokens-auth-react/recipe/session'; -import SuperTokens from 'supertokens-auth-react'; +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; export const TryRefreshComponent = () => { - const router = useRouter(); - useEffect(() => { - void Session.attemptRefreshingSession() - .then(() => { - router.refresh(); - }) - .catch(console.error); - }, []); - - return
Loading...
; + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; }; ``` diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx index 5e13c87c4..ba08c08cd 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx @@ -9,50 +9,49 @@ hide_title: true # 7. Making requests from Server Components -Lets modify the Home page we made in a previous step to make a call to this API +Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' -import { SignOut } from './signOut'; +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + return redirect("/auth"); + } + + if (hasInvalidClaims) { + return ; + } else { + return ; + } } - return ; - } - - const userEmailResponse = await fetch('http://localhost:3000/api/user', { - headers: { - Authorization: 'Bearer ' + session.getAccessToken(), - }, - }); - - let email = ""; - - if (userEmailResponse.status !== 200) { - email = "error with status " + userEmailResponse.status; - } else { - email = (await userEmailResponse.json()).email; - } - - return ( -
-
-

- Server side component got userId: {session.getUserId()}
- Server side component got email: {email} -

-
- -
- ); + + const userInfoResponse = await fetch('http://localhost:3000/api/user', { + headers: { + /** + * We read the access token from the session and use that as a Bearer token when + * making network requests. + */ + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + // You can use `userInfoResponse` to read the users session information + + return ( + +
+ Hello world +
+
+ ); } ``` diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx index e6b86f8d2..56ab98a16 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,32 +20,23 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" -import { NextResponse, NextRequest } from 'next/server'; -import { withSession } from '../../sessionUtils'; -import SuperTokens from 'supertokens-node'; +import { NextResponse, NextRequest } from "next/server"; +import { withSession } from "../../sessionUtils"; +import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { return withSession(request, async (session) => { - // Session will be undefined if it does not exist - if (!session) { - // The frontend sdk will try to refresh the session if it recieves a 401 status code - return new NextResponse('Authentication required', { status: 401 }); - } - - // The session object exposes utility functions to fetch information such as the user id - const userId = session.getUserId(); - const user = await SuperTokens.getUser(userId); - - if (user === undefined) { - return NextResponse.json({ - email: "not found", - }) - } - - return NextResponse.json({ - email: user.emails[0], - }); - }) + if (!session) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + note: "Fetch any data from your application for authenticated user after using verifySession middleware", + userId: session.getUserId(), + sessionHandle: session.getHandle(), + accessTokenPayload: session.getAccessTokenPayload(), + }); + }); } ``` diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx index 3fa371b7e..de6044e47 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -15,9 +15,9 @@ import AppInfoForm from "/src/components/appInfoForm" We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. -## 1) Create the `app/api/auth/[[...path]].tsx` page +## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. -- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). - An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 35d8292b0..4ee4da0b0 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -13,66 +13,142 @@ Protecting a website route means that it cannot be accessed unless a user is sig ## Sessions with Server Components -Let's say we want to protect the home page of your website (`/` route). In this case we first create a server component that can check if the session exists and return any session information we may need: + +### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + +:::info +This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +::: + +```tsx title="app/components/sessionAuthForNextJS.tsx" +"use client"; + +import { SessionAuth } from "supertokens-auth-react/recipe/session"; + +type Props = Parameters[0] & { + children?: React.ReactNode | undefined; +}; + +export const SessionAuthForNextJS = (props: Props) => { + if (typeof window === "undefined") { + return props.children; + } + + return {props.children}; +}; +``` + +This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. + +### Using `SessionAuthForNextJS` and checking for sessions + +We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - */ - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + /** + * The session will be undefined if it does not exist or has expired, if the session has expired + * hasToken will be true because there are session tokens present (but are expired). In this case + * we will try to refresh the session using the TryRefreshComponent client component which will call + * the refresh endpoint. + * + * If session is undefined and hasToken is false, we can safely assume that there is no session for the user + * and redirect them to the login page + * + * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email + * verification is required but the user has not verified their email. + */ + if (!session) { + if (!hasToken) { + /** + * This means that the user is not logged in. If you want to display some other UI in this + * case, you can do so here. + */ + return redirect("/auth"); + } + + if (hasInvalidClaims) { + /** + * This will make sure that the user is redirected based on their session claims. For example they + * will be redirected to the email verification screen if needed. + * + * We pass in no children in this case to prevent hydration issues and still be able to redirect the + * user. + */ + return ; + } else { + return ; + } } - return ; - } - return ( -
-
-

+ /** + * SessionAuthForNextJS will handle proper redirection for the user based on the different session states. + * It will redirect to the login page if the session does not exist etc. + */ + return ( + +

Hello world -

-
-
- ); +
+
+ ); } ``` `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" -'use client'; +"use client"; -import { useEffect } from 'react'; -import { useRouter, redirect } from 'next/navigation'; -import Session from 'supertokens-auth-react/recipe/session'; -import SuperTokens from 'supertokens-auth-react'; +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; export const TryRefreshComponent = () => { - const router = useRouter(); - useEffect(() => { - void Session.attemptRefreshingSession() - .then(() => { - router.refresh(); - }) - .catch(console.error); - }, []); - - return
Loading...
; + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; }; ``` @@ -167,4 +243,4 @@ export const HomeClientComponent = () => { :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: \ No newline at end of file +::: diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx index 5e13c87c4..94724ecfa 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx @@ -9,51 +9,50 @@ hide_title: true # 7. Making requests from Server Components -Lets modify the Home page we made in a previous step to make a call to this API +Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from '../sessionUtils'; -import { TryRefreshComponent } from './tryRefreshClientComponent'; -import styles from '../page.module.css'; -import { redirect } from 'next/navigation' -import { SignOut } from './signOut'; +import { getSSRSession } from "../sessionUtils"; +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import styles from "../page.module.css"; +import { redirect } from "next/navigation"; +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { - const { session, hasToken } = await getSSRSession(); - - if (!session) { - if (!hasToken) { - redirect('/auth'); + const { session, hasToken, hasInvalidClaims } = await getSSRSession(); + + if (!session) { + if (!hasToken) { + return redirect("/auth"); + } + + if (hasInvalidClaims) { + return ; + } else { + return ; + } } - return ; - } - - const userEmailResponse = await fetch('http://localhost:3000/api/user', { - headers: { - Authorization: 'Bearer ' + session.getAccessToken(), - }, - }); - - let email = ""; - - if (userEmailResponse.status !== 200) { - email = "error with status " + userEmailResponse.status; - } else { - email = (await userEmailResponse.json()).email; - } - - return ( -
-
-

- Server side component got userId: {session.getUserId()}
- Server side component got email: {email} -

-
- -
- ); + + const userInfoResponse = await fetch('http://localhost:3000/api/user', { + headers: { + /** + * We read the access token from the session and use that as a Bearer token when + * making network requests. + */ + Authorization: 'Bearer ' + session.getAccessToken(), + }, + }); + + // You can use `userInfoResponse` to read the users session information + + return ( + +
+ Hello world +
+
+ ); } ``` -We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. \ No newline at end of file +We use `session` returned by `getSSRSession` to get the access token of the user. We can then send the access token as a header to the API. When the API calls `withSession` it will try to read the access token from the headers and if a session exists it will return the information. You can use the `session` object to fetch other information such as `session.getUserId()`. diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx index e6b86f8d2..5c79c374d 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,33 +20,24 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" -import { NextResponse, NextRequest } from 'next/server'; -import { withSession } from '../../sessionUtils'; -import SuperTokens from 'supertokens-node'; +import { NextResponse, NextRequest } from "next/server"; +import { withSession } from "../../sessionUtils"; +import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { return withSession(request, async (session) => { - // Session will be undefined if it does not exist - if (!session) { - // The frontend sdk will try to refresh the session if it recieves a 401 status code - return new NextResponse('Authentication required', { status: 401 }); - } - - // The session object exposes utility functions to fetch information such as the user id - const userId = session.getUserId(); - const user = await SuperTokens.getUser(userId); - - if (user === undefined) { - return NextResponse.json({ - email: "not found", - }) - } - - return NextResponse.json({ - email: user.emails[0], - }); - }) + if (!session) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + note: "Fetch any data from your application for authenticated user after using verifySession middleware", + userId: session.getUserId(), + sessionHandle: session.getHandle(), + accessTokenPayload: session.getAccessTokenPayload(), + }); + }); } ``` -In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx index 3fa371b7e..de6044e47 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx @@ -15,9 +15,9 @@ import AppInfoForm from "/src/components/appInfoForm" We will add all the backend APIs for auth on `/api/auth`. This can be changed by setting the `apiBasePath` property in the `appInfo` object in the `appInfo.ts` file. For the rest of this page, we will assume you are using `/api/auth`. -## 1) Create the `app/api/auth/[[...path]].tsx` page +## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. -- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc.. +- `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). - An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs From 2057aa5d49856e6f014421853bff1b8af99014d7 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 19 Oct 2023 10:32:30 +0530 Subject: [PATCH 10/27] Add highlighr --- .../nextjs/app-directory/server-components-requests.mdx | 2 ++ .../nextjs/app-directory/server-components-requests.mdx | 2 ++ .../nextjs/app-directory/server-components-requests.mdx | 2 ++ .../nextjs/app-directory/server-components-requests.mdx | 2 ++ .../nextjs/app-directory/server-components-requests.mdx | 2 ++ 5 files changed, 10 insertions(+) diff --git a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx index 94724ecfa..1e0e4aac7 100644 --- a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx @@ -33,6 +33,7 @@ export async function HomePage() { } } + // highlight-start const userInfoResponse = await fetch('http://localhost:3000/api/user', { headers: { /** @@ -44,6 +45,7 @@ export async function HomePage() { }); // You can use `userInfoResponse` to read the users session information + // highlight-end return ( diff --git a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx index 94724ecfa..1e0e4aac7 100644 --- a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx @@ -33,6 +33,7 @@ export async function HomePage() { } } + // highlight-start const userInfoResponse = await fetch('http://localhost:3000/api/user', { headers: { /** @@ -44,6 +45,7 @@ export async function HomePage() { }); // You can use `userInfoResponse` to read the users session information + // highlight-end return ( diff --git a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx index 94724ecfa..1e0e4aac7 100644 --- a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx @@ -33,6 +33,7 @@ export async function HomePage() { } } + // highlight-start const userInfoResponse = await fetch('http://localhost:3000/api/user', { headers: { /** @@ -44,6 +45,7 @@ export async function HomePage() { }); // You can use `userInfoResponse` to read the users session information + // highlight-end return ( diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx index ba08c08cd..c4ce66903 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx @@ -33,6 +33,7 @@ export async function HomePage() { } } + // highlight-start const userInfoResponse = await fetch('http://localhost:3000/api/user', { headers: { /** @@ -44,6 +45,7 @@ export async function HomePage() { }); // You can use `userInfoResponse` to read the users session information + // highlight-end return ( diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx index 94724ecfa..1e0e4aac7 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx @@ -33,6 +33,7 @@ export async function HomePage() { } } + // highlight-start const userInfoResponse = await fetch('http://localhost:3000/api/user', { headers: { /** @@ -44,6 +45,7 @@ export async function HomePage() { }); // You can use `userInfoResponse` to read the users session information + // highlight-end return ( From 082829b4bb2bf75954c8738c206411b5a8c78fc1 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 19 Oct 2023 14:37:05 +0530 Subject: [PATCH 11/27] Type fixes --- v2/emailpassword/nextjs/app-directory/init.mdx | 14 ++++++++++---- .../nextjs/app-directory/protecting-route.mdx | 17 ++++++++--------- .../server-components-requests.mdx | 9 ++++++--- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-middleware.mdx | 5 +++-- .../session-verification-session-guard.mdx | 3 ++- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- v2/passwordless/nextjs/app-directory/init.mdx | 12 ++++++++---- .../nextjs/app-directory/protecting-route.mdx | 17 ++++++++--------- .../server-components-requests.mdx | 9 ++++++--- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-middleware.mdx | 5 +++-- .../session-verification-session-guard.mdx | 3 ++- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../plugins/codeTypeChecking/jsEnv/package.json | 4 ++-- v2/thirdparty/nextjs/app-directory/init.mdx | 12 ++++++++---- .../nextjs/app-directory/protecting-route.mdx | 17 ++++++++--------- .../server-components-requests.mdx | 9 ++++++--- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-middleware.mdx | 5 +++-- .../session-verification-session-guard.mdx | 3 ++- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../nextjs/app-directory/init.mdx | 14 ++++++++++---- .../nextjs/app-directory/protecting-route.mdx | 17 ++++++++--------- .../server-components-requests.mdx | 9 ++++++--- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-middleware.mdx | 3 ++- .../session-verification-session-guard.mdx | 3 ++- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../nextjs/app-directory/init.mdx | 12 ++++++++---- .../nextjs/app-directory/protecting-route.mdx | 17 ++++++++--------- .../server-components-requests.mdx | 9 ++++++--- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-middleware.mdx | 5 +++-- .../session-verification-session-guard.mdx | 3 ++- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- 36 files changed, 150 insertions(+), 106 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index 20f11805d..72da8e583 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -114,6 +114,8 @@ import SessionReact from 'supertokens-auth-react/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { useRouter } from "next/navigation"; +import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -126,7 +128,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -158,6 +160,8 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { useRouter } from "next/navigation"; +import { SuperTokensConfig } from "supertokens-web-js/types" const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -170,7 +174,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -261,10 +265,11 @@ export function ensureSuperTokensInit() { ```tsx title="/app/components/supertokensProvider.tsx" 'use client'; +declare let frontendConfig: any; // typecheck-only, removed from output +declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; -import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -282,10 +287,10 @@ export const SuperTokensProvider: React.FC> = ({ ``` ```tsx title="/app/layout.tsx" +declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' -import { SuperTokensProvider } from './components/supertokensProvider' const inter = Inter({ subsets: ['latin'] }) @@ -319,6 +324,7 @@ export default function RootLayout({ ```tsx title="/app/layout.tsx" +declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 4ee4da0b0..297fc7a7e 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -46,11 +46,14 @@ This is a client component that renders just its children on the server side and We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -155,7 +158,7 @@ export const TryRefreshComponent = () => { And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" -import { HomePage } from './components/home' +declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' export default function Home() { @@ -190,11 +193,7 @@ export const HomeClientComponent = () => { return (
-
-

- Client side component got userId: {session.userId}
-

-
+ Hello world
); diff --git a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx index 1e0e4aac7..6e4da9a63 100644 --- a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx @@ -12,11 +12,14 @@ hide_title: true Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index 75c484581..26b3c7314 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -12,11 +12,11 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { serialize } from "cookie"; import { cookies, headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; -import { ensureSuperTokensInit } from "./config/backend"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx index 0bf314643..6efb1676a 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -10,10 +10,11 @@ hide_title: true # Using the Next.js middleware ```tsx title="middleware.tsx" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' -import { withSession } from './app/sessionUtils'; export async function middleware( @@ -54,4 +55,4 @@ In the middleware we check if a session exists using the `withSession` helper fu :::note As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: \ No newline at end of file +::: diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx index 5c79c374d..881c1118a 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,8 +20,9 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; -import { withSession } from "../../sessionUtils"; import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx index de6044e47..9eba0aa00 100644 --- a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -29,9 +29,9 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by > ```tsx title="app/api/auth/[[...path]].ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; -import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index 540a0d142..ce4ec82a3 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -114,6 +114,7 @@ import SessionReact from 'supertokens-auth-react/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -126,7 +127,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -160,6 +161,7 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { SuperTokensConfig } from "supertokens-web-js/types" const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -172,7 +174,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -268,10 +270,11 @@ export function ensureSuperTokensInit() { ```tsx title="/app/components/supertokensProvider.tsx" 'use client'; +declare let frontendConfig: any; // typecheck-only, removed from output +declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; -import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -289,10 +292,10 @@ export const SuperTokensProvider: React.FC> = ({ ``` ```tsx title="/app/layout.tsx" +declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' -import { SuperTokensProvider } from './components/supertokensProvider' const inter = Inter({ subsets: ['latin'] }) @@ -326,6 +329,7 @@ export default function RootLayout({ ```tsx title="/app/layout.tsx" +declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 4ee4da0b0..297fc7a7e 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -46,11 +46,14 @@ This is a client component that renders just its children on the server side and We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -155,7 +158,7 @@ export const TryRefreshComponent = () => { And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" -import { HomePage } from './components/home' +declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' export default function Home() { @@ -190,11 +193,7 @@ export const HomeClientComponent = () => { return (
-
-

- Client side component got userId: {session.userId}
-

-
+ Hello world
); diff --git a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx index 1e0e4aac7..6e4da9a63 100644 --- a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx @@ -12,11 +12,14 @@ hide_title: true Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index 75c484581..26b3c7314 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -12,11 +12,11 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { serialize } from "cookie"; import { cookies, headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; -import { ensureSuperTokensInit } from "./config/backend"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; diff --git a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx index 0bf314643..6efb1676a 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx @@ -10,10 +10,11 @@ hide_title: true # Using the Next.js middleware ```tsx title="middleware.tsx" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' -import { withSession } from './app/sessionUtils'; export async function middleware( @@ -54,4 +55,4 @@ In the middleware we check if a session exists using the `withSession` helper fu :::note As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: \ No newline at end of file +::: diff --git a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx index 5c79c374d..881c1118a 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,8 +20,9 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; -import { withSession } from "../../sessionUtils"; import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { diff --git a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx index de6044e47..9eba0aa00 100644 --- a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx @@ -29,9 +29,9 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by > ```tsx title="app/api/auth/[[...path]].ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; -import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/src/plugins/codeTypeChecking/jsEnv/package.json b/v2/src/plugins/codeTypeChecking/jsEnv/package.json index d6c3971da..eb2b12c4f 100644 --- a/v2/src/plugins/codeTypeChecking/jsEnv/package.json +++ b/v2/src/plugins/codeTypeChecking/jsEnv/package.json @@ -46,7 +46,7 @@ "koa-router": "^10.1.1", "libphonenumber-js": "^1.10.18", "loopback": "^3.28.0", - "next": "^12.0.10", + "next": "^13.5.6", "nextjs-cors": "^2.1.0", "react-router-dom": "^6.2.1", "react-router-dom5": "npm:react-router-dom@^5.3.0", @@ -62,4 +62,4 @@ "supertokens-website-script": "github:supertokens/supertokens-website#17.0", "typescript": "^4.9.5" } -} \ No newline at end of file +} diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index a8c9cab4f..a188b4a4d 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -114,6 +114,7 @@ import SessionReact from 'supertokens-auth-react/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -126,7 +127,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -167,6 +168,7 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { SuperTokensConfig } from "supertokens-web-js/types" const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -179,7 +181,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -343,10 +345,11 @@ Make sure to enable `https` to be able to use the test users of the Facebook app ```tsx title="/app/components/supertokensProvider.tsx" 'use client'; +declare let frontendConfig: any; // typecheck-only, removed from output +declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; -import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -364,10 +367,10 @@ export const SuperTokensProvider: React.FC> = ({ ``` ```tsx title="/app/layout.tsx" +declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' -import { SuperTokensProvider } from './components/supertokensProvider' const inter = Inter({ subsets: ['latin'] }) @@ -401,6 +404,7 @@ export default function RootLayout({ ```tsx title="/app/layout.tsx" +declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 4ee4da0b0..297fc7a7e 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -46,11 +46,14 @@ This is a client component that renders just its children on the server side and We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -155,7 +158,7 @@ export const TryRefreshComponent = () => { And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" -import { HomePage } from './components/home' +declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' export default function Home() { @@ -190,11 +193,7 @@ export const HomeClientComponent = () => { return (
-
-

- Client side component got userId: {session.userId}
-

-
+ Hello world
); diff --git a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx index 1e0e4aac7..6e4da9a63 100644 --- a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx @@ -12,11 +12,14 @@ hide_title: true Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index 75c484581..26b3c7314 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -12,11 +12,11 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { serialize } from "cookie"; import { cookies, headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; -import { ensureSuperTokensInit } from "./config/backend"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx index 0bf314643..6efb1676a 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx @@ -10,10 +10,11 @@ hide_title: true # Using the Next.js middleware ```tsx title="middleware.tsx" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' -import { withSession } from './app/sessionUtils'; export async function middleware( @@ -54,4 +55,4 @@ In the middleware we check if a session exists using the `withSession` helper fu :::note As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: \ No newline at end of file +::: diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx index 5c79c374d..881c1118a 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,8 +20,9 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; -import { withSession } from "../../sessionUtils"; import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx index de6044e47..9eba0aa00 100644 --- a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx @@ -29,9 +29,9 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by > ```tsx title="app/api/auth/[[...path]].ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; -import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx index 3d880a624..8fed165e8 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -114,6 +114,8 @@ import SessionReact from 'supertokens-auth-react/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { useRouter } from "next/navigation"; +import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -126,7 +128,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -167,6 +169,8 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { useRouter } from "next/navigation"; +import { SuperTokensConfig } from "supertokens-web-js/types" const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -179,7 +183,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -341,10 +345,11 @@ Make sure to enable `https` to be able to use the test users of the Facebook app ```tsx title="/app/components/supertokensProvider.tsx" 'use client'; +declare let frontendConfig: any; // typecheck-only, removed from output +declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; -import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -362,10 +367,10 @@ export const SuperTokensProvider: React.FC> = ({ ``` ```tsx title="/app/layout.tsx" +declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' -import { SuperTokensProvider } from './components/supertokensProvider' const inter = Inter({ subsets: ['latin'] }) @@ -399,6 +404,7 @@ export default function RootLayout({ ```tsx title="/app/layout.tsx" +declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index b4cd52794..58d944313 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -46,11 +46,14 @@ This is a client component that renders just its children on the server side and We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -155,7 +158,7 @@ export const TryRefreshComponent = () => { And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" -import { HomePage } from './components/home' +declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' export default function Home() { @@ -190,11 +193,7 @@ export const HomeClientComponent = () => { return (
-
-

- Client side component got userId: {session.userId}
-

-
+ Hello world
); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx index c4ce66903..145e919d6 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx @@ -12,11 +12,14 @@ hide_title: true Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index 6806140c2..191e0c8ba 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -12,11 +12,11 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { serialize } from "cookie"; import { cookies, headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; -import { ensureSuperTokensInit } from "./config/backend"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx index 0bf314643..5d038294c 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -10,10 +10,11 @@ hide_title: true # Using the Next.js middleware ```tsx title="middleware.tsx" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' -import { withSession } from './app/sessionUtils'; export async function middleware( diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx index 56ab98a16..a38bc87b1 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,8 +20,9 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; -import { withSession } from "../../sessionUtils"; import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx index de6044e47..9eba0aa00 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -29,9 +29,9 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by > ```tsx title="app/api/auth/[[...path]].ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; -import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index 566a46b46..952295cdb 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -114,6 +114,7 @@ import SessionReact from 'supertokens-auth-react/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -126,7 +127,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -160,6 +161,7 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' import Router from 'next/navigation' +import { SuperTokensConfig } from "supertokens-web-js/types" const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -172,7 +174,7 @@ export function setRouter( routerInfo.pathName = pathName; } -export const frontendConfig = () => { +export const frontendConfig = (): SuperTokensConfig => { return { appInfo, recipeList: [ @@ -266,10 +268,11 @@ export function ensureSuperTokensInit() { ```tsx title="/app/components/supertokensProvider.tsx" 'use client'; +declare let frontendConfig: any; // typecheck-only, removed from output +declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; -import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -287,10 +290,10 @@ export const SuperTokensProvider: React.FC> = ({ ``` ```tsx title="/app/layout.tsx" +declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' -import { SuperTokensProvider } from './components/supertokensProvider' const inter = Inter({ subsets: ['latin'] }) @@ -324,6 +327,7 @@ export default function RootLayout({ ```tsx title="/app/layout.tsx" +declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 4ee4da0b0..297fc7a7e 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -46,11 +46,14 @@ This is a client component that renders just its children on the server side and We then create a server component that can check if the session exists and return any session information we may need: ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -155,7 +158,7 @@ export const TryRefreshComponent = () => { And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" -import { HomePage } from './components/home' +declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' export default function Home() { @@ -190,11 +193,7 @@ export const HomeClientComponent = () => { return (
-
-

- Client side component got userId: {session.userId}
-

-
+ Hello world
); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx index 1e0e4aac7..6e4da9a63 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx @@ -12,11 +12,14 @@ hide_title: true Lets modify the Home page we made in a [previous step](../protecting-route.mdx) to make a call to this API ```tsx title="app/components/home.tsx" -import { getSSRSession } from "../sessionUtils"; -import { TryRefreshComponent } from "./tryRefreshClientComponent"; +import { NextRequest, NextResponse } from "next/server";// typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session";// typecheck-only, removed from output +import { CollectingResponse } from "supertokens-node/framework/custom";// typecheck-only, removed from output +declare let getSSRSession: (req?: NextRequest, options?: VerifySessionOptions) => Promise<{session: SessionContainer | undefined;hasToken: boolean;hasInvalidClaims: boolean;baseResponse: CollectingResponse;nextResponse?: NextResponse;}>; // typecheck-only, removed from output +declare let TryRefreshComponent: any; // typecheck-only, removed from output +declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; -import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index 75c484581..26b3c7314 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -12,11 +12,11 @@ hide_title: true To make it easy to access session information and protect our API routes we will create some helper functions: ```ts title="app/sessionUtils.ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { serialize } from "cookie"; import { cookies, headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; -import { ensureSuperTokensInit } from "./config/backend"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx index 0bf314643..6efb1676a 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx @@ -10,10 +10,11 @@ hide_title: true # Using the Next.js middleware ```tsx title="middleware.tsx" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' -import { withSession } from './app/sessionUtils'; export async function middleware( @@ -54,4 +55,4 @@ In the middleware we check if a session exists using the `withSession` helper fu :::note As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: \ No newline at end of file +::: diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx index 5c79c374d..881c1118a 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -20,8 +20,9 @@ Create a new file `/app/api/user/route.ts` - An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/user/route.ts). ```ts title="app/api/user/route.ts" +declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output +import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; -import { withSession } from "../../sessionUtils"; import SuperTokens from "supertokens-node"; export function GET(request: NextRequest) { diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx index de6044e47..9eba0aa00 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx @@ -29,9 +29,9 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by > ```tsx title="app/api/auth/[[...path]].ts" +declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; -import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); From 1a5523037b95c091f891f6336862301bcf26aa56 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Thu, 19 Oct 2023 14:39:57 +0530 Subject: [PATCH 12/27] Type fixes --- v2/passwordless/nextjs/app-directory/init.mdx | 2 ++ v2/thirdparty/nextjs/app-directory/init.mdx | 2 ++ v2/thirdpartypasswordless/nextjs/app-directory/init.mdx | 2 ++ 3 files changed, 6 insertions(+) diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index ce4ec82a3..e88e1b3ec 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -115,6 +115,7 @@ import SessionReact from 'supertokens-auth-react/recipe/session' import { appInfo } from './appInfo' import Router from 'next/navigation' import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' +import { useRouter } from "next/navigation"; const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -162,6 +163,7 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' import { appInfo } from './appInfo' import Router from 'next/navigation' import { SuperTokensConfig } from "supertokens-web-js/types" +import { useRouter } from "next/navigation"; const routerInfo: { router?: ReturnType; pathName?: string } = {}; diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index a188b4a4d..3b8baaa7c 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -115,6 +115,7 @@ import SessionReact from 'supertokens-auth-react/recipe/session' import { appInfo } from './appInfo' import Router from 'next/navigation' import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' +import { useRouter } from "next/navigation"; const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -169,6 +170,7 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' import { appInfo } from './appInfo' import Router from 'next/navigation' import { SuperTokensConfig } from "supertokens-web-js/types" +import { useRouter } from "next/navigation"; const routerInfo: { router?: ReturnType; pathName?: string } = {}; diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index 952295cdb..8e217c20d 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -115,6 +115,7 @@ import SessionReact from 'supertokens-auth-react/recipe/session' import { appInfo } from './appInfo' import Router from 'next/navigation' import { SuperTokensConfig } from 'supertokens-auth-react/lib/build/types' +import { useRouter } from "next/navigation"; const routerInfo: { router?: ReturnType; pathName?: string } = {}; @@ -162,6 +163,7 @@ import SessionWebJs from 'supertokens-web-js/recipe/session' import { appInfo } from './appInfo' import Router from 'next/navigation' import { SuperTokensConfig } from "supertokens-web-js/types" +import { useRouter } from "next/navigation"; const routerInfo: { router?: ReturnType; pathName?: string } = {}; From 2414c2ffd7b3ece266325e8c48a87d3bbea5b237 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 11:46:31 +0530 Subject: [PATCH 13/27] Add about section for nextjs --- .../nextjs/app-directory/about.mdx | 65 +++++++++++++++++++ v2/emailpassword/sidebars.js | 1 + .../nextjs/app-directory/about.mdx | 65 +++++++++++++++++++ v2/passwordless/sidebars.js | 1 + v2/thirdparty/nextjs/app-directory/about.mdx | 65 +++++++++++++++++++ v2/thirdparty/sidebars.js | 1 + .../nextjs/app-directory/about.mdx | 65 +++++++++++++++++++ v2/thirdpartyemailpassword/sidebars.js | 1 + .../nextjs/app-directory/about.mdx | 65 +++++++++++++++++++ v2/thirdpartypasswordless/sidebars.js | 1 + 10 files changed, 330 insertions(+) create mode 100644 v2/emailpassword/nextjs/app-directory/about.mdx create mode 100644 v2/passwordless/nextjs/app-directory/about.mdx create mode 100644 v2/thirdparty/nextjs/app-directory/about.mdx create mode 100644 v2/thirdpartyemailpassword/nextjs/app-directory/about.mdx create mode 100644 v2/thirdpartypasswordless/nextjs/app-directory/about.mdx diff --git a/v2/emailpassword/nextjs/app-directory/about.mdx b/v2/emailpassword/nextjs/app-directory/about.mdx new file mode 100644 index 000000000..a82d63e1a --- /dev/null +++ b/v2/emailpassword/nextjs/app-directory/about.mdx @@ -0,0 +1,65 @@ +--- +id: about +title: About +hide_title: true +show_ui_switcher: true +--- + + + + +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Adding a website page to display the auth related widgets (on `/auth` by default) +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Building the various auth flows as per the [custom UI setup guide](../custom-ui/init/frontend). +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app + +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + +:::note +This example app uses our pre built UI +::: + + + + + diff --git a/v2/emailpassword/sidebars.js b/v2/emailpassword/sidebars.js index aef8631c4..d86d0c7b8 100644 --- a/v2/emailpassword/sidebars.js +++ b/v2/emailpassword/sidebars.js @@ -180,6 +180,7 @@ module.exports = { type: 'category', label: 'Using the App directory', items: [ + "nextjs/app-directory/about", "nextjs/app-directory/init", "nextjs/app-directory/setting-up-frontend", "nextjs/app-directory/setting-up-backend", diff --git a/v2/passwordless/nextjs/app-directory/about.mdx b/v2/passwordless/nextjs/app-directory/about.mdx new file mode 100644 index 000000000..a82d63e1a --- /dev/null +++ b/v2/passwordless/nextjs/app-directory/about.mdx @@ -0,0 +1,65 @@ +--- +id: about +title: About +hide_title: true +show_ui_switcher: true +--- + + + + +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Adding a website page to display the auth related widgets (on `/auth` by default) +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Building the various auth flows as per the [custom UI setup guide](../custom-ui/init/frontend). +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app + +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + +:::note +This example app uses our pre built UI +::: + + + + + diff --git a/v2/passwordless/sidebars.js b/v2/passwordless/sidebars.js index 4a6622449..18ee657e4 100644 --- a/v2/passwordless/sidebars.js +++ b/v2/passwordless/sidebars.js @@ -179,6 +179,7 @@ module.exports = { type: 'category', label: 'Using the App directory', items: [ + "nextjs/app-directory/about", "nextjs/app-directory/init", "nextjs/app-directory/setting-up-frontend", "nextjs/app-directory/setting-up-backend", diff --git a/v2/thirdparty/nextjs/app-directory/about.mdx b/v2/thirdparty/nextjs/app-directory/about.mdx new file mode 100644 index 000000000..a82d63e1a --- /dev/null +++ b/v2/thirdparty/nextjs/app-directory/about.mdx @@ -0,0 +1,65 @@ +--- +id: about +title: About +hide_title: true +show_ui_switcher: true +--- + + + + +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Adding a website page to display the auth related widgets (on `/auth` by default) +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Building the various auth flows as per the [custom UI setup guide](../custom-ui/init/frontend). +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app + +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + +:::note +This example app uses our pre built UI +::: + + + + + diff --git a/v2/thirdparty/sidebars.js b/v2/thirdparty/sidebars.js index 386a2c723..b1a9e862a 100644 --- a/v2/thirdparty/sidebars.js +++ b/v2/thirdparty/sidebars.js @@ -178,6 +178,7 @@ module.exports = { type: 'category', label: 'Using the App directory', items: [ + "nextjs/app-directory/about", "nextjs/app-directory/init", "nextjs/app-directory/setting-up-frontend", "nextjs/app-directory/setting-up-backend", diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/about.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/about.mdx new file mode 100644 index 000000000..a82d63e1a --- /dev/null +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/about.mdx @@ -0,0 +1,65 @@ +--- +id: about +title: About +hide_title: true +show_ui_switcher: true +--- + + + + +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Adding a website page to display the auth related widgets (on `/auth` by default) +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Building the various auth flows as per the [custom UI setup guide](../custom-ui/init/frontend). +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app + +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + +:::note +This example app uses our pre built UI +::: + + + + + diff --git a/v2/thirdpartyemailpassword/sidebars.js b/v2/thirdpartyemailpassword/sidebars.js index 013804ac0..d8ca24e41 100644 --- a/v2/thirdpartyemailpassword/sidebars.js +++ b/v2/thirdpartyemailpassword/sidebars.js @@ -183,6 +183,7 @@ module.exports = { type: 'category', label: 'Using the App directory', items: [ + "nextjs/app-directory/about", "nextjs/app-directory/init", "nextjs/app-directory/setting-up-frontend", "nextjs/app-directory/setting-up-backend", diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/about.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/about.mdx new file mode 100644 index 000000000..a82d63e1a --- /dev/null +++ b/v2/thirdpartypasswordless/nextjs/app-directory/about.mdx @@ -0,0 +1,65 @@ +--- +id: about +title: About +hide_title: true +show_ui_switcher: true +--- + + + + +import {PreBuiltOrCustomUISwitcher, PreBuiltUIContent, CustomUIContent} from "/src/components/preBuiltOrCustomUISwitcher" + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Adding a website page to display the auth related widgets (on `/auth` by default) +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + + + + + +# Overview steps + +Integrating SuperTokens with a Next.js app involves: +- Calling the frontend and backend init functions +- Building the various auth flows as per the [custom UI setup guide](../custom-ui/init/frontend). +- Creating a serverless function to expose the auth related APIs which will be consumed by the frontend widgets (on `/api/auth/` by default) +- Protecting website routes: Displaying them only when a user is logged in, else redirecting them to the login page +- Performing session verification: + - In your APIs + - In your frontend routes + +## Try an example app + +Download and run an example NextJS app quickly using the following command: + +```bash +npx create-supertokens-app@latest --frontend=next --recipe=^{codeImportRecipeName} +``` + +:::note +This example app uses our pre built UI +::: + + + + + diff --git a/v2/thirdpartypasswordless/sidebars.js b/v2/thirdpartypasswordless/sidebars.js index e6197b1cf..e21bab01c 100644 --- a/v2/thirdpartypasswordless/sidebars.js +++ b/v2/thirdpartypasswordless/sidebars.js @@ -181,6 +181,7 @@ module.exports = { type: 'category', label: 'Using the App directory', items: [ + "nextjs/app-directory/about", "nextjs/app-directory/init", "nextjs/app-directory/setting-up-frontend", "nextjs/app-directory/setting-up-backend", From f2cb73659948d731ae09ce0ef1e605afd72bc9bd Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 12:46:46 +0530 Subject: [PATCH 14/27] Fix init snippets --- .../nextjs/app-directory/init.mdx | 22 --------------- v2/passwordless/nextjs/app-directory/init.mdx | 26 +++-------------- v2/thirdparty/nextjs/app-directory/init.mdx | 22 --------------- .../nextjs/app-directory/init.mdx | 22 --------------- .../nextjs/app-directory/init.mdx | 28 ++++--------------- 5 files changed, 10 insertions(+), 110 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index 72da8e583..e65fd5a7e 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -159,21 +159,8 @@ import EmailPasswordWebJs from 'supertokens-web-js/recipe/emailpassword' import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' -import Router from 'next/navigation' -import { useRouter } from "next/navigation"; import { SuperTokensConfig } from "supertokens-web-js/types" -const routerInfo: { router?: ReturnType; pathName?: string } = - {}; - -export function setRouter( - router: ReturnType, - pathName: string, -) { - routerInfo.router = router; - routerInfo.pathName = pathName; -} - export const frontendConfig = (): SuperTokensConfig => { return { appInfo, @@ -181,15 +168,6 @@ export const frontendConfig = (): SuperTokensConfig => { EmailPasswordWebJs.init(), SessionWebJs.init(), ], - windowHandler: (original) => ({ - ...original, - location: { - ...original.location, - getPathName: () => routerInfo.pathName!, - assign: (url) => routerInfo.router!.push(url.toString()), - setHref: (url) => routerInfo.router!.push(url.toString()), - }, - }), } } ``` diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index e88e1b3ec..f181b1d8f 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -5,6 +5,10 @@ hide_title: true show_ui_switcher: true --- +import { PasswordlessFrontendForm } from "/src/components/snippetConfigForm/passwordlessFrontendForm"; +import { PasswordlessBackendForm } from "/src/components/snippetConfigForm/passwordlessBackendForm"; +import BackendDeliveryMethod from "../../reusableMD/backendDeliveryMethod.mdx" + @@ -161,20 +165,7 @@ import PasswordlessWebJs from 'supertokens-web-js/recipe/passwordless' import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' -import Router from 'next/navigation' import { SuperTokensConfig } from "supertokens-web-js/types" -import { useRouter } from "next/navigation"; - -const routerInfo: { router?: ReturnType; pathName?: string } = - {}; - -export function setRouter( - router: ReturnType, - pathName: string, -) { - routerInfo.router = router; - routerInfo.pathName = pathName; -} export const frontendConfig = (): SuperTokensConfig => { return { @@ -183,15 +174,6 @@ export const frontendConfig = (): SuperTokensConfig => { PasswordlessWebJs.init(), SessionWebJs.init(), ], - windowHandler: (original) => ({ - ...original, - location: { - ...original.location, - getPathName: () => routerInfo.pathName!, - assign: (url) => routerInfo.router!.push(url.toString()), - setHref: (url) => routerInfo.router!.push(url.toString()), - }, - }), } } ``` diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index 3b8baaa7c..625c1fb97 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -168,20 +168,7 @@ import ThirdPartyWebJs from 'supertokens-web-js/recipe/thirdparty' import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' -import Router from 'next/navigation' import { SuperTokensConfig } from "supertokens-web-js/types" -import { useRouter } from "next/navigation"; - -const routerInfo: { router?: ReturnType; pathName?: string } = - {}; - -export function setRouter( - router: ReturnType, - pathName: string, -) { - routerInfo.router = router; - routerInfo.pathName = pathName; -} export const frontendConfig = (): SuperTokensConfig => { return { @@ -190,15 +177,6 @@ export const frontendConfig = (): SuperTokensConfig => { ThirdPartyWebJs.init(), SessionWebJs.init(), ], - windowHandler: (original) => ({ - ...original, - location: { - ...original.location, - getPathName: () => routerInfo.pathName!, - assign: (url) => routerInfo.router!.push(url.toString()), - setHref: (url) => routerInfo.router!.push(url.toString()), - }, - }), } } ``` diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx index 8fed165e8..244d785c6 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -168,21 +168,8 @@ import ThirdPartyEmailPasswordWebJs from 'supertokens-web-js/recipe/thirdpartyem import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' -import Router from 'next/navigation' -import { useRouter } from "next/navigation"; import { SuperTokensConfig } from "supertokens-web-js/types" -const routerInfo: { router?: ReturnType; pathName?: string } = - {}; - -export function setRouter( - router: ReturnType, - pathName: string, -) { - routerInfo.router = router; - routerInfo.pathName = pathName; -} - export const frontendConfig = (): SuperTokensConfig => { return { appInfo, @@ -190,15 +177,6 @@ export const frontendConfig = (): SuperTokensConfig => { ThirdPartyEmailPasswordWebJs.init(), SessionWebJs.init(), ], - windowHandler: (original) => ({ - ...original, - location: { - ...original.location, - getPathName: () => routerInfo.pathName!, - assign: (url) => routerInfo.router!.push(url.toString()), - setHref: (url) => routerInfo.router!.push(url.toString()), - }, - }), } } ``` diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index 8e217c20d..c2a6b7ae8 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -5,6 +5,10 @@ hide_title: true show_ui_switcher: true --- +import { PasswordlessFrontendForm } from "/src/components/snippetConfigForm/passwordlessFrontendForm"; +import { PasswordlessBackendForm } from "/src/components/snippetConfigForm/passwordlessBackendForm"; +import BackendDeliveryMethod from "../../../passwordless/reusableMD/backendDeliveryMethod.mdx" + @@ -161,20 +165,7 @@ import ThirdPartyPasswordlessWebJs from 'supertokens-web-js/recipe/thirdpartypas import SessionWebJs from 'supertokens-web-js/recipe/session' // @ts-ignore import { appInfo } from './appInfo' -import Router from 'next/navigation' import { SuperTokensConfig } from "supertokens-web-js/types" -import { useRouter } from "next/navigation"; - -const routerInfo: { router?: ReturnType; pathName?: string } = - {}; - -export function setRouter( - router: ReturnType, - pathName: string, -) { - routerInfo.router = router; - routerInfo.pathName = pathName; -} export const frontendConfig = (): SuperTokensConfig => { return { @@ -183,15 +174,6 @@ export const frontendConfig = (): SuperTokensConfig => { ThirdPartyPasswordlessWebJs.init(), SessionWebJs.init(), ], - windowHandler: (original) => ({ - ...original, - location: { - ...original.location, - getPathName: () => routerInfo.pathName!, - assign: (url) => routerInfo.router!.push(url.toString()), - setHref: (url) => routerInfo.router!.push(url.toString()), - }, - }), } } ``` @@ -208,6 +190,7 @@ export const frontendConfig = (): SuperTokensConfig => { showNextJSAPIRouteCheckbox > + ```tsx title="/config/backendConfig.ts" @@ -251,6 +234,7 @@ export function ensureSuperTokensInit() { `ensureSuperTokensinit` is a helper function that can be used in your API routes to make sure SuperTokens is initiailised before using any functionality exposed by the backend SDKs. + From 88526b7af02de7564b9f4241e60c84ac95870458 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 12:55:19 +0530 Subject: [PATCH 15/27] Remove links to example app from pages directory --- v2/emailpassword/nextjs/init.mdx | 2 -- v2/emailpassword/nextjs/protecting-route.mdx | 4 ---- v2/emailpassword/nextjs/session-verification/in-api.mdx | 1 - v2/emailpassword/nextjs/session-verification/in-ssr.mdx | 4 ---- v2/emailpassword/nextjs/setting-up-backend.mdx | 1 - v2/emailpassword/nextjs/setting-up-frontend.mdx | 1 - v2/passwordless/nextjs/init.mdx | 3 +-- v2/passwordless/nextjs/protecting-route.mdx | 4 ---- v2/passwordless/nextjs/session-verification/in-api.mdx | 1 - v2/passwordless/nextjs/session-verification/in-ssr.mdx | 3 --- v2/passwordless/nextjs/setting-up-backend.mdx | 3 +-- v2/passwordless/nextjs/setting-up-frontend.mdx | 1 - v2/thirdparty/nextjs/init.mdx | 2 -- v2/thirdparty/nextjs/protecting-route.mdx | 4 ---- v2/thirdparty/nextjs/session-verification/in-api.mdx | 1 - v2/thirdparty/nextjs/session-verification/in-ssr.mdx | 3 --- v2/thirdparty/nextjs/setting-up-backend.mdx | 1 - v2/thirdparty/nextjs/setting-up-frontend.mdx | 1 - v2/thirdpartyemailpassword/nextjs/init.mdx | 2 -- v2/thirdpartyemailpassword/nextjs/protecting-route.mdx | 4 ---- .../nextjs/session-verification/in-api.mdx | 1 - .../nextjs/session-verification/in-ssr.mdx | 3 --- v2/thirdpartyemailpassword/nextjs/setting-up-backend.mdx | 1 - v2/thirdpartyemailpassword/nextjs/setting-up-frontend.mdx | 1 - v2/thirdpartypasswordless/nextjs/init.mdx | 2 -- v2/thirdpartypasswordless/nextjs/protecting-route.mdx | 4 ---- .../nextjs/session-verification/in-api.mdx | 1 - .../nextjs/session-verification/in-ssr.mdx | 3 --- v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx | 3 +-- v2/thirdpartypasswordless/nextjs/setting-up-frontend.mdx | 1 - 30 files changed, 3 insertions(+), 63 deletions(-) diff --git a/v2/emailpassword/nextjs/init.mdx b/v2/emailpassword/nextjs/init.mdx index 315b6e469..d6c24d92a 100644 --- a/v2/emailpassword/nextjs/init.mdx +++ b/v2/emailpassword/nextjs/init.mdx @@ -31,7 +31,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). ## 3) Create the `appInfo` configuration. @@ -207,7 +206,6 @@ export const backendConfig = (): TypeInput => { ## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component - Create a `/pages/_app.tsx` file. You can learn more about this file [here](https://nextjs.org/docs/advanced-features/custom-app). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx) ```tsx title="/pages/_app.tsx" diff --git a/v2/emailpassword/nextjs/protecting-route.mdx b/v2/emailpassword/nextjs/protecting-route.mdx index 03e6187c7..21505368e 100644 --- a/v2/emailpassword/nextjs/protecting-route.mdx +++ b/v2/emailpassword/nextjs/protecting-route.mdx @@ -38,10 +38,6 @@ export default function Home() { } ``` -:::important -An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L36). -::: - :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: diff --git a/v2/emailpassword/nextjs/session-verification/in-api.mdx b/v2/emailpassword/nextjs/session-verification/in-api.mdx index a8220ae96..e0d57c781 100644 --- a/v2/emailpassword/nextjs/session-verification/in-api.mdx +++ b/v2/emailpassword/nextjs/session-verification/in-api.mdx @@ -18,7 +18,6 @@ This is applicable for when the frontend calls an API in the `/pages/api` folder For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. ## 1) Create a new file `/pages/api/user.ts` -- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/user.ts). ## 2) Call the `supertokens.init` function Remember that whenever we want to use any functions from the `supertokens-node` lib, we have to call the `supertokens.init` function at the top of that serverless function file. diff --git a/v2/emailpassword/nextjs/session-verification/in-ssr.mdx b/v2/emailpassword/nextjs/session-verification/in-ssr.mdx index e33b15cf2..8f6dc12fb 100644 --- a/v2/emailpassword/nextjs/session-verification/in-ssr.mdx +++ b/v2/emailpassword/nextjs/session-verification/in-ssr.mdx @@ -24,9 +24,6 @@ For this guide, we will assume that we want to pass the logged in user's ID as a If using `getInitialProps`, the method described below applies as well. The only difference is the way the props are returned (see comments in the code). ::: -An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L15). - - ```tsx import Session from 'supertokens-node/recipe/session' import supertokensNode from 'supertokens-node' @@ -90,7 +87,6 @@ Do not use the `verifySession` function here. The reason is that this will send - The following will refresh a session if needed, for all your website pages - This goes in the `/pages/_app.tsx` file -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx#L16). ```tsx title="/pages/_app.tsx" diff --git a/v2/emailpassword/nextjs/setting-up-backend.mdx b/v2/emailpassword/nextjs/setting-up-backend.mdx index 89c0d6321..3658e19f8 100644 --- a/v2/emailpassword/nextjs/setting-up-backend.mdx +++ b/v2/emailpassword/nextjs/setting-up-backend.mdx @@ -18,7 +18,6 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. - `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/emailpassword/nextjs/setting-up-frontend.mdx b/v2/emailpassword/nextjs/setting-up-frontend.mdx index f1ff8f414..9f7cf1295 100644 --- a/v2/emailpassword/nextjs/setting-up-frontend.mdx +++ b/v2/emailpassword/nextjs/setting-up-frontend.mdx @@ -27,7 +27,6 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `pages/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/auth/%5B%5B...path%5D%5D.tsx). ## 2) Create the `Auth` component: diff --git a/v2/passwordless/nextjs/init.mdx b/v2/passwordless/nextjs/init.mdx index 1a4136d9a..317b856eb 100644 --- a/v2/passwordless/nextjs/init.mdx +++ b/v2/passwordless/nextjs/init.mdx @@ -36,7 +36,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). ## 3) Create the `appInfo` configuration. @@ -227,7 +226,7 @@ export const backendConfig = (): TypeInput => { ## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component - Create a `/pages/_app.tsx` file. You can learn more about this file [here](https://nextjs.org/docs/advanced-features/custom-app). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx) + ```tsx title="/pages/_app.tsx" diff --git a/v2/passwordless/nextjs/protecting-route.mdx b/v2/passwordless/nextjs/protecting-route.mdx index 03e6187c7..21505368e 100644 --- a/v2/passwordless/nextjs/protecting-route.mdx +++ b/v2/passwordless/nextjs/protecting-route.mdx @@ -38,10 +38,6 @@ export default function Home() { } ``` -:::important -An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L36). -::: - :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: diff --git a/v2/passwordless/nextjs/session-verification/in-api.mdx b/v2/passwordless/nextjs/session-verification/in-api.mdx index a8220ae96..e0d57c781 100644 --- a/v2/passwordless/nextjs/session-verification/in-api.mdx +++ b/v2/passwordless/nextjs/session-verification/in-api.mdx @@ -18,7 +18,6 @@ This is applicable for when the frontend calls an API in the `/pages/api` folder For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. ## 1) Create a new file `/pages/api/user.ts` -- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/user.ts). ## 2) Call the `supertokens.init` function Remember that whenever we want to use any functions from the `supertokens-node` lib, we have to call the `supertokens.init` function at the top of that serverless function file. diff --git a/v2/passwordless/nextjs/session-verification/in-ssr.mdx b/v2/passwordless/nextjs/session-verification/in-ssr.mdx index e33b15cf2..4e7a8cdb0 100644 --- a/v2/passwordless/nextjs/session-verification/in-ssr.mdx +++ b/v2/passwordless/nextjs/session-verification/in-ssr.mdx @@ -24,8 +24,6 @@ For this guide, we will assume that we want to pass the logged in user's ID as a If using `getInitialProps`, the method described below applies as well. The only difference is the way the props are returned (see comments in the code). ::: -An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L15). - ```tsx import Session from 'supertokens-node/recipe/session' @@ -90,7 +88,6 @@ Do not use the `verifySession` function here. The reason is that this will send - The following will refresh a session if needed, for all your website pages - This goes in the `/pages/_app.tsx` file -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx#L16). ```tsx title="/pages/_app.tsx" diff --git a/v2/passwordless/nextjs/setting-up-backend.mdx b/v2/passwordless/nextjs/setting-up-backend.mdx index 89c0d6321..377d6d735 100644 --- a/v2/passwordless/nextjs/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/setting-up-backend.mdx @@ -17,8 +17,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. -- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/auth/%5B%5B...path%5D%5D.ts). +- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc. ## 2) Expose the SuperTokens APIs diff --git a/v2/passwordless/nextjs/setting-up-frontend.mdx b/v2/passwordless/nextjs/setting-up-frontend.mdx index f1ff8f414..9f7cf1295 100644 --- a/v2/passwordless/nextjs/setting-up-frontend.mdx +++ b/v2/passwordless/nextjs/setting-up-frontend.mdx @@ -27,7 +27,6 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `pages/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/auth/%5B%5B...path%5D%5D.tsx). ## 2) Create the `Auth` component: diff --git a/v2/thirdparty/nextjs/init.mdx b/v2/thirdparty/nextjs/init.mdx index 86b3bfb68..f2cee4d00 100644 --- a/v2/thirdparty/nextjs/init.mdx +++ b/v2/thirdparty/nextjs/init.mdx @@ -31,7 +31,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). ## 3) Create the `appInfo` configuration. @@ -301,7 +300,6 @@ Make sure to enable `https` to be able to use the test users of the Facebook app ## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component - Create a `/pages/_app.tsx` file. You can learn more about this file [here](https://nextjs.org/docs/advanced-features/custom-app). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx) ```tsx title="/pages/_app.tsx" diff --git a/v2/thirdparty/nextjs/protecting-route.mdx b/v2/thirdparty/nextjs/protecting-route.mdx index 03e6187c7..21505368e 100644 --- a/v2/thirdparty/nextjs/protecting-route.mdx +++ b/v2/thirdparty/nextjs/protecting-route.mdx @@ -38,10 +38,6 @@ export default function Home() { } ``` -:::important -An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L36). -::: - :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: diff --git a/v2/thirdparty/nextjs/session-verification/in-api.mdx b/v2/thirdparty/nextjs/session-verification/in-api.mdx index a8220ae96..e0d57c781 100644 --- a/v2/thirdparty/nextjs/session-verification/in-api.mdx +++ b/v2/thirdparty/nextjs/session-verification/in-api.mdx @@ -18,7 +18,6 @@ This is applicable for when the frontend calls an API in the `/pages/api` folder For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. ## 1) Create a new file `/pages/api/user.ts` -- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/user.ts). ## 2) Call the `supertokens.init` function Remember that whenever we want to use any functions from the `supertokens-node` lib, we have to call the `supertokens.init` function at the top of that serverless function file. diff --git a/v2/thirdparty/nextjs/session-verification/in-ssr.mdx b/v2/thirdparty/nextjs/session-verification/in-ssr.mdx index e33b15cf2..4e7a8cdb0 100644 --- a/v2/thirdparty/nextjs/session-verification/in-ssr.mdx +++ b/v2/thirdparty/nextjs/session-verification/in-ssr.mdx @@ -24,8 +24,6 @@ For this guide, we will assume that we want to pass the logged in user's ID as a If using `getInitialProps`, the method described below applies as well. The only difference is the way the props are returned (see comments in the code). ::: -An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L15). - ```tsx import Session from 'supertokens-node/recipe/session' @@ -90,7 +88,6 @@ Do not use the `verifySession` function here. The reason is that this will send - The following will refresh a session if needed, for all your website pages - This goes in the `/pages/_app.tsx` file -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx#L16). ```tsx title="/pages/_app.tsx" diff --git a/v2/thirdparty/nextjs/setting-up-backend.mdx b/v2/thirdparty/nextjs/setting-up-backend.mdx index 89c0d6321..3658e19f8 100644 --- a/v2/thirdparty/nextjs/setting-up-backend.mdx +++ b/v2/thirdparty/nextjs/setting-up-backend.mdx @@ -18,7 +18,6 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. - `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdparty/nextjs/setting-up-frontend.mdx b/v2/thirdparty/nextjs/setting-up-frontend.mdx index f1ff8f414..9f7cf1295 100644 --- a/v2/thirdparty/nextjs/setting-up-frontend.mdx +++ b/v2/thirdparty/nextjs/setting-up-frontend.mdx @@ -27,7 +27,6 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `pages/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/auth/%5B%5B...path%5D%5D.tsx). ## 2) Create the `Auth` component: diff --git a/v2/thirdpartyemailpassword/nextjs/init.mdx b/v2/thirdpartyemailpassword/nextjs/init.mdx index 45c4f1e9a..d115fd5be 100644 --- a/v2/thirdpartyemailpassword/nextjs/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/init.mdx @@ -32,7 +32,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). ## 3) Create the `appInfo` configuration. @@ -294,7 +293,6 @@ Make sure to enable `https` to be able to use the test users of the Facebook app ## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component - Create a `/pages/_app.tsx` file. You can learn more about this file [here](https://nextjs.org/docs/advanced-features/custom-app). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx) ```tsx title="/pages/_app.tsx" diff --git a/v2/thirdpartyemailpassword/nextjs/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/protecting-route.mdx index b955acd6d..adb5b3dcb 100644 --- a/v2/thirdpartyemailpassword/nextjs/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/protecting-route.mdx @@ -38,10 +38,6 @@ export default function Home() { } ``` -:::important -An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L36). -::: - :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: diff --git a/v2/thirdpartyemailpassword/nextjs/session-verification/in-api.mdx b/v2/thirdpartyemailpassword/nextjs/session-verification/in-api.mdx index a8220ae96..e0d57c781 100644 --- a/v2/thirdpartyemailpassword/nextjs/session-verification/in-api.mdx +++ b/v2/thirdpartyemailpassword/nextjs/session-verification/in-api.mdx @@ -18,7 +18,6 @@ This is applicable for when the frontend calls an API in the `/pages/api` folder For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. ## 1) Create a new file `/pages/api/user.ts` -- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/user.ts). ## 2) Call the `supertokens.init` function Remember that whenever we want to use any functions from the `supertokens-node` lib, we have to call the `supertokens.init` function at the top of that serverless function file. diff --git a/v2/thirdpartyemailpassword/nextjs/session-verification/in-ssr.mdx b/v2/thirdpartyemailpassword/nextjs/session-verification/in-ssr.mdx index e33b15cf2..4e7a8cdb0 100644 --- a/v2/thirdpartyemailpassword/nextjs/session-verification/in-ssr.mdx +++ b/v2/thirdpartyemailpassword/nextjs/session-verification/in-ssr.mdx @@ -24,8 +24,6 @@ For this guide, we will assume that we want to pass the logged in user's ID as a If using `getInitialProps`, the method described below applies as well. The only difference is the way the props are returned (see comments in the code). ::: -An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L15). - ```tsx import Session from 'supertokens-node/recipe/session' @@ -90,7 +88,6 @@ Do not use the `verifySession` function here. The reason is that this will send - The following will refresh a session if needed, for all your website pages - This goes in the `/pages/_app.tsx` file -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx#L16). ```tsx title="/pages/_app.tsx" diff --git a/v2/thirdpartyemailpassword/nextjs/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/setting-up-backend.mdx index 89c0d6321..3658e19f8 100644 --- a/v2/thirdpartyemailpassword/nextjs/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/setting-up-backend.mdx @@ -18,7 +18,6 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. - `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/auth/%5B%5B...path%5D%5D.ts). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdpartyemailpassword/nextjs/setting-up-frontend.mdx b/v2/thirdpartyemailpassword/nextjs/setting-up-frontend.mdx index bd7ef00c3..6c0ab1129 100644 --- a/v2/thirdpartyemailpassword/nextjs/setting-up-frontend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/setting-up-frontend.mdx @@ -27,7 +27,6 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `pages/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/auth/%5B%5B...path%5D%5D.tsx). ## 2) Create the `Auth` component: diff --git a/v2/thirdpartypasswordless/nextjs/init.mdx b/v2/thirdpartypasswordless/nextjs/init.mdx index 2650118e5..8f60e64aa 100644 --- a/v2/thirdpartypasswordless/nextjs/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/init.mdx @@ -36,7 +36,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). ## 3) Create the `appInfo` configuration. @@ -227,7 +226,6 @@ export const backendConfig = (): TypeInput => { ## ^{nextjsinitlastnumber}) Call the frontend `init` functions and wrap with `` component - Create a `/pages/_app.tsx` file. You can learn more about this file [here](https://nextjs.org/docs/advanced-features/custom-app). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx) ```tsx title="/pages/_app.tsx" diff --git a/v2/thirdpartypasswordless/nextjs/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/protecting-route.mdx index 03e6187c7..21505368e 100644 --- a/v2/thirdpartypasswordless/nextjs/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/protecting-route.mdx @@ -38,10 +38,6 @@ export default function Home() { } ``` -:::important -An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L36). -::: - :::tip Test by navigating to `/` You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: diff --git a/v2/thirdpartypasswordless/nextjs/session-verification/in-api.mdx b/v2/thirdpartypasswordless/nextjs/session-verification/in-api.mdx index a8220ae96..e0d57c781 100644 --- a/v2/thirdpartypasswordless/nextjs/session-verification/in-api.mdx +++ b/v2/thirdpartypasswordless/nextjs/session-verification/in-api.mdx @@ -18,7 +18,6 @@ This is applicable for when the frontend calls an API in the `/pages/api` folder For this guide, we will assume that we want an API `/api/user GET` which returns the current session information. ## 1) Create a new file `/pages/api/user.ts` -- An example of this is [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/user.ts). ## 2) Call the `supertokens.init` function Remember that whenever we want to use any functions from the `supertokens-node` lib, we have to call the `supertokens.init` function at the top of that serverless function file. diff --git a/v2/thirdpartypasswordless/nextjs/session-verification/in-ssr.mdx b/v2/thirdpartypasswordless/nextjs/session-verification/in-ssr.mdx index e33b15cf2..4e7a8cdb0 100644 --- a/v2/thirdpartypasswordless/nextjs/session-verification/in-ssr.mdx +++ b/v2/thirdpartypasswordless/nextjs/session-verification/in-ssr.mdx @@ -24,8 +24,6 @@ For this guide, we will assume that we want to pass the logged in user's ID as a If using `getInitialProps`, the method described below applies as well. The only difference is the way the props are returned (see comments in the code). ::: -An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/index.tsx#L15). - ```tsx import Session from 'supertokens-node/recipe/session' @@ -90,7 +88,6 @@ Do not use the `verifySession` function here. The reason is that this will send - The following will refresh a session if needed, for all your website pages - This goes in the `/pages/_app.tsx` file -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/_app.tsx#L16). ```tsx title="/pages/_app.tsx" diff --git a/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx index 89c0d6321..377d6d735 100644 --- a/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx @@ -17,8 +17,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. -- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/api/auth/%5B%5B...path%5D%5D.ts). +- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc. ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdpartypasswordless/nextjs/setting-up-frontend.mdx b/v2/thirdpartypasswordless/nextjs/setting-up-frontend.mdx index f1ff8f414..9f7cf1295 100644 --- a/v2/thirdpartypasswordless/nextjs/setting-up-frontend.mdx +++ b/v2/thirdpartypasswordless/nextjs/setting-up-frontend.mdx @@ -27,7 +27,6 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `pages/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/pages/auth/%5B%5B...path%5D%5D.tsx). ## 2) Create the `Auth` component: From 855909ecb59a0d5f2bfd36212de972741185111e Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 12:59:41 +0530 Subject: [PATCH 16/27] Fix all example app links --- v2/emailpassword/nextjs/app-directory/init.mdx | 2 +- v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx | 2 +- v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx | 2 +- v2/passwordless/nextjs/app-directory/init.mdx | 2 +- v2/passwordless/nextjs/app-directory/setting-up-backend.mdx | 2 +- v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx | 2 +- v2/thirdparty/nextjs/app-directory/init.mdx | 2 +- v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx | 2 +- v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx | 2 +- v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx | 2 +- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../nextjs/app-directory/setting-up-frontend.mdx | 2 +- v2/thirdpartypasswordless/nextjs/app-directory/init.mdx | 2 +- .../nextjs/app-directory/setting-up-backend.mdx | 2 +- .../nextjs/app-directory/setting-up-frontend.mdx | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index e65fd5a7e..44e4ebe97 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -32,7 +32,7 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx index 9eba0aa00..20f58b8dc 100644 --- a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -18,7 +18,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. - `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/api/auth/%5B...path%5D). ## 2) Expose the SuperTokens APIs diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx index 6d2d488b9..635a11d76 100644 --- a/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx +++ b/v2/emailpassword/nextjs/app-directory/setting-up-frontend.mdx @@ -27,7 +27,7 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `app/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `app` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D). ## 2) Create the `Auth` component: diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index f181b1d8f..7506f5f2a 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -36,7 +36,7 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx index 9eba0aa00..20f58b8dc 100644 --- a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx @@ -18,7 +18,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. - `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/api/auth/%5B...path%5D). ## 2) Expose the SuperTokens APIs diff --git a/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx index 6d2d488b9..635a11d76 100644 --- a/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx +++ b/v2/passwordless/nextjs/app-directory/setting-up-frontend.mdx @@ -27,7 +27,7 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `app/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `app` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D). ## 2) Create the `Auth` component: diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index 625c1fb97..525cd750a 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -32,7 +32,7 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx index 9eba0aa00..20f58b8dc 100644 --- a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx @@ -18,7 +18,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. - `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/api/auth/%5B...path%5D). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx index 6d2d488b9..635a11d76 100644 --- a/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx +++ b/v2/thirdparty/nextjs/app-directory/setting-up-frontend.mdx @@ -27,7 +27,7 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `app/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `app` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D). ## 2) Create the `Auth` component: diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx index 244d785c6..06ee99bb4 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -32,7 +32,7 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx index 9eba0aa00..20f58b8dc 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -18,7 +18,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. - `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/api/auth/%5B...path%5D). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx index 6d2d488b9..635a11d76 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-frontend.mdx @@ -27,7 +27,7 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `app/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `app` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D). ## 2) Create the `Auth` component: diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index c2a6b7ae8..593a9f264 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -36,7 +36,7 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/config/). +- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx index 9eba0aa00..20f58b8dc 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx @@ -18,7 +18,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the API folder - Be sure to create the `auth` folder in the `app/api/` folder. - `[[...path]].tsx` will use the `getAppDirRequestHandler` helper function exposed by `supertokens-node` which helps in calling all the APIs like sign in, sign up etc. (the full folder path should be `/app/api/auth/[[...path]].tsx`). -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/api/auth/%5B%5B...path%5D%5D.ts). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/api/auth/%5B...path%5D). ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx index 6d2d488b9..635a11d76 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-frontend.mdx @@ -27,7 +27,7 @@ import AppInfoForm from "/src/components/appInfoForm"; ## 1) Create the `app/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `app` folder. - `[[...path]].tsx` will contain the component for showing SuperTokens UI -- An example of this can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D.tsx). +- An example of this can be found [here](https://github.com/supertokens/next.js/tree/canary/examples/with-supertokens/app/auth/%5B%5B...path%5D%5D). ## 2) Create the `Auth` component: From e8cd388fce62aa6fffc3e58ed2b4b36f51445a10 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 13:05:12 +0530 Subject: [PATCH 17/27] Fix cache control snippet --- v2/emailpassword/nextjs/app-directory/init.mdx | 1 - v2/emailpassword/nextjs/app-directory/session-helpers.mdx | 2 +- v2/emailpassword/nextjs/session-verification/in-ssr.mdx | 1 + v2/passwordless/nextjs/app-directory/init.mdx | 1 - v2/passwordless/nextjs/app-directory/session-helpers.mdx | 2 +- v2/passwordless/nextjs/init.mdx | 1 - v2/passwordless/nextjs/setting-up-backend.mdx | 2 +- v2/thirdparty/nextjs/app-directory/init.mdx | 1 - v2/thirdparty/nextjs/app-directory/session-helpers.mdx | 2 +- v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx | 1 - .../nextjs/app-directory/session-helpers.mdx | 2 +- v2/thirdpartypasswordless/nextjs/app-directory/init.mdx | 1 - .../nextjs/app-directory/session-helpers.mdx | 2 +- v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx | 2 +- 14 files changed, 8 insertions(+), 13 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index 44e4ebe97..505db0ee4 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -32,7 +32,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index 26b3c7314..26090990e 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -143,7 +143,7 @@ export async function withSession( * to make sure that the final result is not a cached version. */ if (didAddCookies || didAddHeaders) { - if (userResponse.headers.has("Cache-Control")) { + if (!userResponse.headers.has("Cache-Control")) { // This is needed for production deployments with Vercel userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); } diff --git a/v2/emailpassword/nextjs/session-verification/in-ssr.mdx b/v2/emailpassword/nextjs/session-verification/in-ssr.mdx index 8f6dc12fb..4e7a8cdb0 100644 --- a/v2/emailpassword/nextjs/session-verification/in-ssr.mdx +++ b/v2/emailpassword/nextjs/session-verification/in-ssr.mdx @@ -24,6 +24,7 @@ For this guide, we will assume that we want to pass the logged in user's ID as a If using `getInitialProps`, the method described below applies as well. The only difference is the way the props are returned (see comments in the code). ::: + ```tsx import Session from 'supertokens-node/recipe/session' import supertokensNode from 'supertokens-node' diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index 7506f5f2a..795adf7ba 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -36,7 +36,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index 26b3c7314..26090990e 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -143,7 +143,7 @@ export async function withSession( * to make sure that the final result is not a cached version. */ if (didAddCookies || didAddHeaders) { - if (userResponse.headers.has("Cache-Control")) { + if (!userResponse.headers.has("Cache-Control")) { // This is needed for production deployments with Vercel userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); } diff --git a/v2/passwordless/nextjs/init.mdx b/v2/passwordless/nextjs/init.mdx index 317b856eb..6b1aa5fdc 100644 --- a/v2/passwordless/nextjs/init.mdx +++ b/v2/passwordless/nextjs/init.mdx @@ -228,7 +228,6 @@ export const backendConfig = (): TypeInput => { - Create a `/pages/_app.tsx` file. You can learn more about this file [here](https://nextjs.org/docs/advanced-features/custom-app). - ```tsx title="/pages/_app.tsx" import '../styles/globals.css' import React from 'react' diff --git a/v2/passwordless/nextjs/setting-up-backend.mdx b/v2/passwordless/nextjs/setting-up-backend.mdx index 377d6d735..3658e19f8 100644 --- a/v2/passwordless/nextjs/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/setting-up-backend.mdx @@ -17,7 +17,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. -- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc. +- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. ## 2) Expose the SuperTokens APIs diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index 525cd750a..62e0e3a60 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -32,7 +32,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index 26b3c7314..26090990e 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -143,7 +143,7 @@ export async function withSession( * to make sure that the final result is not a cached version. */ if (didAddCookies || didAddHeaders) { - if (userResponse.headers.has("Cache-Control")) { + if (!userResponse.headers.has("Cache-Control")) { // This is needed for production deployments with Vercel userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); } diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx index 06ee99bb4..3c83650cf 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -32,7 +32,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index 191e0c8ba..cecb0f81c 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -143,7 +143,7 @@ export async function withSession( * to make sure that the final result is not a cached version. */ if (didAddCookies || didAddHeaders) { - if (userResponse.headers.has("Cache-Control")) { + if (!userResponse.headers.has("Cache-Control")) { // This is needed for production deployments with Vercel userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); } diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index 593a9f264..3db94554b 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -36,7 +36,6 @@ yarn add supertokens-node supertokens-auth-react supertokens-web-js nextjs-cors - Create an `appInfo.ts` inside the `config` folder. - Create a `backendConfig.ts` inside the `config` folder. - Create a `frontendConfig.ts` inside the `config` folder. -- An example of these files can be found [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/config/). ## 3) Create the `appInfo` configuration. diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index 26b3c7314..26090990e 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -143,7 +143,7 @@ export async function withSession( * to make sure that the final result is not a cached version. */ if (didAddCookies || didAddHeaders) { - if (userResponse.headers.has("Cache-Control")) { + if (!userResponse.headers.has("Cache-Control")) { // This is needed for production deployments with Vercel userResponse.headers.set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); } diff --git a/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx index 377d6d735..3658e19f8 100644 --- a/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/setting-up-backend.mdx @@ -17,7 +17,7 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by ## 1) Create the `pages/api/auth/[[...path]].tsx` page - Be sure to create the `auth` folder in the `pages/api/` folder. -- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc. +- `[[...path]].tsx` will use the middleware exposed by `supertokens-node` which exposes all the APIs like sign in, sign up etc.. ## 2) Expose the SuperTokens APIs From d2ccbf83359b86f331563ac3564d62c0676593ec Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 13:14:24 +0530 Subject: [PATCH 18/27] Modify explanation for getSSRSession and withSession --- .../nextjs/app-directory/session-helpers.mdx | 12 +++++++++--- .../nextjs/app-directory/session-helpers.mdx | 12 +++++++++--- .../nextjs/app-directory/session-helpers.mdx | 12 +++++++++--- .../nextjs/app-directory/session-helpers.mdx | 12 +++++++++--- .../nextjs/app-directory/session-helpers.mdx | 12 +++++++++--- 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index 26090990e..03131b77b 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -153,6 +153,12 @@ export async function withSession( } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: + - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: undefined, hasToken: false}` if a session does not exist + - `{..., session: undefined, hasToken: true}` if the session is expired + - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: + - Return status `401` if the session has expired + - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index 26090990e..03131b77b 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -153,6 +153,12 @@ export async function withSession( } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: + - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: undefined, hasToken: false}` if a session does not exist + - `{..., session: undefined, hasToken: true}` if the session is expired + - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: + - Return status `401` if the session has expired + - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index 26090990e..03131b77b 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -153,6 +153,12 @@ export async function withSession( } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: + - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: undefined, hasToken: false}` if a session does not exist + - `{..., session: undefined, hasToken: true}` if the session is expired + - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: + - Return status `401` if the session has expired + - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index cecb0f81c..bf5b945f1 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -153,6 +153,12 @@ export async function withSession( } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. \ No newline at end of file +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: + - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: undefined, hasToken: false}` if a session does not exist + - `{..., session: undefined, hasToken: true}` if the session is expired + - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: + - Return status `401` if the session has expired + - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index 26090990e..03131b77b 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -153,6 +153,12 @@ export async function withSession( } ``` -- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function tries to get the session tokens from the request and attaches those tokens to the response if they exist. `baseResponse` will contain all session tokens if a valid session exists and is then used in `withSession` to attach those tokens to the final result. `hasToken` indicates whether or not the user has session tokens (that may be valid or expired), if a session does not exist but there are tokens present we would ideally refresh the session. `hasInvalidClaims` indicates if all the session claims pass their validation, for example this would be false if email verification is required but the user has not verified their email. - -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will attach all session tokens to the final response if a valid session exists. +- `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: + - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: undefined, hasToken: false}` if a session does not exist + - `{..., session: undefined, hasToken: true}` if the session is expired + - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + +- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: + - Return status `401` if the session has expired + - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. From b6097d9ae059fbfc44aa84503554194283be1dda Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 14:24:58 +0530 Subject: [PATCH 19/27] Changes --- .../nextjs/app-directory/protecting-route.mdx | 260 +++++++++--------- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-session-guard.mdx | 4 + .../nextjs/app-directory/protecting-route.mdx | 260 +++++++++--------- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-session-guard.mdx | 4 + .../nextjs/app-directory/protecting-route.mdx | 260 +++++++++--------- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-session-guard.mdx | 4 + .../nextjs/app-directory/protecting-route.mdx | 260 +++++++++--------- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-session-guard.mdx | 6 +- .../nextjs/app-directory/protecting-route.mdx | 260 +++++++++--------- .../nextjs/app-directory/session-helpers.mdx | 2 +- .../session-verification-session-guard.mdx | 4 + 15 files changed, 676 insertions(+), 656 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 297fc7a7e..5e3c313e4 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -11,16 +11,81 @@ hide_title: true Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. -## Sessions with Server Components +## Sessions with Client Components +Lets create a client component for the `/` route of our website. -### Creating a wrapper around `SessionAuth` -Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+ Hello world +
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. + +:::caution +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +::: + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. :::info -This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +`useSessionContext` does not need to be used along with `SessionAuth`. Since our app is wrapped by the `SuperTokensWrapper` component, the `useSessionContext` hook can be used in any of our components. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: +## Sessions with Server Components {#server-components} + +### Creating some helper Components + +#### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + ```tsx title="app/components/sessionAuthForNextJS.tsx" "use client"; @@ -41,6 +106,56 @@ export const SessionAuthForNextJS = (props: Props) => { This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. +#### Creating the `TryRefreshComponent` + +This component will refres hthe user's session if their current session has expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; + +export const TryRefreshComponent = () => { + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; +}; +``` + ### Using `SessionAuthForNextJS` and checking for sessions We then create a server component that can check if the session exists and return any session information we may need: @@ -57,19 +172,8 @@ import { redirect } from "next/navigation"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - * - * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email - * verification is required but the user has not verified their email. - */ + + // `session` will be undefined if it does not exist or has expired if (!session) { if (!hasToken) { /** @@ -79,6 +183,10 @@ export async function HomePage() { return redirect("/auth"); } + /** + * `hasInvalidClaims` indicates that session claims did not pass validation. For example if email + * verification is required but the user's email has not been verified. + */ if (hasInvalidClaims) { /** * This will make sure that the user is redirected based on their session claims. For example they @@ -89,6 +197,10 @@ export async function HomePage() { */ return ; } else { + /** + * This means that the session does not exist but we have session tokens for the user. In this case + * the `TryRefreshComponent` will try to refresh the session. + */ return ; } } @@ -100,7 +212,7 @@ export async function HomePage() { return (
- Hello world + Your user id is: {session.getUserId()}
); @@ -109,52 +221,6 @@ export async function HomePage() { `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. -```tsx title="app/components/tryRefreshClientComponent.tsx" -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; -import Session from "supertokens-auth-react/recipe/session"; -import SuperTokens from "supertokens-auth-react"; - -export const TryRefreshComponent = () => { - const router = useRouter(); - const [didError, setDidError] = useState(false); - - useEffect(() => { - /** - * `attemptRefreshingSession` will call the refresh token endpoint to try and - * refresh the session. This will throw an error if the session cannot be refreshed. - */ - void Session.attemptRefreshingSession() - .then((hasSession) => { - /** - * If the user has a valid session, we reload the page to restart the flow - * with valid session tokens - */ - if (hasSession) { - router.refresh(); - } else { - redirect("/auth"); - } - }) - .catch(() => { - setDidError(true); - }); - }, []); - - /** - * We add this check to make sure we handle the case where the refresh API fails with - * an unexpected error - */ - if (didError) { - return
Something went wrong, please reload the page
; - } - - return
Loading...
; -}; -``` - And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" @@ -177,69 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - -## Sessions with Client Components - -Lets create a client component for the `/` route of our website. - -### Using the `SessionAuth` wrapper component - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { SessionAuth } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - return ( - -
- Hello world -
-
- ); -} -``` - -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. - -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. - -### Using `useSessionContext` - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { useSessionContext } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - const session = useSessionContext(); - - if (session.loading) { - return
Loading...
; - } - - if (session.doesSessionExist === false) { - return
Session does not exist
; - } - - return ( -
-
-

- Client side component got userId: {session.userId}
-

-
-
- ); -} -``` - -`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. - -:::info -`useSessionContext` can be used along with the `SessionAuth` wrapper component. -::: - -:::tip Test by navigating to `/` -You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index 03131b77b..f4e0e0e77 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -159,6 +159,6 @@ export async function withSession( - `{..., session: undefined, hasToken: true}` if the session is expired - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: +- `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx index 881c1118a..031aebc25 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -42,3 +42,7 @@ export function GET(request: NextRequest) { ``` In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. + +The `withSession` guard will return: + - Status `401` if the session does not exist or has expired + - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 297fc7a7e..5e3c313e4 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -11,16 +11,81 @@ hide_title: true Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. -## Sessions with Server Components +## Sessions with Client Components +Lets create a client component for the `/` route of our website. -### Creating a wrapper around `SessionAuth` -Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+ Hello world +
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. + +:::caution +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +::: + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. :::info -This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +`useSessionContext` does not need to be used along with `SessionAuth`. Since our app is wrapped by the `SuperTokensWrapper` component, the `useSessionContext` hook can be used in any of our components. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: +## Sessions with Server Components {#server-components} + +### Creating some helper Components + +#### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + ```tsx title="app/components/sessionAuthForNextJS.tsx" "use client"; @@ -41,6 +106,56 @@ export const SessionAuthForNextJS = (props: Props) => { This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. +#### Creating the `TryRefreshComponent` + +This component will refres hthe user's session if their current session has expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; + +export const TryRefreshComponent = () => { + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; +}; +``` + ### Using `SessionAuthForNextJS` and checking for sessions We then create a server component that can check if the session exists and return any session information we may need: @@ -57,19 +172,8 @@ import { redirect } from "next/navigation"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - * - * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email - * verification is required but the user has not verified their email. - */ + + // `session` will be undefined if it does not exist or has expired if (!session) { if (!hasToken) { /** @@ -79,6 +183,10 @@ export async function HomePage() { return redirect("/auth"); } + /** + * `hasInvalidClaims` indicates that session claims did not pass validation. For example if email + * verification is required but the user's email has not been verified. + */ if (hasInvalidClaims) { /** * This will make sure that the user is redirected based on their session claims. For example they @@ -89,6 +197,10 @@ export async function HomePage() { */ return ; } else { + /** + * This means that the session does not exist but we have session tokens for the user. In this case + * the `TryRefreshComponent` will try to refresh the session. + */ return ; } } @@ -100,7 +212,7 @@ export async function HomePage() { return (
- Hello world + Your user id is: {session.getUserId()}
); @@ -109,52 +221,6 @@ export async function HomePage() { `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. -```tsx title="app/components/tryRefreshClientComponent.tsx" -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; -import Session from "supertokens-auth-react/recipe/session"; -import SuperTokens from "supertokens-auth-react"; - -export const TryRefreshComponent = () => { - const router = useRouter(); - const [didError, setDidError] = useState(false); - - useEffect(() => { - /** - * `attemptRefreshingSession` will call the refresh token endpoint to try and - * refresh the session. This will throw an error if the session cannot be refreshed. - */ - void Session.attemptRefreshingSession() - .then((hasSession) => { - /** - * If the user has a valid session, we reload the page to restart the flow - * with valid session tokens - */ - if (hasSession) { - router.refresh(); - } else { - redirect("/auth"); - } - }) - .catch(() => { - setDidError(true); - }); - }, []); - - /** - * We add this check to make sure we handle the case where the refresh API fails with - * an unexpected error - */ - if (didError) { - return
Something went wrong, please reload the page
; - } - - return
Loading...
; -}; -``` - And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" @@ -177,69 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - -## Sessions with Client Components - -Lets create a client component for the `/` route of our website. - -### Using the `SessionAuth` wrapper component - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { SessionAuth } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - return ( - -
- Hello world -
-
- ); -} -``` - -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. - -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. - -### Using `useSessionContext` - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { useSessionContext } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - const session = useSessionContext(); - - if (session.loading) { - return
Loading...
; - } - - if (session.doesSessionExist === false) { - return
Session does not exist
; - } - - return ( -
-
-

- Client side component got userId: {session.userId}
-

-
-
- ); -} -``` - -`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. - -:::info -`useSessionContext` can be used along with the `SessionAuth` wrapper component. -::: - -:::tip Test by navigating to `/` -You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index 03131b77b..f4e0e0e77 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -159,6 +159,6 @@ export async function withSession( - `{..., session: undefined, hasToken: true}` if the session is expired - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: +- `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx index 881c1118a..031aebc25 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -42,3 +42,7 @@ export function GET(request: NextRequest) { ``` In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. + +The `withSession` guard will return: + - Status `401` if the session does not exist or has expired + - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 297fc7a7e..5e3c313e4 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -11,16 +11,81 @@ hide_title: true Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. -## Sessions with Server Components +## Sessions with Client Components +Lets create a client component for the `/` route of our website. -### Creating a wrapper around `SessionAuth` -Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+ Hello world +
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. + +:::caution +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +::: + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. :::info -This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +`useSessionContext` does not need to be used along with `SessionAuth`. Since our app is wrapped by the `SuperTokensWrapper` component, the `useSessionContext` hook can be used in any of our components. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: +## Sessions with Server Components {#server-components} + +### Creating some helper Components + +#### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + ```tsx title="app/components/sessionAuthForNextJS.tsx" "use client"; @@ -41,6 +106,56 @@ export const SessionAuthForNextJS = (props: Props) => { This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. +#### Creating the `TryRefreshComponent` + +This component will refres hthe user's session if their current session has expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; + +export const TryRefreshComponent = () => { + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; +}; +``` + ### Using `SessionAuthForNextJS` and checking for sessions We then create a server component that can check if the session exists and return any session information we may need: @@ -57,19 +172,8 @@ import { redirect } from "next/navigation"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - * - * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email - * verification is required but the user has not verified their email. - */ + + // `session` will be undefined if it does not exist or has expired if (!session) { if (!hasToken) { /** @@ -79,6 +183,10 @@ export async function HomePage() { return redirect("/auth"); } + /** + * `hasInvalidClaims` indicates that session claims did not pass validation. For example if email + * verification is required but the user's email has not been verified. + */ if (hasInvalidClaims) { /** * This will make sure that the user is redirected based on their session claims. For example they @@ -89,6 +197,10 @@ export async function HomePage() { */ return ; } else { + /** + * This means that the session does not exist but we have session tokens for the user. In this case + * the `TryRefreshComponent` will try to refresh the session. + */ return ; } } @@ -100,7 +212,7 @@ export async function HomePage() { return (
- Hello world + Your user id is: {session.getUserId()}
); @@ -109,52 +221,6 @@ export async function HomePage() { `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. -```tsx title="app/components/tryRefreshClientComponent.tsx" -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; -import Session from "supertokens-auth-react/recipe/session"; -import SuperTokens from "supertokens-auth-react"; - -export const TryRefreshComponent = () => { - const router = useRouter(); - const [didError, setDidError] = useState(false); - - useEffect(() => { - /** - * `attemptRefreshingSession` will call the refresh token endpoint to try and - * refresh the session. This will throw an error if the session cannot be refreshed. - */ - void Session.attemptRefreshingSession() - .then((hasSession) => { - /** - * If the user has a valid session, we reload the page to restart the flow - * with valid session tokens - */ - if (hasSession) { - router.refresh(); - } else { - redirect("/auth"); - } - }) - .catch(() => { - setDidError(true); - }); - }, []); - - /** - * We add this check to make sure we handle the case where the refresh API fails with - * an unexpected error - */ - if (didError) { - return
Something went wrong, please reload the page
; - } - - return
Loading...
; -}; -``` - And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" @@ -177,69 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - -## Sessions with Client Components - -Lets create a client component for the `/` route of our website. - -### Using the `SessionAuth` wrapper component - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { SessionAuth } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - return ( - -
- Hello world -
-
- ); -} -``` - -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. - -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. - -### Using `useSessionContext` - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { useSessionContext } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - const session = useSessionContext(); - - if (session.loading) { - return
Loading...
; - } - - if (session.doesSessionExist === false) { - return
Session does not exist
; - } - - return ( -
-
-

- Client side component got userId: {session.userId}
-

-
-
- ); -} -``` - -`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. - -:::info -`useSessionContext` can be used along with the `SessionAuth` wrapper component. -::: - -:::tip Test by navigating to `/` -You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index 03131b77b..f4e0e0e77 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -159,6 +159,6 @@ export async function withSession( - `{..., session: undefined, hasToken: true}` if the session is expired - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: +- `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx index 881c1118a..031aebc25 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx @@ -42,3 +42,7 @@ export function GET(request: NextRequest) { ``` In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. + +The `withSession` guard will return: + - Status `401` if the session does not exist or has expired + - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 58d944313..f1bfca327 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -11,16 +11,81 @@ hide_title: true Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. -## Sessions with Server Components +## Sessions with Client Components +Lets create a client component for the `/` route of our website. -### Creating a wrapper around `SessionAuth` -Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+ Hello world +
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. + +:::caution +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +::: + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. :::info -This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +`useSessionContext` does not need to be used along with `SessionAuth`. Since our app is wrapped by the `SuperTokensWrapper` component, the `useSessionContext` hook can be used in any of our components. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: +## Sessions with Server Components {#server-components} + +### Creating some helper Components + +#### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + ```tsx title="app/components/sessionAuthForNextJS.tsx" "use client"; @@ -41,6 +106,56 @@ export const SessionAuthForNextJS = (props: Props) => { This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. +#### Creating the `TryRefreshComponent` + +This component will refres hthe user's session if their current session has expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; + +export const TryRefreshComponent = () => { + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; +}; +``` + ### Using `SessionAuthForNextJS` and checking for sessions We then create a server component that can check if the session exists and return any session information we may need: @@ -57,19 +172,8 @@ import { redirect } from "next/navigation"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - * - * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email - * verification is required but the user has not verified their email. - */ + + // `session` will be undefined if it does not exist or has expired if (!session) { if (!hasToken) { /** @@ -79,6 +183,10 @@ export async function HomePage() { return redirect("/auth"); } + /** + * `hasInvalidClaims` indicates that session claims did not pass validation. For example if email + * verification is required but the user's email has not been verified. + */ if (hasInvalidClaims) { /** * This will make sure that the user is redirected based on their session claims. For example they @@ -89,6 +197,10 @@ export async function HomePage() { */ return ; } else { + /** + * This means that the session does not exist but we have session tokens for the user. In this case + * the `TryRefreshComponent` will try to refresh the session. + */ return ; } } @@ -100,7 +212,7 @@ export async function HomePage() { return (
- Hello world + Your user id is: {session.getUserId()}
); @@ -109,52 +221,6 @@ export async function HomePage() { `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. -```tsx title="app/components/tryRefreshClientComponent.tsx" -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; -import Session from "supertokens-auth-react/recipe/session"; -import SuperTokens from "supertokens-auth-react"; - -export const TryRefreshComponent = () => { - const router = useRouter(); - const [didError, setDidError] = useState(false); - - useEffect(() => { - /** - * `attemptRefreshingSession` will call the refresh token endpoint to try and - * refresh the session. This will throw an error if the session cannot be refreshed. - */ - void Session.attemptRefreshingSession() - .then((hasSession) => { - /** - * If the user has a valid session, we reload the page to restart the flow - * with valid session tokens - */ - if (hasSession) { - router.refresh(); - } else { - redirect("/auth"); - } - }) - .catch(() => { - setDidError(true); - }); - }, []); - - /** - * We add this check to make sure we handle the case where the refresh API fails with - * an unexpected error - */ - if (didError) { - return
Something went wrong, please reload the page
; - } - - return
Loading...
; -}; -``` - And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" @@ -176,70 +242,4 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). -::: - -## Sessions with Client Components - -Lets create a client component for the `/` route of our website. - -### Using the `SessionAuth` wrapper component - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { SessionAuth } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - return ( - -
- Hello world -
-
- ); -} -``` - -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. - -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. - -### Using `useSessionContext` - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { useSessionContext } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - const session = useSessionContext(); - - if (session.loading) { - return
Loading...
; - } - - if (session.doesSessionExist === false) { - return
Session does not exist
; - } - - return ( -
-
-

- Client side component got userId: {session.userId}
-

-
-
- ); -} -``` - -`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. - -:::info -`useSessionContext` can be used along with the `SessionAuth` wrapper component. -::: - -:::tip Test by navigating to `/` -You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index bf5b945f1..b3e096867 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -159,6 +159,6 @@ export async function withSession( - `{..., session: undefined, hasToken: true}` if the session is expired - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: +- `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. \ No newline at end of file diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx index a38bc87b1..8b00b604e 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -41,4 +41,8 @@ export function GET(request: NextRequest) { } ``` -In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. \ No newline at end of file +In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. + +The `withSession` guard will return: + - Status `401` if the session does not exist or has expired + - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 297fc7a7e..5e3c313e4 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -11,16 +11,81 @@ hide_title: true Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. -## Sessions with Server Components +## Sessions with Client Components +Lets create a client component for the `/` route of our website. -### Creating a wrapper around `SessionAuth` -Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. +### Using the `SessionAuth` wrapper component + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { SessionAuth } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + return ( + +
+ Hello world +
+
+ ); +} +``` + +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. + +:::caution +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +::: + +### Using `useSessionContext` + +```tsx title="app/components/homeClientComponent.tsx" +'use client' + +import { useSessionContext } from "supertokens-auth-react/recipe/session" + +export const HomeClientComponent = () => { + const session = useSessionContext(); + + if (session.loading) { + return
Loading...
; + } + + if (session.doesSessionExist === false) { + return
Session does not exist
; + } + + return ( +
+
+

+ Client side component got userId: {session.userId}
+

+
+
+ ); +} +``` + +`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. :::info -This is only needed because currently `SessionAuth` does not support server side rendering, this will be fixed in a future version of the SDK and this step can be skipped after that. +`useSessionContext` does not need to be used along with `SessionAuth`. Since our app is wrapped by the `SuperTokensWrapper` component, the `useSessionContext` hook can be used in any of our components. +::: + +:::tip Test by navigating to `/` +You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: +## Sessions with Server Components {#server-components} + +### Creating some helper Components + +#### Creating a wrapper around `SessionAuth` +Let's say we want to protect the home page of your website (`/` route). First we will create a wrapper around the `SessionAuth` component provided by SuperTokens to allow us to use it on both server side and client side. + ```tsx title="app/components/sessionAuthForNextJS.tsx" "use client"; @@ -41,6 +106,56 @@ export const SessionAuthForNextJS = (props: Props) => { This is a client component that renders just its children on the server side and renders the children wrapped with `SessionAuth` on the client side. +#### Creating the `TryRefreshComponent` + +This component will refres hthe user's session if their current session has expired. + +```tsx title="app/components/tryRefreshClientComponent.tsx" +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter, redirect } from "next/navigation"; +import Session from "supertokens-auth-react/recipe/session"; +import SuperTokens from "supertokens-auth-react"; + +export const TryRefreshComponent = () => { + const router = useRouter(); + const [didError, setDidError] = useState(false); + + useEffect(() => { + /** + * `attemptRefreshingSession` will call the refresh token endpoint to try and + * refresh the session. This will throw an error if the session cannot be refreshed. + */ + void Session.attemptRefreshingSession() + .then((hasSession) => { + /** + * If the user has a valid session, we reload the page to restart the flow + * with valid session tokens + */ + if (hasSession) { + router.refresh(); + } else { + redirect("/auth"); + } + }) + .catch(() => { + setDidError(true); + }); + }, []); + + /** + * We add this check to make sure we handle the case where the refresh API fails with + * an unexpected error + */ + if (didError) { + return
Something went wrong, please reload the page
; + } + + return
Loading...
; +}; +``` + ### Using `SessionAuthForNextJS` and checking for sessions We then create a server component that can check if the session exists and return any session information we may need: @@ -57,19 +172,8 @@ import { redirect } from "next/navigation"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); - - /** - * The session will be undefined if it does not exist or has expired, if the session has expired - * hasToken will be true because there are session tokens present (but are expired). In this case - * we will try to refresh the session using the TryRefreshComponent client component which will call - * the refresh endpoint. - * - * If session is undefined and hasToken is false, we can safely assume that there is no session for the user - * and redirect them to the login page - * - * hasInvalidClaims indicates that some of the session claims have failed validation, for example if email - * verification is required but the user has not verified their email. - */ + + // `session` will be undefined if it does not exist or has expired if (!session) { if (!hasToken) { /** @@ -79,6 +183,10 @@ export async function HomePage() { return redirect("/auth"); } + /** + * `hasInvalidClaims` indicates that session claims did not pass validation. For example if email + * verification is required but the user's email has not been verified. + */ if (hasInvalidClaims) { /** * This will make sure that the user is redirected based on their session claims. For example they @@ -89,6 +197,10 @@ export async function HomePage() { */ return ; } else { + /** + * This means that the session does not exist but we have session tokens for the user. In this case + * the `TryRefreshComponent` will try to refresh the session. + */ return ; } } @@ -100,7 +212,7 @@ export async function HomePage() { return (
- Hello world + Your user id is: {session.getUserId()}
); @@ -109,52 +221,6 @@ export async function HomePage() { `getSSRSession` is a utility function we created in the [previous step](./session-helpers.mdx). The `TryRefreshComponent` is a client component that checks if a session exists and tries to refresh the session if it is expired. -```tsx title="app/components/tryRefreshClientComponent.tsx" -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; -import Session from "supertokens-auth-react/recipe/session"; -import SuperTokens from "supertokens-auth-react"; - -export const TryRefreshComponent = () => { - const router = useRouter(); - const [didError, setDidError] = useState(false); - - useEffect(() => { - /** - * `attemptRefreshingSession` will call the refresh token endpoint to try and - * refresh the session. This will throw an error if the session cannot be refreshed. - */ - void Session.attemptRefreshingSession() - .then((hasSession) => { - /** - * If the user has a valid session, we reload the page to restart the flow - * with valid session tokens - */ - if (hasSession) { - router.refresh(); - } else { - redirect("/auth"); - } - }) - .catch(() => { - setDidError(true); - }); - }, []); - - /** - * We add this check to make sure we handle the case where the refresh API fails with - * an unexpected error - */ - if (didError) { - return
Something went wrong, please reload the page
; - } - - return
Loading...
; -}; -``` - And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" @@ -177,69 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - -## Sessions with Client Components - -Lets create a client component for the `/` route of our website. - -### Using the `SessionAuth` wrapper component - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { SessionAuth } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - return ( - -
- Hello world -
-
- ); -} -``` - -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it wil ltry to call the refresh endpoint. If the session cannot be refreshed or does not exist at all it will redirect to the login page. - -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. - -### Using `useSessionContext` - -```tsx title="app/components/homeClientComponent.tsx" -'use client' - -import { useSessionContext } from "supertokens-auth-react/recipe/session" - -export const HomeClientComponent = () => { - const session = useSessionContext(); - - if (session.loading) { - return
Loading...
; - } - - if (session.doesSessionExist === false) { - return
Session does not exist
; - } - - return ( -
-
-

- Client side component got userId: {session.userId}
-

-
-
- ); -} -``` - -`useSessionContext` lets you access the session information on the client side using the React Context API. `session.loading` indicates if the session is currently being loaded into the context, this will also refresh the session for you if it is expired. You can use `session.doesSessionExist` to check if a valid session exists and handle it accordingly. - -:::info -`useSessionContext` can be used along with the `SessionAuth` wrapper component. -::: - -:::tip Test by navigating to `/` -You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. -::: diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index 03131b77b..f4e0e0e77 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -159,6 +159,6 @@ export async function withSession( - `{..., session: undefined, hasToken: true}` if the session is expired - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. -- `withSession` will be used as a session guard for our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: +- `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired - Return status `403` if the session claims fail their validation. For example if email verification if required but the user's email has not been verified. diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx index 881c1118a..031aebc25 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -42,3 +42,7 @@ export function GET(request: NextRequest) { ``` In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. + +The `withSession` guard will return: + - Status `401` if the session does not exist or has expired + - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. From 0c9f3dd38962ca04393a5bd3cda5c0d1aa3a6075 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 14:31:35 +0530 Subject: [PATCH 20/27] Refactor redirection --- v2/emailpassword/nextjs/app-directory/protecting-route.mdx | 4 ++-- v2/passwordless/nextjs/app-directory/protecting-route.mdx | 4 ++-- v2/thirdparty/nextjs/app-directory/protecting-route.mdx | 4 ++-- .../nextjs/app-directory/protecting-route.mdx | 4 ++-- .../nextjs/app-directory/protecting-route.mdx | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 5e3c313e4..56bfda0b6 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -114,7 +114,7 @@ This component will refres hthe user's session if their current session has expi "use client"; import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; +import { useRouter } from "next/navigation"; import Session from "supertokens-auth-react/recipe/session"; import SuperTokens from "supertokens-auth-react"; @@ -136,7 +136,7 @@ export const TryRefreshComponent = () => { if (hasSession) { router.refresh(); } else { - redirect("/auth"); + SuperTokens.redirectToAuth(); } }) .catch(() => { diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 5e3c313e4..56bfda0b6 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -114,7 +114,7 @@ This component will refres hthe user's session if their current session has expi "use client"; import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; +import { useRouter } from "next/navigation"; import Session from "supertokens-auth-react/recipe/session"; import SuperTokens from "supertokens-auth-react"; @@ -136,7 +136,7 @@ export const TryRefreshComponent = () => { if (hasSession) { router.refresh(); } else { - redirect("/auth"); + SuperTokens.redirectToAuth(); } }) .catch(() => { diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 5e3c313e4..56bfda0b6 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -114,7 +114,7 @@ This component will refres hthe user's session if their current session has expi "use client"; import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; +import { useRouter } from "next/navigation"; import Session from "supertokens-auth-react/recipe/session"; import SuperTokens from "supertokens-auth-react"; @@ -136,7 +136,7 @@ export const TryRefreshComponent = () => { if (hasSession) { router.refresh(); } else { - redirect("/auth"); + SuperTokens.redirectToAuth(); } }) .catch(() => { diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index f1bfca327..6ada59386 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -114,7 +114,7 @@ This component will refres hthe user's session if their current session has expi "use client"; import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; +import { useRouter } from "next/navigation"; import Session from "supertokens-auth-react/recipe/session"; import SuperTokens from "supertokens-auth-react"; @@ -136,7 +136,7 @@ export const TryRefreshComponent = () => { if (hasSession) { router.refresh(); } else { - redirect("/auth"); + SuperTokens.redirectToAuth(); } }) .catch(() => { diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 5e3c313e4..56bfda0b6 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -114,7 +114,7 @@ This component will refres hthe user's session if their current session has expi "use client"; import { useEffect, useState } from "react"; -import { useRouter, redirect } from "next/navigation"; +import { useRouter } from "next/navigation"; import Session from "supertokens-auth-react/recipe/session"; import SuperTokens from "supertokens-auth-react"; @@ -136,7 +136,7 @@ export const TryRefreshComponent = () => { if (hasSession) { router.refresh(); } else { - redirect("/auth"); + SuperTokens.redirectToAuth(); } }) .catch(() => { From 5437138f98ac61c9d7d76466e2320c2fc10940d5 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 15:05:36 +0530 Subject: [PATCH 21/27] Change middleware --- .../session-verification-middleware.mdx | 37 +++++++++++++++++-- .../session-verification-middleware.mdx | 37 +++++++++++++++++-- .../session-verification-middleware.mdx | 37 +++++++++++++++++-- .../session-verification-middleware.mdx | 37 +++++++++++++++++-- .../session-verification-middleware.mdx | 37 +++++++++++++++++-- 5 files changed, 165 insertions(+), 20 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx index 6efb1676a..fb797b73c 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -9,6 +9,18 @@ hide_title: true # Using the Next.js middleware +:::important +This method is an alternative method for using sessions in an API. If you are already using [session guards](./session-verification-session-guard.mdx), you can skip this step. +::: + +## Setting up the middleware + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). +::: + ```tsx title="middleware.tsx" declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output @@ -51,8 +63,25 @@ export const config = { } ``` -In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. +## Using the middleware in APIs -:::note -As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: +The middleware will run for all our routes, we can read information set by the middleware in our API routes: + +```tsx title="app/api/userid/route.ts" +import { NextResponse, NextRequest } from "next/server"; + +export function GET(request: NextRequest) { + const userId = request.headers.get("x-user-id"); + + // The middleware only adds the userId if a session exists + if (userId === null) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + userId, + }); +} +``` + +This creates a `GET` request for the `/api/userid` route which returns the user id of the currently logged in user. diff --git a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx index 6efb1676a..fb797b73c 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx @@ -9,6 +9,18 @@ hide_title: true # Using the Next.js middleware +:::important +This method is an alternative method for using sessions in an API. If you are already using [session guards](./session-verification-session-guard.mdx), you can skip this step. +::: + +## Setting up the middleware + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). +::: + ```tsx title="middleware.tsx" declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output @@ -51,8 +63,25 @@ export const config = { } ``` -In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. +## Using the middleware in APIs -:::note -As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: +The middleware will run for all our routes, we can read information set by the middleware in our API routes: + +```tsx title="app/api/userid/route.ts" +import { NextResponse, NextRequest } from "next/server"; + +export function GET(request: NextRequest) { + const userId = request.headers.get("x-user-id"); + + // The middleware only adds the userId if a session exists + if (userId === null) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + userId, + }); +} +``` + +This creates a `GET` request for the `/api/userid` route which returns the user id of the currently logged in user. diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx index 6efb1676a..fb797b73c 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx @@ -9,6 +9,18 @@ hide_title: true # Using the Next.js middleware +:::important +This method is an alternative method for using sessions in an API. If you are already using [session guards](./session-verification-session-guard.mdx), you can skip this step. +::: + +## Setting up the middleware + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). +::: + ```tsx title="middleware.tsx" declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output @@ -51,8 +63,25 @@ export const config = { } ``` -In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. +## Using the middleware in APIs -:::note -As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: +The middleware will run for all our routes, we can read information set by the middleware in our API routes: + +```tsx title="app/api/userid/route.ts" +import { NextResponse, NextRequest } from "next/server"; + +export function GET(request: NextRequest) { + const userId = request.headers.get("x-user-id"); + + // The middleware only adds the userId if a session exists + if (userId === null) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + userId, + }); +} +``` + +This creates a `GET` request for the `/api/userid` route which returns the user id of the currently logged in user. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx index 5d038294c..fefcf069c 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -9,6 +9,18 @@ hide_title: true # Using the Next.js middleware +:::important +This method is an alternative method for using sessions in an API. If you are already using [session guards](./session-verification-session-guard.mdx), you can skip this step. +::: + +## Setting up the middleware + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). +::: + ```tsx title="middleware.tsx" declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output @@ -51,8 +63,25 @@ export const config = { } ``` -In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. +## Using the middleware in APIs -:::note -As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: \ No newline at end of file +The middleware will run for all our routes, we can read information set by the middleware in our API routes: + +```tsx title="app/api/userid/route.ts" +import { NextResponse, NextRequest } from "next/server"; + +export function GET(request: NextRequest) { + const userId = request.headers.get("x-user-id"); + + // The middleware only adds the userId if a session exists + if (userId === null) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + userId, + }); +} +``` + +This creates a `GET` request for the `/api/userid` route which returns the user id of the currently logged in user. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx index 6efb1676a..fb797b73c 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx @@ -9,6 +9,18 @@ hide_title: true # Using the Next.js middleware +:::important +This method is an alternative method for using sessions in an API. If you are already using [session guards](./session-verification-session-guard.mdx), you can skip this step. +::: + +## Setting up the middleware + +In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. + +:::note +You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). +::: + ```tsx title="middleware.tsx" declare let withSession: (request: NextRequest,handler: (session: SessionContainer | undefined) => Promise,options?: VerifySessionOptions) => void; // typecheck-only, removed from output import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output @@ -51,8 +63,25 @@ export const config = { } ``` -In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. +## Using the middleware in APIs -:::note -As you can see, this method does not provide the `session` object to your API route. If you need that, please see [this method](./session-verification-session-guard.mdx) -::: +The middleware will run for all our routes, we can read information set by the middleware in our API routes: + +```tsx title="app/api/userid/route.ts" +import { NextResponse, NextRequest } from "next/server"; + +export function GET(request: NextRequest) { + const userId = request.headers.get("x-user-id"); + + // The middleware only adds the userId if a session exists + if (userId === null) { + return new NextResponse("Authentication required", { status: 401 }); + } + + return NextResponse.json({ + userId, + }); +} +``` + +This creates a `GET` request for the `/api/userid` route which returns the user id of the currently logged in user. From 7d1d1c8c05bf72fdcb1c8744d769180adc18dcdb Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 15:32:10 +0530 Subject: [PATCH 22/27] Add custom ui link for route protection --- .../nextjs/app-directory/protecting-route.mdx | 20 ++++++++++++++++ .../nextjs/app-directory/protecting-route.mdx | 20 ++++++++++++++++ .../nextjs/app-directory/protecting-route.mdx | 20 ++++++++++++++++ .../nextjs/app-directory/protecting-route.mdx | 23 ++++++++++++++++++- .../nextjs/app-directory/protecting-route.mdx | 20 ++++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..390036a45 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -7,10 +7,20 @@ hide_title: true +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + + + + ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -243,3 +253,13 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: + + + + + +To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). + + + + diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..390036a45 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -7,10 +7,20 @@ hide_title: true +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + + + + ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -243,3 +253,13 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: + + + + + +To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). + + + + diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..390036a45 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -7,10 +7,20 @@ hide_title: true +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + + + + ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -243,3 +253,13 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: + + + + + +To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). + + + + diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 6ada59386..9f705856e 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -2,15 +2,26 @@ id: protecting-route title: 5. Checking for sessions in frontend routes hide_title: true +show_ui_switcher: true --- +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + + + + ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -242,4 +253,14 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). -::: \ No newline at end of file +::: + + + + + +To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). + + + + \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..390036a45 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -7,10 +7,20 @@ hide_title: true +import { + PreBuiltOrCustomUISwitcher, + PreBuiltUIContent, + CustomUIContent, +} from "/src/components/preBuiltOrCustomUISwitcher"; + # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. + + + + ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -243,3 +253,13 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: + + + + + +To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). + + + + From 77c10ed19f2fcc2ac3f8287bbeecf082f84c9749 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 15:59:44 +0530 Subject: [PATCH 23/27] Refactor --- .../nextjs/app-directory/protecting-route.mdx | 20 ---------------- .../nextjs/app-directory/protecting-route.mdx | 20 ---------------- .../nextjs/app-directory/protecting-route.mdx | 20 ---------------- .../nextjs/app-directory/protecting-route.mdx | 23 +------------------ .../nextjs/app-directory/protecting-route.mdx | 20 ---------------- 5 files changed, 1 insertion(+), 102 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 390036a45..56bfda0b6 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -7,20 +7,10 @@ hide_title: true -import { - PreBuiltOrCustomUISwitcher, - PreBuiltUIContent, - CustomUIContent, -} from "/src/components/preBuiltOrCustomUISwitcher"; - # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. - - - - ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -253,13 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - - - - - -To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). - - - - diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 390036a45..56bfda0b6 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -7,20 +7,10 @@ hide_title: true -import { - PreBuiltOrCustomUISwitcher, - PreBuiltUIContent, - CustomUIContent, -} from "/src/components/preBuiltOrCustomUISwitcher"; - # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. - - - - ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -253,13 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - - - - - -To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). - - - - diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 390036a45..56bfda0b6 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -7,20 +7,10 @@ hide_title: true -import { - PreBuiltOrCustomUISwitcher, - PreBuiltUIContent, - CustomUIContent, -} from "/src/components/preBuiltOrCustomUISwitcher"; - # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. - - - - ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -253,13 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - - - - - -To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). - - - - diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 9f705856e..6ada59386 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -2,26 +2,15 @@ id: protecting-route title: 5. Checking for sessions in frontend routes hide_title: true -show_ui_switcher: true --- -import { - PreBuiltOrCustomUISwitcher, - PreBuiltUIContent, - CustomUIContent, -} from "/src/components/preBuiltOrCustomUISwitcher"; - # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. - - - - ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -253,14 +242,4 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). -::: - - - - - -To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). - - - - \ No newline at end of file +::: \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 390036a45..56bfda0b6 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -7,20 +7,10 @@ hide_title: true -import { - PreBuiltOrCustomUISwitcher, - PreBuiltUIContent, - CustomUIContent, -} from "/src/components/preBuiltOrCustomUISwitcher"; - # 5. Checking for sessions in frontend routes Protecting a website route means that it cannot be accessed unless a user is signed in. If a non signed in user tries to access it, they will be redirected to the login page. - - - - ## Sessions with Client Components Lets create a client component for the `/` route of our website. @@ -253,13 +243,3 @@ You should be redirected to the login page. After that, sign in, and then visit :::important An example of this can be seen [here](https://github.com/supertokens/next.js/blob/canary/examples/with-supertokens/app/page.tsx). ::: - - - - - -To learn how to protect your frontend routes, refer to [this page](../../common-customizations/sessions/protecting-frontend-routes.mdx). - - - - From 540bd4456b47535e3c1ce49e63e815e581df0992 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 16:08:03 +0530 Subject: [PATCH 24/27] Refactor --- v2/emailpassword/nextjs/app-directory/protecting-route.mdx | 4 ++-- v2/passwordless/nextjs/app-directory/protecting-route.mdx | 4 ++-- v2/thirdparty/nextjs/app-directory/protecting-route.mdx | 4 ++-- .../nextjs/app-directory/protecting-route.mdx | 4 ++-- .../nextjs/app-directory/protecting-route.mdx | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..f214f7372 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -36,7 +36,7 @@ export const HomeClientComponent = () => { `SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -79,7 +79,7 @@ export const HomeClientComponent = () => { You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: -## Sessions with Server Components {#server-components} +## Sessions with Server Components ### Creating some helper Components diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..f214f7372 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -36,7 +36,7 @@ export const HomeClientComponent = () => { `SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -79,7 +79,7 @@ export const HomeClientComponent = () => { You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: -## Sessions with Server Components {#server-components} +## Sessions with Server Components ### Creating some helper Components diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..f214f7372 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -36,7 +36,7 @@ export const HomeClientComponent = () => { `SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -79,7 +79,7 @@ export const HomeClientComponent = () => { You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: -## Sessions with Server Components {#server-components} +## Sessions with Server Components ### Creating some helper Components diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 6ada59386..1a2e6a856 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -36,7 +36,7 @@ export const HomeClientComponent = () => { `SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -79,7 +79,7 @@ export const HomeClientComponent = () => { You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: -## Sessions with Server Components {#server-components} +## Sessions with Server Components ### Creating some helper Components diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index 56bfda0b6..f214f7372 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -36,7 +36,7 @@ export const HomeClientComponent = () => { `SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -79,7 +79,7 @@ export const HomeClientComponent = () => { You should be redirected to the login page. After that, sign in, and then visit `/` again. This time, there should be no redirection. ::: -## Sessions with Server Components {#server-components} +## Sessions with Server Components ### Creating some helper Components From a3326a9cb5b4cfd8bd8f5686ef77e6855bea7dcb Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 16:26:38 +0530 Subject: [PATCH 25/27] Fix types --- v2/emailpassword/nextjs/app-directory/init.mdx | 6 ++++++ .../nextjs/app-directory/protecting-route.mdx | 8 ++++++++ .../nextjs/app-directory/server-components-requests.mdx | 6 ++++++ v2/emailpassword/nextjs/app-directory/session-helpers.mdx | 2 ++ .../app-directory/session-verification-middleware.mdx | 2 ++ .../app-directory/session-verification-session-guard.mdx | 2 ++ .../nextjs/app-directory/setting-up-backend.mdx | 2 ++ v2/passwordless/nextjs/app-directory/init.mdx | 6 ++++++ v2/passwordless/nextjs/app-directory/protecting-route.mdx | 8 ++++++++ .../nextjs/app-directory/server-components-requests.mdx | 6 ++++++ v2/passwordless/nextjs/app-directory/session-helpers.mdx | 2 ++ .../app-directory/session-verification-middleware.mdx | 2 ++ .../app-directory/session-verification-session-guard.mdx | 2 ++ .../nextjs/app-directory/setting-up-backend.mdx | 2 ++ v2/thirdparty/nextjs/app-directory/init.mdx | 6 ++++++ v2/thirdparty/nextjs/app-directory/protecting-route.mdx | 8 ++++++++ .../nextjs/app-directory/server-components-requests.mdx | 6 ++++++ v2/thirdparty/nextjs/app-directory/session-helpers.mdx | 2 ++ .../app-directory/session-verification-middleware.mdx | 2 ++ .../app-directory/session-verification-session-guard.mdx | 2 ++ v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx | 2 ++ v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx | 6 ++++++ .../nextjs/app-directory/protecting-route.mdx | 8 ++++++++ .../nextjs/app-directory/server-components-requests.mdx | 6 ++++++ .../nextjs/app-directory/session-helpers.mdx | 2 ++ .../app-directory/session-verification-middleware.mdx | 2 ++ .../app-directory/session-verification-session-guard.mdx | 2 ++ .../nextjs/app-directory/setting-up-backend.mdx | 2 ++ v2/thirdpartypasswordless/nextjs/app-directory/init.mdx | 6 ++++++ .../nextjs/app-directory/protecting-route.mdx | 8 ++++++++ .../nextjs/app-directory/server-components-requests.mdx | 6 ++++++ .../nextjs/app-directory/session-helpers.mdx | 2 ++ .../app-directory/session-verification-middleware.mdx | 2 ++ .../app-directory/session-verification-session-guard.mdx | 2 ++ .../nextjs/app-directory/setting-up-backend.mdx | 2 ++ 35 files changed, 140 insertions(+) diff --git a/v2/emailpassword/nextjs/app-directory/init.mdx b/v2/emailpassword/nextjs/app-directory/init.mdx index 505db0ee4..f9ce92ccd 100644 --- a/v2/emailpassword/nextjs/app-directory/init.mdx +++ b/v2/emailpassword/nextjs/app-directory/init.mdx @@ -247,6 +247,8 @@ declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; +// @ts-ignore +import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -268,6 +270,8 @@ declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { SuperTokensProvider } from "./components/supertokensProvider"; const inter = Inter({ subsets: ['latin'] }) @@ -305,6 +309,8 @@ declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { frontendConfig } from '../config/frontend'; import SuperTokensWebJs from 'supertokens-web-js' const inter = Inter({ subsets: ['latin'] }) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index f214f7372..d8989798c 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -169,6 +169,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -226,6 +232,8 @@ And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' +// @ts-ignore +import { HomePage } from "./components/home"; export default function Home() { return ( diff --git a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx index 6e4da9a63..63bbb8494 100644 --- a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx @@ -20,6 +20,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index f4e0e0e77..e6c48ed58 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -19,6 +19,8 @@ import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; +// @ts-ignore +import { ensureSuperTokensInit } from "./config/backend"; ensureSuperTokensInit(); diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx index fb797b73c..69398edae 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -27,6 +27,8 @@ import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typech import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' +// @ts-ignore +import { withSession } from './app/sessionUtils'; export async function middleware( diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx index 031aebc25..18ee79f92 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -24,6 +24,8 @@ declare let withSession: (request: NextRequest,handler: (session: SessionContain import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; import SuperTokens from "supertokens-node"; +// @ts-ignore +import { withSession } from "../../sessionUtils"; export function GET(request: NextRequest) { return withSession(request, async (session) => { diff --git a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx index 20f58b8dc..6beea3b80 100644 --- a/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/emailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -32,6 +32,8 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; +// @ts-ignore +import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/passwordless/nextjs/app-directory/init.mdx b/v2/passwordless/nextjs/app-directory/init.mdx index 795adf7ba..9ff045085 100644 --- a/v2/passwordless/nextjs/app-directory/init.mdx +++ b/v2/passwordless/nextjs/app-directory/init.mdx @@ -258,6 +258,8 @@ declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; +// @ts-ignore +import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -279,6 +281,8 @@ declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { SuperTokensProvider } from "./components/supertokensProvider"; const inter = Inter({ subsets: ['latin'] }) @@ -316,6 +320,8 @@ declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { frontendConfig } from '../config/frontend'; import SuperTokensWebJs from 'supertokens-web-js' const inter = Inter({ subsets: ['latin'] }) diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index f214f7372..d8989798c 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -169,6 +169,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -226,6 +232,8 @@ And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' +// @ts-ignore +import { HomePage } from "./components/home"; export default function Home() { return ( diff --git a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx index 6e4da9a63..63bbb8494 100644 --- a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx @@ -20,6 +20,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index f4e0e0e77..e6c48ed58 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -19,6 +19,8 @@ import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; +// @ts-ignore +import { ensureSuperTokensInit } from "./config/backend"; ensureSuperTokensInit(); diff --git a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx index fb797b73c..69398edae 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx @@ -27,6 +27,8 @@ import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typech import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' +// @ts-ignore +import { withSession } from './app/sessionUtils'; export async function middleware( diff --git a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx index 031aebc25..18ee79f92 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -24,6 +24,8 @@ declare let withSession: (request: NextRequest,handler: (session: SessionContain import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; import SuperTokens from "supertokens-node"; +// @ts-ignore +import { withSession } from "../../sessionUtils"; export function GET(request: NextRequest) { return withSession(request, async (session) => { diff --git a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx index 20f58b8dc..6beea3b80 100644 --- a/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/passwordless/nextjs/app-directory/setting-up-backend.mdx @@ -32,6 +32,8 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; +// @ts-ignore +import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/thirdparty/nextjs/app-directory/init.mdx b/v2/thirdparty/nextjs/app-directory/init.mdx index 62e0e3a60..8bf4aebf6 100644 --- a/v2/thirdparty/nextjs/app-directory/init.mdx +++ b/v2/thirdparty/nextjs/app-directory/init.mdx @@ -329,6 +329,8 @@ declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; +// @ts-ignore +import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -350,6 +352,8 @@ declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { SuperTokensProvider } from "./components/supertokensProvider"; const inter = Inter({ subsets: ['latin'] }) @@ -387,6 +391,8 @@ declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { frontendConfig } from '../config/frontend'; import SuperTokensWebJs from 'supertokens-web-js' const inter = Inter({ subsets: ['latin'] }) diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index f214f7372..d8989798c 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -169,6 +169,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -226,6 +232,8 @@ And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' +// @ts-ignore +import { HomePage } from "./components/home"; export default function Home() { return ( diff --git a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx index 6e4da9a63..63bbb8494 100644 --- a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx @@ -20,6 +20,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index f4e0e0e77..e6c48ed58 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -19,6 +19,8 @@ import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; +// @ts-ignore +import { ensureSuperTokensInit } from "./config/backend"; ensureSuperTokensInit(); diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx index fb797b73c..69398edae 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx @@ -27,6 +27,8 @@ import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typech import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' +// @ts-ignore +import { withSession } from './app/sessionUtils'; export async function middleware( diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx index 031aebc25..18ee79f92 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx @@ -24,6 +24,8 @@ declare let withSession: (request: NextRequest,handler: (session: SessionContain import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; import SuperTokens from "supertokens-node"; +// @ts-ignore +import { withSession } from "../../sessionUtils"; export function GET(request: NextRequest) { return withSession(request, async (session) => { diff --git a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx index 20f58b8dc..6beea3b80 100644 --- a/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdparty/nextjs/app-directory/setting-up-backend.mdx @@ -32,6 +32,8 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; +// @ts-ignore +import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx index 3c83650cf..ec26195f6 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/init.mdx @@ -327,6 +327,8 @@ declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; +// @ts-ignore +import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -348,6 +350,8 @@ declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { SuperTokensProvider } from "./components/supertokensProvider"; const inter = Inter({ subsets: ['latin'] }) @@ -385,6 +389,8 @@ declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { frontendConfig } from '../config/frontend'; import SuperTokensWebJs from 'supertokens-web-js' const inter = Inter({ subsets: ['latin'] }) diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index 1a2e6a856..a983a7a5c 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -169,6 +169,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -226,6 +232,8 @@ And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' +// @ts-ignore +import { HomePage } from "./components/home"; export default function Home() { return ( diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx index 145e919d6..ed41e9ef0 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx @@ -20,6 +20,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index b3e096867..f4380ae98 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -19,6 +19,8 @@ import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; +// @ts-ignore +import { ensureSuperTokensInit } from "./config/backend"; ensureSuperTokensInit(); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx index fefcf069c..36d577b2b 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -27,6 +27,8 @@ import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typech import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' +// @ts-ignore +import { withSession } from './app/sessionUtils'; export async function middleware( diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx index 8b00b604e..a6afca5eb 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -24,6 +24,8 @@ declare let withSession: (request: NextRequest,handler: (session: SessionContain import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; import SuperTokens from "supertokens-node"; +// @ts-ignore +import { withSession } from "../../sessionUtils"; export function GET(request: NextRequest) { return withSession(request, async (session) => { diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx index 20f58b8dc..6beea3b80 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/setting-up-backend.mdx @@ -32,6 +32,8 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; +// @ts-ignore +import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx index 3db94554b..660106f76 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/init.mdx @@ -258,6 +258,8 @@ declare let setRouter: any; // typecheck-only, removed from output import React from 'react'; import { SuperTokensWrapper } from 'supertokens-auth-react'; import SuperTokensReact from 'supertokens-auth-react'; +// @ts-ignore +import { frontendConfig, setRouter } from '../config/frontend'; import { usePathname, useRouter } from 'next/navigation'; if (typeof window !== 'undefined') { @@ -279,6 +281,8 @@ declare let SuperTokensProvider: any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { SuperTokensProvider } from "./components/supertokensProvider"; const inter = Inter({ subsets: ['latin'] }) @@ -316,6 +320,8 @@ declare let frontendConfig: () => any; // typecheck-only, removed from output import './globals.css' import type { Metadata } from 'next' import { Inter } from 'next/font/google' +// @ts-ignore +import { frontendConfig } from '../config/frontend'; import SuperTokensWebJs from 'supertokens-web-js' const inter = Inter({ subsets: ['latin'] }) diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index f214f7372..d8989798c 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -169,6 +169,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); @@ -226,6 +232,8 @@ And then we can modify the `/app/page.tsx` file to use our server component ```tsx title="app/page.tsx" declare let HomePage: any; // typecheck-only, removed from output import styles from './page.module.css' +// @ts-ignore +import { HomePage } from "./components/home"; export default function Home() { return ( diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx index 6e4da9a63..63bbb8494 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx @@ -20,6 +20,12 @@ declare let TryRefreshComponent: any; // typecheck-only, removed from output declare let SessionAuthForNextJS: any; // typecheck-only, removed from output import styles from "../page.module.css"; import { redirect } from "next/navigation"; +// @ts-ignore +import { getSSRSession } from "../sessionUtils"; +// @ts-ignore +import { TryRefreshComponent } from "./tryRefreshClientComponent"; +// @ts-ignore +import { SessionAuthForNextJS } from "./sessionAuthForNextJS"; export async function HomePage() { const { session, hasToken, hasInvalidClaims } = await getSSRSession(); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index f4e0e0e77..e6c48ed58 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -19,6 +19,8 @@ import { NextRequest, NextResponse } from "next/server"; import Session, { SessionContainer, VerifySessionOptions } from "supertokens-node/recipe/session"; import { PreParsedRequest, CollectingResponse } from "supertokens-node/framework/custom"; import { HTTPMethod } from "supertokens-node/types"; +// @ts-ignore +import { ensureSuperTokensInit } from "./config/backend"; ensureSuperTokensInit(); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx index fb797b73c..69398edae 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx @@ -27,6 +27,8 @@ import { VerifySessionOptions } from 'supertokens-node/recipe/session' // typech import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { SessionContainer } from 'supertokens-node/recipe/session' +// @ts-ignore +import { withSession } from './app/sessionUtils'; export async function middleware( diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx index 031aebc25..18ee79f92 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -24,6 +24,8 @@ declare let withSession: (request: NextRequest,handler: (session: SessionContain import { SessionContainer, VerifySessionOptions } from 'supertokens-node/recipe/session' // typecheck-only, removed from output import { NextResponse, NextRequest } from "next/server"; import SuperTokens from "supertokens-node"; +// @ts-ignore +import { withSession } from "../../sessionUtils"; export function GET(request: NextRequest) { return withSession(request, async (session) => { diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx index 20f58b8dc..6beea3b80 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/setting-up-backend.mdx @@ -32,6 +32,8 @@ We will add all the backend APIs for auth on `/api/auth`. This can be changed by declare let ensureSuperTokensInit: () => void; // typecheck-only, removed from output import { getAppDirRequestHandler } from 'supertokens-node/nextjs'; import { NextRequest, NextResponse } from 'next/server'; +// @ts-ignore +import { ensureSuperTokensInit } from '../../../config/backend'; ensureSuperTokensInit(); From ba5f5088b1a7b49ebc8d3f02c1f6e51ff9983476 Mon Sep 17 00:00:00 2001 From: Nemi Shah Date: Fri, 20 Oct 2023 16:37:33 +0530 Subject: [PATCH 26/27] Refactor --- .../app-directory/server-components-requests.mdx | 16 +++++++++++++++- .../app-directory/server-components-requests.mdx | 16 +++++++++++++++- .../app-directory/server-components-requests.mdx | 16 +++++++++++++++- .../app-directory/server-components-requests.mdx | 16 +++++++++++++++- .../app-directory/server-components-requests.mdx | 16 +++++++++++++++- 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx index 63bbb8494..536a7e39e 100644 --- a/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/emailpassword/nextjs/app-directory/server-components-requests.mdx @@ -53,13 +53,27 @@ export async function HomePage() { }, }); + let message = ""; + + if (userInfoResponse.status === 200) { + message = `Your user id is: ${session.getUserId()}` + } else if (userInfoResponse.status === 500) { + message = "Something went wrong" + } else if (userInfoResponse.status === 401) { + // The TryRefreshComponent will try to refresh the session + return + } else if (userInfoResponse.status === 403) { + // SessionAuthForNextJS will redirect based on which claim is invalid + return ; + } + // You can use `userInfoResponse` to read the users session information // highlight-end return (
- Hello world + {message}
); diff --git a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx index 63bbb8494..536a7e39e 100644 --- a/v2/passwordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/passwordless/nextjs/app-directory/server-components-requests.mdx @@ -53,13 +53,27 @@ export async function HomePage() { }, }); + let message = ""; + + if (userInfoResponse.status === 200) { + message = `Your user id is: ${session.getUserId()}` + } else if (userInfoResponse.status === 500) { + message = "Something went wrong" + } else if (userInfoResponse.status === 401) { + // The TryRefreshComponent will try to refresh the session + return + } else if (userInfoResponse.status === 403) { + // SessionAuthForNextJS will redirect based on which claim is invalid + return ; + } + // You can use `userInfoResponse` to read the users session information // highlight-end return (
- Hello world + {message}
); diff --git a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx index 63bbb8494..536a7e39e 100644 --- a/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdparty/nextjs/app-directory/server-components-requests.mdx @@ -53,13 +53,27 @@ export async function HomePage() { }, }); + let message = ""; + + if (userInfoResponse.status === 200) { + message = `Your user id is: ${session.getUserId()}` + } else if (userInfoResponse.status === 500) { + message = "Something went wrong" + } else if (userInfoResponse.status === 401) { + // The TryRefreshComponent will try to refresh the session + return + } else if (userInfoResponse.status === 403) { + // SessionAuthForNextJS will redirect based on which claim is invalid + return ; + } + // You can use `userInfoResponse` to read the users session information // highlight-end return (
- Hello world + {message}
); diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx index ed41e9ef0..102a3a2b9 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/server-components-requests.mdx @@ -53,13 +53,27 @@ export async function HomePage() { }, }); + let message = ""; + + if (userInfoResponse.status === 200) { + message = `Your user id is: ${session.getUserId()}` + } else if (userInfoResponse.status === 500) { + message = "Something went wrong" + } else if (userInfoResponse.status === 401) { + // The TryRefreshComponent will try to refresh the session + return + } else if (userInfoResponse.status === 403) { + // SessionAuthForNextJS will redirect based on which claim is invalid + return ; + } + // You can use `userInfoResponse` to read the users session information // highlight-end return (
- Hello world + {message}
); diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx index 63bbb8494..536a7e39e 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/server-components-requests.mdx @@ -53,13 +53,27 @@ export async function HomePage() { }, }); + let message = ""; + + if (userInfoResponse.status === 200) { + message = `Your user id is: ${session.getUserId()}` + } else if (userInfoResponse.status === 500) { + message = "Something went wrong" + } else if (userInfoResponse.status === 401) { + // The TryRefreshComponent will try to refresh the session + return + } else if (userInfoResponse.status === 403) { + // SessionAuthForNextJS will redirect based on which claim is invalid + return ; + } + // You can use `userInfoResponse` to read the users session information // highlight-end return (
- Hello world + {message}
); From d047fcb2238c2b01feb01d252beb23560f57131f Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Fri, 20 Oct 2023 17:10:58 +0530 Subject: [PATCH 27/27] small fixes --- .../nextjs/app-directory/protecting-route.mdx | 8 +++++--- v2/emailpassword/nextjs/app-directory/session-helpers.mdx | 6 +++--- .../app-directory/session-verification-middleware.mdx | 6 +++--- .../app-directory/session-verification-session-guard.mdx | 8 ++++---- v2/passwordless/nextjs/app-directory/protecting-route.mdx | 8 +++++--- v2/passwordless/nextjs/app-directory/session-helpers.mdx | 6 +++--- .../app-directory/session-verification-middleware.mdx | 6 +++--- .../app-directory/session-verification-session-guard.mdx | 8 ++++---- v2/thirdparty/nextjs/app-directory/protecting-route.mdx | 8 +++++--- v2/thirdparty/nextjs/app-directory/session-helpers.mdx | 6 +++--- .../app-directory/session-verification-middleware.mdx | 6 +++--- .../app-directory/session-verification-session-guard.mdx | 8 ++++---- .../nextjs/app-directory/protecting-route.mdx | 8 +++++--- .../nextjs/app-directory/session-helpers.mdx | 6 +++--- .../app-directory/session-verification-middleware.mdx | 6 +++--- .../app-directory/session-verification-session-guard.mdx | 8 ++++---- .../nextjs/app-directory/protecting-route.mdx | 8 +++++--- .../nextjs/app-directory/session-helpers.mdx | 6 +++--- .../app-directory/session-verification-middleware.mdx | 6 +++--- .../app-directory/session-verification-session-guard.mdx | 8 ++++---- 20 files changed, 75 insertions(+), 65 deletions(-) diff --git a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx index d8989798c..19c693a80 100644 --- a/v2/emailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/emailpassword/nextjs/app-directory/protecting-route.mdx @@ -33,10 +33,12 @@ export const HomeClientComponent = () => { } ``` -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. It also does session claim checking on the frontend and take appropriate action if the claim validators fail. For example, if you have set the email verification recipe to be `"REQUIRED"`, and the user's email is not verified, this component will redirect the user to the email verification page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. On the server side, this component renders an empty screen. + +Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -108,7 +110,7 @@ This is a client component that renders just its children on the server side and #### Creating the `TryRefreshComponent` -This component will refres hthe user's session if their current session has expired. +This component will refresh the user's session if their current session has expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" "use client"; diff --git a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx index e6c48ed58..f168b0b6a 100644 --- a/v2/emailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-helpers.mdx @@ -156,10 +156,10 @@ export async function withSession( ``` - `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: - - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: Object, hasToken: true}` if a session exists - `{..., session: undefined, hasToken: false}` if a session does not exist - - `{..., session: undefined, hasToken: true}` if the session is expired - - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + - `{..., session: undefined, hasToken: true, hasInvalidClaims: false}` if the session is expired + - `{..., session: undefined, hasToken: true, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. - `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx index 69398edae..967c12710 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -17,7 +17,7 @@ This method is an alternative method for using sessions in an API. If you are al In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. -:::note +:::caution You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). ::: @@ -65,9 +65,9 @@ export const config = { } ``` -## Using the middleware in APIs +## Fetching the user ID in your APIs -The middleware will run for all our routes, we can read information set by the middleware in our API routes: +The middleware will run for all routes, we can read information set by the middleware in the API routes: ```tsx title="app/api/userid/route.ts" import { NextResponse, NextRequest } from "next/server"; diff --git a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx index 18ee79f92..5b64f688a 100644 --- a/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/emailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -1,13 +1,13 @@ --- id: session-verification-session-guard -title: Adding a session guard to all API routes +title: Adding a session guard to each API route hide_title: true --- -# Adding a session guard to all API routes +# Adding a session guard to each API route :::note This is applicable for when the frontend calls an API in the `/app/api` folder. @@ -46,5 +46,5 @@ export function GET(request: NextRequest) { In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. The `withSession` guard will return: - - Status `401` if the session does not exist or has expired - - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. +- Status `401` if the session does not exist or has expired +- Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. diff --git a/v2/passwordless/nextjs/app-directory/protecting-route.mdx b/v2/passwordless/nextjs/app-directory/protecting-route.mdx index d8989798c..19c693a80 100644 --- a/v2/passwordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/passwordless/nextjs/app-directory/protecting-route.mdx @@ -33,10 +33,12 @@ export const HomeClientComponent = () => { } ``` -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. It also does session claim checking on the frontend and take appropriate action if the claim validators fail. For example, if you have set the email verification recipe to be `"REQUIRED"`, and the user's email is not verified, this component will redirect the user to the email verification page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. On the server side, this component renders an empty screen. + +Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -108,7 +110,7 @@ This is a client component that renders just its children on the server side and #### Creating the `TryRefreshComponent` -This component will refres hthe user's session if their current session has expired. +This component will refresh the user's session if their current session has expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" "use client"; diff --git a/v2/passwordless/nextjs/app-directory/session-helpers.mdx b/v2/passwordless/nextjs/app-directory/session-helpers.mdx index e6c48ed58..f168b0b6a 100644 --- a/v2/passwordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/passwordless/nextjs/app-directory/session-helpers.mdx @@ -156,10 +156,10 @@ export async function withSession( ``` - `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: - - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: Object, hasToken: true}` if a session exists - `{..., session: undefined, hasToken: false}` if a session does not exist - - `{..., session: undefined, hasToken: true}` if the session is expired - - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + - `{..., session: undefined, hasToken: true, hasInvalidClaims: false}` if the session is expired + - `{..., session: undefined, hasToken: true, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. - `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired diff --git a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx index 69398edae..967c12710 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-middleware.mdx @@ -17,7 +17,7 @@ This method is an alternative method for using sessions in an API. If you are al In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. -:::note +:::caution You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). ::: @@ -65,9 +65,9 @@ export const config = { } ``` -## Using the middleware in APIs +## Fetching the user ID in your APIs -The middleware will run for all our routes, we can read information set by the middleware in our API routes: +The middleware will run for all routes, we can read information set by the middleware in the API routes: ```tsx title="app/api/userid/route.ts" import { NextResponse, NextRequest } from "next/server"; diff --git a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx index 18ee79f92..5b64f688a 100644 --- a/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/passwordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -1,13 +1,13 @@ --- id: session-verification-session-guard -title: Adding a session guard to all API routes +title: Adding a session guard to each API route hide_title: true --- -# Adding a session guard to all API routes +# Adding a session guard to each API route :::note This is applicable for when the frontend calls an API in the `/app/api` folder. @@ -46,5 +46,5 @@ export function GET(request: NextRequest) { In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. The `withSession` guard will return: - - Status `401` if the session does not exist or has expired - - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. +- Status `401` if the session does not exist or has expired +- Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. diff --git a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx index d8989798c..19c693a80 100644 --- a/v2/thirdparty/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdparty/nextjs/app-directory/protecting-route.mdx @@ -33,10 +33,12 @@ export const HomeClientComponent = () => { } ``` -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. It also does session claim checking on the frontend and take appropriate action if the claim validators fail. For example, if you have set the email verification recipe to be `"REQUIRED"`, and the user's email is not verified, this component will redirect the user to the email verification page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. On the server side, this component renders an empty screen. + +Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -108,7 +110,7 @@ This is a client component that renders just its children on the server side and #### Creating the `TryRefreshComponent` -This component will refres hthe user's session if their current session has expired. +This component will refresh the user's session if their current session has expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" "use client"; diff --git a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx index e6c48ed58..f168b0b6a 100644 --- a/v2/thirdparty/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-helpers.mdx @@ -156,10 +156,10 @@ export async function withSession( ``` - `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: - - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: Object, hasToken: true}` if a session exists - `{..., session: undefined, hasToken: false}` if a session does not exist - - `{..., session: undefined, hasToken: true}` if the session is expired - - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + - `{..., session: undefined, hasToken: true, hasInvalidClaims: false}` if the session is expired + - `{..., session: undefined, hasToken: true, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. - `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx index 69398edae..967c12710 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-middleware.mdx @@ -17,7 +17,7 @@ This method is an alternative method for using sessions in an API. If you are al In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. -:::note +:::caution You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). ::: @@ -65,9 +65,9 @@ export const config = { } ``` -## Using the middleware in APIs +## Fetching the user ID in your APIs -The middleware will run for all our routes, we can read information set by the middleware in our API routes: +The middleware will run for all routes, we can read information set by the middleware in the API routes: ```tsx title="app/api/userid/route.ts" import { NextResponse, NextRequest } from "next/server"; diff --git a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx index 18ee79f92..5b64f688a 100644 --- a/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdparty/nextjs/app-directory/session-verification-session-guard.mdx @@ -1,13 +1,13 @@ --- id: session-verification-session-guard -title: Adding a session guard to all API routes +title: Adding a session guard to each API route hide_title: true --- -# Adding a session guard to all API routes +# Adding a session guard to each API route :::note This is applicable for when the frontend calls an API in the `/app/api` folder. @@ -46,5 +46,5 @@ export function GET(request: NextRequest) { In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. The `withSession` guard will return: - - Status `401` if the session does not exist or has expired - - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. +- Status `401` if the session does not exist or has expired +- Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx index a983a7a5c..db88e2d54 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/protecting-route.mdx @@ -33,10 +33,12 @@ export const HomeClientComponent = () => { } ``` -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. It also does session claim checking on the frontend and take appropriate action if the claim validators fail. For example, if you have set the email verification recipe to be `"REQUIRED"`, and the user's email is not verified, this component will redirect the user to the email verification page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. On the server side, this component renders an empty screen. + +Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -108,7 +110,7 @@ This is a client component that renders just its children on the server side and #### Creating the `TryRefreshComponent` -This component will refres hthe user's session if their current session has expired. +This component will refresh the user's session if their current session has expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" "use client"; diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx index f4380ae98..154ca4e19 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-helpers.mdx @@ -156,10 +156,10 @@ export async function withSession( ``` - `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: - - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: Object, hasToken: true}` if a session exists - `{..., session: undefined, hasToken: false}` if a session does not exist - - `{..., session: undefined, hasToken: true}` if the session is expired - - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + - `{..., session: undefined, hasToken: true, hasInvalidClaims: false}` if the session is expired + - `{..., session: undefined, hasToken: true, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. - `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx index 36d577b2b..866317321 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-middleware.mdx @@ -17,7 +17,7 @@ This method is an alternative method for using sessions in an API. If you are al In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. -:::note +:::caution You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). ::: @@ -65,9 +65,9 @@ export const config = { } ``` -## Using the middleware in APIs +## Fetching the user ID in your APIs -The middleware will run for all our routes, we can read information set by the middleware in our API routes: +The middleware will run for all routes, we can read information set by the middleware in the API routes: ```tsx title="app/api/userid/route.ts" import { NextResponse, NextRequest } from "next/server"; diff --git a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx index a6afca5eb..11f00d736 100644 --- a/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartyemailpassword/nextjs/app-directory/session-verification-session-guard.mdx @@ -1,13 +1,13 @@ --- id: session-verification-session-guard -title: Adding a session guard to all API routes +title: Adding a session guard to each API route hide_title: true --- -# Adding a session guard to all API routes +# Adding a session guard to each API route :::note This is applicable for when the frontend calls an API in the `/app/api` folder. @@ -46,5 +46,5 @@ export function GET(request: NextRequest) { In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. The `withSession` guard will return: - - Status `401` if the session does not exist or has expired - - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. \ No newline at end of file +- Status `401` if the session does not exist or has expired +- Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. \ No newline at end of file diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx index d8989798c..19c693a80 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/protecting-route.mdx @@ -33,10 +33,12 @@ export const HomeClientComponent = () => { } ``` -`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. +`SessionAuth` is a component exposed by the SuperTokens React SDK, it checks if a session exists and if it does not exist it will redirect the user to the login page. It also does session claim checking on the frontend and take appropriate action if the claim validators fail. For example, if you have set the email verification recipe to be `"REQUIRED"`, and the user's email is not verified, this component will redirect the user to the email verification page. :::caution -At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. Refer to the next section of this page to learn how to use sessions on the server side. +At the moment the `SessionAuth` component does not support server side rendering and will only work on the client side. On the server side, this component renders an empty screen. + +Refer to the next section of this page to learn how to use sessions on the server side. ::: ### Using `useSessionContext` @@ -108,7 +110,7 @@ This is a client component that renders just its children on the server side and #### Creating the `TryRefreshComponent` -This component will refres hthe user's session if their current session has expired. +This component will refresh the user's session if their current session has expired. ```tsx title="app/components/tryRefreshClientComponent.tsx" "use client"; diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx index e6c48ed58..f168b0b6a 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-helpers.mdx @@ -156,10 +156,10 @@ export async function withSession( ``` - `getSSRSession` will be used in our frontend routes to get session information when rendering on the server side. This function will: - - `{..., session: Object, hasToken: true}` is a session exists + - `{..., session: Object, hasToken: true}` if a session exists - `{..., session: undefined, hasToken: false}` if a session does not exist - - `{..., session: undefined, hasToken: true}` if the session is expired - - `{..., session: undefined, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. + - `{..., session: undefined, hasToken: true, hasInvalidClaims: false}` if the session is expired + - `{..., session: undefined, hasToken: true, hasInvalidClaims: true}` If the session claims fail their validation. For example if email verification if required but the user's email has not been verified. - `withSession` will be used as a session guard for each of our API routes. If a session exists it will be passed to the callback, which is where we will write our API logic. If no session exists it will pass `undefined` to the callback. This function will: - Return status `401` if the session has expired diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx index 69398edae..967c12710 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-middleware.mdx @@ -17,7 +17,7 @@ This method is an alternative method for using sessions in an API. If you are al In the middleware we check if a session exists using the `withSession` helper function we created [here](./session-helpers.mdx) and set the user's user id to the request headers using the session object. You can set other information in the same way. -:::note +:::caution You cannot pass the full session container through the middleware because the Next.js middleware does not allow objects to be passed. If you need to access the full session container in your APIs switch to using [session guards](./session-verification-session-guard.mdx). ::: @@ -65,9 +65,9 @@ export const config = { } ``` -## Using the middleware in APIs +## Fetching the user ID in your APIs -The middleware will run for all our routes, we can read information set by the middleware in our API routes: +The middleware will run for all routes, we can read information set by the middleware in the API routes: ```tsx title="app/api/userid/route.ts" import { NextResponse, NextRequest } from "next/server"; diff --git a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx index 18ee79f92..5b64f688a 100644 --- a/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx +++ b/v2/thirdpartypasswordless/nextjs/app-directory/session-verification-session-guard.mdx @@ -1,13 +1,13 @@ --- id: session-verification-session-guard -title: Adding a session guard to all API routes +title: Adding a session guard to each API route hide_title: true --- -# Adding a session guard to all API routes +# Adding a session guard to each API route :::note This is applicable for when the frontend calls an API in the `/app/api` folder. @@ -46,5 +46,5 @@ export function GET(request: NextRequest) { In the above snippet we are creating a `GET` handler for the `/api/user` route. We call the `withSession` helper function we created in a [previous step](./session-helpers.mdx), the function will pass the session object in the callback which we then use to read user information. If a session does not exist `undefined` will be passed intead. The `withSession` guard will return: - - Status `401` if the session does not exist or has expired - - Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified. +- Status `401` if the session does not exist or has expired +- Stauts `403` if the session claims fail their validation. For example if email verification is required but the user's email is not verified.