From be8f34f45e98b7557a9051ba86d15f5758c29ca6 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Fri, 12 May 2023 10:21:25 +0100 Subject: [PATCH 01/61] Initial commit of app directory support --- .circleci/config.yml | 10 +- CONTRIBUTING.md | 19 +- EXAMPLES.md | 15 +- .../.env.local.template | 0 .../.env.production | 0 example-app/.eslintrc.json | 3 + example-app/.gitignore | 35 +++ .../basic-example => example-app}/.npmrc | 0 example-app/README.md | 34 +++ .../app}/api/auth/[auth0].ts | 2 +- example-app/app/favicon.ico | Bin 0 -> 25931 bytes example-app/app/globals.css | 107 ++++++++ example-app/app/layout.tsx | 21 ++ example-app/app/page.module.css | 229 ++++++++++++++++++ example-app/app/page.tsx | 95 ++++++++ .../middleware.ts | 0 example-app/next.config.js | 4 + example-app/package.json | 24 ++ example-app/public/next.svg | 1 + example-app/public/vercel.svg | 1 + .../server.js | 2 +- .../tsconfig.json | 26 +- examples/.eslintrc | 30 --- examples/README.md | 115 --------- examples/basic-example/.env.local.template | 5 - examples/basic-example/.gitignore | 6 - examples/basic-example/components/header.jsx | 80 ------ examples/basic-example/components/layout.jsx | 34 --- examples/basic-example/next-env.d.ts | 5 - examples/basic-example/next.config.js | 3 - examples/basic-example/now.json | 12 - examples/basic-example/package.json | 18 -- examples/basic-example/pages/_app.jsx | 15 -- .../basic-example/pages/api/auth/[auth0].ts | 3 - examples/basic-example/pages/index.jsx | 41 ---- .../basic-example/pages/protected-page.jsx | 33 --- examples/basic-example/tsconfig.json | 29 --- examples/kitchen-sink-example/.eslintrc | 50 ---- examples/kitchen-sink-example/.gitignore | 6 - examples/kitchen-sink-example/.npmrc | 1 - examples/kitchen-sink-example/.prettierrc | 11 - .../kitchen-sink-example/.vscode/launch.json | 12 - examples/kitchen-sink-example/app/global.css | 9 - examples/kitchen-sink-example/app/head.tsx | 9 - examples/kitchen-sink-example/app/layout.tsx | 17 -- .../app/profile-experimental-rsc/page.tsx | 31 --- .../components/header.tsx | 104 -------- .../components/layout.tsx | 34 --- examples/kitchen-sink-example/lib/use-api.ts | 60 ----- examples/kitchen-sink-example/next-env.d.ts | 5 - examples/kitchen-sink-example/next.config.js | 6 - examples/kitchen-sink-example/package.json | 46 ---- examples/kitchen-sink-example/pages/_app.tsx | 13 - .../kitchen-sink-example/pages/_document.tsx | 16 -- examples/kitchen-sink-example/pages/about.tsx | 15 -- .../pages/api/hello-world-mw.ts | 3 - .../pages/api/my/shows.ts | 37 --- .../kitchen-sink-example/pages/api/shows.ts | 30 --- examples/kitchen-sink-example/pages/index.tsx | 41 ---- .../kitchen-sink-example/pages/profile-mw.tsx | 18 -- .../pages/profile-ssr.tsx | 19 -- .../kitchen-sink-example/pages/profile.tsx | 14 -- examples/kitchen-sink-example/pages/shows.tsx | 39 --- package.json | 23 +- 64 files changed, 596 insertions(+), 1130 deletions(-) rename {examples/kitchen-sink-example => example-app}/.env.local.template (100%) rename {examples/kitchen-sink-example => example-app}/.env.production (100%) create mode 100644 example-app/.eslintrc.json create mode 100644 example-app/.gitignore rename {examples/basic-example => example-app}/.npmrc (100%) create mode 100644 example-app/README.md rename {examples/kitchen-sink-example/pages => example-app/app}/api/auth/[auth0].ts (85%) create mode 100644 example-app/app/favicon.ico create mode 100644 example-app/app/globals.css create mode 100644 example-app/app/layout.tsx create mode 100644 example-app/app/page.module.css create mode 100644 example-app/app/page.tsx rename {examples/kitchen-sink-example => example-app}/middleware.ts (100%) create mode 100644 example-app/next.config.js create mode 100644 example-app/package.json create mode 100644 example-app/public/next.svg create mode 100644 example-app/public/vercel.svg rename {examples/kitchen-sink-example => example-app}/server.js (91%) rename {examples/kitchen-sink-example => example-app}/tsconfig.json (65%) delete mode 100644 examples/.eslintrc delete mode 100644 examples/README.md delete mode 100644 examples/basic-example/.env.local.template delete mode 100644 examples/basic-example/.gitignore delete mode 100644 examples/basic-example/components/header.jsx delete mode 100644 examples/basic-example/components/layout.jsx delete mode 100644 examples/basic-example/next-env.d.ts delete mode 100644 examples/basic-example/next.config.js delete mode 100644 examples/basic-example/now.json delete mode 100644 examples/basic-example/package.json delete mode 100644 examples/basic-example/pages/_app.jsx delete mode 100644 examples/basic-example/pages/api/auth/[auth0].ts delete mode 100644 examples/basic-example/pages/index.jsx delete mode 100644 examples/basic-example/pages/protected-page.jsx delete mode 100644 examples/basic-example/tsconfig.json delete mode 100644 examples/kitchen-sink-example/.eslintrc delete mode 100644 examples/kitchen-sink-example/.gitignore delete mode 100644 examples/kitchen-sink-example/.npmrc delete mode 100644 examples/kitchen-sink-example/.prettierrc delete mode 100644 examples/kitchen-sink-example/.vscode/launch.json delete mode 100644 examples/kitchen-sink-example/app/global.css delete mode 100644 examples/kitchen-sink-example/app/head.tsx delete mode 100644 examples/kitchen-sink-example/app/layout.tsx delete mode 100644 examples/kitchen-sink-example/app/profile-experimental-rsc/page.tsx delete mode 100644 examples/kitchen-sink-example/components/header.tsx delete mode 100644 examples/kitchen-sink-example/components/layout.tsx delete mode 100644 examples/kitchen-sink-example/lib/use-api.ts delete mode 100644 examples/kitchen-sink-example/next-env.d.ts delete mode 100644 examples/kitchen-sink-example/next.config.js delete mode 100644 examples/kitchen-sink-example/package.json delete mode 100644 examples/kitchen-sink-example/pages/_app.tsx delete mode 100644 examples/kitchen-sink-example/pages/_document.tsx delete mode 100644 examples/kitchen-sink-example/pages/about.tsx delete mode 100644 examples/kitchen-sink-example/pages/api/hello-world-mw.ts delete mode 100644 examples/kitchen-sink-example/pages/api/my/shows.ts delete mode 100644 examples/kitchen-sink-example/pages/api/shows.ts delete mode 100644 examples/kitchen-sink-example/pages/index.tsx delete mode 100644 examples/kitchen-sink-example/pages/profile-mw.tsx delete mode 100644 examples/kitchen-sink-example/pages/profile-ssr.tsx delete mode 100644 examples/kitchen-sink-example/pages/profile.tsx delete mode 100644 examples/kitchen-sink-example/pages/shows.tsx diff --git a/.circleci/config.yml b/.circleci/config.yml index 21ad2a104..3286ccae5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,16 +9,16 @@ jobs: steps: - checkout - restore_cache: - key: dependencies-{{ .Branch }}-{{ checksum "package-lock.json" }}-{{ checksum "examples/kitchen-sink-example/package.json" }} + key: dependencies-{{ .Branch }}-{{ checksum "package-lock.json" }}-{{ checksum "example-app/package.json" }} - run: npm ci - run: - name: npm run install:kitchen-sink + name: npm run install:example command: | if [ -z "$CIRCLE_PR_NUMBER" ]; then - npm run install:kitchen-sink + npm run install:example fi - save_cache: - key: dependencies-{{ .Branch }}-{{ checksum "package-lock.json" }}-{{ checksum "examples/kitchen-sink-example/package.json" }} + key: dependencies-{{ .Branch }}-{{ checksum "package-lock.json" }}-{{ checksum "example-app/package.json" }} paths: - ~/.npm - ~/.cache @@ -29,7 +29,7 @@ jobs: name: browserstack command: | if [ -z "$CIRCLE_PR_NUMBER" ]; then - npx start-server-and-test 'start:kitchen-sink-local' http://localhost:3000 'browserstack-cypress run --build-name $CIRCLE_BRANCH --no-wrap' + npx start-server-and-test 'start:example-local' http://localhost:3000 'browserstack-cypress run --build-name $CIRCLE_BRANCH --no-wrap' fi - store_test_results: path: test-results diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 40b23d4b2..e2b47aab2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,16 +15,15 @@ Please read [Auth0's contribution guidelines](https://github.com/auth0/open-sour - `npm run build:test`: Do this once to build the test harness for the tests - `npm test`: Run the unit tests - `npm run test:watch`: Run the unit tests and watch for changes -- `npm run install:examples`: Install the examples -- Setup the examples https://github.com/auth0/nextjs-auth0/tree/main/examples -- `npm run start:basic`: Run the basic example -- `npm run start:kitchen-sink`: Run the kitchen sink example -- `npm run test:kitchen-sink`: Run the E2E tests (you will need to populate the `CYPRESS_USER_EMAIL` and `CYPRESS_USER_PASSWORD` env vars) -- `npm run test:kitchen-sink:watch`: Run the E2E tests and watch for changes +- `npm run install:example`: Install the examples +- Setup the examples https://github.com/auth0/nextjs-auth0/tree/main/example-app +- `npm run start:example`: Run the example +- `npm run test:example`: Run the E2E tests (you will need to populate the `CYPRESS_USER_EMAIL` and `CYPRESS_USER_PASSWORD` env vars) +- `npm run test:example:watch`: Run the E2E tests and watch for changes ## Running examples against a mock openid provider -Your env vars in `/examples/kitchen-sink-example/.env.local` should look like +Your env vars in `/example-app/.env.local` should look like ```bash AUTH0_SECRET=#ANY LONG RANDOM VALUE @@ -36,6 +35,6 @@ AUTH0_CLIENT_SECRET=testing Then run one of the commands: -- `start:kitchen-sink-local`: "npm run dev:local --prefix=examples/kitchen-sink-example", -- `test:kitchen-sink-local`: Run the E2E tests against a mock openid provider -- `test:kitchen-sink-local:watch`: Run the E2E tests against a mock openid provider and watch for changes +- `start:example-local`: Run the example app with a mock openid provider +- `test:example-local`: Run the E2E tests with a mock openid provider +- `test:example-local:watch`: Run the E2E tests with a mock openid provider and watch for changes diff --git a/EXAMPLES.md b/EXAMPLES.md index 6659b9d33..57eb5b23f 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -13,7 +13,7 @@ - [Use with Base Path and Internationalized Routing](#use-with-base-path-and-internationalized-routing) - [Use a custom session store](#use-a-custom-session-store) -All examples can be seen running in the [Kitchen Sink example app](./examples/kitchen-sink-example). +See also the the [example app](./example-app). ## Basic Setup @@ -141,7 +141,8 @@ export default auth0.handleAuth(); import auth0 from '../../../utils/auth0'; import { handleLogin } from '@auth0/nextjs-auth0'; -export default auth0.handleAuth({ // <= instance method +export default auth0.handleAuth({ + // <= instance method async login(req, res) { try { // `auth0.handleAuth` and `handleLogin` will be using separate instances @@ -233,7 +234,7 @@ export default function Profile({ user }) { export const getServerSideProps = withPageAuthRequired(); ``` -See a running example of an [SSR protected page](./examples/kitchen-sink-example/pages/profile-ssr.tsx) in the kitchen-sink example app or refer to the full list of configuration options for `withPageAuthRequired` [here](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequiredoptions). +See a running example of an [SSR protected page](./example-app/pages/profile-ssr.tsx) in the example app or refer to the full list of configuration options for `withPageAuthRequired` [here](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequiredoptions). ## Protecting a Client-Side Rendered (CSR) Page @@ -248,7 +249,7 @@ export default withPageAuthRequired(function Profile({ user }) { }); ``` -See a running example of a [CSR protected page](./examples/kitchen-sink-example/pages/profile.tsx) in the kitchen-sink example app. +See a running example of a [CSR protected page](./example-app/pages/profile.tsx) in the example app. ## Protect an API Route @@ -284,8 +285,8 @@ export default withPageAuthRequired(function Products() { }); ``` -See a running example in the kitchen-sink example app, the [protected API route](./examples/kitchen-sink-example/pages/api/shows.ts) and -the [frontend code to access the protected API](./examples/kitchen-sink-example/pages/shows.tsx). +See a running example in the example app, the [protected API route](./example-app/pages/api/shows.ts) and +the [frontend code to access the protected API](./example-app/pages/shows.tsx). ## Protecting pages with Middleware @@ -389,7 +390,7 @@ export default withApiAuthRequired(async function products(req, res) { }); ``` -See a running example of the [API route acting as a proxy to an External API](./examples/kitchen-sink-example/pages/api/shows.ts) in the kitchen-sink example app. +See a running example of the [API route acting as a proxy to an External API](./example-app/pages/api/shows.ts) in the example app. ### Getting a Refresh Token diff --git a/examples/kitchen-sink-example/.env.local.template b/example-app/.env.local.template similarity index 100% rename from examples/kitchen-sink-example/.env.local.template rename to example-app/.env.local.template diff --git a/examples/kitchen-sink-example/.env.production b/example-app/.env.production similarity index 100% rename from examples/kitchen-sink-example/.env.production rename to example-app/.env.production diff --git a/example-app/.eslintrc.json b/example-app/.eslintrc.json new file mode 100644 index 000000000..bffb357a7 --- /dev/null +++ b/example-app/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/example-app/.gitignore b/example-app/.gitignore new file mode 100644 index 000000000..8f322f0d8 --- /dev/null +++ b/example-app/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/basic-example/.npmrc b/example-app/.npmrc similarity index 100% rename from examples/basic-example/.npmrc rename to example-app/.npmrc diff --git a/example-app/README.md b/example-app/README.md new file mode 100644 index 000000000..f4da3c4c1 --- /dev/null +++ b/example-app/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/kitchen-sink-example/pages/api/auth/[auth0].ts b/example-app/app/api/auth/[auth0].ts similarity index 85% rename from examples/kitchen-sink-example/pages/api/auth/[auth0].ts rename to example-app/app/api/auth/[auth0].ts index 1511a73e0..e46a7cafc 100644 --- a/examples/kitchen-sink-example/pages/api/auth/[auth0].ts +++ b/example-app/app/api/auth/[auth0].ts @@ -1,6 +1,6 @@ import { handleAuth } from '@auth0/nextjs-auth0'; -export default handleAuth({ +export const GET = handleAuth({ onError(req, res, error) { console.error(error); res.status(error.status || 500).end('Check the console for the error'); diff --git a/example-app/app/favicon.ico b/example-app/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/example-app/app/globals.css b/example-app/app/globals.css new file mode 100644 index 000000000..d4f491e15 --- /dev/null +++ b/example-app/app/globals.css @@ -0,0 +1,107 @@ +:root { + --max-width: 1100px; + --border-radius: 12px; + --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', + 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', + 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; + + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; + + --primary-glow: conic-gradient( + from 180deg at 50% 50%, + #16abff33 0deg, + #0885ff33 55deg, + #54d6ff33 120deg, + #0071ff33 160deg, + transparent 360deg + ); + --secondary-glow: radial-gradient( + rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0) + ); + + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient( + #00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080 + ); + + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); + --secondary-glow: linear-gradient( + to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3) + ); + + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient( + #ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80 + ); + + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} diff --git a/example-app/app/layout.tsx b/example-app/app/layout.tsx new file mode 100644 index 000000000..71b3fbf05 --- /dev/null +++ b/example-app/app/layout.tsx @@ -0,0 +1,21 @@ +import './globals.css' +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/example-app/app/page.module.css b/example-app/app/page.module.css new file mode 100644 index 000000000..9411a5e6f --- /dev/null +++ b/example-app/app/page.module.css @@ -0,0 +1,229 @@ +.main { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 6rem; + min-height: 100vh; +} + +.description { + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + width: 100%; + z-index: 2; + font-family: var(--font-mono); +} + +.description a { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; +} + +.description p { + position: relative; + margin: 0; + padding: 1rem; + background-color: rgba(var(--callout-rgb), 0.5); + border: 1px solid rgba(var(--callout-border-rgb), 0.3); + border-radius: var(--border-radius); +} + +.code { + font-weight: 700; + font-family: var(--font-mono); +} + +.grid { + display: grid; + grid-template-columns: repeat(4, minmax(25%, auto)); + width: var(--max-width); + max-width: 100%; +} + +.card { + padding: 1rem 1.2rem; + border-radius: var(--border-radius); + background: rgba(var(--card-rgb), 0); + border: 1px solid rgba(var(--card-border-rgb), 0); + transition: background 200ms, border 200ms; +} + +.card span { + display: inline-block; + transition: transform 200ms; +} + +.card h2 { + font-weight: 600; + margin-bottom: 0.7rem; +} + +.card p { + margin: 0; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + max-width: 30ch; +} + +.center { + display: flex; + justify-content: center; + align-items: center; + position: relative; + padding: 4rem 0; +} + +.center::before { + background: var(--secondary-glow); + border-radius: 50%; + width: 480px; + height: 360px; + margin-left: -400px; +} + +.center::after { + background: var(--primary-glow); + width: 240px; + height: 180px; + z-index: -1; +} + +.center::before, +.center::after { + content: ''; + left: 50%; + position: absolute; + filter: blur(45px); + transform: translateZ(0); +} + +.logo { + position: relative; +} +/* Enable hover only on non-touch devices */ +@media (hover: hover) and (pointer: fine) { + .card:hover { + background: rgba(var(--card-rgb), 0.1); + border: 1px solid rgba(var(--card-border-rgb), 0.15); + } + + .card:hover span { + transform: translateX(4px); + } +} + +@media (prefers-reduced-motion) { + .card:hover span { + transform: none; + } +} + +/* Mobile */ +@media (max-width: 700px) { + .content { + padding: 4rem; + } + + .grid { + grid-template-columns: 1fr; + margin-bottom: 120px; + max-width: 320px; + text-align: center; + } + + .card { + padding: 1rem 2.5rem; + } + + .card h2 { + margin-bottom: 0.5rem; + } + + .center { + padding: 8rem 0 6rem; + } + + .center::before { + transform: none; + height: 300px; + } + + .description { + font-size: 0.8rem; + } + + .description a { + padding: 1rem; + } + + .description p, + .description div { + display: flex; + justify-content: center; + position: fixed; + width: 100%; + } + + .description p { + align-items: center; + inset: 0 0 auto; + padding: 2rem 1rem 1.4rem; + border-radius: 0; + border: none; + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); + background: linear-gradient( + to bottom, + rgba(var(--background-start-rgb), 1), + rgba(var(--callout-rgb), 0.5) + ); + background-clip: padding-box; + backdrop-filter: blur(24px); + } + + .description div { + align-items: flex-end; + pointer-events: none; + inset: auto 0 0; + padding: 2rem; + height: 200px; + background: linear-gradient( + to bottom, + transparent 0%, + rgb(var(--background-end-rgb)) 40% + ); + z-index: 1; + } +} + +/* Tablet and Smaller Desktop */ +@media (min-width: 701px) and (max-width: 1120px) { + .grid { + grid-template-columns: repeat(2, 50%); + } +} + +@media (prefers-color-scheme: dark) { + .vercelLogo { + filter: invert(1); + } + + .logo { + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); + } +} + +@keyframes rotate { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/example-app/app/page.tsx b/example-app/app/page.tsx new file mode 100644 index 000000000..eec39a2dc --- /dev/null +++ b/example-app/app/page.tsx @@ -0,0 +1,95 @@ +import Image from 'next/image' +import styles from './page.module.css' + +export default function Home() { + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+ +
+ +
+ Next.js Logo +
+ + +
+ ) +} diff --git a/examples/kitchen-sink-example/middleware.ts b/example-app/middleware.ts similarity index 100% rename from examples/kitchen-sink-example/middleware.ts rename to example-app/middleware.ts diff --git a/example-app/next.config.js b/example-app/next.config.js new file mode 100644 index 000000000..767719fc4 --- /dev/null +++ b/example-app/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/example-app/package.json b/example-app/package.json new file mode 100644 index 000000000..729cf3284 --- /dev/null +++ b/example-app/package.json @@ -0,0 +1,24 @@ +{ + "name": "example-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@auth0/nextjs-auth0": "file:../", + "@serverless-jwt/next": "^0.2.1", + "@types/node": "file:../node_modules/@types/node", + "@types/react": "file:../node_modules/@types/react", + "@types/react-dom": "file:../node_modules/@types/react-dom", + "eslint": "8.40.0", + "eslint-config-next": "13.4.1", + "next": "file:../node_modules/next", + "react": "file:../node_modules/react", + "react-dom": "file:../node_modules/react-dom", + "typescript": "5.0.4" + } +} diff --git a/example-app/public/next.svg b/example-app/public/next.svg new file mode 100644 index 000000000..5174b28c5 --- /dev/null +++ b/example-app/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/example-app/public/vercel.svg b/example-app/public/vercel.svg new file mode 100644 index 000000000..d2f842227 --- /dev/null +++ b/example-app/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/kitchen-sink-example/server.js b/example-app/server.js similarity index 91% rename from examples/kitchen-sink-example/server.js rename to example-app/server.js index 16646565d..8340cec37 100644 --- a/examples/kitchen-sink-example/server.js +++ b/example-app/server.js @@ -1,6 +1,6 @@ const express = require('express'); const next = require('next'); -const oidc = require('../../scripts/oidc-provider'); +const oidc = require('../scripts/oidc-provider'); const port = process.env.PORT || 3000; const app = next({ dev: true, hostname: 'localhost', port }); diff --git a/examples/kitchen-sink-example/tsconfig.json b/example-app/tsconfig.json similarity index 65% rename from examples/kitchen-sink-example/tsconfig.json rename to example-app/tsconfig.json index 50eb114d0..e06a4454a 100644 --- a/examples/kitchen-sink-example/tsconfig.json +++ b/example-app/tsconfig.json @@ -1,36 +1,28 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, - "strict": false, + "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, - "jsx": "preserve", "isolatedModules": true, + "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } - ] + ], + "paths": { + "@/*": ["./*"] + } }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/examples/.eslintrc b/examples/.eslintrc deleted file mode 100644 index fb4071a5e..000000000 --- a/examples/.eslintrc +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extends": ["plugin:prettier/recommended", "plugin:react/recommended"], - "plugins": ["prettier", "react"], - "root": true, - "env": { - "node": true, - "jest": true, - "es6": true, - "browser": true - }, - "parserOptions": { - "ecmaVersion": 2019, - "sourceType": "module" - }, - "rules": { - "prettier/prettier": "error", - "indent": ["error", 2], - "no-console": [ - 1, - { - "allow": ["error", "info", "warn"] - } - ], - "react/prop-types": 0, - "max-len": ["error", 120], - "comma-dangle": ["error", "never"], - "no-trailing-spaces": "error", - "semi": ["off"] - } -} diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 634d1df6e..000000000 --- a/examples/README.md +++ /dev/null @@ -1,115 +0,0 @@ -# Next.js Auth0 Examples - -In this folder we'll be showing off different examples on how to use the [@auth0/nextjs-auth0](https://www.npmjs.com/package/@auth0/nextjs-auth0) package in your Next.js applications. - -## Configuration - -### Local Development - -#### Configuring Auth0 - -Go to the [Auth0 dashboard](https://manage.auth0.com/) and create a new application of type **Web Application** and make sure to configure the following: - -| Setting | Description | -| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Allowed Callback URLs | Should be set to `http://localhost:3000/api/auth/callback` when testing locally or typically to `https://myapp.com/api/auth/callback` when deploying your application. | -| Allowed Logout URLs | Should be set to `http://localhost:3000/` when testing locally or typically to `https://myapp.com/` when deploying your application. | - -#### Environment Variables - -For local development you'll just want to create a `.env.local` file with the necessary settings: - -``` -AUTH0_SECRET=viloxyf_z2GW6K4CT-KQD_MoLEA2wqv5jWuq4Jd0P7ymgG5GJGMpvMneXZzhK3sL (at least 32 characters, used to encrypt the cookie) -AUTH0_ISSUER_BASE_URL=https://YOUR_AUTH0_DOMAIN -AUTH0_BASE_URL=http://localhost:3000/ -AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID -AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET -AUTH0_SCOPE=openid profile read:shows -AUTH0_AUDIENCE=YOUR_AUTH0_API_IDENTIFIER -``` - -### Hosting on Vercel - -The kitchen-sink example application is hosted on Vercel, including preview deployments to make Pull Request reviewing a bit easier in terms of verifying the functionalities in the example application. - -#### Configuring Auth0 - -As every environment in Vercel, including preview deployments, has its unique URL, your Auth0 application needs to be configured to allow the corresponding Callback and Logout URLs. -This can be done manually, by going to the Application Settings on your [Auth0 dashboard](https://manage.auth0.com/) and make sure to configure the following: - -| Setting | Description | -| --------------------- | ---------------------------------------------------------------------------------------------------------- | -| Allowed Callback URLs | Should be set to `https://{YOUR_VERCEL_URL_PREFIX}.vercel.app/api/auth/callback` when deploying to vercel. | -| Allowed Logout URLs | Should be set to `https://{YOUR_VERCEL_URL_PREFIX}.vercel.app/` when deploying to vercel. | - -##### Wildcards - -By default, Vercel uses the `vercel.app` domain for all of your environments. Using wildcards for a shared domain opens the possibility to redirect back to a malicious website, as long as the Callback URLs matches the wildcard configuration. Because of that, you should only consider using wildcards for the preview deployments when using a [Preview Deployment Suffix](https://vercel.com/docs/concepts/deployments/automatic-urls#preview-deployment-suffix), which is available as part of Vercel's Pro or Enterprise plan. - -| Setting | Description | -| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| Allowed Callback URLs | Should be set to `https://{VERCEL_GIT_REPO_SLUG}-*-{VERCEL_TEAM}.yourdomain.com/api/auth/callback` when deploying to vercel. | -| Allowed Logout URLs | Should be set to `https://{VERCEL_GIT_REPO_SLUG}-*-{VERCEL_TEAM}.yourdomain.com/` when deploying to vercel. | - -#### Configuring Vercel - -If you do not have a vercel account yet, move over to https://vercel.com/ to sign up for one. -Once logged in to your account, you can create a new project and import a Git repository. - -Vercel should automatically select the `Next.js` Framework Preset. -Because our deployment is a bit different from a standard Next.js repository, we will need to override the `Build and Output settings`: - -- Build Command: `npm run build:vercel` -- Output Directory: `examples/kitchen-sink-example/.next` - -The reason why we need to overrride these settings is because the Next.js app we want to build does not sit in the root of the repository. The example application is also dependent on the Next.js SDK, so we will need to ensure that Vercel executes the following commands when running `npm run build:vercel`: - -- Build the SDK: `npm run build` -- Install the dependencies for the sample application: `npm i --prefix=examples/kitchen-sink-example` -- Build the sample application: `npm run build --prefix=examples/kitchen-sink-example` - -As Vercel wants one single build command, we make use of the `build:vercel` npm script to run all of the above: - -``` -"build:vercel": "npm run install:examples && npm run build && npm run build:examples", -``` - -**Note**: Vercel runs `npm install` in the root of the repository by default, so we do not need to worry about that. - -#### Environment Variables - -Configure the following environment variables when importing your project or in "Settings > Environment Variables": - -| Name | Value | -| --------------------- | --------------------------------------------------------------------------------------------------------------------- | -| AUTH0_SECRET | viloxyf_z2GW6K4CT-KQD_MoLEA2wqv5jWuq4Jd0P7ymgG5GJGMpvMneXZzhK3sL (at least 32 characters, used to encrypt the cookie) | -| AUTH0_ISSUER_BASE_URL | https://YOUR_AUTH0_DOMAIN | -| AUTH0_CLIENT_ID | YOUR_AUTH0_CLIENT_ID | -| AUTH0_CLIENT_SECRET | YOUR_AUTH0_CLIENT_SECRET | -| AUTH0_AUDIENCE | YOUR_AUTH0_API_IDENTIFIER | -| AUTH0_SCOPE | openid profile read:shows | - -##### Assigning the AUTH0_BASE_URL - -###### Preview deployments - -For preview deployments you will either want to assign this to: - -- **Automatic Deployment URL:** For example `project-d418mhwf5-team.vercel.app` which is defined by the `VERCEL_URL` environment variable. -- **Automatic Branch URL:** For example `project-git-update-team.vercel.app` which can be constructed using `${VERCEL_GIT_REPO_SLUG}-git-${VERCEL_GIT_COMMIT_REF}-${VERCEL_GIT_REPO_OWNER}.vercel.app` - -See here for more information about Vercel's Automatic Urls: https://vercel.com/docs/concepts/deployments/automatic-urls - -To do this, make sure **Automatically expose System Environment Variables** is checked in **Settings > Environment Variables** and assign either the Automatic Deployment URL or the Automatic Branch URL to `AUTH0_BASE_URL` in your `.env.production` file. For example: - -```shell -# .env.production -AUTH0_BASE_URL=$VERCEL_URL -``` - -Unlike other `.env` files, You will need to check in `.env.production` so it should **not** contain any secrets. See how we define `.env.production` in the [kitchen-sink example app](./kitchen-sink-example/.env.production). - -###### Production deployments (or other environments with fixed urls) - -For production deployments or [custom domains assigned to a git branch](https://vercel.com/docs/custom-domains#assigning-a-domain-to-a-git-branch) you should assign the correct url to the `AUTH0_BASE_URL` environment variable in "Settings > Environment Variables". See the [Vercel docs on Environment Variables](https://vercel.com/docs/environment-variables#preview-environment-variables) for more information. This will override your `.env.production` file. diff --git a/examples/basic-example/.env.local.template b/examples/basic-example/.env.local.template deleted file mode 100644 index 7ac7eb8fd..000000000 --- a/examples/basic-example/.env.local.template +++ /dev/null @@ -1,5 +0,0 @@ -AUTH0_SECRET= -AUTH0_ISSUER_BASE_URL= -AUTH0_BASE_URL= -AUTH0_CLIENT_ID= -AUTH0_CLIENT_SECRET= diff --git a/examples/basic-example/.gitignore b/examples/basic-example/.gitignore deleted file mode 100644 index 5d054be8d..000000000 --- a/examples/basic-example/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -dist -.env -.next -.DS_Store -package-lock.json diff --git a/examples/basic-example/components/header.jsx b/examples/basic-example/components/header.jsx deleted file mode 100644 index 086c04444..000000000 --- a/examples/basic-example/components/header.jsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -const Header = () => { - const { user } = useUser(); - - return ( -
- - - -
- ); -}; - -export default Header; diff --git a/examples/basic-example/components/layout.jsx b/examples/basic-example/components/layout.jsx deleted file mode 100644 index 81ae4f953..000000000 --- a/examples/basic-example/components/layout.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import Head from 'next/head'; - -import Header from './header'; - -const Layout = ({ children }) => ( - <> - - Next.js with Auth0 - - -
- -
-
{children}
-
- - - - -); - -export default Layout; diff --git a/examples/basic-example/next-env.d.ts b/examples/basic-example/next-env.d.ts deleted file mode 100644 index 4f11a03dc..000000000 --- a/examples/basic-example/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/basic-example/next.config.js b/examples/basic-example/next.config.js deleted file mode 100644 index 4f336cb74..000000000 --- a/examples/basic-example/next.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - poweredByHeader: false -}; diff --git a/examples/basic-example/now.json b/examples/basic-example/now.json deleted file mode 100644 index 46ed05ac1..000000000 --- a/examples/basic-example/now.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "build": { - "env": { - "AUTH0_DOMAIN": "YOUR_AUTH0_DOMAIN", - "AUTH0_CLIENT_ID": "YOUR_AUTH0_CLIENT_ID", - "AUTH0_CLIENT_SECRET": "@auth0_client_secret", - "REDIRECT_URI": "https://my-website.now.sh/api/auth/callback", - "POST_LOGOUT_REDIRECT_URI": "https://my-website.now.sh/", - "SESSION_COOKIE_SECRET": "@session_cookie_secret" - } - } -} diff --git a/examples/basic-example/package.json b/examples/basic-example/package.json deleted file mode 100644 index 68daa8c9f..000000000 --- a/examples/basic-example/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "nextjs-auth0-example", - "version": "1.0.0", - "description": "Example of how to sign in to your Next.js application using Auth0", - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start" - }, - "author": "", - "license": "MIT", - "dependencies": { - "@auth0/nextjs-auth0": "file:../../", - "next": "file:../../node_modules/next", - "react": "file:../../node_modules/react", - "react-dom": "file:../../node_modules/react-dom" - } -} diff --git a/examples/basic-example/pages/_app.jsx b/examples/basic-example/pages/_app.jsx deleted file mode 100644 index a78730d70..000000000 --- a/examples/basic-example/pages/_app.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { UserProvider } from '@auth0/nextjs-auth0/client'; - -export default function App({ Component, pageProps }) { - // If you've used `withAuth`, pageProps.user can pre-populate the hook - // if you haven't used `withAuth`, pageProps.user is undefined so the hook - // fetches the user from the API routes - const { user } = pageProps; - - return ( - - - - ); -} diff --git a/examples/basic-example/pages/api/auth/[auth0].ts b/examples/basic-example/pages/api/auth/[auth0].ts deleted file mode 100644 index 304b4012a..000000000 --- a/examples/basic-example/pages/api/auth/[auth0].ts +++ /dev/null @@ -1,3 +0,0 @@ -import { handleAuth } from '@auth0/nextjs-auth0'; - -export default handleAuth(); diff --git a/examples/basic-example/pages/index.jsx b/examples/basic-example/pages/index.jsx deleted file mode 100644 index 9f80ea0d4..000000000 --- a/examples/basic-example/pages/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -import Layout from '../components/layout'; - -export default function Home() { - const { user, error, isLoading } = useUser(); - - return ( - -

Next.js and Auth0 Example

- - {isLoading &&

Loading login info...

} - - {error && ( - <> -

Error

-
{error.message}
- - )} - - {user && ( - <> -

Rendered user info on the client

-
{JSON.stringify(user, null, 2)}
- - )} - - {!isLoading && !error && !user && ( - <> -

- To test the login click in Login -

-

- Once you have logged in you should be able to click in Protected Page and Logout -

- - )} -
- ); -} diff --git a/examples/basic-example/pages/protected-page.jsx b/examples/basic-example/pages/protected-page.jsx deleted file mode 100644 index 89020d3a5..000000000 --- a/examples/basic-example/pages/protected-page.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import { withPageAuthRequired } from '@auth0/nextjs-auth0'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -import Layout from '../components/layout'; - -export default function ProtectedPage() { - const { user, error, isLoading } = useUser(); - - return ( - -

Protected Page

- - {isLoading &&

Loading profile...

} - - {error && ( - <> -

Error

-
{error.message}
- - )} - - {user && ( - <> -

Profile

-
{JSON.stringify(user, null, 2)}
- - )} -
- ); -} - -export const getServerSideProps = withPageAuthRequired(); diff --git a/examples/basic-example/tsconfig.json b/examples/basic-example/tsconfig.json deleted file mode 100644 index 35d51eac9..000000000 --- a/examples/basic-example/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve" - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/examples/kitchen-sink-example/.eslintrc b/examples/kitchen-sink-example/.eslintrc deleted file mode 100644 index ece3c8be8..000000000 --- a/examples/kitchen-sink-example/.eslintrc +++ /dev/null @@ -1,50 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@next/next/recommended", - "plugin:prettier/recommended" - ], - "settings": { - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - }, - "react": { - "version": "detect" - } - }, - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true, - "experimentalObjectRestSpread": true - } - }, - "env": { - "es6": true, - "browser": true, - "node": true - }, - "rules": { - "react/jsx-uses-react": 1, - "react/jsx-no-undef": 2, - "react/jsx-wrap-multilines": 2, - "react/no-string-refs": 0, - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error" - ] - }, - "plugins": [ - "import", - "@typescript-eslint", - "react", - "prettier" - ] -} diff --git a/examples/kitchen-sink-example/.gitignore b/examples/kitchen-sink-example/.gitignore deleted file mode 100644 index 5d054be8d..000000000 --- a/examples/kitchen-sink-example/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -dist -.env -.next -.DS_Store -package-lock.json diff --git a/examples/kitchen-sink-example/.npmrc b/examples/kitchen-sink-example/.npmrc deleted file mode 100644 index 43c97e719..000000000 --- a/examples/kitchen-sink-example/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/examples/kitchen-sink-example/.prettierrc b/examples/kitchen-sink-example/.prettierrc deleted file mode 100644 index 8ef5b8a3b..000000000 --- a/examples/kitchen-sink-example/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "requirePragma": false, - "printWidth": 120, - "tabWidth": 2, - "useTabs": false, - "semi": true, - "singleQuote": true, - "trailingComma": "none", - "bracketSpacing": true, - "jsxBracketSameLine": false -} diff --git a/examples/kitchen-sink-example/.vscode/launch.json b/examples/kitchen-sink-example/.vscode/launch.json deleted file mode 100644 index 72cb4c691..000000000 --- a/examples/kitchen-sink-example/.vscode/launch.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "attach", - "name": "Launch Program", - "skipFiles": ["/**"], - "port": 9229 - } - ] -} diff --git a/examples/kitchen-sink-example/app/global.css b/examples/kitchen-sink-example/app/global.css deleted file mode 100644 index 9d8c42298..000000000 --- a/examples/kitchen-sink-example/app/global.css +++ /dev/null @@ -1,9 +0,0 @@ -body { - margin: 0; - color: #333; - font-family: -apple-system, 'Segoe UI', sans-serif; -} -.container { - max-width: 42rem; - margin: 1.5rem auto; -} diff --git a/examples/kitchen-sink-example/app/head.tsx b/examples/kitchen-sink-example/app/head.tsx deleted file mode 100644 index df1652511..000000000 --- a/examples/kitchen-sink-example/app/head.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; - -export default function Head() { - return ( - <> - Next.js with Auth0 - - ); -} diff --git a/examples/kitchen-sink-example/app/layout.tsx b/examples/kitchen-sink-example/app/layout.tsx deleted file mode 100644 index c7ade2392..000000000 --- a/examples/kitchen-sink-example/app/layout.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import './global.css'; -import Header from '../components/header'; -import { UserProvider } from '@auth0/nextjs-auth0/client'; - -export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - - -
-
{children}
- - - - ); -} diff --git a/examples/kitchen-sink-example/app/profile-experimental-rsc/page.tsx b/examples/kitchen-sink-example/app/profile-experimental-rsc/page.tsx deleted file mode 100644 index 7fec11ed4..000000000 --- a/examples/kitchen-sink-example/app/profile-experimental-rsc/page.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { headers } from 'next/headers'; -import { getSession as auth0GetSession } from '@auth0/nextjs-auth0'; -import { IncomingMessage, ServerResponse } from 'http'; -import { Socket } from 'net'; - -// Note: This is an experiment to test that the SDK works in the experimental app directory. -// You should not rely on this code (or the app directory) in production. -const reqRes = () => { - const req = new IncomingMessage(new Socket()); - headers().forEach((v, k) => { - req.headers[k] = v; - }); - const res = new ServerResponse(req); - return { req, res }; -}; - -export function getSession() { - const { req, res } = reqRes(); - return auth0GetSession(req, res); -} - -export default async function ExperimentalRscPage() { - const session = await getSession(); - return ( -
-

Profile

-

Profile

-
{JSON.stringify(session || {}, null, 2)}
-
- ); -} diff --git a/examples/kitchen-sink-example/components/header.tsx b/examples/kitchen-sink-example/components/header.tsx deleted file mode 100644 index e18286d7a..000000000 --- a/examples/kitchen-sink-example/components/header.tsx +++ /dev/null @@ -1,104 +0,0 @@ -'use client'; -import React from 'react'; -import Link from 'next/link'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -const Header = (): React.ReactElement => { - const { user } = useUser(); - - return ( - - ); -}; - -export default Header; diff --git a/examples/kitchen-sink-example/components/layout.tsx b/examples/kitchen-sink-example/components/layout.tsx deleted file mode 100644 index 85df63b5e..000000000 --- a/examples/kitchen-sink-example/components/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import Head from 'next/head'; - -import Header from './header'; - -const Layout = ({ children }: React.PropsWithChildren): React.ReactElement => ( - <> - - Next.js with Auth0 - - -
- -
-
{children}
-
- - - - -); - -export default Layout; diff --git a/examples/kitchen-sink-example/lib/use-api.ts b/examples/kitchen-sink-example/lib/use-api.ts deleted file mode 100644 index a3350c9e2..000000000 --- a/examples/kitchen-sink-example/lib/use-api.ts +++ /dev/null @@ -1,60 +0,0 @@ -import * as React from 'react'; - -function initialState(args: { error?: any; isLoading?: boolean; response?: any }) { - return { - response: null, - error: null, - isLoading: true, - ...args - }; -} - -const useApi = ( - url: RequestInfo, - options = {} -): { - error: unknown; - isLoading: boolean; - response: any; -} => { - const [state, setState] = React.useState(() => initialState({})); - - React.useEffect(() => { - const fetchData = async () => { - try { - const res = await fetch(url, { - ...options - }); - - if (res.status >= 400) { - setState( - initialState({ - error: await res.json(), - isLoading: false - }) - ); - } else { - setState( - initialState({ - response: await res.json(), - isLoading: false - }) - ); - } - } catch (error) { - setState( - initialState({ - error: { - error: error.message - }, - isLoading: false - }) - ); - } - }; - fetchData(); - }, []); - return state; -}; - -export default useApi; diff --git a/examples/kitchen-sink-example/next-env.d.ts b/examples/kitchen-sink-example/next-env.d.ts deleted file mode 100644 index 4f11a03dc..000000000 --- a/examples/kitchen-sink-example/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/kitchen-sink-example/next.config.js b/examples/kitchen-sink-example/next.config.js deleted file mode 100644 index cae4f577f..000000000 --- a/examples/kitchen-sink-example/next.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - poweredByHeader: false, - experimental: { - appDir: true - } -}; diff --git a/examples/kitchen-sink-example/package.json b/examples/kitchen-sink-example/package.json deleted file mode 100644 index e65db4a12..000000000 --- a/examples/kitchen-sink-example/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "nextjs-auth0-kitchen-sink-example", - "version": "0.0.1", - "description": "Typescript example for nextjs-auth0", - "main": "index.js", - "scripts": { - "dev:local": "node server.js", - "dev": "next", - "build": "next build", - "start": "next start", - "format": "prettier --write \"{components,pages}/**/*.{ts,tsx}\"", - "lint": "eslint \"{components,pages}/**/*.{ts,tsx}\"", - "lint:fix": "eslint \"{components,pages}/**/*.{ts,tsx}\" --fix" - }, - "keywords": [ - "auth0", - "nextjs", - "typescript", - "react", - "eslint" - ], - "devDependencies": { - "@next/eslint-plugin-next": "^13.0.3", - "@types/node": "^14.14.21", - "@types/react": "^18.0.25", - "@types/styled-jsx": "^3.4.4", - "@typescript-eslint/eslint-plugin": "^4.13.0", - "@typescript-eslint/parser": "^4.13.0", - "eslint": "^7.9.0", - "eslint-config-prettier": "^8.1.0", - "eslint-plugin-prettier": "^3.1.4", - "express": "^4.17.1", - "lint-staged": "^10.3.0", - "prettier": "^2.1.1", - "typescript": "^4.1.3" - }, - "dependencies": { - "@auth0/nextjs-auth0": "file:../../", - "@serverless-jwt/next": "^0.2.1", - "next": "file:../../node_modules/next", - "react": "file:../../node_modules/react", - "react-dom": "file:../../node_modules/react-dom" - }, - "author": "Auth0", - "license": "MIT" -} diff --git a/examples/kitchen-sink-example/pages/_app.tsx b/examples/kitchen-sink-example/pages/_app.tsx deleted file mode 100644 index b42124c0d..000000000 --- a/examples/kitchen-sink-example/pages/_app.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import type { AppProps } from 'next/app'; -import { UserProvider } from '@auth0/nextjs-auth0/client'; - -export default function App({ Component, pageProps }: AppProps): React.ReactElement { - const { user } = pageProps; - - return ( - - - - ); -} diff --git a/examples/kitchen-sink-example/pages/_document.tsx b/examples/kitchen-sink-example/pages/_document.tsx deleted file mode 100644 index 13efaab3b..000000000 --- a/examples/kitchen-sink-example/pages/_document.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; -import Document, { Html, Head, Main, NextScript } from 'next/document'; - -export default class CustomDocument extends Document { - render(): React.ReactElement { - return ( - - - -
- - - - ); - } -} diff --git a/examples/kitchen-sink-example/pages/about.tsx b/examples/kitchen-sink-example/pages/about.tsx deleted file mode 100644 index 2951cf944..000000000 --- a/examples/kitchen-sink-example/pages/about.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import Layout from '../components/layout'; - -export default function About(): React.ReactElement { - return ( - -

About

-

- This is the about page, navigating between this page and Home is always pretty fast. However, when you - navigate to the Profile page it takes more time because it uses SSR to fetch the user first; -

-
- ); -} diff --git a/examples/kitchen-sink-example/pages/api/hello-world-mw.ts b/examples/kitchen-sink-example/pages/api/hello-world-mw.ts deleted file mode 100644 index 77c77f470..000000000 --- a/examples/kitchen-sink-example/pages/api/hello-world-mw.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default async function helloWorld(req, res) { - res.status(200).json({ text: 'Hello World!' }); -} diff --git a/examples/kitchen-sink-example/pages/api/my/shows.ts b/examples/kitchen-sink-example/pages/api/my/shows.ts deleted file mode 100644 index c7e6e23da..000000000 --- a/examples/kitchen-sink-example/pages/api/my/shows.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NextJwtVerifier } from '@serverless-jwt/next'; -import { NextAuthenticatedApiRequest } from '@serverless-jwt/next/dist/types'; -import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'; - -const verifyJwt = NextJwtVerifier({ - issuer: process.env.AUTH0_ISSUER_BASE_URL, - audience: process.env.AUTH0_AUDIENCE -}); - -const requireScope = (scope: string, apiRoute: NextApiHandler) => - verifyJwt(async (req: NextAuthenticatedApiRequest, res) => { - const { claims } = req.identityContext; - if (!claims || !claims.scope || (claims.scope as string).indexOf(scope) === -1) { - return res.status(403).json({ - error: 'access_denied', - error_description: `Token does not contain the required '${scope}' scope` - }); - } - return apiRoute(req, res) as void; - }); - -const apiRoute = async (req: NextApiRequest, res: NextApiResponse) => { - try { - const response = await fetch('https://api.tvmaze.com/search/shows?q=identity'); - const shows = await response.json(); - - res.status(200).json({ shows }); - } catch (error) { - console.error(error); - res.status(error.status || 500).json({ - code: error.code, - error: error.message - }); - } -}; - -export default requireScope('read:shows', apiRoute); diff --git a/examples/kitchen-sink-example/pages/api/shows.ts b/examples/kitchen-sink-example/pages/api/shows.ts deleted file mode 100644 index 993e7456d..000000000 --- a/examples/kitchen-sink-example/pages/api/shows.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { withApiAuthRequired, getAccessToken } from '@auth0/nextjs-auth0'; - -export default withApiAuthRequired(async function shows(req, res) { - try { - const { accessToken } = await getAccessToken(req, res, { - scopes: ['read:shows'] - }); - - const baseURL = - process.env.AUTH0_BASE_URL?.indexOf('http') === 0 - ? process.env.AUTH0_BASE_URL - : `https://${process.env.AUTH0_BASE_URL}`; - - // This is a contrived example, normally your external API would exist on another domain. - const response = await fetch(baseURL + '/api/my/shows', { - headers: { - Authorization: `Bearer ${accessToken}` - } - }); - - const shows = await response.json(); - res.status(response.status || 200).json(shows); - } catch (error) { - console.error(error); - res.status(error.status || 500).json({ - code: error.code, - error: error.message - }); - } -}); diff --git a/examples/kitchen-sink-example/pages/index.tsx b/examples/kitchen-sink-example/pages/index.tsx deleted file mode 100644 index e7287dd4a..000000000 --- a/examples/kitchen-sink-example/pages/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -import Layout from '../components/layout'; - -export default function Home(): React.ReactElement { - const { user, error, isLoading } = useUser(); - - return ( - -

Next.js and Auth0 Example

- - {isLoading &&

Loading login info...

} - - {error && ( - <> -

Error

-
{error.message}
- - )} - - {user && ( - <> -

Rendered user info on the client

-
{JSON.stringify(user, null, 2)}
- - )} - - {!isLoading && !error && !user && ( - <> -

- To test the login click in Login -

-

- Once you have logged in you should be able to click in Protected Page and Logout -

- - )} -
- ); -} diff --git a/examples/kitchen-sink-example/pages/profile-mw.tsx b/examples/kitchen-sink-example/pages/profile-mw.tsx deleted file mode 100644 index 9f17f5461..000000000 --- a/examples/kitchen-sink-example/pages/profile-mw.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -import Layout from '../components/layout'; - -export default function Profile() { - const { user, isLoading } = useUser(); - if (isLoading) { - return

Loading...

; - } - return ( - -

Profile

-

Profile

-
{JSON.stringify(user, null, 2)}
-
- ); -} diff --git a/examples/kitchen-sink-example/pages/profile-ssr.tsx b/examples/kitchen-sink-example/pages/profile-ssr.tsx deleted file mode 100644 index 839ce240c..000000000 --- a/examples/kitchen-sink-example/pages/profile-ssr.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { withPageAuthRequired } from '@auth0/nextjs-auth0'; -import { UserProfile } from '@auth0/nextjs-auth0/client'; - -import Layout from '../components/layout'; - -type ProfileProps = { user: UserProfile }; - -export default function Profile({ user }: ProfileProps): React.ReactElement { - return ( - -

Profile

-

Profile (server rendered)

-
{JSON.stringify(user, null, 2)}
-
- ); -} - -export const getServerSideProps = withPageAuthRequired(); diff --git a/examples/kitchen-sink-example/pages/profile.tsx b/examples/kitchen-sink-example/pages/profile.tsx deleted file mode 100644 index e40ef1535..000000000 --- a/examples/kitchen-sink-example/pages/profile.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'; - -import Layout from '../components/layout'; - -export default withPageAuthRequired(function Profile({ user }) { - return ( - -

Profile

-

Profile

-
{JSON.stringify(user, null, 2)}
-
- ); -}); diff --git a/examples/kitchen-sink-example/pages/shows.tsx b/examples/kitchen-sink-example/pages/shows.tsx deleted file mode 100644 index ed36edcee..000000000 --- a/examples/kitchen-sink-example/pages/shows.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; - -import useApi from '../lib/use-api'; -import Layout from '../components/layout'; -import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'; - -type TVShow = { show: { name: string } }; - -export default withPageAuthRequired(function TvShows(): React.ReactElement { - const { response, error, isLoading } = useApi('/api/shows'); - - return ( - -

TV Shows

- - {isLoading &&

Loading TV shows...

} - - {response && ( - <> -

My favourite TV shows:

-
-            {JSON.stringify(
-              response.shows.map((s: TVShow) => s.show.name),
-              null,
-              2
-            )}
-          
- - )} - - {error && ( - <> -

Error loading TV shows

-
{JSON.stringify(error, null, 2)}
- - )} -
- ); -}); diff --git a/package.json b/package.json index 6a1ae9e68..93f2c4be4 100644 --- a/package.json +++ b/package.json @@ -33,24 +33,19 @@ "lint": "tsc --noEmit && eslint --fix --ext .ts ./src", "docs": "typedoc --options typedoc.js src", "prepack": "npm run build", - "install:basic": "npm i --prefix=examples/basic-example --no-package-lock", - "install:kitchen-sink": "npm i --prefix=examples/kitchen-sink-example --no-package-lock", - "install:examples": "npm run install:basic && npm run install:kitchen-sink", + "install:example": "npm i --prefix=example-app --no-package-lock", "build": "npm run clean && tsc -p tsconfig.build.json", "build:test": "next build tests/fixtures/test-app", - "build:basic": "npm run build --prefix=examples/basic-example", - "build:kitchen-sink": "npm run build --prefix=examples/kitchen-sink-example", - "build:examples": "npm run build:basic && npm run build:kitchen-sink", - "build:vercel": "npm run install:kitchen-sink && npm run build && npm run build:kitchen-sink", - "start:basic": "npm run dev --prefix=examples/basic-example", - "start:kitchen-sink": "npm run dev --prefix=examples/kitchen-sink-example", - "start:kitchen-sink-local": "npm run dev:local --prefix=examples/kitchen-sink-example", + "build:example": "npm run build --prefix=example-app", + "build:vercel": "npm run install:example && npm run build && npm run build:example", + "start:example": "npm run dev --prefix=example-app", + "start:example-local": "npm run dev:local --prefix=example-app", "test": "jest tests --coverage", "test:watch": "jest --coverage --watch", - "test:kitchen-sink": "start-server-and-test start:kitchen-sink http-get://localhost:3000 cypress:run", - "test:kitchen-sink:watch": "start-server-and-test start:kitchen-sink 3000 cypress:open", - "test:kitchen-sink-local": "npx start-server-and-test 'start:kitchen-sink-local' http://localhost:3000 'cypress run --config-file=./cypress-local.config.js'", - "test:kitchen-sink-local:watch": "npx start-server-and-test 'start:kitchen-sink-local' http://localhost:3000 'cypress open --config-file=./cypress-local.config.js'", + "test:example": "start-server-and-test start:example http-get://localhost:3000 cypress:run", + "test:example:watch": "start-server-and-test start:example 3000 cypress:open", + "test:example-local": "npx start-server-and-test 'start:example-local' http://localhost:3000 'cypress run --config-file=./cypress-local.config.js'", + "test:example-local:watch": "npx start-server-and-test 'start:example-local' http://localhost:3000 'cypress open --config-file=./cypress-local.config.js'", "cypress:run": "cypress run", "cypress:open": "cypress open" }, From bcf1a50c638618f2877c35f394447d68f086f48e Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 18 May 2023 11:14:04 +0100 Subject: [PATCH 02/61] Merge branch 'main' into server-components # Conflicts: # examples/README.md # examples/kitchen-sink-example/pages/api/shows.ts --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 798515a77..3831b61df 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ dist/ # Mac OSX files .DS_Store + +# IDE +.idea From 8b677f0a8505d3d30af94859744a2e279e87cd2d Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 18 May 2023 12:12:37 +0100 Subject: [PATCH 03/61] Example app updates --- .../api/auth/{[auth0].ts => [auth0]/route.ts} | 1 - example-app/app/page.module.css | 26 --- example-app/app/page.tsx | 67 +------ package-lock.json | 178 +++++++++--------- package.json | 2 +- 5 files changed, 91 insertions(+), 183 deletions(-) rename example-app/app/api/auth/{[auth0].ts => [auth0]/route.ts} (99%) diff --git a/example-app/app/api/auth/[auth0].ts b/example-app/app/api/auth/[auth0]/route.ts similarity index 99% rename from example-app/app/api/auth/[auth0].ts rename to example-app/app/api/auth/[auth0]/route.ts index e46a7cafc..dace42ab9 100644 --- a/example-app/app/api/auth/[auth0].ts +++ b/example-app/app/api/auth/[auth0]/route.ts @@ -1,5 +1,4 @@ import { handleAuth } from '@auth0/nextjs-auth0'; - export const GET = handleAuth({ onError(req, res, error) { console.error(error); diff --git a/example-app/app/page.module.css b/example-app/app/page.module.css index 9411a5e6f..93051def8 100644 --- a/example-app/app/page.module.css +++ b/example-app/app/page.module.css @@ -46,32 +46,6 @@ max-width: 100%; } -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - .center { display: flex; justify-content: center; diff --git a/example-app/app/page.tsx b/example-app/app/page.tsx index eec39a2dc..b630f5f95 100644 --- a/example-app/app/page.tsx +++ b/example-app/app/page.tsx @@ -10,21 +10,7 @@ export default function Home() { app/page.tsx

@@ -39,57 +25,6 @@ export default function Home() { /> -
) } diff --git a/package-lock.json b/package-lock.json index 2ef13877f..7f3c9a454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,7 @@ "eslint-plugin-react-hooks": "^4.2.0", "jest": "^27.2.0", "jest-environment-node-single-context": "^27.3.0", - "next": "^13.1.3", + "next": "^13.4.2", "nock": "^13.0.5", "oidc-provider": "^7.6.0", "on-headers": "^1.0.2", @@ -1789,15 +1789,15 @@ } }, "node_modules/@next/env": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.1.tgz", - "integrity": "sha512-eD6WCBMFjLFooLM19SIhSkWBHtaFrZFfg2Cxnyl3vS3DAdFRfnx5TY2RxlkuKXdIRCC0ySbtK9JXXt8qLCqzZg==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.2.tgz", + "integrity": "sha512-Wqvo7lDeS0KGwtwg9TT9wKQ8raelmUxt+TQKWvG/xKfcmDXNOtCuaszcfCF8JzlBG1q0VhpI6CKaRMbVPMDWgw==", "dev": true }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.1.tgz", - "integrity": "sha512-eF8ARHtYfnoYtDa6xFHriUKA/Mfj/cCbmKb3NofeKhMccs65G6/loZ15a6wYCCx4rPAd6x4t1WmVYtri7EdeBg==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.2.tgz", + "integrity": "sha512-6BBlqGu3ewgJflv9iLCwO1v1hqlecaIH2AotpKfVUEzUxuuDNJQZ2a4KLb4MBl8T9/vca1YuWhSqtbF6ZuUJJw==", "cpu": [ "arm64" ], @@ -1811,9 +1811,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.1.tgz", - "integrity": "sha512-7cmDgF9tGWTgn5Gw+vP17miJbH4wcraMHDCOHTYWkO/VeKT73dUWG23TNRLfgtCNSPgH4V5B4uLHoZTanx9bAw==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.2.tgz", + "integrity": "sha512-iZuYr7ZvGLPjPmfhhMl0ISm+z8EiyLBC1bLyFwGBxkWmPXqdJ60mzuTaDSr5WezDwv0fz32HB7JHmRC6JVHSZg==", "cpu": [ "x64" ], @@ -1827,9 +1827,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.1.tgz", - "integrity": "sha512-qwJqmCri2ie8aTtE5gjTSr8S6O8B67KCYgVZhv9gKH44yvc/zXbAY8u23QGULsYOyh1islWE5sWfQNLOj9iryg==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.2.tgz", + "integrity": "sha512-2xVabFtIge6BJTcJrW8YuUnYTuQjh4jEuRuS2mscyNVOj6zUZkom3CQg+egKOoS+zh2rrro66ffSKIS+ztFJTg==", "cpu": [ "arm64" ], @@ -1843,9 +1843,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.1.tgz", - "integrity": "sha512-qcC54tWNGDv/VVIFkazxhqH1Bnagjfs4enzELVRlUOoJPD2BGJTPI7z08pQPbbgxLtRiu8gl2mXvpB8WlOkMeA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.2.tgz", + "integrity": "sha512-wKRCQ27xCUJx5d6IivfjYGq8oVngqIhlhSAJntgXLt7Uo9sRT/3EppMHqUZRfyuNBTbykEre1s5166z+pvRB5A==", "cpu": [ "arm64" ], @@ -1859,9 +1859,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.1.tgz", - "integrity": "sha512-9TeWFlpLsBosZ+tsm/rWBaMwt5It9tPH8m3nawZqFUUrZyGRfGcI67js774vtx0k3rL9qbyY6+3pw9BCVpaYUA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.2.tgz", + "integrity": "sha512-NpCa+UVhhuNeaFVUP1Bftm0uqtvLWq2JTm7+Ta48+2Uqj2mNXrDIvyn1DY/ZEfmW/1yvGBRaUAv9zkMkMRixQA==", "cpu": [ "x64" ], @@ -1875,9 +1875,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.1.tgz", - "integrity": "sha512-sNDGaWmSqTS4QRUzw61wl4mVPeSqNIr1OOjLlQTRuyInxMxtqImRqdvzDvFTlDfdeUMU/DZhWGYoHrXLlZXe6A==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.2.tgz", + "integrity": "sha512-ZWVC72x0lW4aj44e3khvBrj2oSYj1bD0jESmyah3zG/3DplEy/FOtYkMzbMjHTdDSheso7zH8GIlW6CDQnKhmQ==", "cpu": [ "x64" ], @@ -1891,9 +1891,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.1.tgz", - "integrity": "sha512-+CXZC7u1iXdLRudecoUYbhbsXpglYv8KFYsFxKBPn7kg+bk7eJo738wAA4jXIl8grTF2mPdmO93JOQym+BlYGA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.2.tgz", + "integrity": "sha512-pLT+OWYpzJig5K4VKhLttlIfBcVZfr2+Xbjra0Tjs83NQSkFS+y7xx+YhCwvpEmXYLIvaggj2ONPyjbiigOvHQ==", "cpu": [ "arm64" ], @@ -1907,9 +1907,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.1.tgz", - "integrity": "sha512-vIoXVVc7UYO68VwVMDKwJC2+HqAZQtCYiVlApyKEeIPIQpz2gpufzGxk1z3/gwrJt/kJ5CDZjlhYDCzd3hdz+g==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.2.tgz", + "integrity": "sha512-dhpiksQCyGca4WY0fJyzK3FxMDFoqMb0Cn+uDB+9GYjpU2K5//UGPQlCwiK4JHxuhg8oLMag5Nf3/IPSJNG8jw==", "cpu": [ "ia32" ], @@ -1923,9 +1923,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.1.tgz", - "integrity": "sha512-n8V5ImLQZibKTu10UUdI3nIeTLkliEXe628qxqW9v8My3BAH2a7H0SaCqkV2OgqFnn8sG1wxKYw9/SNJ632kSA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.2.tgz", + "integrity": "sha512-O7bort1Vld00cu8g0jHZq3cbSTUNMohOEvYqsqE10+yfohhdPHzvzO+ziJRz4Dyyr/fYKREwS7gR4JC0soSOMw==", "cpu": [ "x64" ], @@ -10809,12 +10809,12 @@ } }, "node_modules/next": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.1.tgz", - "integrity": "sha512-JBw2kAIyhKDpjhEWvNVoFeIzNp9xNxg8wrthDOtMctfn3EpqGCmW0FSviNyGgOSOSn6zDaX48pmvbdf6X2W9xA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.2.tgz", + "integrity": "sha512-aNFqLs3a3nTGvLWlO9SUhCuMUHVPSFQC0+tDNGAsDXqx+WJDFSbvc233gOJ5H19SBc7nw36A9LwQepOJ2u/8Kg==", "dev": true, "dependencies": { - "@next/env": "13.4.1", + "@next/env": "13.4.2", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -10829,15 +10829,15 @@ "node": ">=16.8.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.1", - "@next/swc-darwin-x64": "13.4.1", - "@next/swc-linux-arm64-gnu": "13.4.1", - "@next/swc-linux-arm64-musl": "13.4.1", - "@next/swc-linux-x64-gnu": "13.4.1", - "@next/swc-linux-x64-musl": "13.4.1", - "@next/swc-win32-arm64-msvc": "13.4.1", - "@next/swc-win32-ia32-msvc": "13.4.1", - "@next/swc-win32-x64-msvc": "13.4.1" + "@next/swc-darwin-arm64": "13.4.2", + "@next/swc-darwin-x64": "13.4.2", + "@next/swc-linux-arm64-gnu": "13.4.2", + "@next/swc-linux-arm64-musl": "13.4.2", + "@next/swc-linux-x64-gnu": "13.4.2", + "@next/swc-linux-x64-musl": "13.4.2", + "@next/swc-win32-arm64-msvc": "13.4.2", + "@next/swc-win32-ia32-msvc": "13.4.2", + "@next/swc-win32-x64-msvc": "13.4.2" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -15573,71 +15573,71 @@ } }, "@next/env": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.1.tgz", - "integrity": "sha512-eD6WCBMFjLFooLM19SIhSkWBHtaFrZFfg2Cxnyl3vS3DAdFRfnx5TY2RxlkuKXdIRCC0ySbtK9JXXt8qLCqzZg==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.2.tgz", + "integrity": "sha512-Wqvo7lDeS0KGwtwg9TT9wKQ8raelmUxt+TQKWvG/xKfcmDXNOtCuaszcfCF8JzlBG1q0VhpI6CKaRMbVPMDWgw==", "dev": true }, "@next/swc-darwin-arm64": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.1.tgz", - "integrity": "sha512-eF8ARHtYfnoYtDa6xFHriUKA/Mfj/cCbmKb3NofeKhMccs65G6/loZ15a6wYCCx4rPAd6x4t1WmVYtri7EdeBg==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.2.tgz", + "integrity": "sha512-6BBlqGu3ewgJflv9iLCwO1v1hqlecaIH2AotpKfVUEzUxuuDNJQZ2a4KLb4MBl8T9/vca1YuWhSqtbF6ZuUJJw==", "dev": true, "optional": true }, "@next/swc-darwin-x64": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.1.tgz", - "integrity": "sha512-7cmDgF9tGWTgn5Gw+vP17miJbH4wcraMHDCOHTYWkO/VeKT73dUWG23TNRLfgtCNSPgH4V5B4uLHoZTanx9bAw==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.2.tgz", + "integrity": "sha512-iZuYr7ZvGLPjPmfhhMl0ISm+z8EiyLBC1bLyFwGBxkWmPXqdJ60mzuTaDSr5WezDwv0fz32HB7JHmRC6JVHSZg==", "dev": true, "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.1.tgz", - "integrity": "sha512-qwJqmCri2ie8aTtE5gjTSr8S6O8B67KCYgVZhv9gKH44yvc/zXbAY8u23QGULsYOyh1islWE5sWfQNLOj9iryg==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.2.tgz", + "integrity": "sha512-2xVabFtIge6BJTcJrW8YuUnYTuQjh4jEuRuS2mscyNVOj6zUZkom3CQg+egKOoS+zh2rrro66ffSKIS+ztFJTg==", "dev": true, "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.1.tgz", - "integrity": "sha512-qcC54tWNGDv/VVIFkazxhqH1Bnagjfs4enzELVRlUOoJPD2BGJTPI7z08pQPbbgxLtRiu8gl2mXvpB8WlOkMeA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.2.tgz", + "integrity": "sha512-wKRCQ27xCUJx5d6IivfjYGq8oVngqIhlhSAJntgXLt7Uo9sRT/3EppMHqUZRfyuNBTbykEre1s5166z+pvRB5A==", "dev": true, "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.1.tgz", - "integrity": "sha512-9TeWFlpLsBosZ+tsm/rWBaMwt5It9tPH8m3nawZqFUUrZyGRfGcI67js774vtx0k3rL9qbyY6+3pw9BCVpaYUA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.2.tgz", + "integrity": "sha512-NpCa+UVhhuNeaFVUP1Bftm0uqtvLWq2JTm7+Ta48+2Uqj2mNXrDIvyn1DY/ZEfmW/1yvGBRaUAv9zkMkMRixQA==", "dev": true, "optional": true }, "@next/swc-linux-x64-musl": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.1.tgz", - "integrity": "sha512-sNDGaWmSqTS4QRUzw61wl4mVPeSqNIr1OOjLlQTRuyInxMxtqImRqdvzDvFTlDfdeUMU/DZhWGYoHrXLlZXe6A==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.2.tgz", + "integrity": "sha512-ZWVC72x0lW4aj44e3khvBrj2oSYj1bD0jESmyah3zG/3DplEy/FOtYkMzbMjHTdDSheso7zH8GIlW6CDQnKhmQ==", "dev": true, "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.1.tgz", - "integrity": "sha512-+CXZC7u1iXdLRudecoUYbhbsXpglYv8KFYsFxKBPn7kg+bk7eJo738wAA4jXIl8grTF2mPdmO93JOQym+BlYGA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.2.tgz", + "integrity": "sha512-pLT+OWYpzJig5K4VKhLttlIfBcVZfr2+Xbjra0Tjs83NQSkFS+y7xx+YhCwvpEmXYLIvaggj2ONPyjbiigOvHQ==", "dev": true, "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.1.tgz", - "integrity": "sha512-vIoXVVc7UYO68VwVMDKwJC2+HqAZQtCYiVlApyKEeIPIQpz2gpufzGxk1z3/gwrJt/kJ5CDZjlhYDCzd3hdz+g==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.2.tgz", + "integrity": "sha512-dhpiksQCyGca4WY0fJyzK3FxMDFoqMb0Cn+uDB+9GYjpU2K5//UGPQlCwiK4JHxuhg8oLMag5Nf3/IPSJNG8jw==", "dev": true, "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.1.tgz", - "integrity": "sha512-n8V5ImLQZibKTu10UUdI3nIeTLkliEXe628qxqW9v8My3BAH2a7H0SaCqkV2OgqFnn8sG1wxKYw9/SNJ632kSA==", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.2.tgz", + "integrity": "sha512-O7bort1Vld00cu8g0jHZq3cbSTUNMohOEvYqsqE10+yfohhdPHzvzO+ziJRz4Dyyr/fYKREwS7gR4JC0soSOMw==", "dev": true, "optional": true }, @@ -22558,21 +22558,21 @@ "dev": true }, "next": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.1.tgz", - "integrity": "sha512-JBw2kAIyhKDpjhEWvNVoFeIzNp9xNxg8wrthDOtMctfn3EpqGCmW0FSviNyGgOSOSn6zDaX48pmvbdf6X2W9xA==", - "dev": true, - "requires": { - "@next/env": "13.4.1", - "@next/swc-darwin-arm64": "13.4.1", - "@next/swc-darwin-x64": "13.4.1", - "@next/swc-linux-arm64-gnu": "13.4.1", - "@next/swc-linux-arm64-musl": "13.4.1", - "@next/swc-linux-x64-gnu": "13.4.1", - "@next/swc-linux-x64-musl": "13.4.1", - "@next/swc-win32-arm64-msvc": "13.4.1", - "@next/swc-win32-ia32-msvc": "13.4.1", - "@next/swc-win32-x64-msvc": "13.4.1", + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.2.tgz", + "integrity": "sha512-aNFqLs3a3nTGvLWlO9SUhCuMUHVPSFQC0+tDNGAsDXqx+WJDFSbvc233gOJ5H19SBc7nw36A9LwQepOJ2u/8Kg==", + "dev": true, + "requires": { + "@next/env": "13.4.2", + "@next/swc-darwin-arm64": "13.4.2", + "@next/swc-darwin-x64": "13.4.2", + "@next/swc-linux-arm64-gnu": "13.4.2", + "@next/swc-linux-arm64-musl": "13.4.2", + "@next/swc-linux-x64-gnu": "13.4.2", + "@next/swc-linux-x64-musl": "13.4.2", + "@next/swc-win32-arm64-msvc": "13.4.2", + "@next/swc-win32-ia32-msvc": "13.4.2", + "@next/swc-win32-x64-msvc": "13.4.2", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", diff --git a/package.json b/package.json index 23c05dac8..d625bb5d6 100644 --- a/package.json +++ b/package.json @@ -100,8 +100,8 @@ "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^4.2.0", "jest": "^27.2.0", - "next": "^13.1.3", "jest-environment-node-single-context": "^27.3.0", + "next": "^13.4.2", "nock": "^13.0.5", "oidc-provider": "^7.6.0", "on-headers": "^1.0.2", From 9de2938709d00c1dff7bce093da3ef6e925a4f13 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Fri, 19 May 2023 10:05:20 +0100 Subject: [PATCH 04/61] Update auth0-session with AbstractRequest/Response --- src/auth0-session/config.ts | 7 +- src/auth0-session/handlers/callback.ts | 24 ++--- src/auth0-session/handlers/login.ts | 12 +-- src/auth0-session/handlers/logout.ts | 20 +--- src/auth0-session/hooks/get-login-state.ts | 2 +- src/auth0-session/http/abstract-request.ts | 15 +++ src/auth0-session/http/abstract-response.ts | 31 ++++++ src/auth0-session/http/index.ts | 4 + src/auth0-session/http/node-request.ts | 25 +++++ src/auth0-session/http/node-response.ts | 31 ++++++ src/auth0-session/index.ts | 1 - src/auth0-session/session-cache.ts | 4 +- src/auth0-session/session/abstract-session.ts | 27 ++++-- src/auth0-session/session/stateful-session.ts | 36 ++++--- .../session/stateless-session.ts | 58 +++++------ src/auth0-session/transient-store.ts | 23 ++--- src/auth0-session/utils/cookies.ts | 61 ------------ src/utils/middleware-cookies.ts | 4 +- tests/auth0-session/fixtures/server.ts | 64 +++++++++---- tests/auth0-session/handlers/callback.test.ts | 4 +- tests/auth0-session/handlers/login.test.ts | 3 +- tests/auth0-session/http/node-request.test.ts | 38 ++++++++ .../auth0-session/http/node-response.test.ts | 57 +++++++++++ tests/auth0-session/transient-store.test.ts | 50 +++++----- tests/auth0-session/utils/cookie.test.ts | 95 ------------------- tests/auth0-session/utils/errors.test.ts | 2 +- 26 files changed, 370 insertions(+), 328 deletions(-) create mode 100644 src/auth0-session/http/abstract-request.ts create mode 100644 src/auth0-session/http/abstract-response.ts create mode 100644 src/auth0-session/http/index.ts create mode 100644 src/auth0-session/http/node-request.ts create mode 100644 src/auth0-session/http/node-response.ts delete mode 100644 src/auth0-session/utils/cookies.ts create mode 100644 tests/auth0-session/http/node-request.test.ts create mode 100644 tests/auth0-session/http/node-response.test.ts delete mode 100644 tests/auth0-session/utils/cookie.test.ts diff --git a/src/auth0-session/config.ts b/src/auth0-session/config.ts index f2e99dbba..bb2a847b7 100644 --- a/src/auth0-session/config.ts +++ b/src/auth0-session/config.ts @@ -1,4 +1,3 @@ -import type { IncomingMessage } from 'http'; import type { AuthorizationParameters as OidcAuthorizationParameters, ClientAuthMethod } from 'openid-client'; import { SessionStore } from './session/stateful-session'; @@ -111,7 +110,7 @@ export interface Config { * ```js * app.use(auth({ * ... - * getLoginState(req, options) { + * getLoginState(options) { * return { * returnTo: options.returnTo || req.originalUrl, * customState: 'foo' @@ -120,7 +119,7 @@ export interface Config { * })); * ``` */ - getLoginState: (req: IncomingMessage, options: LoginOptions) => Record; + getLoginState: (options: LoginOptions) => Record; /** * Array value of claims to remove from the ID token before storing the cookie session. @@ -302,7 +301,7 @@ export interface AuthorizationParameters extends OidcAuthorizationParameters { response_type: 'id_token' | 'code id_token' | 'code'; } -export type GetLoginState = (req: any, options: LoginOptions) => { [key: string]: any }; +export type GetLoginState = (options: LoginOptions) => { [key: string]: any }; /** * Custom options to pass to the login handler. diff --git a/src/auth0-session/handlers/callback.ts b/src/auth0-session/handlers/callback.ts index 907bd2c31..11ab995cd 100644 --- a/src/auth0-session/handlers/callback.ts +++ b/src/auth0-session/handlers/callback.ts @@ -1,4 +1,3 @@ -import { IncomingMessage, ServerResponse } from 'http'; import urlJoin from 'url-join'; import createHttpError from 'http-errors'; import { errors } from 'openid-client'; @@ -10,22 +9,18 @@ import { SessionCache } from '../session-cache'; import { ApplicationError, EscapedError, - htmlSafe, IdentityProviderError, MissingStateCookieError, MissingStateParamError } from '../utils/errors'; +import { AbstractRequest, AbstractResponse } from '../http'; +import { IncomingMessage } from 'http'; function getRedirectUri(config: Config): string { return urlJoin(config.baseURL, config.routes.callback); } -export type AfterCallback = ( - req: any, - res: any, - session: any, - state?: Record -) => Promise | any | undefined; +export type AfterCallback = (session: any, state?: Record) => Promise | any | undefined; export type CallbackOptions = { afterCallback?: AfterCallback; @@ -37,7 +32,7 @@ export type CallbackOptions = { type ValidState = { [key: string]: any; returnTo?: string }; -export type HandleCallback = (req: IncomingMessage, res: ServerResponse, options?: CallbackOptions) => Promise; +export type HandleCallback = (req: AbstractRequest, res: AbstractResponse, options?: CallbackOptions) => Promise; export default function callbackHandlerFactory( config: Config, @@ -51,7 +46,7 @@ export default function callbackHandlerFactory( let tokenSet; - const callbackParams = client.callbackParams(req); + const callbackParams = client.callbackParams(req as unknown as IncomingMessage); if (!callbackParams.state) { throw createHttpError(404, new MissingStateParamError()); @@ -98,18 +93,13 @@ export default function callbackHandlerFactory( let session = await sessionCache.fromTokenSet(tokenSet); if (options?.afterCallback) { - session = await options.afterCallback(req, res, session, openidState); + session = await options.afterCallback(session, openidState); } if (session) { await sessionCache.create(req, res, session); } - if (!res.writableEnded) { - res.writeHead(302, { - Location: res.getHeader('Location') || openidState.returnTo || config.baseURL - }); - res.end(htmlSafe(openidState.returnTo || config.baseURL)); - } + res.redirect(openidState.returnTo || config.baseURL); }; } diff --git a/src/auth0-session/handlers/login.ts b/src/auth0-session/handlers/login.ts index 1a745471d..d4f19745f 100644 --- a/src/auth0-session/handlers/login.ts +++ b/src/auth0-session/handlers/login.ts @@ -1,4 +1,3 @@ -import { IncomingMessage, ServerResponse } from 'http'; import urlJoin from 'url-join'; import { strict as assert } from 'assert'; import { Config, LoginOptions } from '../config'; @@ -6,7 +5,7 @@ import TransientStore, { StoreOptions } from '../transient-store'; import { encodeState } from '../utils/encoding'; import { ClientFactory } from '../client'; import createDebug from '../utils/debug'; -import { htmlSafe } from '../utils/errors'; +import { AbstractRequest, AbstractResponse } from '../http'; const debug = createDebug('handlers'); @@ -14,7 +13,7 @@ function getRedirectUri(config: Config): string { return urlJoin(config.baseURL, config.routes.callback); } -export type HandleLogin = (req: IncomingMessage, res: ServerResponse, options?: LoginOptions) => Promise; +export type HandleLogin = (req: AbstractRequest, res: AbstractResponse, options?: LoginOptions) => Promise; export default function loginHandlerFactory( config: Config, @@ -43,7 +42,7 @@ export default function loginHandlerFactory( sameSite: opts.authorizationParams.response_mode === 'form_post' ? 'none' : config.session.cookie.sameSite }; - const stateValue = await opts.getLoginState(req as any, opts); + const stateValue = await opts.getLoginState(opts); if (typeof stateValue !== 'object') { throw new Error('Custom state value must be an object.'); } @@ -98,9 +97,6 @@ export default function loginHandlerFactory( const authorizationUrl = client.authorizationUrl(authParams); debug('redirecting to %s', authorizationUrl); - res.writeHead(302, { - Location: authorizationUrl - }); - res.end(htmlSafe(authorizationUrl)); + res.redirect(authorizationUrl); }; } diff --git a/src/auth0-session/handlers/logout.ts b/src/auth0-session/handlers/logout.ts index 7154e4316..56d7f0ba2 100644 --- a/src/auth0-session/handlers/logout.ts +++ b/src/auth0-session/handlers/logout.ts @@ -1,15 +1,14 @@ -import { IncomingMessage, ServerResponse } from 'http'; import url from 'url'; import urlJoin from 'url-join'; import createDebug from '../utils/debug'; import { Config, LogoutOptions } from '../config'; import { ClientFactory } from '../client'; import { SessionCache } from '../session-cache'; -import { htmlSafe } from '../utils/errors'; +import { AbstractRequest, AbstractResponse } from '../http'; const debug = createDebug('logout'); -export type HandleLogout = (req: IncomingMessage, res: ServerResponse, options?: LogoutOptions) => Promise; +export type HandleLogout = (req: AbstractRequest, res: AbstractResponse, options?: LogoutOptions) => Promise; export default function logoutHandlerFactory( config: Config, @@ -27,10 +26,7 @@ export default function logoutHandlerFactory( const isAuthenticated = await sessionCache.isAuthenticated(req, res); if (!isAuthenticated) { debug('end-user already logged out, redirecting to %s', returnURL); - res.writeHead(302, { - Location: returnURL - }); - res.end(htmlSafe(returnURL)); + res.redirect(returnURL); return; } @@ -39,10 +35,7 @@ export default function logoutHandlerFactory( if (!config.idpLogout) { debug('performing a local only logout, redirecting to %s', returnURL); - res.writeHead(302, { - Location: returnURL - }); - res.end(htmlSafe(returnURL)); + res.redirect(returnURL); return; } @@ -54,9 +47,6 @@ export default function logoutHandlerFactory( }); debug('logging out of identity provider, redirecting to %s', returnURL); - res.writeHead(302, { - Location: returnURL - }); - res.end(htmlSafe(returnURL)); + res.redirect(returnURL); }; } diff --git a/src/auth0-session/hooks/get-login-state.ts b/src/auth0-session/hooks/get-login-state.ts index aade6b1e6..06e34e179 100644 --- a/src/auth0-session/hooks/get-login-state.ts +++ b/src/auth0-session/hooks/get-login-state.ts @@ -13,7 +13,7 @@ const debug = createDebug('get-login-state'); * * @return {object} */ -export const getLoginState: GetLoginState = (_req, options) => { +export const getLoginState: GetLoginState = (options) => { const state = { returnTo: options.returnTo }; debug('adding default state %O', state); return state; diff --git a/src/auth0-session/http/abstract-request.ts b/src/auth0-session/http/abstract-request.ts new file mode 100644 index 000000000..06079565d --- /dev/null +++ b/src/auth0-session/http/abstract-request.ts @@ -0,0 +1,15 @@ +export default abstract class AbstractRequest { + public url: string; + public method: string; + public body: Record; + protected constructor(protected req: Req) { + this.url = this.getUrl(); + this.method = this.getMethod(); + this.body = this.getBody(); + } + + public abstract getUrl(): string; + public abstract getMethod(): string; + public abstract getBody(): Record; + public abstract getCookies(): Record; +} diff --git a/src/auth0-session/http/abstract-response.ts b/src/auth0-session/http/abstract-response.ts new file mode 100644 index 000000000..ef3b0fd83 --- /dev/null +++ b/src/auth0-session/http/abstract-response.ts @@ -0,0 +1,31 @@ +import { CookieSerializeOptions, serialize } from 'cookie'; + +export default abstract class AbstractResponse { + protected constructor(protected res: Res) {} + + public setCookie(name: string, value: string, options: CookieSerializeOptions = {}): void { + let previousCookies = this.getSetCookieHeader(); + + this.setSetCookieHeader([...previousCookies, serialize(name, value, options)]); + } + + public clearCookie(name: string, options: CookieSerializeOptions = {}): void { + const { domain, path, secure, sameSite } = options; + const clearOptions: CookieSerializeOptions = { + domain, + path, + maxAge: 0 + }; + // If SameSite=None is set, the cookie Secure attribute must also be set (or the cookie will be blocked) + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none + if (sameSite === 'none') { + clearOptions.secure = secure; + clearOptions.sameSite = sameSite; + } + this.setCookie(name, '', clearOptions); + } + + protected abstract getSetCookieHeader(): string[]; + protected abstract setSetCookieHeader(cookies: string[]): void; + public abstract redirect(location: string, status?: number): void; +} diff --git a/src/auth0-session/http/index.ts b/src/auth0-session/http/index.ts new file mode 100644 index 000000000..ec020e998 --- /dev/null +++ b/src/auth0-session/http/index.ts @@ -0,0 +1,4 @@ +export { default as AbstractRequest } from './abstract-request'; +export { default as AbstractResponse } from './abstract-response'; +export { default as NodeRequest } from './node-request'; +export { default as NodeResponse } from './node-response'; diff --git a/src/auth0-session/http/node-request.ts b/src/auth0-session/http/node-request.ts new file mode 100644 index 000000000..7f22b3546 --- /dev/null +++ b/src/auth0-session/http/node-request.ts @@ -0,0 +1,25 @@ +import { IncomingMessage } from 'http'; +import { parse } from 'cookie'; +import AbstractRequest from './abstract-request'; + +export default class NodeRequest extends AbstractRequest { + public constructor(protected req: IncomingMessage) { + super(req); + } + + public getUrl() { + return this.req.url as string; + } + + public getMethod() { + return this.req.method as string; + } + + public getBody() { + return (this.req as any).body as Record; + } + + public getCookies(): Record { + return parse(this.req.headers.cookie || ''); + } +} diff --git a/src/auth0-session/http/node-response.ts b/src/auth0-session/http/node-response.ts new file mode 100644 index 000000000..63d77344f --- /dev/null +++ b/src/auth0-session/http/node-response.ts @@ -0,0 +1,31 @@ +import { ServerResponse } from 'http'; +import AbstractResponse from './abstract-response'; +import { htmlSafe } from '../utils/errors'; + +export default class NodeResponse extends AbstractResponse { + public constructor(protected res: ServerResponse) { + super(res); + } + + protected getSetCookieHeader(): string[] { + let cookies = this.res.getHeader('Set-Cookie') || []; + if (!Array.isArray(cookies)) { + cookies = [cookies as string]; + } + return cookies; + } + + protected setSetCookieHeader(cookies: string[]): void { + this.res.setHeader('Set-Cookie', cookies); + } + + public redirect(location: string, status = 302): void { + if (this.res.writableEnded) { + return; + } + this.res.writeHead(status, { + Location: this.res.getHeader('Location') || location + }); + this.res.end(htmlSafe(location)); + } +} diff --git a/src/auth0-session/index.ts b/src/auth0-session/index.ts index fad584a89..d043b59ab 100644 --- a/src/auth0-session/index.ts +++ b/src/auth0-session/index.ts @@ -1,4 +1,3 @@ -export { default as NodeCookies, Cookies } from './utils/cookies'; export { MissingStateParamError, MissingStateCookieError, diff --git a/src/auth0-session/session-cache.ts b/src/auth0-session/session-cache.ts index 5dcc1f756..a50ddcc41 100644 --- a/src/auth0-session/session-cache.ts +++ b/src/auth0-session/session-cache.ts @@ -1,7 +1,7 @@ import type { TokenSet } from 'openid-client'; -import type { IncomingMessage, ServerResponse } from 'http'; +import { AbstractRequest, AbstractResponse } from './http'; -export interface SessionCache { +export interface SessionCache { create(req: Req, res: Res, session: Session): Promise; delete(req: Req, res: Res): Promise; isAuthenticated(req: Req, res: Res): Promise; diff --git a/src/auth0-session/session/abstract-session.ts b/src/auth0-session/session/abstract-session.ts index 5d6f9ecb2..d9015cb1b 100644 --- a/src/auth0-session/session/abstract-session.ts +++ b/src/auth0-session/session/abstract-session.ts @@ -1,7 +1,7 @@ import createDebug from '../utils/debug'; import { CookieSerializeOptions } from 'cookie'; import { Config } from '../config'; -import { Cookies } from '../utils/cookies'; +import { AbstractRequest, AbstractResponse } from '../http'; const debug = createDebug('session'); @@ -35,14 +35,14 @@ const assert = (bool: boolean, msg: string) => { } }; -export abstract class AbstractSession { - constructor(protected config: Config, protected Cookies: new () => Cookies) {} +export abstract class AbstractSession { + constructor(protected config: Config) {} - abstract getSession(req: Req): Promise | undefined | null>; + abstract getSession(req: AbstractRequest): Promise | undefined | null>; abstract setSession( - req: Req, - res: Res, + req: AbstractRequest, + res: AbstractResponse, session: Session, uat: number, iat: number, @@ -51,9 +51,13 @@ export abstract class AbstractSession { isNewSession: boolean ): Promise; - abstract deleteSession(req: Req, res: Res, cookieOptions: CookieSerializeOptions): Promise; + abstract deleteSession( + req: AbstractRequest, + res: AbstractResponse, + cookieOptions: CookieSerializeOptions + ): Promise; - public async read(req: Req): Promise<[Session?, number?]> { + public async read(req: AbstractRequest): Promise<[Session?, number?]> { const { rollingDuration, absoluteDuration } = this.config.session; try { @@ -85,7 +89,12 @@ export abstract class AbstractSession { return []; } - public async save(req: Req, res: Res, session: Session | null | undefined, createdAt?: number): Promise { + public async save( + req: AbstractRequest, + res: AbstractResponse, + session: Session | null | undefined, + createdAt?: number + ): Promise { const { cookie: { transient, ...cookieConfig } } = this.config.session; diff --git a/src/auth0-session/session/stateful-session.ts b/src/auth0-session/session/stateful-session.ts index eb56d4305..c6beb8e58 100644 --- a/src/auth0-session/session/stateful-session.ts +++ b/src/auth0-session/session/stateful-session.ts @@ -1,10 +1,10 @@ import { CookieSerializeOptions } from 'cookie'; import createDebug from '../utils/debug'; import { Config } from '../config'; -import { Cookies } from '../utils/cookies'; import { AbstractSession, SessionPayload } from './abstract-session'; import { generateCookieValue, getCookieValue } from '../utils/signed-cookies'; import { signing } from '../utils/hkdf'; +import { AbstractRequest, AbstractResponse } from '../http'; const debug = createDebug('stateful-session'); @@ -26,15 +26,13 @@ export interface SessionStore { } export class StatefulSession< - Req, - Res, Session extends { [key: string]: any } = { [key: string]: any } -> extends AbstractSession { +> extends AbstractSession { private keys?: Uint8Array[]; private store: SessionStore; - constructor(protected config: Config, protected Cookies: new () => Cookies) { - super(config, Cookies); + constructor(protected config: Config) { + super(config); this.store = config.session.store as SessionStore; } @@ -47,9 +45,9 @@ export class StatefulSession< return this.keys; } - async getSession(req: Req): Promise | undefined | null> { + async getSession(req: AbstractRequest): Promise | undefined | null> { const { name: sessionName } = this.config.session; - const cookies = new this.Cookies().getAll(req); + const cookies = req.getCookies(); const keys = await this.getKeys(); const sessionId = await getCookieValue(sessionName, cookies[sessionName], keys); @@ -61,8 +59,8 @@ export class StatefulSession< } async setSession( - req: Req, - res: Res, + req: AbstractRequest, + res: AbstractResponse, session: Session, uat: number, iat: number, @@ -71,8 +69,7 @@ export class StatefulSession< isNewSession: boolean ): Promise { const { name: sessionName, genId } = this.config.session; - const cookieSetter = new this.Cookies(); - const cookies = cookieSetter.getAll(req); + const cookies = req.getCookies(); const keys = await this.getKeys(); let sessionId = await getCookieValue(sessionName, cookies[sessionName], keys); @@ -90,25 +87,26 @@ export class StatefulSession< } debug('set session %o', sessionId); const cookieValue = await generateCookieValue(sessionName, sessionId, keys[0]); - cookieSetter.set(sessionName, cookieValue, cookieOptions); - cookieSetter.commit(res); + res.setCookie(sessionName, cookieValue, cookieOptions); await this.store.set(sessionId, { header: { iat, uat, exp }, data: session }); } - async deleteSession(req: Req, res: Res, cookieOptions: CookieSerializeOptions): Promise { + async deleteSession( + req: AbstractRequest, + res: AbstractResponse, + cookieOptions: CookieSerializeOptions + ): Promise { const { name: sessionName } = this.config.session; - const cookieSetter = new this.Cookies(); - const cookies = cookieSetter.getAll(req); + const cookies = req.getCookies(); const keys = await this.getKeys(); const sessionId = await getCookieValue(sessionName, cookies[sessionName], keys); if (sessionId) { debug('deleting session %o', sessionId); - cookieSetter.clear(sessionName, cookieOptions); - cookieSetter.commit(res); + res.clearCookie(sessionName, cookieOptions); await this.store.delete(sessionId); } } diff --git a/src/auth0-session/session/stateless-session.ts b/src/auth0-session/session/stateless-session.ts index 0d3d9d40a..d28a00f2d 100644 --- a/src/auth0-session/session/stateless-session.ts +++ b/src/auth0-session/session/stateless-session.ts @@ -2,9 +2,9 @@ import * as jose from 'jose'; import { CookieSerializeOptions, serialize } from 'cookie'; import createDebug from '../utils/debug'; import { Config } from '../config'; -import { Cookies } from '../utils/cookies'; import { encryption } from '../utils/hkdf'; import { AbstractSession, Header, SessionPayload } from './abstract-session'; +import { AbstractRequest, AbstractResponse } from '../http'; const debug = createDebug('stateless-session'); @@ -15,15 +15,13 @@ const enc = 'A256GCM'; const notNull = (value: T | null): value is T => value !== null; export class StatelessSession< - Req, - Res, Session extends { [key: string]: any } = { [key: string]: any } -> extends AbstractSession { +> extends AbstractSession { private keys?: Uint8Array[]; private chunkSize: number; - constructor(protected config: Config, protected Cookies: new () => Cookies) { - super(config, Cookies); + constructor(protected config: Config) { + super(config); const { cookie: { transient, ...cookieConfig }, name: sessionName @@ -66,12 +64,12 @@ export class StatelessSession< throw err; } - async getSession(req: Req): Promise | undefined | null> { + async getSession(req: AbstractRequest): Promise | undefined | null> { const { name: sessionName } = this.config.session; - const cookies = new this.Cookies().getAll(req); + const cookies = req.getCookies(); let existingSessionValue: string | undefined; if (sessionName in cookies) { - // get JWE from unchunked session cookie + // get JWE from un-chunked session cookie debug('reading session from %s cookie', sessionName); existingSessionValue = cookies[sessionName]; } else if (`${sessionName}.0` in cookies) { @@ -106,8 +104,8 @@ export class StatelessSession< } async setSession( - req: Req, - res: Res, + req: AbstractRequest, + res: AbstractResponse, session: Session, uat: number, iat: number, @@ -115,43 +113,45 @@ export class StatelessSession< cookieOptions: CookieSerializeOptions ): Promise { const { name: sessionName } = this.config.session; - const cookieSetter = new this.Cookies(); - const cookies = cookieSetter.getAll(req); + const cookies = req.getCookies(); debug('found session, creating signed session cookie(s) with name %o(.i)', sessionName); const value = await this.encrypt(session, { iat, uat, exp }); const chunkCount = Math.ceil(value.length / this.chunkSize); + + const existingCookies = new Set( + Object.keys(cookies).filter((cookie) => cookie.match(`^${sessionName}(?:\\.\\d)?$`)) + ); + if (chunkCount > 1) { debug('cookie size greater than %d, chunking', this.chunkSize); for (let i = 0; i < chunkCount; i++) { const chunkValue = value.slice(i * this.chunkSize, (i + 1) * this.chunkSize); const chunkCookieName = `${sessionName}.${i}`; - cookieSetter.set(chunkCookieName, chunkValue, cookieOptions); - } - if (sessionName in cookies) { - cookieSetter.clear(sessionName, cookieOptions); + res.setCookie(chunkCookieName, chunkValue, cookieOptions); + existingCookies.delete(chunkCookieName); } } else { - cookieSetter.set(sessionName, value, cookieOptions); - for (const cookieName of Object.keys(cookies)) { - if (cookieName.match(`^${sessionName}\\.\\d$`)) { - cookieSetter.clear(cookieName, cookieOptions); - } - } + res.setCookie(sessionName, value, cookieOptions); + existingCookies.delete(sessionName); } - cookieSetter.commit(res, this.config.session.name); + + // When you're going from n + 1 chunks to n you need to delete the hanging cookies. + existingCookies.forEach((cookie) => res.clearCookie(cookie, cookieOptions)); } - async deleteSession(req: Req, res: Res, cookieOptions: CookieSerializeOptions): Promise { + async deleteSession( + req: AbstractRequest, + res: AbstractResponse, + cookieOptions: CookieSerializeOptions + ): Promise { const { name: sessionName } = this.config.session; - const cookieSetter = new this.Cookies(); - const cookies = cookieSetter.getAll(req); + const cookies = req.getCookies(); for (const cookieName of Object.keys(cookies)) { if (cookieName.match(`^${sessionName}(?:\\.\\d)?$`)) { - cookieSetter.clear(cookieName, cookieOptions); - cookieSetter.commit(res, this.config.session.name); + res.clearCookie(cookieName, cookieOptions); } } } diff --git a/src/auth0-session/transient-store.ts b/src/auth0-session/transient-store.ts index 43101d93a..d9e18655c 100644 --- a/src/auth0-session/transient-store.ts +++ b/src/auth0-session/transient-store.ts @@ -1,9 +1,8 @@ -import { IncomingMessage, ServerResponse } from 'http'; import { generators } from 'openid-client'; import { generateCookieValue, getCookieValue } from './utils/signed-cookies'; import { signing } from './utils/hkdf'; -import NodeCookies from './utils/cookies'; import { Config } from './config'; +import { AbstractRequest, AbstractResponse } from './http'; export interface StoreOptions { sameSite?: boolean | 'lax' | 'strict' | 'none'; @@ -38,8 +37,8 @@ export default class TransientStore { */ async save( key: string, - _req: IncomingMessage, - res: ServerResponse, + _req: AbstractRequest, + res: AbstractResponse, { sameSite = 'none', value = this.generateNonce() }: StoreOptions ): Promise { const isSameSiteNone = sameSite === 'none'; @@ -51,12 +50,11 @@ export default class TransientStore { path }; const [signingKey] = await this.getKeys(); - const cookieSetter = new NodeCookies(); { const cookieValue = await generateCookieValue(key, value, signingKey); // Set the cookie with the SameSite attribute and, if needed, the Secure flag. - cookieSetter.set(key, cookieValue, { + res.setCookie(key, cookieValue, { ...basicAttr, sameSite, secure: isSameSiteNone ? true : basicAttr.secure @@ -66,10 +64,9 @@ export default class TransientStore { if (isSameSiteNone && this.config.legacySameSiteCookie) { const cookieValue = await generateCookieValue(`_${key}`, value, signingKey); // Set the fallback cookie with no SameSite or Secure attributes. - cookieSetter.set(`_${key}`, cookieValue, basicAttr); + res.setCookie(`_${key}`, cookieValue, basicAttr); } - cookieSetter.commit(res); return value; } @@ -82,15 +79,14 @@ export default class TransientStore { * * @return {String|undefined} Cookie value or undefined if cookie was not found. */ - async read(key: string, req: IncomingMessage, res: ServerResponse): Promise { - const cookies = new NodeCookies().getAll(req); + async read(key: string, req: AbstractRequest, res: AbstractResponse): Promise { + const cookies = req.getCookies(); const cookie = cookies[key]; const cookieConfig = this.config.session.cookie; - const cookieSetter = new NodeCookies(); const verifyingKeys = await this.getKeys(); let value = await getCookieValue(key, cookie, verifyingKeys); - cookieSetter.clear(key, cookieConfig); + res.clearCookie(key, cookieConfig); if (this.config.legacySameSiteCookie) { const fallbackKey = `_${key}`; @@ -98,10 +94,9 @@ export default class TransientStore { const fallbackCookie = cookies[fallbackKey]; value = await getCookieValue(fallbackKey, fallbackCookie, verifyingKeys); } - cookieSetter.clear(fallbackKey, cookieConfig); + res.clearCookie(fallbackKey, cookieConfig); } - cookieSetter.commit(res); return value; } diff --git a/src/auth0-session/utils/cookies.ts b/src/auth0-session/utils/cookies.ts deleted file mode 100644 index 9ee36c3b0..000000000 --- a/src/auth0-session/utils/cookies.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { IncomingMessage, ServerResponse } from 'http'; -import { CookieSerializeOptions, parse, serialize } from 'cookie'; - -export abstract class Cookies { - protected cookies: string[]; - - constructor() { - this.cookies = []; - } - - set(name: string, value: string, options: CookieSerializeOptions = {}): void { - this.cookies.push(serialize(name, value, options)); - } - - clear(name: string, options: CookieSerializeOptions = {}): void { - const { domain, path, secure, sameSite } = options; - const clearOptions: CookieSerializeOptions = { - domain, - path, - maxAge: 0 - }; - // If SameSite=None is set, the cookie Secure attribute must also be set (or the cookie will be blocked) - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none - if (sameSite === 'none') { - clearOptions.secure = secure; - clearOptions.sameSite = sameSite; - } - this.set(name, '', clearOptions); - } - - commit(res: unknown, filterCookiePrefix?: string): void { - let previousCookies = this.getSetCookieHeader(res); - if (filterCookiePrefix) { - const re = new RegExp(`^${filterCookiePrefix}(\\.\\d+)?=`); - previousCookies = previousCookies.filter((cookie: string) => !re.test(cookie)); - } - this.setSetCookieHeader(res, [...previousCookies, ...this.cookies]); - } - - protected abstract getSetCookieHeader(res: unknown): string[]; - protected abstract setSetCookieHeader(res: unknown, cookies: string[]): void; - abstract getAll(req: unknown): Record; -} - -export default class NodeCookies extends Cookies { - protected getSetCookieHeader(res: ServerResponse): string[] { - let cookies = res.getHeader('Set-Cookie') || []; - if (!Array.isArray(cookies)) { - cookies = [cookies as string]; - } - return cookies; - } - - protected setSetCookieHeader(res: ServerResponse, cookies: string[]): void { - res.setHeader('Set-Cookie', cookies); - } - - getAll(req: IncomingMessage): Record { - return parse(req.headers.cookie || ''); - } -} diff --git a/src/utils/middleware-cookies.ts b/src/utils/middleware-cookies.ts index d593bfdf3..ba9a4c269 100644 --- a/src/utils/middleware-cookies.ts +++ b/src/utils/middleware-cookies.ts @@ -1,7 +1,7 @@ -import { Cookies } from '../auth0-session/utils/cookies'; +import { AbstractCookies } from '../auth0-session/http/abstract-cookies'; import { NextRequest, NextResponse } from 'next/server'; -export default class MiddlewareCookies extends Cookies { +export default class MiddlewareCookies extends AbstractCookies { protected getSetCookieHeader(res: NextResponse): string[] { const value = res.headers.get('set-cookie'); return value?.split(', ') || []; diff --git a/tests/auth0-session/fixtures/server.ts b/tests/auth0-session/fixtures/server.ts index c1aadd807..31b4531de 100644 --- a/tests/auth0-session/fixtures/server.ts +++ b/tests/auth0-session/fixtures/server.ts @@ -6,7 +6,6 @@ import nock from 'nock'; import { TokenSet, TokenSetParameters } from 'openid-client'; import bodyParser from 'body-parser'; import { - NodeCookies as Cookies, loginHandler, getConfig, ConfigParameters, @@ -27,22 +26,32 @@ import { jwks } from './cert'; import { cert, key } from './https'; import { Claims } from '../../../src/session'; import version from '../../../src/version'; +import { NodeRequest, NodeResponse } from '../../../src/auth0-session/http'; export type SessionResponse = TokenSetParameters & { claims: Claims }; +interface NodeCallbackOptions extends Omit { + afterCallback?: ( + req: IncomingMessage, + res: ServerResponse, + session: any, + state?: Record + ) => Promise | any | undefined; +} + class TestSessionCache implements SessionCache { - constructor(private cookieStore: AbstractSession) {} - async create(req: IncomingMessage, res: ServerResponse, tokenSet: TokenSet): Promise { + constructor(private cookieStore: AbstractSession) {} + async create(req: NodeRequest, res: NodeResponse, tokenSet: TokenSet): Promise { await this.cookieStore.save(req, res, tokenSet); } - async delete(req: IncomingMessage, res: ServerResponse): Promise { + async delete(req: NodeRequest, res: NodeResponse): Promise { await this.cookieStore.save(req, res, null); } - async isAuthenticated(req: IncomingMessage): Promise { + async isAuthenticated(req: NodeRequest): Promise { const [session] = await this.cookieStore.read(req); return !!session?.id_token; } - async getIdToken(req: IncomingMessage): Promise { + async getIdToken(req: NodeRequest): Promise { const [session] = await this.cookieStore.read(req); return session?.id_token; } @@ -52,9 +61,9 @@ class TestSessionCache implements SessionCache { } type Handlers = { - handleLogin: (req: IncomingMessage, res: ServerResponse, opts?: LoginOptions) => Promise; - handleLogout: (req: IncomingMessage, res: ServerResponse, opts?: LogoutOptions) => Promise; - handleCallback: (req: IncomingMessage, res: ServerResponse, opts?: CallbackOptions) => Promise; + handleLogin: (req: NodeRequest, res: NodeResponse, opts?: LoginOptions) => Promise; + handleLogout: (req: NodeRequest, res: NodeResponse, opts?: LogoutOptions) => Promise; + handleCallback: (req: NodeRequest, res: NodeResponse, opts?: CallbackOptions) => Promise; handleSession: (req: IncomingMessage, res: ServerResponse) => Promise; }; @@ -62,9 +71,7 @@ const createHandlers = (params: ConfigParameters): Handlers => { const config = getConfig(params); const getClient = clientFactory(config, { name: 'nextjs-auth0', version }); const transientStore = new TransientStore(config); - const cookieStore = params.session?.store - ? new StatefulSession(config, Cookies) - : new StatelessSession(config, Cookies); + const cookieStore = params.session?.store ? new StatefulSession(config) : new StatelessSession(config); const sessionCache = new TestSessionCache(cookieStore); return { @@ -72,14 +79,15 @@ const createHandlers = (params: ConfigParameters): Handlers => { handleLogout: logoutHandler(config, getClient, sessionCache), handleCallback: callbackHandler(config, getClient, sessionCache, transientStore), handleSession: async (req: IncomingMessage, res: ServerResponse) => { - const [json, iat] = await cookieStore.read(req); + const nodeReq = new NodeRequest(req); + const [json, iat] = await cookieStore.read(nodeReq); if (!json?.id_token) { res.writeHead(401); res.end(); return; } const session = new TokenSet(json); - await cookieStore.save(req, res, session, iat); + await cookieStore.save(nodeReq, new NodeResponse(res), session, iat); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ ...session, claims: session?.claims() } as SessionResponse)); } @@ -102,25 +110,39 @@ const requestListener = ( handlers: Handlers, { - callbackOptions, + callbackOptions: nodeCallbackOptions, loginOptions, logoutOptions - }: { callbackOptions?: CallbackOptions; loginOptions?: LoginOptions; logoutOptions?: LogoutOptions } + }: { callbackOptions?: NodeCallbackOptions; loginOptions?: LoginOptions; logoutOptions?: LogoutOptions } ) => async (req: IncomingMessage, res: ServerResponse): Promise => { const { pathname } = url.parse(req.url as string, true); const parsedReq = await parseJson(req, res); + const nodeReq = new NodeRequest(parsedReq); + const nodeRes = new NodeResponse(res); + let callbackOptions: CallbackOptions | undefined = undefined; + if (nodeCallbackOptions?.afterCallback) { + const fn = nodeCallbackOptions.afterCallback; + callbackOptions = { + ...nodeCallbackOptions, + afterCallback: (...args) => fn(req, res, ...args) + }; + } try { switch (pathname) { case '/login': - return await handlers.handleLogin(parsedReq, res, loginOptions); + return await handlers.handleLogin(nodeReq, nodeRes, loginOptions); case '/logout': - return await handlers.handleLogout(parsedReq, res, logoutOptions); + return await handlers.handleLogout(nodeReq, nodeRes, logoutOptions); case '/callback': - return await handlers.handleCallback(parsedReq, res, callbackOptions); + return await handlers.handleCallback( + nodeReq, + nodeRes, + (callbackOptions || nodeCallbackOptions) as CallbackOptions + ); case '/session': - return await handlers.handleSession(parsedReq, res); + return await handlers.handleSession(req, res); default: res.writeHead(404); res.end(); @@ -143,7 +165,7 @@ export const setup = async ( https }: { https?: boolean; - callbackOptions?: CallbackOptions; + callbackOptions?: NodeCallbackOptions; loginOptions?: LoginOptions; logoutOptions?: LogoutOptions; customListener?: (req: IncomingMessage, res: ServerResponse) => void; diff --git a/tests/auth0-session/handlers/callback.test.ts b/tests/auth0-session/handlers/callback.test.ts index e17779e1e..bfed184e6 100644 --- a/tests/auth0-session/handlers/callback.test.ts +++ b/tests/auth0-session/handlers/callback.test.ts @@ -6,7 +6,7 @@ import { encodeState } from '../../../src/auth0-session/utils/encoding'; import { SessionResponse, setup, teardown } from '../fixtures/server'; import { makeIdToken } from '../fixtures/cert'; import { toSignedCookieJar, get, post, defaultConfig, decodeJWT } from '../fixtures/helpers'; -import { ServerResponse } from 'http'; +import { IncomingMessage, ServerResponse } from 'http'; import { readFileSync } from 'fs'; import { join } from 'path'; import * as qs from 'querystring'; @@ -536,7 +536,7 @@ describe('callback', () => { it('should not overwrite location header if set in after callback', async () => { const baseURL = await setup(defaultConfig, { callbackOptions: { - afterCallback(_req, res: ServerResponse, session) { + afterCallback(_req: IncomingMessage, res: ServerResponse, session: any) { res.setHeader('Location', '/foo'); return session; } diff --git a/tests/auth0-session/handlers/login.test.ts b/tests/auth0-session/handlers/login.test.ts index dd13ad555..31f7fbd80 100644 --- a/tests/auth0-session/handlers/login.test.ts +++ b/tests/auth0-session/handlers/login.test.ts @@ -4,7 +4,6 @@ import { setup, teardown } from '../fixtures/server'; import { defaultConfig, fromCookieJar, get, getCookie } from '../fixtures/helpers'; import { decodeState, encodeState } from '../../../src/auth0-session/utils/encoding'; import { LoginOptions } from '../../../src/auth0-session'; -import { IncomingMessage } from 'http'; describe('login', () => { afterEach(teardown); @@ -181,7 +180,7 @@ describe('login', () => { it('should use a custom state builder', async () => { const baseURL = await setup({ ...defaultConfig, - getLoginState: (_req: IncomingMessage, opts: LoginOptions) => { + getLoginState: (opts: LoginOptions) => { return { returnTo: opts.returnTo + '/custom-page', customProp: '__test_custom_prop__' diff --git a/tests/auth0-session/http/node-request.test.ts b/tests/auth0-session/http/node-request.test.ts new file mode 100644 index 000000000..99b87b4b4 --- /dev/null +++ b/tests/auth0-session/http/node-request.test.ts @@ -0,0 +1,38 @@ +import { AddressInfo } from 'net'; +import { createServer, get as getRequest, IncomingMessage, ServerResponse } from 'http'; +import { NodeRequest } from '../../../src/auth0-session/http'; + +const setup = (): Promise<[IncomingMessage, ServerResponse, Function]> => + new Promise((resolve) => { + const server = createServer((req, res) => { + resolve([ + req, + res, + (): Promise => + new Promise((resolve) => { + res.end(); + server.close(resolve as (err?: Error) => void); + }) + ]); + }); + server.listen(0, () => { + const url = `http://localhost:${(server.address() as AddressInfo).port}`; + getRequest(url); + }); + }); + +describe('NodeRequest', () => { + it('should get all cookies', async () => { + const [req, , teardown] = await setup(); + req.headers.cookie = 'foo=bar; bar=baz;'; + expect(new NodeRequest(req).getCookies()).toMatchObject({ foo: 'bar', bar: 'baz' }); + await teardown(); + }); + + it('should get a cookie by name', async () => { + const [req, , teardown] = await setup(); + req.headers.cookie = 'foo=bar; bar=baz;'; + expect(new NodeRequest(req).getCookies()['foo']).toEqual('bar'); + await teardown(); + }); +}); diff --git a/tests/auth0-session/http/node-response.test.ts b/tests/auth0-session/http/node-response.test.ts new file mode 100644 index 000000000..9837a73cd --- /dev/null +++ b/tests/auth0-session/http/node-response.test.ts @@ -0,0 +1,57 @@ +import { AddressInfo } from 'net'; +import { createServer, get as getRequest, IncomingMessage, ServerResponse } from 'http'; +import { NodeResponse } from '../../../src/auth0-session/http'; + +const setup = (): Promise<[IncomingMessage, ServerResponse, Function]> => + new Promise((resolve) => { + const server = createServer((req, res) => { + resolve([ + req, + res, + (): Promise => + new Promise((resolve) => { + res.end(); + server.close(resolve as (err?: Error) => void); + }) + ]); + }); + server.listen(0, () => { + const url = `http://localhost:${(server.address() as AddressInfo).port}`; + getRequest(url); + }); + }); + +describe('NodeResponse', () => { + it('should set a cookie', async () => { + const [, res, teardown] = await setup(); + const setter = new NodeResponse(res); + setter.setCookie('foo', 'bar'); + expect(res.getHeader('Set-Cookie')).toEqual(['foo=bar']); + await teardown(); + }); + + it('should set a cookie with opts', async () => { + const [, res, teardown] = await setup(); + const setter = new NodeResponse(res); + setter.setCookie('foo', 'bar', { httpOnly: true, sameSite: 'strict' }); + expect(res.getHeader('Set-Cookie')).toEqual(['foo=bar; HttpOnly; SameSite=Strict']); + await teardown(); + }); + + it('should not overwrite existing set cookie', async () => { + const [, res, teardown] = await setup(); + res.setHeader('Set-Cookie', 'foo=bar'); + const setter = new NodeResponse(res); + setter.setCookie('baz', 'qux'); + expect(res.getHeader('Set-Cookie')).toEqual(['foo=bar', 'baz=qux']); + await teardown(); + }); + + it('should clear cookies', async () => { + const [, res, teardown] = await setup(); + const setter = new NodeResponse(res); + setter.clearCookie('foo'); + expect(res.getHeader('Set-Cookie')).toEqual(['foo=; Max-Age=0']); + await teardown(); + }); +}); diff --git a/tests/auth0-session/transient-store.test.ts b/tests/auth0-session/transient-store.test.ts index a50162781..e99602a52 100644 --- a/tests/auth0-session/transient-store.test.ts +++ b/tests/auth0-session/transient-store.test.ts @@ -1,10 +1,10 @@ -import { IncomingMessage, ServerResponse } from 'http'; import * as jose from 'jose'; import { CookieJar } from 'tough-cookie'; import { getConfig, TransientStore } from '../../src/auth0-session/'; import { signing } from '../../src/auth0-session/utils/hkdf'; import { defaultConfig, fromCookieJar, get, getCookie, toSignedCookieJar } from './fixtures/helpers'; import { setup as createServer, teardown } from './fixtures/server'; +import { NodeRequest, NodeResponse } from '../../src/auth0-session/http'; const generateSignature = async (cookie: string, value: string): Promise => { const key = await signing(defaultConfig.secret as string); @@ -14,10 +14,14 @@ const generateSignature = async (cookie: string, value: string): Promise return signature; }; -const setup = async (params = defaultConfig, cb: Function, https = true): Promise => +const setup = async ( + params = defaultConfig, + cb: (req: NodeRequest, res: NodeResponse) => Promise, + https = true +): Promise => createServer(params, { customListener: async (req, res) => { - const value = await cb(req, res); + const value = await cb(new NodeRequest(req), new NodeResponse(res)); res.end(JSON.stringify({ value })); }, https @@ -27,12 +31,10 @@ describe('TransientStore', () => { afterEach(teardown); it('should use the passed-in key to set the cookies', async () => { - const baseURL = await setup( - defaultConfig, - async (req: IncomingMessage, res: ServerResponse) => - await transientStore.save('test_key', req, res, { value: 'foo' }) + const baseURL: string = await setup(defaultConfig, async (req: NodeRequest, res: NodeResponse) => + transientStore.save('test_key', req, res, { value: 'foo' }) ); - const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); + const transientStore: TransientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); const cookieJar = new CookieJar(); const { value } = await get(baseURL, '/', { cookieJar }); const cookies = fromCookieJar(cookieJar, baseURL); @@ -43,7 +45,7 @@ describe('TransientStore', () => { it('should accept list of secrets', async () => { const config = { ...defaultConfig, secret: ['__old_secret__', defaultConfig.secret as string] }; - const baseURL = await setup(config, (req: IncomingMessage, res: ServerResponse) => + const baseURL: string = await setup(config, (req: NodeRequest, res: NodeResponse) => transientStore.save('test_key', req, res, { value: 'foo' }) ); const transientStore = new TransientStore(getConfig({ ...config, baseURL })); @@ -56,7 +58,7 @@ describe('TransientStore', () => { }); it('should set cookie to secure by default when baseURL protocol is https', async () => { - const baseURL = await setup(defaultConfig, (req: IncomingMessage, res: ServerResponse) => + const baseURL: string = await setup(defaultConfig, (req: NodeRequest, res: NodeResponse) => transientStore.save('test_key', req, res, { value: 'foo' }) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -68,9 +70,9 @@ describe('TransientStore', () => { }); it('should set cookie to not secure when baseURL protocol is http and SameSite=Lax', async () => { - const baseURL = await setup( + const baseURL: string = await setup( defaultConfig, - (req: IncomingMessage, res: ServerResponse) => transientStore.save('test_key', req, res, { value: 'foo' }), + (req: NodeRequest, res: NodeResponse) => transientStore.save('test_key', req, res, { value: 'foo' }), false ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -82,9 +84,9 @@ describe('TransientStore', () => { }); it('should set SameSite=None, Secure=False for fallback cookie by default for http', async () => { - const baseURL = await setup( + const baseURL: string = await setup( defaultConfig, - (req: IncomingMessage, res: ServerResponse) => transientStore.save('test_key', req, res, { value: 'foo' }), + (req: NodeRequest, res: NodeResponse) => transientStore.save('test_key', req, res, { value: 'foo' }), false ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -100,9 +102,9 @@ describe('TransientStore', () => { }); it('should turn off fallback', async () => { - const baseURL = await setup( + const baseURL: string = await setup( { ...defaultConfig, legacySameSiteCookie: false }, - (req: IncomingMessage, res: ServerResponse) => transientStore.save('test_key', req, res, { value: 'foo' }) + (req: NodeRequest, res: NodeResponse) => transientStore.save('test_key', req, res, { value: 'foo' }) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL, legacySameSiteCookie: false })); const cookieJar = new CookieJar(); @@ -113,7 +115,7 @@ describe('TransientStore', () => { }); it('should set custom SameSite with no fallback', async () => { - const baseURL = await setup(defaultConfig, (req: IncomingMessage, res: ServerResponse) => + const baseURL: string = await setup(defaultConfig, (req: NodeRequest, res: NodeResponse) => transientStore.save('test_key', req, res, { sameSite: 'lax', value: 'foo' }) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -132,7 +134,7 @@ describe('TransientStore', () => { }); it('should return undefined if there are no cookies', async () => { - const baseURL = await setup(defaultConfig, (req: IncomingMessage, res: ServerResponse) => + const baseURL: string = await setup(defaultConfig, (req: NodeRequest, res: NodeResponse) => transientStore.read('test_key', req, res) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -141,7 +143,7 @@ describe('TransientStore', () => { }); it('should return main value and delete both cookies by default', async () => { - const baseURL = await setup(defaultConfig, (req: IncomingMessage, res: ServerResponse) => + const baseURL: string = await setup(defaultConfig, (req: NodeRequest, res: NodeResponse) => transientStore.read('test_key', req, res) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -161,7 +163,7 @@ describe('TransientStore', () => { }); it('should return fallback value and delete both cookies if main value not present', async () => { - const baseURL = await setup(defaultConfig, (req: IncomingMessage, res: ServerResponse) => + const baseURL: string = await setup(defaultConfig, (req: NodeRequest, res: NodeResponse) => transientStore.read('test_key', req, res) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); @@ -179,9 +181,9 @@ describe('TransientStore', () => { }); it('should not check fallback value when legacySameSiteCookie is false', async () => { - const baseURL = await setup( + const baseURL: string = await setup( { ...defaultConfig, legacySameSiteCookie: false }, - (req: IncomingMessage, res: ServerResponse) => transientStore.read('test_key', req, res) + (req: NodeRequest, res: NodeResponse) => transientStore.read('test_key', req, res) ); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL, legacySameSiteCookie: false })); const cookieJar = await toSignedCookieJar( @@ -197,9 +199,7 @@ describe('TransientStore', () => { }); it("should not throw when it can't verify the signature", async () => { - const baseURL = await setup(defaultConfig, (req: IncomingMessage, res: ServerResponse) => - transientStore.read('test_key', req, res) - ); + const baseURL: string = await setup(defaultConfig, (req, res) => transientStore.read('test_key', req, res)); const transientStore = new TransientStore(getConfig({ ...defaultConfig, baseURL })); const cookieJar = await toSignedCookieJar( { diff --git a/tests/auth0-session/utils/cookie.test.ts b/tests/auth0-session/utils/cookie.test.ts deleted file mode 100644 index 04b1e1c7d..000000000 --- a/tests/auth0-session/utils/cookie.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { AddressInfo } from 'net'; -import { createServer, get as getRequest, IncomingMessage, ServerResponse } from 'http'; -import NodeCookies from '../../../src/auth0-session/utils/cookies'; - -const setup = (): Promise<[IncomingMessage, ServerResponse, Function]> => - new Promise((resolve) => { - const server = createServer((req, res) => { - resolve([ - req, - res, - (): Promise => - new Promise((resolve) => { - res.end(); - server.close(resolve as (err?: Error) => void); - }) - ]); - }); - server.listen(0, () => { - const url = `http://localhost:${(server.address() as AddressInfo).port}`; - getRequest(url); - }); - }); - -describe('cookie', () => { - it('should get all cookies', async () => { - const [req, , teardown] = await setup(); - req.headers.cookie = 'foo=bar; bar=baz;'; - expect(new NodeCookies().getAll(req)).toMatchObject({ foo: 'bar', bar: 'baz' }); - await teardown(); - }); - - it('should get a cookie by name', async () => { - const [req, , teardown] = await setup(); - req.headers.cookie = 'foo=bar; bar=baz;'; - expect(new NodeCookies().getAll(req)['foo']).toEqual('bar'); - await teardown(); - }); - - it('should set a cookie', async () => { - const [, res, teardown] = await setup(); - const setter = new NodeCookies(); - setter.set('foo', 'bar'); - setter.commit(res); - expect(res.getHeader('Set-Cookie')).toEqual(['foo=bar']); - await teardown(); - }); - - it('should set a cookie with opts', async () => { - const [, res, teardown] = await setup(); - const setter = new NodeCookies(); - setter.set('foo', 'bar', { httpOnly: true, sameSite: 'strict' }); - setter.commit(res); - expect(res.getHeader('Set-Cookie')).toEqual(['foo=bar; HttpOnly; SameSite=Strict']); - await teardown(); - }); - - it('should not overwrite existing set cookie', async () => { - const [, res, teardown] = await setup(); - res.setHeader('Set-Cookie', 'foo=bar'); - const setter = new NodeCookies(); - setter.set('baz', 'qux'); - setter.commit(res); - expect(res.getHeader('Set-Cookie')).toEqual(['foo=bar', 'baz=qux']); - await teardown(); - }); - - it('should override existing cookies that equal name', async () => { - const [, res, teardown] = await setup(); - res.setHeader('Set-Cookie', ['foo=bar', 'baz=qux']); - const setter = new NodeCookies(); - setter.set('foo', 'qux'); - setter.commit(res, 'foo'); - expect(res.getHeader('Set-Cookie')).toEqual(['baz=qux', 'foo=qux']); - await teardown(); - }); - - it('should override existing cookies that match name', async () => { - const [, res, teardown] = await setup(); - res.setHeader('Set-Cookie', ['foo.1=bar', 'foo.2=baz']); - const setter = new NodeCookies(); - setter.set('foo', 'qux'); - setter.commit(res, 'foo'); - expect(res.getHeader('Set-Cookie')).toEqual(['foo=qux']); - await teardown(); - }); - - it('should clear cookies', async () => { - const [, res, teardown] = await setup(); - const setter = new NodeCookies(); - setter.clear('foo'); - setter.commit(res); - expect(res.getHeader('Set-Cookie')).toEqual(['foo=; Max-Age=0']); - await teardown(); - }); -}); diff --git a/tests/auth0-session/utils/errors.test.ts b/tests/auth0-session/utils/errors.test.ts index 8981fc809..067d0549f 100644 --- a/tests/auth0-session/utils/errors.test.ts +++ b/tests/auth0-session/utils/errors.test.ts @@ -1,4 +1,4 @@ -import { IdentityProviderError } from '../../../src'; +import { IdentityProviderError } from '../../../src/auth0-session'; describe('IdentityProviderError', () => { test('should escape error fields', () => { From de87edcd4badd44f2e12f566111b8ca5ea071295 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Fri, 19 May 2023 16:38:11 +0100 Subject: [PATCH 05/61] Init implementation in next code --- example-app/app/api/auth/[auth0]/route.ts | 1 + src/auth0-session/handlers/callback.ts | 12 +++-- src/auth0-session/handlers/logout.ts | 8 ++-- src/auth0-session/http/abstract-request.ts | 11 +---- src/auth0-session/http/abstract-response.ts | 2 +- src/auth0-session/http/node-request.ts | 2 +- src/auth0-session/http/node-response.ts | 4 +- src/auth0-session/session-cache.ts | 3 +- .../session/stateless-session.ts | 3 +- src/config.ts | 30 ++++++++++-- src/edge.ts | 5 +- src/handlers/callback.ts | 48 ++++++++++--------- src/handlers/login.ts | 17 ++++++- src/handlers/logout.ts | 3 +- src/helpers/testing.ts | 4 +- src/helpers/with-middleware-auth-required.ts | 5 +- src/http/auth0-next-api-request.ts | 21 ++++++++ src/http/auth0-next-api-response.ts | 11 +++++ src/http/auth0-next-request.ts | 30 ++++++++++++ src/http/auth0-next-response.ts | 19 ++++++++ src/http/index.ts | 4 ++ src/index.ts | 13 +---- src/session/cache.ts | 37 ++++++++++---- src/session/get-access-token.ts | 8 +++- src/session/get-session.ts | 2 - src/utils/middleware-cookies.ts | 26 ---------- 26 files changed, 219 insertions(+), 110 deletions(-) create mode 100644 src/http/auth0-next-api-request.ts create mode 100644 src/http/auth0-next-api-response.ts create mode 100644 src/http/auth0-next-request.ts create mode 100644 src/http/auth0-next-response.ts create mode 100644 src/http/index.ts delete mode 100644 src/utils/middleware-cookies.ts diff --git a/example-app/app/api/auth/[auth0]/route.ts b/example-app/app/api/auth/[auth0]/route.ts index dace42ab9..e46a7cafc 100644 --- a/example-app/app/api/auth/[auth0]/route.ts +++ b/example-app/app/api/auth/[auth0]/route.ts @@ -1,4 +1,5 @@ import { handleAuth } from '@auth0/nextjs-auth0'; + export const GET = handleAuth({ onError(req, res, error) { console.error(error); diff --git a/src/auth0-session/handlers/callback.ts b/src/auth0-session/handlers/callback.ts index 11ab995cd..58b7d3503 100644 --- a/src/auth0-session/handlers/callback.ts +++ b/src/auth0-session/handlers/callback.ts @@ -37,7 +37,7 @@ export type HandleCallback = (req: AbstractRequest, res: AbstractResponse, optio export default function callbackHandlerFactory( config: Config, getClient: ClientFactory, - sessionCache: SessionCache, + sessionCache: SessionCache, transientCookieHandler: TransientStore ): HandleCallback { return async (req, res, options) => { @@ -46,7 +46,11 @@ export default function callbackHandlerFactory( let tokenSet; - const callbackParams = client.callbackParams(req as unknown as IncomingMessage); + const callbackParams = client.callbackParams({ + method: req.getMethod(), + url: req.getUrl(), + body: await req.getBody() + } as unknown as IncomingMessage); if (!callbackParams.state) { throw createHttpError(404, new MissingStateParamError()); @@ -90,14 +94,14 @@ export default function callbackHandlerFactory( } const openidState: { returnTo?: string } = decodeState(expectedState as string) as ValidState; - let session = await sessionCache.fromTokenSet(tokenSet); + let session = sessionCache.fromTokenSet(tokenSet); if (options?.afterCallback) { session = await options.afterCallback(session, openidState); } if (session) { - await sessionCache.create(req, res, session); + await sessionCache.create(req.req, res.res, session); } res.redirect(openidState.returnTo || config.baseURL); diff --git a/src/auth0-session/handlers/logout.ts b/src/auth0-session/handlers/logout.ts index 56d7f0ba2..b4aafddd7 100644 --- a/src/auth0-session/handlers/logout.ts +++ b/src/auth0-session/handlers/logout.ts @@ -13,7 +13,7 @@ export type HandleLogout = (req: AbstractRequest, res: AbstractResponse, options export default function logoutHandlerFactory( config: Config, getClient: ClientFactory, - sessionCache: SessionCache + sessionCache: SessionCache ): HandleLogout { return async (req, res, options = {}) => { let returnURL = options.returnTo || config.routes.postLogoutRedirect; @@ -23,15 +23,15 @@ export default function logoutHandlerFactory( returnURL = urlJoin(config.baseURL, returnURL); } - const isAuthenticated = await sessionCache.isAuthenticated(req, res); + const isAuthenticated = await sessionCache.isAuthenticated(req.req, res.res); if (!isAuthenticated) { debug('end-user already logged out, redirecting to %s', returnURL); res.redirect(returnURL); return; } - const idToken = await sessionCache.getIdToken(req, res); - await sessionCache.delete(req, res); + const idToken = await sessionCache.getIdToken(req.req, res.res); + await sessionCache.delete(req.req, res.res); if (!config.idpLogout) { debug('performing a local only logout, redirecting to %s', returnURL); diff --git a/src/auth0-session/http/abstract-request.ts b/src/auth0-session/http/abstract-request.ts index 06079565d..7a4b6066d 100644 --- a/src/auth0-session/http/abstract-request.ts +++ b/src/auth0-session/http/abstract-request.ts @@ -1,15 +1,8 @@ export default abstract class AbstractRequest { - public url: string; - public method: string; - public body: Record; - protected constructor(protected req: Req) { - this.url = this.getUrl(); - this.method = this.getMethod(); - this.body = this.getBody(); - } + protected constructor(public req: Req) {} public abstract getUrl(): string; public abstract getMethod(): string; - public abstract getBody(): Record; + public abstract getBody(): Promise | string> | Record | string; public abstract getCookies(): Record; } diff --git a/src/auth0-session/http/abstract-response.ts b/src/auth0-session/http/abstract-response.ts index ef3b0fd83..4c0937f65 100644 --- a/src/auth0-session/http/abstract-response.ts +++ b/src/auth0-session/http/abstract-response.ts @@ -1,7 +1,7 @@ import { CookieSerializeOptions, serialize } from 'cookie'; export default abstract class AbstractResponse { - protected constructor(protected res: Res) {} + protected constructor(public res: Res) {} public setCookie(name: string, value: string, options: CookieSerializeOptions = {}): void { let previousCookies = this.getSetCookieHeader(); diff --git a/src/auth0-session/http/node-request.ts b/src/auth0-session/http/node-request.ts index 7f22b3546..9b93bfa14 100644 --- a/src/auth0-session/http/node-request.ts +++ b/src/auth0-session/http/node-request.ts @@ -3,7 +3,7 @@ import { parse } from 'cookie'; import AbstractRequest from './abstract-request'; export default class NodeRequest extends AbstractRequest { - public constructor(protected req: IncomingMessage) { + public constructor(public req: IncomingMessage) { super(req); } diff --git a/src/auth0-session/http/node-response.ts b/src/auth0-session/http/node-response.ts index 63d77344f..0c8996147 100644 --- a/src/auth0-session/http/node-response.ts +++ b/src/auth0-session/http/node-response.ts @@ -2,8 +2,8 @@ import { ServerResponse } from 'http'; import AbstractResponse from './abstract-response'; import { htmlSafe } from '../utils/errors'; -export default class NodeResponse extends AbstractResponse { - public constructor(protected res: ServerResponse) { +export default class NodeResponse extends AbstractResponse { + public constructor(public res: T) { super(res); } diff --git a/src/auth0-session/session-cache.ts b/src/auth0-session/session-cache.ts index a50ddcc41..d6670b3e9 100644 --- a/src/auth0-session/session-cache.ts +++ b/src/auth0-session/session-cache.ts @@ -1,7 +1,6 @@ import type { TokenSet } from 'openid-client'; -import { AbstractRequest, AbstractResponse } from './http'; -export interface SessionCache { +export interface SessionCache { create(req: Req, res: Res, session: Session): Promise; delete(req: Req, res: Res): Promise; isAuthenticated(req: Req, res: Res): Promise; diff --git a/src/auth0-session/session/stateless-session.ts b/src/auth0-session/session/stateless-session.ts index d28a00f2d..22c3c1076 100644 --- a/src/auth0-session/session/stateless-session.ts +++ b/src/auth0-session/session/stateless-session.ts @@ -137,7 +137,8 @@ export class StatelessSession< existingCookies.delete(sessionName); } - // When you're going from n + 1 chunks to n you need to delete the hanging cookies. + // When the number of chunks changes due to the cookie size changing, + // you need to delete any obsolete cookies. existingCookies.forEach((cookie) => res.clearCookie(cookie, cookieOptions)); } diff --git a/src/config.ts b/src/config.ts index 81eda46a8..b0387794a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -113,7 +113,7 @@ export interface BaseConfig { * ```js * { * ... - * getLoginState(req, options) { + * getLoginState(options) { * return { * returnTo: options.returnTo || req.originalUrl, * customState: 'foo' @@ -122,7 +122,7 @@ export interface BaseConfig { * } * ``` */ - getLoginState: (req: IncomingMessage, options: LoginOptions) => Record; + getLoginState: (options: LoginOptions) => Record; /** * Array value of claims to remove from the ID token before storing the cookie session. @@ -358,6 +358,25 @@ export interface NextConfig extends Pick { unauthorized: string; }; session: Pick; + + /** + * Function that returns an object with URL-safe state values for login. + * Used for passing custom state parameters to your authorization server. + * Can also be passed in to {@link HandleLogin}. + * + * ```js + * { + * ... + * getLoginState(req, options) { + * return { + * returnTo: options.returnTo || req.originalUrl, + * customState: 'foo' + * }; + * } + * } + * ``` + */ + getLoginState: (req: IncomingMessage, options: LoginOptions) => Record; } /** @@ -578,7 +597,7 @@ export const getConfig = (params: ConfigParameters = {}): { baseConfig: BaseConf clientAssertionSigningAlg: AUTH0_CLIENT_ASSERTION_SIGNING_ALG }); - const nextConfig = { + const nextConfig: NextConfig = { routes: { ...baseConfig.routes, login: baseParams.routes?.login || getLoginUrl(), @@ -586,7 +605,10 @@ export const getConfig = (params: ConfigParameters = {}): { baseConfig: BaseConf }, identityClaimFilter: baseConfig.identityClaimFilter, organization: organization || AUTH0_ORGANIZATION, - session: { storeIDToken: baseConfig.session.storeIDToken } + session: { storeIDToken: baseConfig.session.storeIDToken }, + getLoginState(_req, options) { + return { returnTo: options.returnTo }; + } }; return { baseConfig, nextConfig }; diff --git a/src/edge.ts b/src/edge.ts index 8b73b246c..3ef179279 100644 --- a/src/edge.ts +++ b/src/edge.ts @@ -1,7 +1,6 @@ import { NextMiddleware, NextRequest, NextResponse } from 'next/server'; import { StatelessSession } from './auth0-session/session/stateless-session'; import { StatefulSession } from './auth0-session/session/stateful-session'; -import MiddlewareCookies from './utils/middleware-cookies'; import Session from './session/session'; import SessionCache from './session/cache'; import { @@ -48,8 +47,8 @@ const _initAuth0: InitAuth0 = (params?) => { // Init base layer (with base config) const sessionStore = baseConfig.session.store - ? new StatefulSession(baseConfig, MiddlewareCookies) - : new StatelessSession(baseConfig, MiddlewareCookies); + ? new StatefulSession(baseConfig) + : new StatelessSession(baseConfig); const sessionCache = new SessionCache(baseConfig, sessionStore); // Init Next layer (with next config) diff --git a/src/handlers/callback.ts b/src/handlers/callback.ts index 9bf41240d..12a14ef7d 100644 --- a/src/handlers/callback.ts +++ b/src/handlers/callback.ts @@ -1,11 +1,16 @@ import { IncomingMessage } from 'http'; import { strict as assert } from 'assert'; import { NextApiResponse, NextApiRequest } from 'next'; -import { AuthorizationParameters, HandleCallback as BaseHandleCallback } from '../auth0-session'; +import { + AuthorizationParameters, + HandleCallback as BaseHandleCallback, + AfterCallback as BaseAfterCallback +} from '../auth0-session'; import { Session } from '../session'; import { assertReqRes } from '../utils/assert'; import { NextConfig } from '../config'; import { CallbackHandlerError, HandlerErrorCause } from '../utils/errors'; +import { Auth0NextApiRequest, Auth0NextApiResponse } from '../http'; /** * Use this function for validating additional claims on the user's ID token or adding removing items from @@ -196,35 +201,32 @@ export type HandleCallback = { */ export type CallbackHandler = (req: NextApiRequest, res: NextApiResponse, options?: CallbackOptions) => Promise; -/** - * @ignore - */ -const idTokenValidator = - (afterCallback?: AfterCallback, organization?: string): AfterCallback => - (req, res, session, state) => { - if (organization) { - assert(session.user.org_id, 'Organization Id (org_id) claim must be a string present in the ID token'); - assert.equal( - session.user.org_id, - organization, - `Organization Id (org_id) claim value mismatch in the ID token; ` + - `expected "${organization}", found "${session.user.org_id}"` - ); - } - if (afterCallback) { - return afterCallback(req, res, session, state); - } - return session; - }; - /** * @ignore */ export default function handleCallbackFactory(handler: BaseHandleCallback, config: NextConfig): HandleCallback { const callback: CallbackHandler = async (req: NextApiRequest, res: NextApiResponse, options = {}): Promise => { + const idTokenValidator = + (afterCallback?: AfterCallback, organization?: string): BaseAfterCallback => + (session, state) => { + if (organization) { + assert(session.user.org_id, 'Organization Id (org_id) claim must be a string present in the ID token'); + assert.equal( + session.user.org_id, + organization, + `Organization Id (org_id) claim value mismatch in the ID token; ` + + `expected "${organization}", found "${session.user.org_id}"` + ); + } + if (afterCallback) { + return afterCallback(req, res, session, state); + } + return session; + }; + try { assertReqRes(req, res); - return await handler(req, res, { + return await handler(new Auth0NextApiRequest(req), new Auth0NextApiResponse(res), { ...options, afterCallback: idTokenValidator(options.afterCallback, options.organization || config.organization) }); diff --git a/src/handlers/login.ts b/src/handlers/login.ts index 5d1bbc994..32be0b3a1 100644 --- a/src/handlers/login.ts +++ b/src/handlers/login.ts @@ -1,10 +1,15 @@ import { IncomingMessage } from 'http'; import { NextApiResponse, NextApiRequest } from 'next'; -import { AuthorizationParameters, HandleLogin as BaseHandleLogin } from '../auth0-session'; +import { + AuthorizationParameters, + HandleLogin as BaseHandleLogin, + LoginOptions as BaseLoginOptions +} from '../auth0-session'; import toSafeRedirect from '../utils/url-helpers'; import { assertReqRes } from '../utils/assert'; import { BaseConfig, NextConfig } from '../config'; import { HandlerErrorCause, LoginHandlerError } from '../utils/errors'; +import { Auth0NextApiRequest, Auth0NextApiResponse } from '../http'; /** * Use this to store additional state for the user before they visit the identity provider to log in. @@ -259,7 +264,15 @@ export default function handleLoginFactory( authorizationParams: { organization: nextConfig.organization, ...options.authorizationParams } }; } - return await handler(req, res, options); + + if (options.getLoginState) { + const fn = options.getLoginState; + (options as BaseLoginOptions).getLoginState = (opts) => { + return fn(req, opts as LoginOptions); + }; + } + + return await handler(new Auth0NextApiRequest(req), new Auth0NextApiResponse(res), options as BaseLoginOptions); } catch (e) { throw new LoginHandlerError(e as HandlerErrorCause); } diff --git a/src/handlers/logout.ts b/src/handlers/logout.ts index 309d905f4..20aab44ce 100644 --- a/src/handlers/logout.ts +++ b/src/handlers/logout.ts @@ -3,6 +3,7 @@ import { NextApiResponse, NextApiRequest } from 'next'; import { HandleLogout as BaseHandleLogout } from '../auth0-session'; import { assertReqRes } from '../utils/assert'; import { HandlerErrorCause, LogoutHandlerError } from '../utils/errors'; +import { Auth0NextApiRequest, Auth0NextApiResponse } from '../http'; /** * Options to customize the logout handler. @@ -104,7 +105,7 @@ export default function handleLogoutFactory(handler: BaseHandleLogout): HandleLo const logout: LogoutHandler = async (req: NextApiRequest, res: NextApiResponse, options = {}): Promise => { try { assertReqRes(req, res); - return await handler(req, res, options); + return await handler(new Auth0NextApiRequest(req), new Auth0NextApiResponse(res), options); } catch (e) { throw new LogoutHandlerError(e as HandlerErrorCause); } diff --git a/src/helpers/testing.ts b/src/helpers/testing.ts index 72a981fd2..400da27d1 100644 --- a/src/helpers/testing.ts +++ b/src/helpers/testing.ts @@ -1,4 +1,4 @@ -import { Config as BaseConfig, CookieConfig, StatelessSession, NodeCookies as Cookies } from '../auth0-session'; +import { Config as BaseConfig, CookieConfig, StatelessSession } from '../auth0-session'; import { Session } from '../session'; /** @@ -27,7 +27,7 @@ export const generateSessionCookie = async ( const weekInSeconds = 7 * 24 * 60 * 60; const { secret, duration: absoluteDuration = weekInSeconds, ...cookie } = config; const cookieStoreConfig = { secret, session: { absoluteDuration, cookie } }; - const cookieStore = new StatelessSession(cookieStoreConfig as BaseConfig, Cookies); + const cookieStore = new StatelessSession(cookieStoreConfig as BaseConfig); const epoch = (Date.now() / 1000) | 0; return cookieStore.encrypt(session, { iat: epoch, uat: epoch, exp: epoch + absoluteDuration }); }; diff --git a/src/helpers/with-middleware-auth-required.ts b/src/helpers/with-middleware-auth-required.ts index 80034ef68..2949ff3bb 100644 --- a/src/helpers/with-middleware-auth-required.ts +++ b/src/helpers/with-middleware-auth-required.ts @@ -1,4 +1,4 @@ -import { NextMiddleware, NextRequest, NextResponse } from 'next/server'; +import { NextMiddleware, NextResponse } from 'next/server'; import { SessionCache } from '../session'; /** @@ -50,7 +50,7 @@ export type WithMiddlewareAuthRequired = (middleware?: NextMiddleware) => NextMi */ export default function withMiddlewareAuthRequiredFactory( { login, callback, unauthorized }: { login: string; callback: string; unauthorized: string }, - getSessionCache: () => SessionCache + getSessionCache: () => SessionCache ): WithMiddlewareAuthRequired { return function withMiddlewareAuthRequired(middleware?): NextMiddleware { return async function wrappedMiddleware(...args) { @@ -80,6 +80,7 @@ export default function withMiddlewareAuthRequiredFactory( const cookies = headers.get('set-cookie')?.split(', ') || []; const authCookies = authRes.headers.get('set-cookie')?.split(', ') || []; if (cookies.length || authCookies.length) { + // TODO: Should Auth0NextResponse keep existing headers? headers.set('set-cookie', [...authCookies, ...cookies].join(', ')); } return NextResponse.next({ ...res, status: res.status, headers }); diff --git a/src/http/auth0-next-api-request.ts b/src/http/auth0-next-api-request.ts new file mode 100644 index 000000000..4b7825e46 --- /dev/null +++ b/src/http/auth0-next-api-request.ts @@ -0,0 +1,21 @@ +import { AbstractRequest } from '../auth0-session/http'; +import { NextApiRequest } from 'next'; + +export default class Auth0NextApiRequest extends AbstractRequest { + public constructor(req: NextApiRequest) { + super(req); + } + + public getUrl(): string { + return this.req.url as string; + } + public getMethod(): string { + return this.req.method as string; + } + public getBody(): Record { + return this.req.body; + } + public getCookies(): Record { + return this.req.cookies as Record; + } +} diff --git a/src/http/auth0-next-api-response.ts b/src/http/auth0-next-api-response.ts new file mode 100644 index 000000000..b031878e1 --- /dev/null +++ b/src/http/auth0-next-api-response.ts @@ -0,0 +1,11 @@ +import { NodeResponse } from '../auth0-session/http'; +import { NextApiResponse } from 'next'; + +export default class Auth0NextApiResponse extends NodeResponse { + public redirect(location: string, status = 302): void { + if (this.res.writableEnded) { + return; + } + this.res.redirect(status, (this.res.getHeader('Location') as string) || location); + } +} diff --git a/src/http/auth0-next-request.ts b/src/http/auth0-next-request.ts new file mode 100644 index 000000000..c18ea9ff6 --- /dev/null +++ b/src/http/auth0-next-request.ts @@ -0,0 +1,30 @@ +import { AbstractRequest } from '../auth0-session/http'; +import { NextRequest } from 'next/server'; + +export default class Auth0NextRequest extends AbstractRequest { + public constructor(req: NextRequest) { + super(req); + } + + public getUrl(): string { + return this.req.url as string; + } + public getMethod(): string { + return this.req.method as string; + } + public async getBody(): Promise | string> { + return this.req.text(); + } + public getCookies(): Record { + const { cookies } = this.req; + if (typeof cookies.getAll === 'function') { + return this.req.cookies.getAll().reduce((memo, { name, value }) => ({ ...memo, [name]: value }), {}); + } + // Edge cookies before Next 13.0.1 have no `getAll` and extend `Map`. + const legacyCookies = cookies as unknown as Map; + return Array.from(legacyCookies.keys()).reduce((memo: { [key: string]: string }, key) => { + memo[key] = legacyCookies.get(key) as string; + return memo; + }, {}); + } +} diff --git a/src/http/auth0-next-response.ts b/src/http/auth0-next-response.ts new file mode 100644 index 000000000..a1e8fa192 --- /dev/null +++ b/src/http/auth0-next-response.ts @@ -0,0 +1,19 @@ +import { AbstractResponse } from '../auth0-session/http'; +import { NextResponse } from 'next/server'; + +export default class Auth0NextResponse extends AbstractResponse { + public constructor(res: NextResponse) { + super(res); + } + protected getSetCookieHeader(): string[] { + const value = this.res.headers.get('set-cookie'); + return value?.split(', ') || []; + } + protected setSetCookieHeader(cookies: string[]): void { + this.res.headers.set('set-cookie', cookies.join(', ')); + } + public redirect(location: string, status = 302): void { + const headers = new Headers({ ...this.res.headers, location: this.res.headers.get('location') || location }); + this.res = NextResponse.next({ ...this.res, status, headers }); + } +} diff --git a/src/http/index.ts b/src/http/index.ts new file mode 100644 index 000000000..348d500c5 --- /dev/null +++ b/src/http/index.ts @@ -0,0 +1,4 @@ +export { default as Auth0NextApiRequest } from './auth0-next-api-request'; +export { default as Auth0NextApiResponse } from './auth0-next-api-response'; +export { default as Auth0NextRequest } from './auth0-next-request'; +export { default as Auth0NextResponse } from './auth0-next-response'; diff --git a/src/index.ts b/src/index.ts index 3b5001c98..db2d4b7ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,5 @@ import crypto from 'crypto'; import { - NodeCookies as Cookies, StatelessSession, StatefulSession, SessionStore as GenericSessionStore, @@ -59,8 +58,6 @@ import { import version from './version'; import { getConfig, getLoginUrl, ConfigParameters } from './config'; import { setIsUsingNamedExports, setIsUsingOwnInstance } from './utils/instance-check'; -import { IncomingMessage, ServerResponse } from 'http'; -import { NextApiRequest, NextApiResponse } from 'next'; /** * The SDK server instance. @@ -166,14 +163,8 @@ export const _initAuth = (params?: ConfigParameters): Auth0Server & { sessionCac const transientStore = new TransientStore(baseConfig); const sessionStore = baseConfig.session.store - ? new StatefulSession( - baseConfig, - Cookies - ) - : new StatelessSession( - baseConfig, - Cookies - ); + ? new StatefulSession(baseConfig) + : new StatelessSession(baseConfig); const sessionCache = new SessionCache(baseConfig, sessionStore); const baseHandleLogin = baseLoginHandler(baseConfig, getClient, transientStore); const baseHandleLogout = baseLogoutHandler(baseConfig, getClient, sessionCache); diff --git a/src/session/cache.ts b/src/session/cache.ts index f7b9e01a8..f65c4cb3c 100644 --- a/src/session/cache.ts +++ b/src/session/cache.ts @@ -1,25 +1,46 @@ import { IncomingMessage, ServerResponse } from 'http'; import { NextApiRequest, NextApiResponse } from 'next'; +import { NextRequest, NextResponse } from 'next/server'; import type { TokenSet } from 'openid-client'; import { Config, SessionCache as ISessionCache, AbstractSession } from '../auth0-session'; import Session, { fromJson, fromTokenSet } from './session'; +import { NodeRequest, NodeResponse } from '../auth0-session/http'; +import { Auth0NextApiRequest, Auth0NextApiResponse, Auth0NextRequest, Auth0NextResponse } from '../http'; -export default class SessionCache< - Req extends object = IncomingMessage | NextApiRequest, // eslint-disable-line @typescript-eslint/ban-types - Res extends object = ServerResponse | NextApiResponse // eslint-disable-line @typescript-eslint/ban-types -> implements ISessionCache -{ +type Req = IncomingMessage | NextRequest | NextApiRequest; +type Res = ServerResponse | NextResponse | NextApiResponse; +const getAuth0Req = (req: Req) => { + if (req instanceof Request) { + return new Auth0NextRequest(req); + } + if ('previewData' in req) { + return new Auth0NextApiRequest(req); + } + return new NodeRequest(req); +}; + +const getAuth0Res = (res: Res) => { + if (res instanceof Response) { + return new Auth0NextResponse(res); + } + if ('setPreviewData' in res) { + return new Auth0NextApiResponse(res); + } + return new NodeResponse(res); +}; + +export default class SessionCache implements ISessionCache { private cache: WeakMap; private iatCache: WeakMap; - constructor(private config: Config, private sessionStore: AbstractSession) { + constructor(private config: Config, private sessionStore: AbstractSession) { this.cache = new WeakMap(); this.iatCache = new WeakMap(); } private async init(req: Req, res: Res, autoSave = true): Promise { if (!this.cache.has(req)) { - const [json, iat] = await this.sessionStore.read(req); + const [json, iat] = await this.sessionStore.read(getAuth0Req(req)); this.iatCache.set(req, iat); this.cache.set(req, fromJson(json)); if (this.config.session.rolling && this.config.session.autoSave && autoSave) { @@ -29,7 +50,7 @@ export default class SessionCache< } async save(req: Req, res: Res): Promise { - await this.sessionStore.save(req, res, this.cache.get(req), this.iatCache.get(req)); + await this.sessionStore.save(getAuth0Req(req), getAuth0Res(res), this.cache.get(req), this.iatCache.get(req)); } async create(req: Req, res: Res, session: Session): Promise { diff --git a/src/session/get-access-token.ts b/src/session/get-access-token.ts index cf99fe83c..e8b2526a0 100644 --- a/src/session/get-access-token.ts +++ b/src/session/get-access-token.ts @@ -7,7 +7,11 @@ import { intersect, match } from '../utils/array'; import { Session, SessionCache, fromTokenSet } from '../session'; import { AuthorizationParameters, NextConfig } from '../config'; -export type AfterRefresh = (req: NextApiRequest, res: NextApiResponse, session: Session) => Promise | Session; +export type AfterRefresh = ( + req: NextApiRequest | IncomingMessage, + res: NextApiRequest | ServerResponse, + session: Session +) => Promise | Session; /** * Custom options to get an access token. @@ -184,7 +188,7 @@ export default function accessTokenFactory( }); if (accessTokenRequest?.afterRefresh) { - session = await accessTokenRequest.afterRefresh(req as NextApiRequest, res as NextApiResponse, session); + session = await accessTokenRequest.afterRefresh(req, res, session); } await sessionCache.set(req, res, session); diff --git a/src/session/get-session.ts b/src/session/get-session.ts index b813879ba..f119ee7b1 100644 --- a/src/session/get-session.ts +++ b/src/session/get-session.ts @@ -1,7 +1,6 @@ import { IncomingMessage, ServerResponse } from 'http'; import { NextApiRequest, NextApiResponse } from 'next'; import { SessionCache, Session } from '../session'; -import { assertReqRes } from '../utils/assert'; /** * Get the user's session from the request. @@ -18,7 +17,6 @@ export type GetSession = ( */ export default function sessionFactory(sessionCache: SessionCache): GetSession { return (req, res) => { - assertReqRes(req, res); return sessionCache.get(req, res); }; } diff --git a/src/utils/middleware-cookies.ts b/src/utils/middleware-cookies.ts deleted file mode 100644 index ba9a4c269..000000000 --- a/src/utils/middleware-cookies.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { AbstractCookies } from '../auth0-session/http/abstract-cookies'; -import { NextRequest, NextResponse } from 'next/server'; - -export default class MiddlewareCookies extends AbstractCookies { - protected getSetCookieHeader(res: NextResponse): string[] { - const value = res.headers.get('set-cookie'); - return value?.split(', ') || []; - } - - protected setSetCookieHeader(res: NextResponse, cookies: string[]): void { - res.headers.set('set-cookie', cookies.join(', ')); - } - - getAll(req: NextRequest): Record { - const { cookies } = req; - if (typeof cookies.getAll === 'function') { - return req.cookies.getAll().reduce((memo, { name, value }) => ({ ...memo, [name]: value }), {}); - } - // Edge cookies before Next 13.0.1 have no `getAll` and extend `Map`. - const legacyCookies = cookies as unknown as Map; - return Array.from(legacyCookies.keys()).reduce((memo: { [key: string]: string }, key) => { - memo[key] = legacyCookies.get(key) as string; - return memo; - }, {}); - } -} From 9ad9b863edbd177da99a6f56d4fe06f2c6f22bcf Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Mon, 22 May 2023 14:52:20 +0100 Subject: [PATCH 06/61] Fix tests --- src/auth0-session/http/abstract-response.ts | 4 +- src/auth0-session/http/node-response.ts | 4 +- .../session/stateless-session.ts | 10 +++ src/http/auth0-next-response.ts | 8 +- tests/auth0-session/fixtures/server.ts | 18 ++-- tests/config.test.ts | 1 + tests/handlers/auth.test.ts | 56 ++++++++----- tests/helpers/testing.test.ts | 11 +-- tests/http/auth0-next-request.test.ts | 31 +++++++ tests/http/auth0-next-response.test.ts | 44 ++++++++++ tests/session/cache.test.ts | 13 ++- tests/session/session.test.ts | 5 ++ tests/utils/middleware-cookies.test.ts | 82 ------------------- 13 files changed, 157 insertions(+), 130 deletions(-) create mode 100644 tests/http/auth0-next-request.test.ts create mode 100644 tests/http/auth0-next-response.test.ts delete mode 100644 tests/utils/middleware-cookies.test.ts diff --git a/src/auth0-session/http/abstract-response.ts b/src/auth0-session/http/abstract-response.ts index 4c0937f65..f082156cb 100644 --- a/src/auth0-session/http/abstract-response.ts +++ b/src/auth0-session/http/abstract-response.ts @@ -25,7 +25,7 @@ export default abstract class AbstractResponse { this.setCookie(name, '', clearOptions); } - protected abstract getSetCookieHeader(): string[]; - protected abstract setSetCookieHeader(cookies: string[]): void; + abstract getSetCookieHeader(): string[]; + abstract setSetCookieHeader(cookies: string[]): void; public abstract redirect(location: string, status?: number): void; } diff --git a/src/auth0-session/http/node-response.ts b/src/auth0-session/http/node-response.ts index 0c8996147..9eb07d645 100644 --- a/src/auth0-session/http/node-response.ts +++ b/src/auth0-session/http/node-response.ts @@ -7,7 +7,7 @@ export default class NodeResponse ext super(res); } - protected getSetCookieHeader(): string[] { + public getSetCookieHeader(): string[] { let cookies = this.res.getHeader('Set-Cookie') || []; if (!Array.isArray(cookies)) { cookies = [cookies as string]; @@ -15,7 +15,7 @@ export default class NodeResponse ext return cookies; } - protected setSetCookieHeader(cookies: string[]): void { + public setSetCookieHeader(cookies: string[]): void { this.res.setHeader('Set-Cookie', cookies); } diff --git a/src/auth0-session/session/stateless-session.ts b/src/auth0-session/session/stateless-session.ts index 22c3c1076..413a72779 100644 --- a/src/auth0-session/session/stateless-session.ts +++ b/src/auth0-session/session/stateless-session.ts @@ -114,6 +114,7 @@ export class StatelessSession< ): Promise { const { name: sessionName } = this.config.session; const cookies = req.getCookies(); + this.resetSetCookieHeader(res); debug('found session, creating signed session cookie(s) with name %o(.i)', sessionName); const value = await this.encrypt(session, { iat, uat, exp }); @@ -149,6 +150,7 @@ export class StatelessSession< ): Promise { const { name: sessionName } = this.config.session; const cookies = req.getCookies(); + this.resetSetCookieHeader(res); for (const cookieName of Object.keys(cookies)) { if (cookieName.match(`^${sessionName}(?:\\.\\d)?$`)) { @@ -156,4 +158,12 @@ export class StatelessSession< } } } + + // TODO: more tests on this + private resetSetCookieHeader(res: AbstractResponse) { + const { name: sessionName } = this.config.session; + let previousCookies = res.getSetCookieHeader().filter((cookie) => !cookie.match(`^${sessionName}(?:\\.\\d)?=`)); + + res.setSetCookieHeader([...previousCookies]); + } } diff --git a/src/http/auth0-next-response.ts b/src/http/auth0-next-response.ts index a1e8fa192..9c39c47cb 100644 --- a/src/http/auth0-next-response.ts +++ b/src/http/auth0-next-response.ts @@ -5,12 +5,14 @@ export default class Auth0NextResponse extends AbstractResponse { public constructor(res: NextResponse) { super(res); } - protected getSetCookieHeader(): string[] { + public getSetCookieHeader(): string[] { const value = this.res.headers.get('set-cookie'); return value?.split(', ') || []; } - protected setSetCookieHeader(cookies: string[]): void { - this.res.headers.set('set-cookie', cookies.join(', ')); + public setSetCookieHeader(cookies: string[]): void { + if (cookies.length) { + this.res.headers.set('set-cookie', cookies.join(', ')); + } } public redirect(location: string, status = 302): void { const headers = new Headers({ ...this.res.headers, location: this.res.headers.get('location') || location }); diff --git a/tests/auth0-session/fixtures/server.ts b/tests/auth0-session/fixtures/server.ts index 31b4531de..083294dcc 100644 --- a/tests/auth0-session/fixtures/server.ts +++ b/tests/auth0-session/fixtures/server.ts @@ -39,20 +39,20 @@ interface NodeCallbackOptions extends Omit { ) => Promise | any | undefined; } -class TestSessionCache implements SessionCache { +class TestSessionCache implements SessionCache { constructor(private cookieStore: AbstractSession) {} - async create(req: NodeRequest, res: NodeResponse, tokenSet: TokenSet): Promise { - await this.cookieStore.save(req, res, tokenSet); + async create(req: IncomingMessage, res: ServerResponse, tokenSet: TokenSet): Promise { + await this.cookieStore.save(new NodeRequest(req), new NodeResponse(res), tokenSet); } - async delete(req: NodeRequest, res: NodeResponse): Promise { - await this.cookieStore.save(req, res, null); + async delete(req: IncomingMessage, res: ServerResponse): Promise { + await this.cookieStore.save(new NodeRequest(req), new NodeResponse(res), null); } - async isAuthenticated(req: NodeRequest): Promise { - const [session] = await this.cookieStore.read(req); + async isAuthenticated(req: IncomingMessage): Promise { + const [session] = await this.cookieStore.read(new NodeRequest(req)); return !!session?.id_token; } - async getIdToken(req: NodeRequest): Promise { - const [session] = await this.cookieStore.read(req); + async getIdToken(req: IncomingMessage): Promise { + const [session] = await this.cookieStore.read(new NodeRequest(req)); return session?.id_token; } fromTokenSet(tokenSet: TokenSet): { [p: string]: any } { diff --git a/tests/config.test.ts b/tests/config.test.ts index ba8b6657b..de5fec7e4 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -96,6 +96,7 @@ describe('config params', () => { 'at_hash', 'c_hash' ], + getLoginState: expect.any(Function), routes: { login: '/api/auth/login', callback: '/api/auth/callback', diff --git a/tests/handlers/auth.test.ts b/tests/handlers/auth.test.ts index b867fcd2b..d57274115 100644 --- a/tests/handlers/auth.test.ts +++ b/tests/handlers/auth.test.ts @@ -153,8 +153,8 @@ describe('custom options', () => { afterEach(teardown); test('accept custom login options', async () => { - const loginHandler = jest.fn(async (_req: IncomingMessage, res: ServerResponse) => { - res.end(); + const loginHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); }); jest.spyOn(baseLoginHandler, 'default').mockImplementation(() => loginHandler); const options: LoginOptions = { authorizationParams: { scope: 'openid' } }; @@ -164,12 +164,16 @@ describe('custom options', () => { login: handleLogin(options) }); await get(baseUrl, '/api/auth/login'); - expect(loginHandler).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), options); + expect(loginHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); test('accept custom logout options', async () => { - const logoutHandler = jest.fn(async (_req: IncomingMessage, res: ServerResponse) => { - res.end(); + const logoutHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); }); jest.spyOn(baseLogoutHandler, 'default').mockImplementation(() => logoutHandler); const options: LogoutOptions = { returnTo: '/foo' }; @@ -179,12 +183,16 @@ describe('custom options', () => { logout: handleLogout(options) }); await get(baseUrl, '/api/auth/logout'); - expect(logoutHandler).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), options); + expect(logoutHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); test('accept custom callback options', async () => { - const callbackHandler = jest.fn(async (_req: IncomingMessage, res: ServerResponse) => { - res.end(); + const callbackHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); }); jest.spyOn(baseCallbackHandler, 'default').mockImplementation(() => callbackHandler); const options: CallbackOptions = { redirectUri: '/foo' }; @@ -195,8 +203,8 @@ describe('custom options', () => { }); await get(baseUrl, '/api/auth/callback'); expect(callbackHandler).toHaveBeenCalledWith( - expect.any(IncomingMessage), - expect.any(ServerResponse), + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), expect.objectContaining(options) ); }); @@ -219,8 +227,8 @@ describe('custom options providers', () => { afterEach(teardown); test('accept custom login options provider', async () => { - const loginHandler = jest.fn(async (_req: IncomingMessage, res: ServerResponse) => { - res.end(); + const loginHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); }); jest.spyOn(baseLoginHandler, 'default').mockImplementation(() => loginHandler); const options = { authorizationParams: { scope: 'openid' } }; @@ -233,12 +241,16 @@ describe('custom options providers', () => { }); await get(baseUrl, '/api/auth/login'); expect(optionsProvider).toHaveBeenCalled(); - expect(loginHandler).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), options); + expect(loginHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); test('accept custom logout options provider', async () => { - const logoutHandler = jest.fn(async (_req: IncomingMessage, res: ServerResponse) => { - res.end(); + const logoutHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); }); jest.spyOn(baseLogoutHandler, 'default').mockImplementation(() => logoutHandler); const options: LogoutOptions = { returnTo: '/foo' }; @@ -250,12 +262,16 @@ describe('custom options providers', () => { }); await get(baseUrl, '/api/auth/logout'); expect(optionsProvider).toHaveBeenCalled(); - expect(logoutHandler).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), options); + expect(logoutHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); test('accept custom callback options provider', async () => { - const callbackHandler = jest.fn(async (_req: IncomingMessage, res: ServerResponse) => { - res.end(); + const callbackHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); }); jest.spyOn(baseCallbackHandler, 'default').mockImplementation(() => callbackHandler); const options: CallbackOptions = { redirectUri: '/foo' }; @@ -268,8 +284,8 @@ describe('custom options providers', () => { await get(baseUrl, '/api/auth/callback'); expect(optionsProvider).toHaveBeenCalled(); expect(callbackHandler).toHaveBeenCalledWith( - expect.any(IncomingMessage), - expect.any(ServerResponse), + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), expect.objectContaining(options) ); }); diff --git a/tests/helpers/testing.test.ts b/tests/helpers/testing.test.ts index 0fb45ddfd..f88f841ef 100644 --- a/tests/helpers/testing.test.ts +++ b/tests/helpers/testing.test.ts @@ -9,10 +9,7 @@ const weekInSeconds = 7 * 24 * 60 * 60; describe('generate-session-cookie', () => { test('use the provided secret', async () => { await generateSessionCookie({}, { secret: '__test_secret__' }); - expect(CookieStore).toHaveBeenCalledWith( - expect.objectContaining({ secret: '__test_secret__' }), - expect.any(Function) - ); + expect(CookieStore).toHaveBeenCalledWith(expect.objectContaining({ secret: '__test_secret__' })); }); test('use the default session configuration values', async () => { @@ -20,8 +17,7 @@ describe('generate-session-cookie', () => { expect(CookieStore).toHaveBeenCalledWith( expect.objectContaining({ session: { absoluteDuration: weekInSeconds, cookie: {} } - }), - expect.any(Function) + }) ); }); @@ -52,8 +48,7 @@ describe('generate-session-cookie', () => { sameSite: 'none' } } - }), - expect.any(Function) + }) ); }); diff --git a/tests/http/auth0-next-request.test.ts b/tests/http/auth0-next-request.test.ts new file mode 100644 index 000000000..c88c5cf7e --- /dev/null +++ b/tests/http/auth0-next-request.test.ts @@ -0,0 +1,31 @@ +/** + * @jest-environment @edge-runtime/jest-environment + */ +import Auth0NextRequest from '../../src/http/auth0-next-request'; +import { NextRequest, NextResponse } from 'next/server'; + +const setup = (reqInit?: RequestInit): [NextRequest, NextResponse] => { + return [new NextRequest(new URL('http://example.com'), reqInit), NextResponse.next()]; +}; + +describe('cookie', () => { + it('should get all cookies', async () => { + const [req] = setup({ headers: { cookie: 'foo=bar; bar=baz;' } }); + expect(new Auth0NextRequest(req).getCookies()).toMatchObject({ foo: 'bar', bar: 'baz' }); + }); + + it('should get all cookies in Next < 13.0.1', async () => { + const req = { + cookies: new Map([ + ['foo', 'bar'], + ['bar', 'baz'] + ]) + } as unknown as NextRequest; + expect(new Auth0NextRequest(req).getCookies()).toMatchObject({ foo: 'bar', bar: 'baz' }); + }); + + it('should get a cookie by name', async () => { + const [req] = setup({ headers: { cookie: 'foo=bar; bar=baz;' } }); + expect(new Auth0NextRequest(req).getCookies()['foo']).toEqual('bar'); + }); +}); diff --git a/tests/http/auth0-next-response.test.ts b/tests/http/auth0-next-response.test.ts new file mode 100644 index 000000000..564660014 --- /dev/null +++ b/tests/http/auth0-next-response.test.ts @@ -0,0 +1,44 @@ +/** + * @jest-environment @edge-runtime/jest-environment + */ +import Auth0NextResponse from '../../src/http/auth0-next-response'; +import { NextRequest, NextResponse } from 'next/server'; + +const setup = (reqInit?: RequestInit): [NextRequest, NextResponse] => { + return [new NextRequest(new URL('http://example.com'), reqInit), NextResponse.next()]; +}; + +describe('cookie', () => { + it('should set a cookie', async () => { + const [, res] = setup(); + const auth0Res = new Auth0NextResponse(res); + auth0Res.setCookie('foo', 'bar'); + + expect(auth0Res.res.headers.get('set-cookie')).toEqual('foo=bar'); + }); + + it('should set a cookie with opts', async () => { + const [, res] = setup(); + const auth0Res = new Auth0NextResponse(res); + auth0Res.setCookie('foo', 'bar', { httpOnly: true, sameSite: 'strict' }); + + expect(auth0Res.res.headers.get('set-cookie')).toEqual('foo=bar; HttpOnly; SameSite=Strict'); + }); + + it('should not overwrite existing set cookie', async () => { + const [, res] = setup(); + res.headers.set('set-cookie', 'foo=bar'); + const auth0Res = new Auth0NextResponse(res); + auth0Res.setCookie('baz', 'qux'); + + expect(auth0Res.res.headers.get('set-cookie')).toEqual(['foo=bar', 'baz=qux'].join(', ')); + }); + + it('should clear cookies', async () => { + const [, res] = setup(); + const auth0Res = new Auth0NextResponse(res); + auth0Res.clearCookie('foo'); + + expect(auth0Res.res.headers.get('set-cookie')).toEqual('foo=; Max-Age=0'); + }); +}); diff --git a/tests/session/cache.test.ts b/tests/session/cache.test.ts index e5d7437c1..66d4f7d0d 100644 --- a/tests/session/cache.test.ts +++ b/tests/session/cache.test.ts @@ -1,7 +1,7 @@ import { IncomingMessage, ServerResponse } from 'http'; import { Socket } from 'net'; import { mocked } from 'ts-jest/utils'; -import { NodeCookies as Cookies, StatelessSession, getConfig } from '../../src/auth0-session'; +import { StatelessSession, getConfig } from '../../src/auth0-session'; import { ConfigParameters, Session, SessionCache } from '../../src'; import { withoutApi } from '../fixtures/default-settings'; @@ -10,11 +10,11 @@ describe('SessionCache', () => { let req: IncomingMessage; let res: ServerResponse; let session: Session; - let sessionStore: StatelessSession; + let sessionStore: StatelessSession; const setup = (conf: ConfigParameters) => { const config = getConfig(conf); - sessionStore = mocked(new StatelessSession(config, Cookies)); + sessionStore = mocked(new StatelessSession(config)); sessionStore.save = jest.fn(); session = new Session({ sub: '__test_user__' }); session.idToken = '__test_id_token__'; @@ -34,7 +34,12 @@ describe('SessionCache', () => { test('should create the session entry', async () => { await cache.create(req, res, session); expect(await cache.get(req, res)).toEqual(session); - expect(sessionStore.save).toHaveBeenCalledWith(req, res, session, undefined); + expect(sessionStore.save).toHaveBeenCalledWith( + expect.objectContaining({ req }), + expect.objectContaining({ res }), + session, + undefined + ); }); test('should delete the session entry', async () => { diff --git a/tests/session/session.test.ts b/tests/session/session.test.ts index fd2094acc..2f0ca0da5 100644 --- a/tests/session/session.test.ts +++ b/tests/session/session.test.ts @@ -5,6 +5,8 @@ import { Session } from '../../src'; const routes = { login: '', callback: '', postLogoutRedirect: '', unauthorized: '' }; +const getLoginState = () => Promise.resolve({}); + describe('session', () => { test('should construct a session with a user', async () => { expect(new Session({ foo: 'bar' }).user).toEqual({ foo: 'bar' }); @@ -16,6 +18,7 @@ describe('session', () => { fromTokenSet(new TokenSet({ id_token: await makeIdToken({ foo: 'bar', bax: 'qux' }) }), { identityClaimFilter: ['baz'], routes, + getLoginState, session: { storeIDToken: true } }).user ).toEqual({ @@ -36,6 +39,7 @@ describe('session', () => { fromTokenSet(new TokenSet({ id_token: await makeIdToken({ foo: 'bar' }) }), { identityClaimFilter: ['baz'], routes, + getLoginState, session: { storeIDToken: true } }).idToken ).toBeDefined(); @@ -52,6 +56,7 @@ describe('session', () => { absoluteDuration: 0, cookie: { transient: false, httpOnly: false, sameSite: 'lax' } }, + getLoginState, identityClaimFilter: ['baz'], routes }).idToken diff --git a/tests/utils/middleware-cookies.test.ts b/tests/utils/middleware-cookies.test.ts deleted file mode 100644 index 7ee1e01cb..000000000 --- a/tests/utils/middleware-cookies.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @jest-environment @edge-runtime/jest-environment - */ -import MiddlewareCookies from '../../src/utils/middleware-cookies'; -import { NextRequest, NextResponse } from 'next/server'; - -const setup = (reqInit?: RequestInit): [NextRequest, NextResponse] => { - return [new NextRequest(new URL('http://example.com'), reqInit), NextResponse.next()]; -}; - -describe('cookie', () => { - it('should get all cookies', async () => { - const [req] = setup({ headers: { cookie: 'foo=bar; bar=baz;' } }); - expect(new MiddlewareCookies().getAll(req)).toMatchObject({ foo: 'bar', bar: 'baz' }); - }); - - it('should get all cookies in Next < 13.0.1', async () => { - const req = { - cookies: new Map([ - ['foo', 'bar'], - ['bar', 'baz'] - ]) - } as unknown as NextRequest; - expect(new MiddlewareCookies().getAll(req)).toMatchObject({ foo: 'bar', bar: 'baz' }); - }); - - it('should get a cookie by name', async () => { - const [req] = setup({ headers: { cookie: 'foo=bar; bar=baz;' } }); - expect(new MiddlewareCookies().getAll(req)['foo']).toEqual('bar'); - }); - - it('should set a cookie', async () => { - const [, res] = setup(); - const setter = new MiddlewareCookies(); - setter.set('foo', 'bar'); - setter.commit(res); - expect(res.headers.get('set-cookie')).toEqual('foo=bar'); - }); - - it('should set a cookie with opts', async () => { - const [, res] = setup(); - const setter = new MiddlewareCookies(); - setter.set('foo', 'bar', { httpOnly: true, sameSite: 'strict' }); - setter.commit(res); - expect(res.headers.get('set-cookie')).toEqual('foo=bar; HttpOnly; SameSite=Strict'); - }); - - it('should not overwrite existing set cookie', async () => { - const [, res] = setup(); - res.headers.set('set-cookie', 'foo=bar'); - const setter = new MiddlewareCookies(); - setter.set('baz', 'qux'); - setter.commit(res); - expect(res.headers.get('set-cookie')).toEqual(['foo=bar', 'baz=qux'].join(', ')); - }); - - it('should override existing cookies that equal name', async () => { - const [, res] = setup(); - res.headers.set('set-cookie', ['foo=bar', 'baz=qux'].join(', ')); - const setter = new MiddlewareCookies(); - setter.set('foo', 'qux'); - setter.commit(res, 'foo'); - expect(res.headers.get('set-cookie')).toEqual(['baz=qux', 'foo=qux'].join(', ')); - }); - - it('should override existing cookies that match name', async () => { - const [, res] = setup(); - res.headers.set('set-cookie', ['foo.1=bar', 'foo.2=baz'].join(', ')); - const setter = new MiddlewareCookies(); - setter.set('foo', 'qux'); - setter.commit(res, 'foo'); - expect(res.headers.get('set-cookie')).toEqual('foo=qux'); - }); - - it('should clear cookies', async () => { - const [, res] = setup(); - const setter = new MiddlewareCookies(); - setter.clear('foo'); - setter.commit(res); - expect(res.headers.get('set-cookie')).toEqual('foo=; Max-Age=0'); - }); -}); From 3064a16b1eaf862a78be5647736c23ac0d04e4bb Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 23 May 2023 11:19:16 +0100 Subject: [PATCH 07/61] Add page router to example app --- example-app/app/globals.css | 145 ++++--------- example-app/app/layout.tsx | 28 +-- example-app/app/nav.tsx | 72 +++++++ example-app/app/page.module.css | 203 ------------------ example-app/app/page.tsx | 30 +-- example-app/components/header.tsx | 116 ++++++++++ example-app/components/layout.tsx | 34 +++ example-app/pages/_app.tsx | 13 ++ .../pages/api/page-router-auth/[...auth0].ts | 10 + example-app/pages/page-router/index.tsx | 11 + example-app/pages/page-router/profile-csr.tsx | 17 ++ .../pages/page-router/profile-middleware.tsx | 17 ++ example-app/pages/page-router/profile-ssr.tsx | 15 ++ 13 files changed, 370 insertions(+), 341 deletions(-) create mode 100644 example-app/app/nav.tsx delete mode 100644 example-app/app/page.module.css create mode 100644 example-app/components/header.tsx create mode 100644 example-app/components/layout.tsx create mode 100644 example-app/pages/_app.tsx create mode 100644 example-app/pages/api/page-router-auth/[...auth0].ts create mode 100644 example-app/pages/page-router/index.tsx create mode 100644 example-app/pages/page-router/profile-csr.tsx create mode 100644 example-app/pages/page-router/profile-middleware.tsx create mode 100644 example-app/pages/page-router/profile-ssr.tsx diff --git a/example-app/app/globals.css b/example-app/app/globals.css index d4f491e15..d9693f79f 100644 --- a/example-app/app/globals.css +++ b/example-app/app/globals.css @@ -1,107 +1,52 @@ -:root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', - 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', - 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; - - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; - - --primary-glow: conic-gradient( - from 180deg at 50% 50%, - #16abff33 0deg, - #0885ff33 55deg, - #54d6ff33 120deg, - #0071ff33 160deg, - transparent 360deg - ); - --secondary-glow: radial-gradient( - rgba(255, 255, 255, 1), - rgba(255, 255, 255, 0) - ); - - --tile-start-rgb: 239, 245, 249; - --tile-end-rgb: 228, 232, 233; - --tile-border: conic-gradient( - #00000080, - #00000040, - #00000030, - #00000020, - #00000010, - #00000010, - #00000080 - ); - - --callout-rgb: 238, 240, 241; - --callout-border-rgb: 172, 175, 176; - --card-rgb: 180, 185, 188; - --card-border-rgb: 131, 134, 135; +body { + margin: 0; + color: #333; + font-family: sans-serif; } - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - - --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); - --secondary-glow: linear-gradient( - to bottom right, - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0.3) - ); - - --tile-start-rgb: 2, 13, 46; - --tile-end-rgb: 2, 5, 19; - --tile-border: conic-gradient( - #ffffff80, - #ffffff40, - #ffffff30, - #ffffff20, - #ffffff10, - #ffffff10, - #ffffff80 - ); - - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; - } +.header { + padding: 0.2rem; + color: #fff; + background-color: #000; } - -* { - box-sizing: border-box; - padding: 0; - margin: 0; +.header.secondary { + background-color: #333; } - -html, -body { - max-width: 100vw; - overflow-x: hidden; +nav { + max-width: 62rem; + margin: 1.5rem auto; } - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); +ul { + display: flex; + list-style: none; + margin-left: 0; + padding-left: 0; +} +li { + margin-right: 1rem; +} +.header.secondary li:nth-last-child(2) { + margin-right: auto; +} +.header a { + color: #fff; + text-decoration: none; +} +.header.home a[href='/'], +.header.page-router a[href$='page-router'], +.header.profile-csr a[href$='profile-csr'], +.header.profile-ssr a[href$='profile-ssr'], +.header.profile-middleware a[href$='profile-middleware'], +a.active { + color: #888; } - -a { - color: inherit; - text-decoration: none; +button { + font-size: 1rem; + color: #fff; + cursor: pointer; + border: none; + background: none; } - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } +.container { + max-width: 62rem; + margin: 1.5rem auto; } diff --git a/example-app/app/layout.tsx b/example-app/app/layout.tsx index 71b3fbf05..01500e796 100644 --- a/example-app/app/layout.tsx +++ b/example-app/app/layout.tsx @@ -1,21 +1,23 @@ -import './globals.css' -import { Inter } from 'next/font/google' - -const inter = Inter({ subsets: ['latin'] }) +import './globals.css'; +import { UserProvider } from '@auth0/nextjs-auth0/client'; +import Nav from '@/app/nav'; export const metadata = { title: 'Create Next App', - description: 'Generated by create next app', -} + description: 'Generated by create next app' +}; -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - {children} + + +
+
+
{children}
+ +
- ) + ); } diff --git a/example-app/app/nav.tsx b/example-app/app/nav.tsx new file mode 100644 index 000000000..5ed26ce1f --- /dev/null +++ b/example-app/app/nav.tsx @@ -0,0 +1,72 @@ +'use client'; + +import React from 'react'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { useUser } from '@auth0/nextjs-auth0/client'; + +export default function Nav() { + const { user } = useUser(); + const pathname = usePathname(); + const pageName = pathname?.split('/').pop(); + + return ( + <> +
+ +
+
+ +
+ + ); +} diff --git a/example-app/app/page.module.css b/example-app/app/page.module.css deleted file mode 100644 index 93051def8..000000000 --- a/example-app/app/page.module.css +++ /dev/null @@ -1,203 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - width: var(--max-width); - max-width: 100%; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} diff --git a/example-app/app/page.tsx b/example-app/app/page.tsx index b630f5f95..0301a1886 100644 --- a/example-app/app/page.tsx +++ b/example-app/app/page.tsx @@ -1,30 +1,10 @@ -import Image from 'next/image' -import styles from './page.module.css' +import Image from 'next/image'; +import React from 'react'; export default function Home() { return ( -
-
-

- Get started by editing  - app/page.tsx -

-
- Login -
-
- -
- Next.js Logo -
- +
+

App router directory

- ) + ); } diff --git a/example-app/components/header.tsx b/example-app/components/header.tsx new file mode 100644 index 000000000..2ba3cf3e6 --- /dev/null +++ b/example-app/components/header.tsx @@ -0,0 +1,116 @@ +import React, { PropsWithChildren } from 'react'; +import Link from 'next/link'; +import { useUser } from '@auth0/nextjs-auth0/client'; +import { useRouter } from 'next/router'; + +const Header = (): React.ReactElement => { + const { user } = useUser(); + const { pathname } = useRouter(); + const pageName = pathname.split('/').pop(); + + return ( + <> +
+ +
+
+ +
+ + + ); +}; + +export default Header; diff --git a/example-app/components/layout.tsx b/example-app/components/layout.tsx new file mode 100644 index 000000000..2bff41be3 --- /dev/null +++ b/example-app/components/layout.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Head from 'next/head'; + +import Header from './header'; + +const Layout = ({ children }: React.PropsWithChildren): React.ReactElement => ( + <> + + Next.js with Auth0 + + +
+ +
+
{children}
+
+ + + + +); + +export default Layout; diff --git a/example-app/pages/_app.tsx b/example-app/pages/_app.tsx new file mode 100644 index 000000000..4d8b46633 --- /dev/null +++ b/example-app/pages/_app.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import type { AppProps } from 'next/app'; +import { UserProvider } from '@auth0/nextjs-auth0/client'; + +export default function App({ Component, pageProps }: AppProps): React.ReactElement { + const { user } = pageProps; + + return ( + + + + ); +} diff --git a/example-app/pages/api/page-router-auth/[...auth0].ts b/example-app/pages/api/page-router-auth/[...auth0].ts new file mode 100644 index 000000000..8ed6e6151 --- /dev/null +++ b/example-app/pages/api/page-router-auth/[...auth0].ts @@ -0,0 +1,10 @@ +import { handleAuth, handleLogin, handleCallback } from '@auth0/nextjs-auth0'; + +export default handleAuth({ + login: handleLogin({ + authorizationParams: { redirect_uri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` } + }), + callback: handleCallback({ + authorizationParams: { redirect_uri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` } + }) +}); diff --git a/example-app/pages/page-router/index.tsx b/example-app/pages/page-router/index.tsx new file mode 100644 index 000000000..f0d9d6852 --- /dev/null +++ b/example-app/pages/page-router/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import Layout from '@/components/layout'; + +export default function Index() { + return ( + +

Legacy page router directory

+
+ ); +} diff --git a/example-app/pages/page-router/profile-csr.tsx b/example-app/pages/page-router/profile-csr.tsx new file mode 100644 index 000000000..d6522b41c --- /dev/null +++ b/example-app/pages/page-router/profile-csr.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { useUser, withPageAuthRequired } from '@auth0/nextjs-auth0/client'; + +import Layout from '@/components/layout'; + +export default withPageAuthRequired(function Profile() { + const { user, isLoading } = useUser(); + if (isLoading) { + return

Loading...

; + } + return ( + +

Profile (client rendered)

+
{JSON.stringify(user, null, 2)}
+
+ ); +}); diff --git a/example-app/pages/page-router/profile-middleware.tsx b/example-app/pages/page-router/profile-middleware.tsx new file mode 100644 index 000000000..c45aa121e --- /dev/null +++ b/example-app/pages/page-router/profile-middleware.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { useUser } from '@auth0/nextjs-auth0/client'; + +import Layout from '@/components/layout'; + +export default function Profile() { + const { user, isLoading } = useUser(); + if (isLoading) { + return

Loading...

; + } + return ( + +

Profile (client rendered / protected by Middleware)

+
{JSON.stringify(user, null, 2)}
+
+ ); +} diff --git a/example-app/pages/page-router/profile-ssr.tsx b/example-app/pages/page-router/profile-ssr.tsx new file mode 100644 index 000000000..1a3aae532 --- /dev/null +++ b/example-app/pages/page-router/profile-ssr.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { InferGetServerSidePropsType } from 'next'; +import { withPageAuthRequired } from '@auth0/nextjs-auth0'; +import Layout from '@/components/layout'; + +export default function Profile({ user }: InferGetServerSidePropsType): React.ReactElement { + return ( + +

Profile (server rendered)

+
{JSON.stringify(user, null, 2)}
+
+ ); +} + +export const getServerSideProps = withPageAuthRequired(); From 4b74f33a6d4ed1c7df566c7cfdae811057cc9098 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 23 May 2023 12:11:45 +0100 Subject: [PATCH 08/61] Add page router to example app and fix e2e tests --- cypress/e2e/smoke.cy.ts | 25 +++++++++---------- example-app/components/header.tsx | 6 ++++- example-app/lib/auth0.ts | 10 ++++++++ example-app/middleware.ts | 8 +++--- example-app/package.json | 2 ++ .../pages/api/page-router-auth/[...auth0].ts | 10 ++++---- example-app/pages/page-router/profile-ssr.tsx | 4 +-- scripts/oidc-provider.js | 4 +-- 8 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 example-app/lib/auth0.ts diff --git a/cypress/e2e/smoke.cy.ts b/cypress/e2e/smoke.cy.ts index 427140ca6..b978211cf 100644 --- a/cypress/e2e/smoke.cy.ts +++ b/cypress/e2e/smoke.cy.ts @@ -21,47 +21,46 @@ const loginToNodeOidc = () => { const login = useAuth0 ? loginToAuth0 : loginToNodeOidc; -describe('smoke tests', () => { +describe('smoke tests page-router', () => { it('should do basic login and show user', () => { - cy.visit('/'); + cy.visit('/page-router'); cy.get('[data-testid=login]').click(); login(); - cy.url().should('eq', `${Cypress.config().baseUrl}/`); - cy.get('[data-testid=profile]').contains(EMAIL); + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router`); cy.get('[data-testid=logout]').should('exist'); }); it('should protect a client-side rendered page', () => { - cy.visit('/profile'); + cy.visit('/page-router/profile-csr'); login(); - cy.url().should('eq', `${Cypress.config().baseUrl}/profile`); + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router/profile-csr`); cy.get('[data-testid=profile]').contains(EMAIL); }); it('should protect a server-side rendered page', () => { - cy.visit('/profile-ssr'); + cy.visit('/page-router/profile-ssr'); login(); - cy.url().should('eq', `${Cypress.config().baseUrl}/profile-ssr`); + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router/profile-ssr`); cy.get('[data-testid=profile]').contains(EMAIL); }); it('should protect a page with middleware', () => { - cy.visit('/profile-mw'); + cy.visit('/page-router/profile-middleware'); login(); - cy.url().should('eq', `${Cypress.config().baseUrl}/profile-mw`); + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router/profile-middleware`); cy.get('[data-testid=profile]').contains(EMAIL); }); it('should logout and return to the index page', () => { - cy.visit('/profile'); + cy.visit('/page-router/profile-csr'); login(); - cy.url().should('eq', `${Cypress.config().baseUrl}/profile`); + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router/profile-csr`); cy.get('[data-testid=logout]').click(); if (!useAuth0) { cy.get('[name=logout]').click(); } - cy.url().should('eq', `${Cypress.config().baseUrl}/`); + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router`); cy.get('[data-testid=login]').should('exist'); }); }); diff --git a/example-app/components/header.tsx b/example-app/components/header.tsx index 2ba3cf3e6..c781d7a63 100644 --- a/example-app/components/header.tsx +++ b/example-app/components/header.tsx @@ -57,7 +57,7 @@ const Header = (): React.ReactElement => { ) : (
  • - + Login
  • @@ -101,6 +101,10 @@ const Header = (): React.ReactElement => { a.active { color: #888; } + .header.page-router a[data-testid='login'], + .header.page-router a[data-testid='logout'] { + color: #fff; + } button { font-size: 1rem; color: #fff; diff --git a/example-app/lib/auth0.ts b/example-app/lib/auth0.ts new file mode 100644 index 000000000..85a5217d6 --- /dev/null +++ b/example-app/lib/auth0.ts @@ -0,0 +1,10 @@ +import { initAuth0 } from '@auth0/nextjs-auth0'; + +export const pageRouterAuth = initAuth0({ + auth0Logout: process.env.USE_AUTH0 ? true : false, + routes: { + login: '/api/page-router-auth/login', + callback: '/api/page-router-auth/callback', + postLogoutRedirect: '/page-router' + } +}); diff --git a/example-app/middleware.ts b/example-app/middleware.ts index fb2930b0b..a4127711e 100644 --- a/example-app/middleware.ts +++ b/example-app/middleware.ts @@ -1,7 +1,9 @@ -import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge'; +import { initAuth0 } from '@auth0/nextjs-auth0/edge'; -export default withMiddlewareAuthRequired(); +const auth0 = initAuth0({ routes: { login: '/api/page-router-auth/login' } }); + +export default auth0.withMiddlewareAuthRequired(); export const config = { - matcher: ['/profile-mw', '/api/hello-world-mw'] + matcher: ['/page-router/profile-middleware'] }; diff --git a/example-app/package.json b/example-app/package.json index 729cf3284..55e6c1eff 100644 --- a/example-app/package.json +++ b/example-app/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "dev": "next dev", + "dev:local": "node server.js", "build": "next build", "start": "next start", "lint": "next lint" @@ -16,6 +17,7 @@ "@types/react-dom": "file:../node_modules/@types/react-dom", "eslint": "8.40.0", "eslint-config-next": "13.4.1", + "express": "^4.18.2", "next": "file:../node_modules/next", "react": "file:../node_modules/react", "react-dom": "file:../node_modules/react-dom", diff --git a/example-app/pages/api/page-router-auth/[...auth0].ts b/example-app/pages/api/page-router-auth/[...auth0].ts index 8ed6e6151..4eaea34f3 100644 --- a/example-app/pages/api/page-router-auth/[...auth0].ts +++ b/example-app/pages/api/page-router-auth/[...auth0].ts @@ -1,10 +1,10 @@ -import { handleAuth, handleLogin, handleCallback } from '@auth0/nextjs-auth0'; +import { pageRouterAuth } from '../../../lib/auth0'; -export default handleAuth({ - login: handleLogin({ +export default pageRouterAuth.handleAuth({ + login: pageRouterAuth.handleLogin({ authorizationParams: { redirect_uri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` } }), - callback: handleCallback({ - authorizationParams: { redirect_uri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` } + callback: pageRouterAuth.handleCallback({ + redirectUri: `${process.env.AUTH0_BASE_URL}/api/page-router-auth/callback` }) }); diff --git a/example-app/pages/page-router/profile-ssr.tsx b/example-app/pages/page-router/profile-ssr.tsx index 1a3aae532..47d387fe9 100644 --- a/example-app/pages/page-router/profile-ssr.tsx +++ b/example-app/pages/page-router/profile-ssr.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { InferGetServerSidePropsType } from 'next'; -import { withPageAuthRequired } from '@auth0/nextjs-auth0'; +import { pageRouterAuth } from '../../lib/auth0'; import Layout from '@/components/layout'; export default function Profile({ user }: InferGetServerSidePropsType): React.ReactElement { @@ -12,4 +12,4 @@ export default function Profile({ user }: InferGetServerSidePropsType Date: Wed, 24 May 2023 11:46:19 +0100 Subject: [PATCH 09/61] Support app router in auth routes --- example-app/app/api/auth/[auth0]/route.ts | 3 +- example-app/app/layout.tsx | 2 +- example-app/next.config.js | 6 +- example-app/package.json | 2 +- package-lock.json | 4 +- src/handlers/auth.ts | 99 ++++++++++++---- src/handlers/callback.ts | 136 +++++++++++++++------- src/handlers/index.ts | 18 ++- src/handlers/login.ts | 136 ++++++++++++++++------ src/handlers/logout.ts | 64 +++++++--- src/handlers/profile.ts | 102 +++++++++++++--- src/http/auth0-next-response.ts | 5 +- src/index.ts | 4 + src/session/cache.ts | 8 +- tests/handlers/auth.test.ts | 18 +-- tests/handlers/callback.test.ts | 11 +- tests/handlers/profile.test.ts | 8 +- tests/session/get-access-token.test.ts | 17 +-- 18 files changed, 467 insertions(+), 176 deletions(-) diff --git a/example-app/app/api/auth/[auth0]/route.ts b/example-app/app/api/auth/[auth0]/route.ts index e46a7cafc..eafedf0d0 100644 --- a/example-app/app/api/auth/[auth0]/route.ts +++ b/example-app/app/api/auth/[auth0]/route.ts @@ -1,8 +1,7 @@ import { handleAuth } from '@auth0/nextjs-auth0'; export const GET = handleAuth({ - onError(req, res, error) { + onError(req: Request, error: Error) { console.error(error); - res.status(error.status || 500).end('Check the console for the error'); } }); diff --git a/example-app/app/layout.tsx b/example-app/app/layout.tsx index 01500e796..05a99a41c 100644 --- a/example-app/app/layout.tsx +++ b/example-app/app/layout.tsx @@ -10,7 +10,7 @@ export const metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - +
    ); -} +}); diff --git a/example-app/components/header.tsx b/example-app/components/header.tsx index 0c0c8c49a..988a5cac2 100644 --- a/example-app/components/header.tsx +++ b/example-app/components/header.tsx @@ -44,6 +44,11 @@ const Header = (): React.ReactElement => { Profile (SSR) {' '} +
  • + + Profile (API) + +
  • {' '}
  • Profile (Middleware) @@ -97,6 +102,7 @@ const Header = (): React.ReactElement => { .header.page-router a[href$='page-router'], .header.profile-csr a[href$='profile-csr'], .header.profile-ssr a[href$='profile-ssr'], + .header.profile-api a[href$='profile-api'], .header.profile-middleware a[href$='profile-middleware'], a.active { color: #888; diff --git a/example-app/lib/auth0.ts b/example-app/lib/auth0.ts index 85a5217d6..c4c70920b 100644 --- a/example-app/lib/auth0.ts +++ b/example-app/lib/auth0.ts @@ -1,6 +1,6 @@ -import { initAuth0 } from '@auth0/nextjs-auth0'; +import { initAuth0, Auth0Server } from '@auth0/nextjs-auth0'; -export const pageRouterAuth = initAuth0({ +export const pageRouterAuth: Auth0Server = initAuth0({ auth0Logout: process.env.USE_AUTH0 ? true : false, routes: { login: '/api/page-router-auth/login', diff --git a/example-app/pages/api/page-router-profile.ts b/example-app/pages/api/page-router-profile.ts new file mode 100644 index 000000000..461f69f6d --- /dev/null +++ b/example-app/pages/api/page-router-profile.ts @@ -0,0 +1,8 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { pageRouterAuth } from '../../lib/auth0'; + +export default pageRouterAuth.withApiAuthRequired(async function profile(req: NextApiRequest, res: NextApiResponse) { + const session = await pageRouterAuth.getSession(req, res); + + res.status(200).json(session?.user); +}); diff --git a/example-app/pages/page-router/index.tsx b/example-app/pages/page-router/index.tsx index f0d9d6852..d9570837e 100644 --- a/example-app/pages/page-router/index.tsx +++ b/example-app/pages/page-router/index.tsx @@ -5,7 +5,7 @@ import Layout from '@/components/layout'; export default function Index() { return ( -

    Legacy page router directory

    +

    Page router directory

    ); } diff --git a/example-app/pages/page-router/profile-api.tsx b/example-app/pages/page-router/profile-api.tsx new file mode 100644 index 000000000..b92d6defe --- /dev/null +++ b/example-app/pages/page-router/profile-api.tsx @@ -0,0 +1,22 @@ +import React, { useEffect, useState } from 'react'; +import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'; + +import Layout from '@/components/layout'; + +export default withPageAuthRequired(function ProfileApi() { + const [user, setUser] = useState(); + + useEffect(() => { + (async () => { + const res = await fetch(`${window.location.origin}/api/page-router-profile`); + setUser(await res.json()); + })(); + }, []); + + return ( + +

    Profile (fetched from API)

    +
    {JSON.stringify(user, null, 2)}
    +
    + ); +}); diff --git a/src/helpers/with-api-auth-required.ts b/src/helpers/with-api-auth-required.ts index 76597cfb7..f816de7e5 100644 --- a/src/helpers/with-api-auth-required.ts +++ b/src/helpers/with-api-auth-required.ts @@ -1,7 +1,27 @@ import { NextApiResponse, NextApiRequest, NextApiHandler } from 'next'; -import { SessionCache } from '../session'; +import { NextRequest, NextResponse } from 'next/server'; +import { get, SessionCache } from '../session'; import { assertReqRes } from '../utils/assert'; +export type AppRouteHandlerFnContext = { + params?: Record; +}; + +/** + * Handler function for app routes. + */ +export type AppRouteHandlerFn = ( + /** + * Incoming request object. + */ + req: NextRequest, + /** + * Context properties on the request (including the parameters if this was a + * dynamic route). + */ + ctx: AppRouteHandlerFnContext +) => Promise | Response; + /** * Wrap an API route to check that the user has a valid session. If they're not logged in the * handler will return a 401 Unauthorized. @@ -20,25 +40,57 @@ import { assertReqRes } from '../utils/assert'; * * @category Server */ -export type WithApiAuthRequired = (apiRoute: NextApiHandler) => NextApiHandler; +export type WithApiAuthRequiredAppRoute = (apiRoute: AppRouteHandlerFn) => AppRouteHandlerFn; +export type WithApiAuthRequiredPageRoute = (apiRoute: NextApiHandler) => NextApiHandler; +export type WithApiAuthRequired = WithApiAuthRequiredAppRoute & WithApiAuthRequiredPageRoute; /** * @ignore */ export default function withApiAuthFactory(sessionCache: SessionCache): WithApiAuthRequired { - return (apiRoute) => - async (req: NextApiRequest, res: NextApiResponse): Promise => { - assertReqRes(req, res); - - const session = await sessionCache.get(req, res); - if (!session || !session.user) { - res.status(401).json({ - error: 'not_authenticated', - description: 'The user does not have an active session or is not authenticated' - }); - return; - } + const pageRouteHandler = pageRouteHandlerFactory(sessionCache); + const appRouteHandler = appRouteHandlerFactory(sessionCache); - await apiRoute(req, res); + return (apiRoute: AppRouteHandlerFn | NextApiHandler): any => + (req: NextRequest | NextApiRequest, resOrParams: AppRouteHandlerFnContext | NextApiResponse) => { + if (req instanceof Request) { + return appRouteHandler(apiRoute as AppRouteHandlerFn)(req, resOrParams as AppRouteHandlerFnContext); + } + return (pageRouteHandler as WithApiAuthRequiredPageRoute)(apiRoute as NextApiHandler)( + req as NextApiRequest, + resOrParams as NextApiResponse + ); }; } + +const appRouteHandlerFactory = + (sessionCache: SessionCache): WithApiAuthRequiredAppRoute => + (apiRoute) => + async (req, params): Promise => { + const [session] = await get({ sessionCache }); + if (!session || !session.user) { + return NextResponse.json( + { error: 'not_authenticated', description: 'The user does not have an active session or is not authenticated' }, + { status: 401 } + ); + } + return apiRoute(req, params); + }; + +const pageRouteHandlerFactory = + (sessionCache: SessionCache): WithApiAuthRequiredPageRoute => + (apiRoute) => + async (req, res) => { + assertReqRes(req, res); + + const session = await sessionCache.get(req, res); + if (!session || !session.user) { + res.status(401).json({ + error: 'not_authenticated', + description: 'The user does not have an active session or is not authenticated' + }); + return; + } + + await apiRoute(req, res); + }; diff --git a/src/helpers/with-page-auth-required.ts b/src/helpers/with-page-auth-required.ts index 69db1774d..007bec8d3 100644 --- a/src/helpers/with-page-auth-required.ts +++ b/src/helpers/with-page-auth-required.ts @@ -1,5 +1,7 @@ +import type React from 'react'; import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from 'next'; -import { Claims, SessionCache } from '../session'; +import { redirect } from 'next/navigation'; +import { Claims, get, SessionCache } from '../session'; import { assertCtx } from '../utils/assert'; import { ParsedUrlQuery } from 'querystring'; @@ -28,9 +30,17 @@ export type GetServerSidePropsResultWithSession

    = GetServerSidePropsRes * @category Server */ export type PageRoute = ( - cts: GetServerSidePropsContext + ctx: GetServerSidePropsContext ) => Promise>; +/** + * @category Server + */ +export type AppRouterPageRoute = (obj: { + params?: { slug: string }; + searchParams?: { [key: string]: string | string[] | undefined }; +}) => Promise; + /** * If you have a custom returnTo url you should specify it in `returnTo`. * @@ -90,13 +100,17 @@ export type WithPageAuthRequiredOptions< * * @category Server */ -export type WithPageAuthRequired = < +type WithPageAuthRequiredPageRouter = < P extends { [key: string]: any } = { [key: string]: any }, Q extends ParsedUrlQuery = ParsedUrlQuery >( opts?: WithPageAuthRequiredOptions ) => PageRoute; +type WithPageAuthRequiredAppRouter = (fn: AppRouterPageRoute, opts?: { returnTo?: string }) => AppRouterPageRoute; + +export type WithPageAuthRequired = WithPageAuthRequiredPageRouter & WithPageAuthRequiredAppRouter; + /** * @ignore */ @@ -104,26 +118,50 @@ export default function withPageAuthRequiredFactory( loginUrl: string, getSessionCache: () => SessionCache ): WithPageAuthRequired { - return ({ getServerSideProps, returnTo } = {}) => - async (ctx) => { - assertCtx(ctx); - const sessionCache = getSessionCache(); - const session = await sessionCache.get(ctx.req, ctx.res); - if (!session?.user) { - return { - redirect: { - destination: `${loginUrl}?returnTo=${encodeURIComponent(returnTo || ctx.resolvedUrl)}`, - permanent: false - } - }; - } - let ret: any = { props: {} }; - if (getServerSideProps) { - ret = await getServerSideProps(ctx); - } - if (ret.props instanceof Promise) { - return { ...ret, props: ret.props.then((props: any) => ({ user: session.user, ...props })) }; - } - return { ...ret, props: { user: session.user, ...ret.props } }; - }; + const appRouteHandler = appRouteHandlerFactory(loginUrl, getSessionCache); + const pageRouteHandler = pageRouteHandlerFactory(loginUrl, getSessionCache); + + return (fnOrOpts: any, opts?: any): any => { + if (typeof fnOrOpts === 'function') { + return appRouteHandler(fnOrOpts, opts); + } + return pageRouteHandler(fnOrOpts); + }; } + +const appRouteHandlerFactory = + (loginUrl: string, getSessionCache: () => SessionCache): WithPageAuthRequiredAppRouter => + (handler, opts = {}) => + async (params) => { + const sessionCache = getSessionCache(); + const [session] = await get({ sessionCache }); + if (!session?.user) { + redirect(`${loginUrl}${opts.returnTo ? `?returnTo=${opts.returnTo}` : ''}`); + } + return handler(params); + }; + +const pageRouteHandlerFactory = + (loginUrl: string, getSessionCache: () => SessionCache): WithPageAuthRequiredPageRouter => + ({ getServerSideProps, returnTo } = {}) => + async (ctx) => { + assertCtx(ctx); + const sessionCache = getSessionCache(); + const session = await sessionCache.get(ctx.req, ctx.res); + if (!session?.user) { + return { + redirect: { + destination: `${loginUrl}?returnTo=${encodeURIComponent(returnTo || ctx.resolvedUrl)}`, + permanent: false + } + }; + } + let ret: any = { props: {} }; + if (getServerSideProps) { + ret = await getServerSideProps(ctx); + } + if (ret.props instanceof Promise) { + return { ...ret, props: ret.props.then((props: any) => ({ user: session.user, ...props })) }; + } + return { ...ret, props: { user: session.user, ...ret.props } }; + }; diff --git a/src/index.ts b/src/index.ts index 1ef80452e..58b74d6a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -206,7 +206,8 @@ const getSessionCache = () => getInstance().sessionCache; export const getSession: GetSession = (...args) => getInstance().getSession(...args); export const updateSession: UpdateSession = (...args) => getInstance().updateSession(...args); export const getAccessToken: GetAccessToken = (...args) => getInstance().getAccessToken(...args); -export const withApiAuthRequired: WithApiAuthRequired = (...args) => getInstance().withApiAuthRequired(...args); +export const withApiAuthRequired: WithApiAuthRequired = (...args) => + (getInstance().withApiAuthRequired as any)(...args); export const withPageAuthRequired: WithPageAuthRequired = withPageAuthRequiredFactory(getLoginUrl(), getSessionCache); export const handleLogin: HandleLogin = ((...args: Parameters) => getInstance().handleLogin(...args)) as HandleLogin; From d1c2eed851e8a4f6a8df3218c539760d8689b5b8 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 1 Jun 2023 13:39:34 +0100 Subject: [PATCH 27/61] Add end to end tests for with(Page/Api)AuthRequired --- cypress/e2e/smoke.cy.ts | 34 ++++++++++++++++++++++++ example-app/app/profile-api/page.tsx | 5 ++-- example-app/app/profile/page.tsx | 39 +++++++++++++++------------- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/cypress/e2e/smoke.cy.ts b/cypress/e2e/smoke.cy.ts index 0f711b4b0..10a7afcc3 100644 --- a/cypress/e2e/smoke.cy.ts +++ b/cypress/e2e/smoke.cy.ts @@ -64,6 +64,23 @@ describe('smoke tests', () => { cy.url().should('eq', `${Cypress.config().baseUrl}/page-router`); cy.get('[data-testid=login]').should('exist'); }); + + it('should protect an api', () => { + cy.request({ url: '/api/page-router-profile', failOnStatusCode: false }).as('unauthorized'); + + cy.get('@unauthorized').should((response: any) => { + expect(response.status).to.eq(401); + expect(response.body.error).to.eq('not_authenticated'); + }); + }); + + it('should access an api', () => { + cy.visit('/page-router/profile-api'); + login(); + + cy.url().should('eq', `${Cypress.config().baseUrl}/page-router/profile-api`); + cy.get('[data-testid=profile-api]').contains(EMAIL); + }); }); describe('app router', () => { it('should render an app route', () => { @@ -73,5 +90,22 @@ describe('smoke tests', () => { cy.get('[data-testid=server-component]').contains(EMAIL); cy.get('[data-testid=client-component]').contains(EMAIL); }); + + it('should protect an api', () => { + cy.request({ url: '/api/profile', failOnStatusCode: false }).as('unauthorized'); + + cy.get('@unauthorized').should((response: any) => { + expect(response.status).to.eq(401); + expect(response.body.error).to.eq('not_authenticated'); + }); + }); + + it.only('should access an api', () => { + cy.visit('/profile-api'); + login(); + + cy.url().should('eq', `${Cypress.config().baseUrl}/profile-api`); + cy.get('[data-testid=profile-api]').contains(EMAIL); + }); }); }); diff --git a/example-app/app/profile-api/page.tsx b/example-app/app/profile-api/page.tsx index 4b813eb64..21c4d21e2 100644 --- a/example-app/app/profile-api/page.tsx +++ b/example-app/app/profile-api/page.tsx @@ -1,8 +1,9 @@ 'use client'; import React, { useState, useEffect } from 'react'; +import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'; -export default function ProfileApi() { +export default withPageAuthRequired(function ProfileApi() { const [user, setUser] = useState(); useEffect(() => { @@ -19,4 +20,4 @@ export default function ProfileApi() {

    {JSON.stringify(user, null, 2)}
    ); -} +}); diff --git a/example-app/app/profile/page.tsx b/example-app/app/profile/page.tsx index 3f9b09075..bbec98035 100644 --- a/example-app/app/profile/page.tsx +++ b/example-app/app/profile/page.tsx @@ -3,22 +3,25 @@ import { getSession, withPageAuthRequired } from '@auth0/nextjs-auth0'; import ServerComponent from '@/app/server-component'; import ClientComponent from '@/app/client-component'; -export default withPageAuthRequired(async function Page() { - const session = await getSession(); +export default withPageAuthRequired( + async function Page() { + const session = await getSession(); - return ( -
    -

    Profile

    -

    Page:

    -

    Access Token

    -
    {JSON.stringify({ accessToken: session?.accessToken }, null, 2)}
    -

    User

    -
    {JSON.stringify(session?.user, null, 2)}
    -

    Server Component:

    - {/*@ts-expect-error Async Server Component*/} - -

    Client Component:

    - -
    - ); -}); + return ( +
    +

    Profile

    +

    Page:

    +

    Access Token

    +
    {JSON.stringify({ accessToken: session?.accessToken }, null, 2)}
    +

    User

    +
    {JSON.stringify(session?.user, null, 2)}
    +

    Server Component:

    + {/*@ts-expect-error Async Server Component*/} + +

    Client Component:

    + +
    + ); + }, + { returnTo: '/profile' } +); From d27ac128b056e65fb445060e2fd7065af77c55c5 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 1 Jun 2023 18:25:53 +0100 Subject: [PATCH 28/61] Add app router tests for auth and callback --- src/auth0-session/http/node-request.ts | 1 + src/auth0-session/http/node-response.ts | 1 + src/handlers/auth.ts | 2 +- src/handlers/router-helpers.ts | 2 +- tests/auth0-session/fixtures/helpers.ts | 8 +- tests/fixtures/app-router-helpers.ts | 54 ++ tests/fixtures/setup.ts | 20 +- tests/handlers/auth.test.ts | 552 +++++++------- tests/handlers/callback.test.ts | 952 +++++++++++++++--------- 9 files changed, 983 insertions(+), 609 deletions(-) create mode 100644 tests/fixtures/app-router-helpers.ts diff --git a/src/auth0-session/http/node-request.ts b/src/auth0-session/http/node-request.ts index 30a72fafc..943667b5b 100644 --- a/src/auth0-session/http/node-request.ts +++ b/src/auth0-session/http/node-request.ts @@ -4,6 +4,7 @@ import Auth0Request from './auth0-request'; export default class NodeRequest extends Auth0Request { public constructor(public req: IncomingMessage) { + /* c8 ignore next */ super(req); } diff --git a/src/auth0-session/http/node-response.ts b/src/auth0-session/http/node-response.ts index 0d109d4a2..733866064 100644 --- a/src/auth0-session/http/node-response.ts +++ b/src/auth0-session/http/node-response.ts @@ -5,6 +5,7 @@ import { htmlSafe } from '../utils/errors'; export default class NodeResponse extends Auth0Response { public constructor(public res: T) { + /* c8 ignore next */ super(res); } diff --git a/src/handlers/auth.ts b/src/handlers/auth.ts index 830339126..c7d2b380f 100644 --- a/src/handlers/auth.ts +++ b/src/handlers/auth.ts @@ -173,7 +173,7 @@ export default function handlerFactory({ const appRouteHandlerFactory: (customHandlers: ApiHandlers, onError?: AppRouterOnError) => AppRouteHandlerFn = (customHandlers, onError) => async (req: NextRequest, ctx) => { const { params } = ctx; - let route = params?.auth0; + let route = params.auth0; if (Array.isArray(route)) { let otherRoutes; diff --git a/src/handlers/router-helpers.ts b/src/handlers/router-helpers.ts index 6e5a7541f..c4a9aff2c 100644 --- a/src/handlers/router-helpers.ts +++ b/src/handlers/router-helpers.ts @@ -2,7 +2,7 @@ import { NextRequest } from 'next/server'; import { NextApiRequest, NextApiResponse } from 'next'; export type AppRouteHandlerFnContext = { - params?: Record; + params: Record; }; /** diff --git a/tests/auth0-session/fixtures/helpers.ts b/tests/auth0-session/fixtures/helpers.ts index 34b0bd21c..a4f368675 100644 --- a/tests/auth0-session/fixtures/helpers.ts +++ b/tests/auth0-session/fixtures/helpers.ts @@ -17,11 +17,15 @@ export const defaultConfig: Omit = { } }; +export const signCookie = async (key: string, value: string) => { + const signingKey = await signing(secret); + return generateCookieValue(key, value, signingKey); +}; + export const toSignedCookieJar = async (cookies: { [key: string]: string }, url: string): Promise => { const cookieJar = new CookieJar(); - const signingKey = await signing(secret); for (const [key, value] of Object.entries(cookies)) { - cookieJar.setCookieSync(`${key}=${await generateCookieValue(key, value, signingKey)}`, url); + cookieJar.setCookieSync(`${key}=${await signCookie(key, value)}`, url); } return cookieJar; }; diff --git a/tests/fixtures/app-router-helpers.ts b/tests/fixtures/app-router-helpers.ts new file mode 100644 index 000000000..ec54e6e37 --- /dev/null +++ b/tests/fixtures/app-router-helpers.ts @@ -0,0 +1,54 @@ +import { CallbackOptions, Claims, ConfigParameters, initAuth0 } from '../../src'; +import { withApi } from './default-settings'; +import { setupNock } from './setup'; +import { NextRequest, NextResponse } from 'next/server'; +import { StatelessSession } from '../../src/auth0-session'; +import { getConfig } from '../../src/config'; +import { Auth0NextRequest } from '../../src/http'; + +export const getResponse = async ({ + url, + config, + cookies, + idTokenClaims, + callbackOpts, + extraHandlers +}: { + url: string; + config?: ConfigParameters; + cookies?: { [key: string]: string }; + idTokenClaims?: Claims; + callbackOpts?: CallbackOptions; + extraHandlers?: any; +}) => { + const opts = { ...withApi, ...config }; + await setupNock(opts, { idTokenClaims }); + const auth0 = url.split('?')[0].split('/').slice(3); + const instance = initAuth0(opts); + const handleAuth = instance.handleAuth({ + ...(callbackOpts && { callback: instance.handleCallback(callbackOpts) }), + onError(_req: any, error: any) { + return new Response(null, { status: error.status || 500, statusText: error.message }); + }, + ...extraHandlers + }); + let headers = new Headers(); + if (cookies) { + headers.set( + 'Cookie', + Object.entries(cookies) + .map(([k, v]) => `${k}=${v}`) + .join('; ') + ); + } + return handleAuth(new NextRequest(new URL(url, opts.baseURL), { headers }), { params: { auth0 } }); +}; + +export const getSession = async (config: any, res: NextResponse) => { + const req = new NextRequest('https://example.com'); + res.cookies.getAll().forEach(({ name, value }: { name: string; value: string }) => req.cookies.set(name, value)); + + const store = new StatelessSession(getConfig(config).baseConfig); + const [session] = await store.read(new Auth0NextRequest(req)); + return session; +}; diff --git a/tests/fixtures/setup.ts b/tests/fixtures/setup.ts index 8a1373e24..dcfd86ee1 100644 --- a/tests/fixtures/setup.ts +++ b/tests/fixtures/setup.ts @@ -48,6 +48,21 @@ export const defaultOnError: OnError = (_req, res, error) => { res.status(error.status || 500).end(error.message); }; +export const setupNock = async ( + config: ConfigParameters, + { + idTokenClaims, + discoveryOptions, + userInfoPayload = {}, + userInfoToken = 'eyJz93a...k4laUWw' + }: Pick = {} +) => { + discovery(config, discoveryOptions); + jwksEndpoint(config, jwks); + codeExchange(config, await makeIdToken({ iss: 'https://acme.auth0.local/', ...idTokenClaims })); + userInfo(config, userInfoToken, userInfoPayload); +}; + export const setup = async ( config: ConfigParameters, { @@ -69,10 +84,7 @@ export const setup = async ( asyncProps }: SetupOptions = {} ): Promise => { - discovery(config, discoveryOptions); - jwksEndpoint(config, jwks); - codeExchange(config, await makeIdToken({ iss: 'https://acme.auth0.local/', ...idTokenClaims })); - userInfo(config, userInfoToken, userInfoPayload); + await setupNock(config, { idTokenClaims, discoveryOptions, userInfoPayload, userInfoToken }); const { handleAuth, handleCallback, diff --git a/tests/handlers/auth.test.ts b/tests/handlers/auth.test.ts index d99dc9dcd..27981f54a 100644 --- a/tests/handlers/auth.test.ts +++ b/tests/handlers/auth.test.ts @@ -4,15 +4,13 @@ import { withoutApi } from '../fixtures/default-settings'; import { login, setup, teardown } from '../fixtures/setup'; import { get } from '../auth0-session/fixtures/helpers'; import { initAuth0, OnError } from '../../src'; -import { LoginOptions } from '../../src/handlers/login'; -import { LogoutOptions } from '../../src/handlers/logout'; -import { CallbackOptions } from '../../src/handlers/callback'; -import { ProfileOptions } from '../../src/handlers/profile'; +import { LoginOptions, LogoutOptions, CallbackOptions, ProfileOptions } from '../../src/handlers'; import * as baseLoginHandler from '../../src/auth0-session/handlers/login'; import * as baseLogoutHandler from '../../src/auth0-session/handlers/logout'; import * as baseCallbackHandler from '../../src/auth0-session/handlers/callback'; import { Handler } from '../../src/handlers/router-helpers'; import { NextApiHandler } from 'next'; +import { getResponse } from '../fixtures/app-router-helpers'; const handlerError = () => expect.objectContaining({ @@ -21,291 +19,343 @@ const handlerError = () => }); describe('auth handler', () => { - afterEach(teardown); - - test('return 500 for unexpected error', async () => { - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth; - delete global.onError; - jest.spyOn(console, 'error').mockImplementation((error) => { - delete error.status; + describe('app router', () => { + test('return 500 for unexpected error', async () => { + await expect( + getResponse({ + url: '/api/auth/foo', + extraHandlers: { + foo: () => { + throw new Error(); + } + } + }) + ).resolves.toMatchObject({ status: 500 }); }); - await expect(get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo')).rejects.toThrow( - 'Internal Server Error' - ); - }); - - test('return 404 for unknown routes', async () => { - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth; - await expect(get(baseUrl, '/api/auth/foo')).rejects.toThrow('Not Found'); - }); - - test('return 404 for unknown routes including builtin props', async () => { - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth; - await expect(get(baseUrl, '/api/auth/__proto__')).rejects.toThrow('Not Found'); - }); - test('return 404 when routes have extra parts', async () => { - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth; - await expect(get(baseUrl, '/api/auth/me.css')).rejects.toThrow('Not Found'); - await expect(get(baseUrl, '/api/auth/me/foo.css')).rejects.toThrow('Not Found'); - await expect(get(baseUrl, '/api/auth/me/foo/bar.css')).rejects.toThrow('Not Found'); - }); -}); + test('return 404 for unknown routes', async () => { + await expect(getResponse({ url: '/api/auth/foo' })).resolves.toMatchObject({ status: 404 }); + }); -describe('custom error handler', () => { - afterEach(teardown); + test('return 404 for unknown routes including builtin props', async () => { + await expect(getResponse({ url: '/api/auth/__proto__' })).resolves.toMatchObject({ status: 404 }); + }); - test('accept custom error handler', async () => { - const onError = jest.fn>((_req, res) => res.end()); - const baseUrl = await setup(withoutApi, { onError }); - await get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo'); - expect(onError).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), handlerError()); - }); + test('return 404 when routes have extra parts', async () => { + await expect(getResponse({ url: '/api/auth/me/foo.css' })).resolves.toMatchObject({ status: 404 }); + }); - test('use default error handler', async () => { - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth; - delete global.onError; - // eslint-disable-next-line @typescript-eslint/no-empty-function - jest.spyOn(console, 'error').mockImplementation(() => {}); - await expect(get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo')).rejects.toThrow( - 'Bad Request' - ); - expect(console.error).toHaveBeenCalledWith(handlerError()); - }); + test('use default error handler', async () => { + jest.spyOn(console, 'error').mockImplementation(() => {}); + await expect( + getResponse({ + url: '/api/auth/foo', + extraHandlers: { foo: jest.fn().mockRejectedValue(new Error()), onError: undefined } + }) + ).resolves.toMatchObject({ status: 500 }); + expect(console.error).toHaveBeenCalledWith(expect.any(Error)); + }); - test('finish response if custom error does not', async () => { - const onError = jest.fn(); - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { onError }); - await expect( - get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo', { fullResponse: true }) - ).rejects.toThrow('Internal Server Error'); - expect(onError).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), handlerError()); - }); + test('accept custom error handler', async () => { + const onError = jest.fn(); + await expect( + getResponse({ + url: '/api/auth/foo', + extraHandlers: { + foo: jest.fn().mockRejectedValue(new Error()), + onError + } + }) + ).resolves.toMatchObject({ status: 500 }); + expect(onError).toHaveBeenCalledWith(expect.any(Request), expect.any(Error)); + }); - test('finish response with custom error status', async () => { - const onError = jest.fn>((_req, res) => res.status(418)); - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { onError }); - await expect( - get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo', { fullResponse: true }) - ).rejects.toThrow("I'm a Teapot"); - expect(onError).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), handlerError()); + test('accept custom error handler response', async () => { + const onError = jest.fn().mockReturnValue(new Response(null, { status: 418 })); + await expect( + getResponse({ + url: '/api/auth/foo', + extraHandlers: { + foo: jest.fn().mockRejectedValue(new Error()), + onError + } + }) + ).resolves.toMatchObject({ status: 418 }); + expect(onError).toHaveBeenCalled(); + }); }); -}); -describe('custom handlers', () => { - afterEach(teardown); + describe('page router', () => { + afterEach(teardown); - test('accept custom login handler', async () => { - const login = jest.fn(async (_req, res) => { - res.end(); - }) as NextApiHandler as Handler; - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { login }); - await get(baseUrl, '/api/auth/login'); - expect(login).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); - }); + test('return 500 for unexpected error', async () => { + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth; + delete global.onError; + jest.spyOn(console, 'error').mockImplementation((error) => { + delete error.status; + }); + await expect(get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo')).rejects.toThrow( + 'Internal Server Error' + ); + }); - test('accept custom logout handler', async () => { - const logout = jest.fn(async (_req, res) => { - res.end(); - }) as NextApiHandler as Handler; - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { logout }); - await get(baseUrl, '/api/auth/logout'); - expect(logout).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); - }); + test('return 404 for unknown routes', async () => { + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth; + await expect(get(baseUrl, '/api/auth/foo')).rejects.toThrow('Not Found'); + }); - test('accept custom callback handler', async () => { - const callback = jest.fn(async (_req, res) => { - res.end(); - }) as NextApiHandler as Handler; - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { callback }); - await get(baseUrl, '/api/auth/callback'); - expect(callback).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); - }); + test('return 404 for unknown routes including builtin props', async () => { + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth; + await expect(get(baseUrl, '/api/auth/__proto__')).rejects.toThrow('Not Found'); + }); - test('accept custom profile handler', async () => { - const profile = jest.fn(async (_req, res) => { - res.end(); - }) as NextApiHandler as Handler; - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { profile }); - await get(baseUrl, '/api/auth/me'); - expect(profile).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); - }); + test('return 404 when routes have extra parts', async () => { + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth; + await expect(get(baseUrl, '/api/auth/me.css')).rejects.toThrow('Not Found'); + await expect(get(baseUrl, '/api/auth/me/foo.css')).rejects.toThrow('Not Found'); + await expect(get(baseUrl, '/api/auth/me/foo/bar.css')).rejects.toThrow('Not Found'); + }); - test('accept custom arbitrary handler', async () => { - const signup = jest.fn(async (_req, res) => { - res.end(); - }) as NextApiHandler as Handler; - const baseUrl = await setup(withoutApi); - global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { signup }); - await get(baseUrl, '/api/auth/signup'); - expect(signup).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); - }); -}); + test('accept custom error handler', async () => { + const onError = jest.fn>((_req, res) => res.end()); + const baseUrl = await setup(withoutApi, { onError }); + await get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo'); + expect(onError).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), handlerError()); + }); -describe('custom options', () => { - afterEach(teardown); + test('use default error handler', async () => { + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth; + delete global.onError; + // eslint-disable-next-line @typescript-eslint/no-empty-function + jest.spyOn(console, 'error').mockImplementation(() => {}); + await expect(get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo')).rejects.toThrow( + 'Bad Request' + ); + expect(console.error).toHaveBeenCalledWith(handlerError()); + }); - test('accept custom login options', async () => { - const loginHandler = jest.fn(async (_req: any, res: any) => { - res.res.end(); + test('finish response if custom error does not', async () => { + const onError = jest.fn(); + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { onError }); + await expect( + get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo', { fullResponse: true }) + ).rejects.toThrow('Internal Server Error'); + expect(onError).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), handlerError()); }); - jest.spyOn(baseLoginHandler, 'default').mockImplementation(() => loginHandler); - const options: LoginOptions = { authorizationParams: { scope: 'openid' } }; - const baseUrl = await setup(withoutApi); - const { handleLogin, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - login: handleLogin(options) + + test('finish response with custom error status', async () => { + const onError = jest.fn>((_req, res) => res.status(418)); + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { onError }); + await expect( + get(baseUrl, '/api/auth/callback?error=foo&error_description=bar&state=foo', { fullResponse: true }) + ).rejects.toThrow("I'm a Teapot"); + expect(onError).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse), handlerError()); }); - await get(baseUrl, '/api/auth/login'); - expect(loginHandler).toHaveBeenCalledWith( - expect.objectContaining({ req: expect.any(IncomingMessage) }), - expect.objectContaining({ res: expect.any(ServerResponse) }), - options - ); - }); - test('accept custom logout options', async () => { - const logoutHandler = jest.fn(async (_req: any, res: any) => { - res.res.end(); + test('accept custom login handler', async () => { + const login = jest.fn(async (_req, res) => { + res.end(); + }) as NextApiHandler as Handler; + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { login }); + await get(baseUrl, '/api/auth/login'); + expect(login).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); }); - jest.spyOn(baseLogoutHandler, 'default').mockImplementation(() => logoutHandler); - const options: LogoutOptions = { returnTo: '/foo' }; - const baseUrl = await setup(withoutApi); - const { handleLogout, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - logout: handleLogout(options) + + test('accept custom logout handler', async () => { + const logout = jest.fn(async (_req, res) => { + res.end(); + }) as NextApiHandler as Handler; + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { logout }); + await get(baseUrl, '/api/auth/logout'); + expect(logout).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); }); - await get(baseUrl, '/api/auth/logout'); - expect(logoutHandler).toHaveBeenCalledWith( - expect.objectContaining({ req: expect.any(IncomingMessage) }), - expect.objectContaining({ res: expect.any(ServerResponse) }), - options - ); - }); - test('accept custom callback options', async () => { - const callbackHandler = jest.fn(async (_req: any, res: any) => { - res.res.end(); + test('accept custom callback handler', async () => { + const callback = jest.fn(async (_req, res) => { + res.end(); + }) as NextApiHandler as Handler; + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { callback }); + await get(baseUrl, '/api/auth/callback'); + expect(callback).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); }); - jest.spyOn(baseCallbackHandler, 'default').mockImplementation(() => callbackHandler); - const options: CallbackOptions = { redirectUri: '/foo' }; - const baseUrl = await setup(withoutApi); - const { handleCallback, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - callback: handleCallback(options) + + test('accept custom profile handler', async () => { + const profile = jest.fn(async (_req, res) => { + res.end(); + }) as NextApiHandler as Handler; + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { profile }); + await get(baseUrl, '/api/auth/me'); + expect(profile).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); }); - await get(baseUrl, '/api/auth/callback'); - expect(callbackHandler).toHaveBeenCalledWith( - expect.objectContaining({ req: expect.any(IncomingMessage) }), - expect.objectContaining({ res: expect.any(ServerResponse) }), - expect.objectContaining(options) - ); - }); - test('accept custom profile options', async () => { - const afterRefetch = jest.fn(async (_req, _res, session) => session); - const options: ProfileOptions = { refetch: true, afterRefetch }; - const baseUrl = await setup(withoutApi); - const { handleProfile, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - profile: handleProfile(options) + test('accept custom arbitrary handler', async () => { + const signup = jest.fn(async (_req, res) => { + res.end(); + }) as NextApiHandler as Handler; + const baseUrl = await setup(withoutApi); + global.handleAuth = initAuth0(withoutApi).handleAuth.bind(null, { signup }); + await get(baseUrl, '/api/auth/signup'); + expect(signup).toHaveBeenCalledWith(expect.any(IncomingMessage), expect.any(ServerResponse)); }); - const cookieJar = await login(baseUrl); - await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(afterRefetch).toHaveBeenCalled(); - }); -}); -describe('custom options providers', () => { - afterEach(teardown); + test('accept custom login options', async () => { + const loginHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); + }); + jest.spyOn(baseLoginHandler, 'default').mockImplementation(() => loginHandler); + const options: LoginOptions = { authorizationParams: { scope: 'openid' } }; + const baseUrl = await setup(withoutApi); + const { handleLogin, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + login: handleLogin(options) + }); + await get(baseUrl, '/api/auth/login'); + expect(loginHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); + }); - test('accept custom login options provider', async () => { - const loginHandler = jest.fn(async (_req: any, res: any) => { - res.res.end(); + test('accept custom logout options', async () => { + const logoutHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); + }); + jest.spyOn(baseLogoutHandler, 'default').mockImplementation(() => logoutHandler); + const options: LogoutOptions = { returnTo: '/foo' }; + const baseUrl = await setup(withoutApi); + const { handleLogout, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + logout: handleLogout(options) + }); + await get(baseUrl, '/api/auth/logout'); + expect(logoutHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); - jest.spyOn(baseLoginHandler, 'default').mockImplementation(() => loginHandler); - const options = { authorizationParams: { scope: 'openid' } }; - const optionsProvider = jest.fn(() => options); - const baseUrl = await setup(withoutApi); - const { handleLogin, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - login: handleLogin(optionsProvider) + test('accept custom callback options', async () => { + const callbackHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); + }); + jest.spyOn(baseCallbackHandler, 'default').mockImplementation(() => callbackHandler); + const options: CallbackOptions = { redirectUri: '/foo' }; + const baseUrl = await setup(withoutApi); + const { handleCallback, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + callback: handleCallback(options) + }); + await get(baseUrl, '/api/auth/callback'); + expect(callbackHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + expect.objectContaining(options) + ); }); - await get(baseUrl, '/api/auth/login'); - expect(optionsProvider).toHaveBeenCalled(); - expect(loginHandler).toHaveBeenCalledWith( - expect.objectContaining({ req: expect.any(IncomingMessage) }), - expect.objectContaining({ res: expect.any(ServerResponse) }), - options - ); - }); - test('accept custom logout options provider', async () => { - const logoutHandler = jest.fn(async (_req: any, res: any) => { - res.res.end(); + test('accept custom profile options', async () => { + const afterRefetch = jest.fn(async (_req, _res, session) => session); + const options: ProfileOptions = { refetch: true, afterRefetch }; + const baseUrl = await setup(withoutApi); + const { handleProfile, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + profile: handleProfile(options) + }); + const cookieJar = await login(baseUrl); + await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(afterRefetch).toHaveBeenCalled(); }); - jest.spyOn(baseLogoutHandler, 'default').mockImplementation(() => logoutHandler); - const options: LogoutOptions = { returnTo: '/foo' }; - const optionsProvider = jest.fn(() => options); - const baseUrl = await setup(withoutApi); - const { handleLogout, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - logout: handleLogout(optionsProvider) + + test('accept custom login options provider', async () => { + const loginHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); + }); + jest.spyOn(baseLoginHandler, 'default').mockImplementation(() => loginHandler); + const options = { authorizationParams: { scope: 'openid' } }; + const optionsProvider = jest.fn(() => options); + const baseUrl = await setup(withoutApi); + const { handleLogin, handleAuth } = initAuth0(withoutApi); + + global.handleAuth = handleAuth.bind(null, { + login: handleLogin(optionsProvider) + }); + await get(baseUrl, '/api/auth/login'); + expect(optionsProvider).toHaveBeenCalled(); + expect(loginHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); - await get(baseUrl, '/api/auth/logout'); - expect(optionsProvider).toHaveBeenCalled(); - expect(logoutHandler).toHaveBeenCalledWith( - expect.objectContaining({ req: expect.any(IncomingMessage) }), - expect.objectContaining({ res: expect.any(ServerResponse) }), - options - ); - }); - test('accept custom callback options provider', async () => { - const callbackHandler = jest.fn(async (_req: any, res: any) => { - res.res.end(); + test('accept custom logout options provider', async () => { + const logoutHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); + }); + jest.spyOn(baseLogoutHandler, 'default').mockImplementation(() => logoutHandler); + const options: LogoutOptions = { returnTo: '/foo' }; + const optionsProvider = jest.fn(() => options); + const baseUrl = await setup(withoutApi); + const { handleLogout, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + logout: handleLogout(optionsProvider) + }); + await get(baseUrl, '/api/auth/logout'); + expect(optionsProvider).toHaveBeenCalled(); + expect(logoutHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + options + ); }); - jest.spyOn(baseCallbackHandler, 'default').mockImplementation(() => callbackHandler); - const options: CallbackOptions = { redirectUri: '/foo' }; - const optionsProvider = jest.fn(() => options); - const baseUrl = await setup(withoutApi); - const { handleCallback, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - callback: handleCallback(optionsProvider) + + test('accept custom callback options provider', async () => { + const callbackHandler = jest.fn(async (_req: any, res: any) => { + res.res.end(); + }); + jest.spyOn(baseCallbackHandler, 'default').mockImplementation(() => callbackHandler); + const options: CallbackOptions = { redirectUri: '/foo' }; + const optionsProvider = jest.fn(() => options); + const baseUrl = await setup(withoutApi); + const { handleCallback, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + callback: handleCallback(optionsProvider) + }); + await get(baseUrl, '/api/auth/callback'); + expect(optionsProvider).toHaveBeenCalled(); + expect(callbackHandler).toHaveBeenCalledWith( + expect.objectContaining({ req: expect.any(IncomingMessage) }), + expect.objectContaining({ res: expect.any(ServerResponse) }), + expect.objectContaining(options) + ); }); - await get(baseUrl, '/api/auth/callback'); - expect(optionsProvider).toHaveBeenCalled(); - expect(callbackHandler).toHaveBeenCalledWith( - expect.objectContaining({ req: expect.any(IncomingMessage) }), - expect.objectContaining({ res: expect.any(ServerResponse) }), - expect.objectContaining(options) - ); - }); - test('accept custom profile options provider', async () => { - const afterRefetch = jest.fn(async (_req, _res, session) => session); - const options: ProfileOptions = { refetch: true, afterRefetch }; - const optionsProvider = jest.fn(() => options); - const baseUrl = await setup(withoutApi); - const { handleProfile, handleAuth } = initAuth0(withoutApi); - global.handleAuth = handleAuth.bind(null, { - profile: handleProfile(optionsProvider) + test('accept custom profile options provider', async () => { + const afterRefetch = jest.fn(async (_req, _res, session) => session); + const options: ProfileOptions = { refetch: true, afterRefetch }; + const optionsProvider = jest.fn(() => options); + const baseUrl = await setup(withoutApi); + const { handleProfile, handleAuth } = initAuth0(withoutApi); + global.handleAuth = handleAuth.bind(null, { + profile: handleProfile(optionsProvider) + }); + const cookieJar = await login(baseUrl); + await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(optionsProvider).toHaveBeenCalled(); + expect(afterRefetch).toHaveBeenCalled(); }); - const cookieJar = await login(baseUrl); - await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(optionsProvider).toHaveBeenCalled(); - expect(afterRefetch).toHaveBeenCalled(); }); }); diff --git a/tests/handlers/callback.test.ts b/tests/handlers/callback.test.ts index 2e13dcc0f..4bef608dd 100644 --- a/tests/handlers/callback.test.ts +++ b/tests/handlers/callback.test.ts @@ -1,15 +1,17 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { NextRequest, NextResponse } from 'next/server'; import { CookieJar } from 'tough-cookie'; import * as jose from 'jose'; import timekeeper from 'timekeeper'; import { withApi, withoutApi } from '../fixtures/default-settings'; import { makeIdToken } from '../auth0-session/fixtures/cert'; -import { defaultConfig, get, post, toSignedCookieJar } from '../auth0-session/fixtures/helpers'; +import { defaultConfig, get, post, signCookie, toSignedCookieJar } from '../auth0-session/fixtures/helpers'; import { encodeState } from '../../src/auth0-session/utils/encoding'; import { defaultOnError, setup, teardown } from '../fixtures/setup'; import { Session, AfterCallbackPageRoute, MissingStateCookieError } from '../../src'; import nock from 'nock'; import { signing } from '../../src/auth0-session/utils/hkdf'; -import { NextApiRequest, NextApiResponse } from 'next'; +import { getResponse, getSession as getSessionFromRes } from '../fixtures/app-router-helpers'; const callback = (baseUrl: string, body: any, cookieJar?: CookieJar): Promise => post(baseUrl, `/api/auth/callback`, { @@ -27,407 +29,657 @@ const generateSignature = async (cookie: string, value: string): Promise }; describe('callback handler', () => { - afterEach(teardown); - - test('should require a state', async () => { - expect.assertions(2); - const baseUrl = await setup(withoutApi, { - onError(req, res, err) { - expect(err.cause).toBeInstanceOf(MissingStateCookieError); - defaultOnError(req, res, err); - } + describe('app router', () => { + afterEach(() => nock.cleanAll()); + + test('should require a state parameter', async () => { + await expect(getResponse({ url: '/api/auth/callback' })).resolves.toMatchObject({ + status: 404, + statusText: expect.stringMatching(/Missing state parameter/) + }); }); - await expect( - callback(baseUrl, { - state: '__test_state__' - }) - ).rejects.toThrow( - 'Callback handler failed. CAUSE: Missing state cookie from login request (check login URL, callback URL and cookie config).' - ); - }); - test('should validate the state', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await toSignedCookieJar( - { - state: '__other_state__' - }, - baseUrl - ); - await expect( - callback( - baseUrl, - { - state: '__test_state__' + test('should require a state cookie', async () => { + await expect( + getResponse({ + url: '/api/auth/callback?state=__test_state__' + }) + ).resolves.toMatchObject({ + status: 400, + statusText: expect.stringMatching(/Missing state cookie from login request/) + }); + }); + + test('should validate the state', async () => { + await expect( + getResponse({ + url: '/api/auth/callback?state=__test_state__', + cookies: { + state: await signCookie('state', 'other_state') + } + }) + ).resolves.toMatchObject({ + status: 400, + statusText: expect.stringMatching(/state mismatch, expected other_state, got: __test_state__/) + }); + }); + + test('should validate the audience', async () => { + const state = encodeState({ returnTo: 'https://example.com' }); + await expect( + getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + }, + idTokenClaims: { aud: 'bar' } + }) + ).resolves.toMatchObject({ + status: 400, + statusText: expect.stringMatching(/aud mismatch, expected __test_client_id__, got: bar/) + }); + }); + + test('should validate the issuer', async () => { + const state = encodeState({ returnTo: 'https://example.com' }); + await expect( + getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + }, + idTokenClaims: { iss: 'other-issuer' } + }) + ).resolves.toMatchObject({ + status: 400, + statusText: expect.stringMatching( + /unexpected iss value, expected https:\/\/acme.auth0.local\/, got: other-issuer/ + ) + }); + }); + + test('should escape html in error qp', async () => { + await expect( + getResponse({ + url: '/api/auth/callback?error=%3Cscript%3Ealert(%27xss%27)%3C%2Fscript%3E&state=foo', + cookies: { + state: await signCookie('state', 'foo') + } + }) + ).resolves.toMatchObject({ + status: 400, + statusText: expect.stringMatching(/<script>alert\('xss'\)<\/script>/) + }); + }); + + test('should create session and strip OIDC claims', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + const res = await getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + } + }); + expect(res.status).toEqual(302); + expect(res.headers.get('location')).toEqual('https://example.com/foo'); + const session = await getSessionFromRes(withApi, res); + expect(session).toMatchObject({ + user: { sub: '__test_sub__' }, + accessToken: expect.any(String) + }); + expect(session?.user).not.toHaveProperty('iss'); + }); + + test('remove properties from session with afterCallback hook', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + const res = await getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') }, - cookieJar - ) - ).rejects.toThrow('state mismatch, expected __other_state__, got: __test_state__'); - }); + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + delete session.accessToken; + return session; + } + } + }); + expect(res.status).toEqual(302); + expect(res.headers.get('location')).toEqual('https://example.com/foo'); + const session = await getSessionFromRes(withApi, res); + expect(session).toMatchObject({ + user: { sub: '__test_sub__' } + }); + expect(session).not.toHaveProperty('accessToken'); + }); - test('should validate the audience', async () => { - const baseUrl = await setup(withoutApi, { idTokenClaims: { aud: 'bar' } }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - await expect( - callback( - baseUrl, - { - state, - code: 'code' + test('add properties to session with afterCallback hook', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + const res = await getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') }, - cookieJar - ) - ).rejects.toThrow('aud mismatch, expected __test_client_id__, got: bar'); - }); + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + return { ...session, foo: 'bar' }; + } + } + }); + expect(res.status).toEqual(302); + expect(res.headers.get('location')).toEqual('https://example.com/foo'); + const session = await getSessionFromRes(withApi, res); + expect(session).toMatchObject({ + user: { sub: '__test_sub__' }, + foo: 'bar' + }); + }); - test('should validate the issuer', async () => { - const baseUrl = await setup(withoutApi, { idTokenClaims: { aud: 'bar', iss: 'other-issuer' } }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - await expect( - callback( - baseUrl, - { - state, - code: 'code' + test('throws from afterCallback hook', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + await expect( + getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + }, + callbackOpts: { + afterCallback() { + throw new Error('some validation error.'); + } + } + }) + ).resolves.toMatchObject({ status: 500, statusText: expect.stringMatching(/some validation error/) }); + }); + + test('redirect from afterCallback hook', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + const res = await getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') }, - cookieJar - ) - ).rejects.toThrow('unexpected iss value, expected https://acme.auth0.local/, got: other-issuer'); - }); + callbackOpts: { + afterCallback() { + return NextResponse.redirect('https://example.com/foo'); + } + } + }); + expect(res.status).toBe(302); + expect(res.headers.get('location')).toBe('https://example.com/foo'); + }); - it('should escape html in error qp', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await toSignedCookieJar( - { - state: `foo.${await generateSignature('state', 'foo')}` - }, - baseUrl - ); - await expect( - get(baseUrl, `/api/auth/callback?error=%3Cscript%3Ealert(%27xss%27)%3C%2Fscript%3E&state=foo`, { cookieJar }) - ).rejects.toThrow('<script>alert('xss')</script>'); - }); + test('throws for missing org_id claim', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + await expect( + getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + }, + callbackOpts: { + organization: 'foo' + } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/Organization Id \(org_id\) claim must be a string present in the ID token/) + }); + }); - test('should create the session without OIDC claims', async () => { - const baseUrl = await setup(withoutApi); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - const { res } = await callback( - baseUrl, - { - state, - code: 'code' - }, - cookieJar - ); - expect(res.statusCode).toBe(302); - const body = await get(baseUrl, `/api/session`, { cookieJar }); - expect(body.user).toStrictEqual({ - nickname: '__test_nickname__', - sub: '__test_sub__' + test('throws for org_id claim mismatch', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + await expect( + getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + }, + callbackOpts: { + organization: 'foo' + }, + idTokenClaims: { org_id: 'bar' } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching( + /Organization Id \(org_id\) claim value mismatch in the ID token; expected "foo", found "bar"/ + ) + }); + }); + + test('accepts a valid organization', async () => { + const state = encodeState({ returnTo: 'https://example.com/foo' }); + await expect( + getResponse({ + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + }, + callbackOpts: { + organization: 'foo' + }, + idTokenClaims: { org_id: 'foo' } + }) + ).resolves.toMatchObject({ + status: 302 + }); }); }); - test('should set the correct expiration', async () => { - timekeeper.freeze(0); - const baseUrl = await setup(withoutApi); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - const { res } = await post(baseUrl, `/api/auth/callback`, { - fullResponse: true, - cookieJar, - body: { - state, - code: 'code' - } + describe('page router', () => { + afterEach(teardown); + + test('should require a state', async () => { + expect.assertions(2); + const baseUrl = await setup(withoutApi, { + onError(req, res, err) { + expect(err.cause).toBeInstanceOf(MissingStateCookieError); + defaultOnError(req, res, err); + } + }); + await expect( + callback(baseUrl, { + state: '__test_state__' + }) + ).rejects.toThrow( + 'Callback handler failed. CAUSE: Missing state cookie from login request (check login URL, callback URL and cookie config).' + ); }); - expect(res.statusCode).toBe(302); - const [sessionCookie] = cookieJar.getCookiesSync(baseUrl); - const expiryInHrs = new Date(sessionCookie.expires).getTime() / 1000 / 60 / 60; - expect(expiryInHrs).toBe(24); - timekeeper.reset(); - }); + test('should validate the state', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await toSignedCookieJar( + { + state: '__other_state__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state: '__test_state__' + }, + cookieJar + ) + ).rejects.toThrow('state mismatch, expected __other_state__, got: __test_state__'); + }); - test('should create the session without OIDC claims with api config', async () => { - timekeeper.freeze(0); - const baseUrl = await setup(withApi); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - const { res } = await callback( - baseUrl, - { - state, - code: 'code' - }, - cookieJar - ); - expect(res.statusCode).toBe(302); - const session = await get(baseUrl, `/api/session`, { cookieJar }); - - expect(session).toStrictEqual({ - accessToken: 'eyJz93a...k4laUWw', - accessTokenExpiresAt: 750, - accessTokenScope: 'read:foo delete:foo', - token_type: 'Bearer', - refreshToken: 'GEbRxBN...edjnXbL', - idToken: await makeIdToken({ iss: 'https://acme.auth0.local/' }), - user: { - nickname: '__test_nickname__', - sub: '__test_sub__' - } + test('should validate the audience', async () => { + const baseUrl = await setup(withoutApi, { idTokenClaims: { aud: 'bar' } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ) + ).rejects.toThrow('aud mismatch, expected __test_client_id__, got: bar'); }); - timekeeper.reset(); - }); - test('remove properties from session with afterCallback hook', async () => { - timekeeper.freeze(0); - const afterCallback: AfterCallbackPageRoute = ( - _req: NextApiRequest, - _res: NextApiResponse, - session: Session - ): Session => { - delete session.accessToken; - delete session.refreshToken; - return session; - }; - const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - const { res } = await callback( - baseUrl, - { - state, - code: 'code' - }, - cookieJar - ); - expect(res.statusCode).toBe(302); - const session = await get(baseUrl, `/api/session`, { cookieJar }); - - expect(session).toStrictEqual({ - accessTokenExpiresAt: 750, - accessTokenScope: 'read:foo delete:foo', - idToken: await makeIdToken({ iss: 'https://acme.auth0.local/' }), - token_type: 'Bearer', - user: { - nickname: '__test_nickname__', - sub: '__test_sub__' - } + test('should validate the issuer', async () => { + const baseUrl = await setup(withoutApi, { idTokenClaims: { aud: 'bar', iss: 'other-issuer' } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ) + ).rejects.toThrow('unexpected iss value, expected https://acme.auth0.local/, got: other-issuer'); }); - timekeeper.reset(); - }); - test('add properties to session with afterCallback hook', async () => { - timekeeper.freeze(0); - const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { - session.foo = 'bar'; - return session; - }; - const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - const { res } = await callback( - baseUrl, - { - state, - code: 'code' - }, - cookieJar - ); - expect(res.statusCode).toBe(302); - const session = await get(baseUrl, '/api/session', { cookieJar }); - - expect(session).toMatchObject({ - foo: 'bar', - user: { - nickname: '__test_nickname__', - sub: '__test_sub__' - } + test('should escape html in error qp', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await toSignedCookieJar( + { + state: `foo.${await generateSignature('state', 'foo')}` + }, + baseUrl + ); + await expect( + get(baseUrl, `/api/auth/callback?error=%3Cscript%3Ealert(%27xss%27)%3C%2Fscript%3E&state=foo`, { cookieJar }) + ).rejects.toThrow('<script>alert('xss')</script>'); }); - timekeeper.reset(); - }); - test('throws from afterCallback hook', async () => { - const afterCallback = (): Session => { - throw new Error('some validation error.'); - }; - const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - await expect( - callback( + test('should create the session without OIDC claims', async () => { + const baseUrl = await setup(withoutApi); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const { res } = await callback( baseUrl, { state, code: 'code' }, cookieJar - ) - ).rejects.toThrow('some validation error.'); - }); + ); + expect(res.statusCode).toBe(302); + const body = await get(baseUrl, `/api/session`, { cookieJar }); + expect(body.user).toStrictEqual({ + nickname: '__test_nickname__', + sub: '__test_sub__' + }); + }); - test('throws for missing org_id claim', async () => { - const baseUrl = await setup({ ...withApi, organization: 'foo' }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - await expect( - callback( + test('should set the correct expiration', async () => { + timekeeper.freeze(0); + const baseUrl = await setup(withoutApi); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const { res } = await post(baseUrl, `/api/auth/callback`, { + fullResponse: true, + cookieJar, + body: { + state, + code: 'code' + } + }); + expect(res.statusCode).toBe(302); + + const [sessionCookie] = cookieJar.getCookiesSync(baseUrl); + const expiryInHrs = new Date(sessionCookie.expires).getTime() / 1000 / 60 / 60; + expect(expiryInHrs).toBe(24); + timekeeper.reset(); + }); + + test('should create the session without OIDC claims with api config', async () => { + timekeeper.freeze(0); + const baseUrl = await setup(withApi); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const { res } = await callback( baseUrl, { state, code: 'code' }, cookieJar - ) - ).rejects.toThrow('Organization Id (org_id) claim must be a string present in the ID token'); - }); + ); + expect(res.statusCode).toBe(302); + const session = await get(baseUrl, `/api/session`, { cookieJar }); - test('throws for org_id claim mismatch', async () => { - const baseUrl = await setup({ ...withApi, organization: 'foo' }, { idTokenClaims: { org_id: 'bar' } }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - await expect( - callback( + expect(session).toStrictEqual({ + accessToken: 'eyJz93a...k4laUWw', + accessTokenExpiresAt: 750, + accessTokenScope: 'read:foo delete:foo', + token_type: 'Bearer', + refreshToken: 'GEbRxBN...edjnXbL', + idToken: await makeIdToken({ iss: 'https://acme.auth0.local/' }), + user: { + nickname: '__test_nickname__', + sub: '__test_sub__' + } + }); + timekeeper.reset(); + }); + + test('remove properties from session with afterCallback hook', async () => { + timekeeper.freeze(0); + const afterCallback: AfterCallbackPageRoute = ( + _req: NextApiRequest, + _res: NextApiResponse, + session: Session + ): Session => { + delete session.accessToken; + delete session.refreshToken; + return session; + }; + const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const { res } = await callback( baseUrl, { state, code: 'code' }, cookieJar - ) - ).rejects.toThrow('Organization Id (org_id) claim value mismatch in the ID token; expected "foo", found "bar"'); - }); + ); + expect(res.statusCode).toBe(302); + const session = await get(baseUrl, `/api/session`, { cookieJar }); - test('accepts a valid organization', async () => { - const baseUrl = await setup(withApi, { - idTokenClaims: { org_id: 'foo' }, - callbackOptions: { organization: 'foo' } + expect(session).toStrictEqual({ + accessTokenExpiresAt: 750, + accessTokenScope: 'read:foo delete:foo', + idToken: await makeIdToken({ iss: 'https://acme.auth0.local/' }), + token_type: 'Bearer', + user: { + nickname: '__test_nickname__', + sub: '__test_sub__' + } + }); + timekeeper.reset(); }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - await expect( - callback( + + test('add properties to session with afterCallback hook', async () => { + timekeeper.freeze(0); + const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { + session.foo = 'bar'; + return session; + }; + const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const { res } = await callback( baseUrl, { state, code: 'code' }, cookieJar - ) - ).resolves.not.toThrow(); - const session = await get(baseUrl, '/api/session', { cookieJar }); + ); + expect(res.statusCode).toBe(302); + const session = await get(baseUrl, '/api/session', { cookieJar }); - expect(session.user.org_id).toEqual('foo'); - }); + expect(session).toMatchObject({ + foo: 'bar', + user: { + nickname: '__test_nickname__', + sub: '__test_sub__' + } + }); + timekeeper.reset(); + }); - test('should pass custom params to the token exchange', async () => { - const baseUrl = await setup(withoutApi, { - callbackOptions: { - authorizationParams: { foo: 'bar' } - } + test('throws from afterCallback hook', async () => { + const afterCallback = (): Session => { + throw new Error('some validation error.'); + }; + const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ) + ).rejects.toThrow('some validation error.'); + }); + + test('throws for missing org_id claim', async () => { + const baseUrl = await setup({ ...withApi, organization: 'foo' }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ) + ).rejects.toThrow('Organization Id (org_id) claim must be a string present in the ID token'); + }); + + test('throws for org_id claim mismatch', async () => { + const baseUrl = await setup({ ...withApi, organization: 'foo' }, { idTokenClaims: { org_id: 'bar' } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ) + ).rejects.toThrow('Organization Id (org_id) claim value mismatch in the ID token; expected "foo", found "bar"'); }); - const state = encodeState({ returnTo: baseUrl }); - const cookieJar = await toSignedCookieJar( - { - state, - nonce: '__test_nonce__' - }, - baseUrl - ); - const spy = jest.fn(); - - nock(`${withoutApi.issuerBaseURL}`) - .post('/oauth/token', /grant_type=authorization_code/) - .reply(200, async (_, body) => { - spy(body); - return { - access_token: 'eyJz93a...k4laUWw', - expires_in: 750, - scope: 'read:foo delete:foo', - refresh_token: 'GEbRxBN...edjnXbL', - id_token: await makeIdToken({ iss: `${withoutApi.issuerBaseURL}/` }), - token_type: 'Bearer' - }; + + test('accepts a valid organization', async () => { + const baseUrl = await setup(withApi, { + idTokenClaims: { org_id: 'foo' }, + callbackOptions: { organization: 'foo' } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + await expect( + callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ) + ).resolves.not.toThrow(); + const session = await get(baseUrl, '/api/session', { cookieJar }); + + expect(session.user.org_id).toEqual('foo'); + }); + + test('should pass custom params to the token exchange', async () => { + const baseUrl = await setup(withoutApi, { + callbackOptions: { + authorizationParams: { foo: 'bar' } + } + }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const spy = jest.fn(); + + nock(`${withoutApi.issuerBaseURL}`) + .post('/oauth/token', /grant_type=authorization_code/) + .reply(200, async (_, body) => { + spy(body); + return { + access_token: 'eyJz93a...k4laUWw', + expires_in: 750, + scope: 'read:foo delete:foo', + refresh_token: 'GEbRxBN...edjnXbL', + id_token: await makeIdToken({ iss: `${withoutApi.issuerBaseURL}/` }), + token_type: 'Bearer' + }; + }); - const { res } = await callback( - baseUrl, - { - state, - code: 'foobar' - }, - cookieJar - ); - expect(res.statusCode).toBe(302); - expect(spy).toHaveBeenCalledWith(expect.stringContaining('foo=bar')); + const { res } = await callback( + baseUrl, + { + state, + code: 'foobar' + }, + cookieJar + ); + expect(res.statusCode).toBe(302); + expect(spy).toHaveBeenCalledWith(expect.stringContaining('foo=bar')); + }); }); }); From 834512081459087d53052e456d1e310355dcf12e Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Fri, 2 Jun 2023 17:36:11 +0100 Subject: [PATCH 29/61] More handler tests --- cypress/e2e/smoke.cy.ts | 2 +- src/handlers/profile.ts | 4 +- src/http/auth0-next-response.ts | 30 +- src/session/get-access-token.ts | 5 +- tests/fixtures/app-router-helpers.ts | 67 ++- tests/handlers/callback.test.ts | 2 - tests/handlers/login.test.ts | 764 ++++++++++++++++++--------- tests/handlers/logout.test.ts | 294 +++++++---- tests/handlers/profile.test.ts | 402 ++++++++++---- 9 files changed, 1054 insertions(+), 516 deletions(-) diff --git a/cypress/e2e/smoke.cy.ts b/cypress/e2e/smoke.cy.ts index 10a7afcc3..d304fe634 100644 --- a/cypress/e2e/smoke.cy.ts +++ b/cypress/e2e/smoke.cy.ts @@ -100,7 +100,7 @@ describe('smoke tests', () => { }); }); - it.only('should access an api', () => { + it('should access an api', () => { cy.visit('/profile-api'); login(); diff --git a/src/handlers/profile.ts b/src/handlers/profile.ts index c5d5f870a..f9591f97f 100644 --- a/src/handlers/profile.ts +++ b/src/handlers/profile.ts @@ -137,7 +137,7 @@ const appRouteHandlerFactory: ( res.headers.set('Cache-Control', 'no-store'); if (options.refetch) { - const { accessToken } = await getAccessToken(); + const { accessToken } = await getAccessToken(req, res); if (!accessToken) { throw new Error('No access token available to refetch the profile'); } @@ -159,7 +159,7 @@ const appRouteHandlerFactory: ( await sessionCache.set(req, res, newSession); - return NextResponse.json(session.user, res); + return NextResponse.json(newSession.user, res); } return NextResponse.json(session.user, res); diff --git a/src/http/auth0-next-response.ts b/src/http/auth0-next-response.ts index 9eddeccc7..dc9bbabaf 100644 --- a/src/http/auth0-next-response.ts +++ b/src/http/auth0-next-response.ts @@ -1,41 +1,29 @@ import { NextResponse } from 'next/server'; import type { CookieSerializeOptions } from 'cookie'; import { Auth0Response } from '../auth0-session/http'; -import Auth0NextResponseCookies from './auth0-next-response-cookies'; export default class Auth0NextResponse extends Auth0Response { - private cookies: Auth0NextResponseCookies; - public constructor(res: NextResponse) { super(res); - this.cookies = new Auth0NextResponseCookies(); } public setCookie(name: string, value: string, options?: CookieSerializeOptions) { - try { - // Can't set multiple cookies with `res.cookies` in app dir - // See: https://github.com/vercel/edge-runtime/issues/283 - this.cookies.setCookie(name, value, options); - } catch (_) { - // This runs on middleware when next/headers fails - this.res.cookies.set(name, value, options); - } + this.res.cookies.set(name, value, options); } public clearCookie(name: string, options?: CookieSerializeOptions) { - try { - // Can't set multiple cookies with `res.cookies` in app dir - // See: https://github.com/vercel/edge-runtime/issues/283 - return this.cookies.clearCookie(name, options); - } catch (_) { - // This runs on middleware when next/headers fails - this.res.cookies.delete({ ...options, name, value: '' }); - } + this.res.cookies.delete({ ...options, name, value: '' }); } public redirect(location: string, status = 302): void { const headers = new Headers({ location }); - this.res.headers.forEach((value, key) => headers.set(key, value)); + this.res.headers.forEach((value, key) => { + if (headers.has(key)) { + headers.append(key, value); + } else { + headers.set(key, value); + } + }); this.res = new NextResponse(null, { ...this.res, status, headers }); } } diff --git a/src/session/get-access-token.ts b/src/session/get-access-token.ts index 188410eea..184774d69 100644 --- a/src/session/get-access-token.ts +++ b/src/session/get-access-token.ts @@ -6,6 +6,7 @@ import { AccessTokenError, AccessTokenErrorCode } from '../utils/errors'; import { intersect, match } from '../utils/array'; import { Session, SessionCache, fromTokenSet, get, set } from '../session'; import { AuthorizationParameters, NextConfig } from '../config'; +import { NextRequest, NextResponse } from 'next/server'; export type AfterRefresh = AfterRefreshPageRoute | AfterRefreshAppRoute; @@ -92,8 +93,8 @@ export interface GetAccessTokenResult { * @category Server */ export type GetAccessToken = ( - req?: IncomingMessage | NextApiRequest | AccessTokenRequest, - res?: ServerResponse | NextApiResponse, + req?: IncomingMessage | NextApiRequest | NextRequest | AccessTokenRequest, + res?: ServerResponse | NextApiResponse | NextResponse, accessTokenRequest?: AccessTokenRequest ) => Promise; diff --git a/tests/fixtures/app-router-helpers.ts b/tests/fixtures/app-router-helpers.ts index ec54e6e37..fd79c7584 100644 --- a/tests/fixtures/app-router-helpers.ts +++ b/tests/fixtures/app-router-helpers.ts @@ -1,32 +1,63 @@ -import { CallbackOptions, Claims, ConfigParameters, initAuth0 } from '../../src'; +import nock from 'nock'; +import { + CallbackOptions, + Claims, + ConfigParameters, + initAuth0, + LoginOptions, + LogoutOptions, + ProfileOptions +} from '../../src'; import { withApi } from './default-settings'; import { setupNock } from './setup'; import { NextRequest, NextResponse } from 'next/server'; import { StatelessSession } from '../../src/auth0-session'; import { getConfig } from '../../src/config'; import { Auth0NextRequest } from '../../src/http'; +import { encodeState } from '../../src/auth0-session/utils/encoding'; +import { signCookie } from '../auth0-session/fixtures/helpers'; -export const getResponse = async ({ - url, - config, - cookies, - idTokenClaims, - callbackOpts, - extraHandlers -}: { +export type GetResponseOpts = { url: string; config?: ConfigParameters; cookies?: { [key: string]: string }; idTokenClaims?: Claims; + discoveryOptions?: Record; + userInfoPayload?: Record; + userInfoToken?: string; callbackOpts?: CallbackOptions; + loginOpts?: LoginOptions; + logoutOpts?: LogoutOptions; + profileOpts?: ProfileOptions; extraHandlers?: any; -}) => { + clearNock?: boolean; +}; + +export const getResponse = async ({ + url, + config, + cookies, + idTokenClaims, + discoveryOptions, + userInfoPayload, + userInfoToken, + callbackOpts, + loginOpts, + logoutOpts, + profileOpts, + extraHandlers, + clearNock = true +}: GetResponseOpts) => { const opts = { ...withApi, ...config }; - await setupNock(opts, { idTokenClaims }); + clearNock && nock.cleanAll(); + await setupNock(opts, { idTokenClaims, discoveryOptions, userInfoPayload, userInfoToken }); const auth0 = url.split('?')[0].split('/').slice(3); const instance = initAuth0(opts); const handleAuth = instance.handleAuth({ ...(callbackOpts && { callback: instance.handleCallback(callbackOpts) }), + ...(loginOpts && { login: instance.handleLogin(loginOpts) }), + ...(logoutOpts && { logout: instance.handleLogout(logoutOpts) }), + ...(profileOpts && { profile: instance.handleProfile(profileOpts) }), onError(_req: any, error: any) { return new Response(null, { status: error.status || 500, statusText: error.message }); }, @@ -52,3 +83,17 @@ export const getSession = async (config: any, res: NextResponse) => { const [session] = await store.read(new Auth0NextRequest(req)); return session; }; + +export const login = async (opts: Omit = {}) => { + const state = encodeState({ returnTo: '/' }); + const res = await getResponse({ + ...opts, + url: `/api/auth/callback?state=${state}&code=code`, + cookies: { + ...opts.cookies, + state: await signCookie('state', state), + nonce: await signCookie('nonce', '__test_nonce__') + } + }); + return res; +}; diff --git a/tests/handlers/callback.test.ts b/tests/handlers/callback.test.ts index 4bef608dd..6606eff1a 100644 --- a/tests/handlers/callback.test.ts +++ b/tests/handlers/callback.test.ts @@ -30,8 +30,6 @@ const generateSignature = async (cookie: string, value: string): Promise describe('callback handler', () => { describe('app router', () => { - afterEach(() => nock.cleanAll()); - test('should require a state parameter', async () => { await expect(getResponse({ url: '/api/auth/callback' })).resolves.toMatchObject({ status: 404, diff --git a/tests/handlers/login.test.ts b/tests/handlers/login.test.ts index 81ecfb3ce..0ae67ec9b 100644 --- a/tests/handlers/login.test.ts +++ b/tests/handlers/login.test.ts @@ -4,315 +4,553 @@ import { decodeState } from '../../src/auth0-session/utils/encoding'; import { setup, teardown } from '../fixtures/setup'; import { get, getCookie } from '../auth0-session/fixtures/helpers'; import { Cookie, CookieJar } from 'tough-cookie'; +import { getResponse } from '../fixtures/app-router-helpers'; describe('login handler', () => { - afterEach(teardown); - - test('should create a state', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login', { cookieJar }); - - expect(cookieJar.getCookiesSync(baseUrl)).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - key: 'nonce', - value: expect.any(String), - path: '/', - sameSite: 'lax' - }), - expect.objectContaining({ - key: 'state', - value: expect.any(String), - path: '/', - sameSite: 'lax' - }), - expect.objectContaining({ - key: 'code_verifier', - value: expect.any(String), - path: '/', - sameSite: 'lax' - }) - ]) - ); - }); + describe('app router', () => { + test('should create a state', async () => { + const res = await getResponse({ + url: '/api/auth/login' + }); + expect(res.cookies.get('nonce')).toMatchObject({ + value: expect.any(String), + path: '/', + sameSite: 'lax' + }); + expect(res.cookies.get('state')).toMatchObject({ + value: expect.any(String), + path: '/', + sameSite: 'lax' + }); + expect(res.cookies.get('code_verifier')).toMatchObject({ + value: expect.any(String), + path: '/', + sameSite: 'lax' + }); + }); - test('should add returnTo to the state', async () => { - const baseUrl = await setup(withoutApi, { loginOptions: { returnTo: '/custom-url' } }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login', { cookieJar }); + test('should add returnTo to the state', async () => { + const res = await getResponse({ + url: '/api/auth/login', + loginOpts: { returnTo: '/custom-url' } + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toEqual('/custom-url'); + }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; - expect(state).toBeTruthy(); + test('should redirect to the identity provider', async () => { + const res = await getResponse({ + url: '/api/auth/login' + }); + const { value: state } = res.cookies.get('state'); + expect(urlParse(res.headers.get('location'), true)).toMatchObject({ + protocol: 'https:', + host: 'acme.auth0.local', + hash: null, + query: { + client_id: '__test_client_id__', + scope: 'openid profile read:customer', + response_type: 'code', + redirect_uri: 'http://www.acme.com/api/auth/callback', + nonce: expect.any(String), + state: state.split('.')[0], + code_challenge: expect.any(String), + code_challenge_method: 'S256' + }, + pathname: '/authorize' + }); + }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState?.returnTo).toEqual('/custom-url'); - }); + test('should allow sending custom parameters to the authorization server', async () => { + const loginOpts = { + authorizationParams: { + max_age: 123, + login_hint: 'foo@acme.com', + ui_locales: 'nl', + scope: 'some other scope openid', + foo: 'bar', + organization: 'foo', + invitation: 'bar' + } + }; + const res = await getResponse({ + url: '/api/auth/login', + loginOpts + }); + expect(res.status).toBe(302); + expect(urlParse(res.headers.get('location'), true)).toMatchObject({ + query: { + ...loginOpts.authorizationParams, + max_age: '123' + } + }); + }); - test('should redirect to the identity provider', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = new CookieJar(); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); - - expect(statusCode).toBe(302); - - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; - expect(urlParse(headers.location, true)).toMatchObject({ - protocol: 'https:', - host: 'acme.auth0.local', - hash: null, - query: { - client_id: '__test_client_id__', - scope: 'openid profile email', - response_type: 'code', - redirect_uri: 'http://www.acme.com/api/auth/callback', - nonce: expect.any(String), - state: state.split('.')[0], - code_challenge: expect.any(String), - code_challenge_method: 'S256' - }, - pathname: '/authorize' + test('should pass organization config to the authorization server', async () => { + const res = await getResponse({ + url: '/api/auth/login', + config: { organization: 'foo' } + }); + expect(res.status).toBe(302); + expect(urlParse(res.headers.get('location'), true).query).toMatchObject({ + organization: 'foo' + }); }); - }); - test('should allow sending custom parameters to the authorization server', async () => { - const loginOptions = { - authorizationParams: { - max_age: 123, - login_hint: 'foo@acme.com', - ui_locales: 'nl', - scope: 'some other scope openid', + test('should prefer organization auth param to config', async () => { + const res = await getResponse({ + url: '/api/auth/login', + config: { organization: 'foo' }, + loginOpts: { authorizationParams: { organization: 'bar' } } + }); + expect(res.status).toBe(302); + expect(urlParse(res.headers.get('location'), true).query).toMatchObject({ + organization: 'bar' + }); + }); + + test('should allow adding custom data to the state', async () => { + const res = await getResponse({ + url: '/api/auth/login', + loginOpts: { + getLoginState() { + return { foo: 'bar' }; + } + } + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ foo: 'bar', - organization: 'foo', - invitation: 'bar' - } - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); - - expect(statusCode).toBe(302); - expect(urlParse(headers.location, true)).toMatchObject({ - query: { - ...loginOptions.authorizationParams, - max_age: '123' - } + returnTo: 'http://www.acme.com/' + }); }); - }); - test('should pass organization config to the authorization server', async () => { - const baseUrl = await setup({ ...withoutApi, organization: 'foo' }); - const cookieJar = new CookieJar(); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); + test('should merge returnTo and state', async () => { + const res = await getResponse({ + url: '/api/auth/login', + loginOpts: { + returnTo: '/profile', + getLoginState() { + return { foo: 'bar' }; + } + } + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + foo: 'bar', + returnTo: '/profile' + }); + }); - expect(statusCode).toBe(302); - expect(urlParse(headers.location, true)).toMatchObject({ - query: { - organization: 'foo' - } + test('should allow the getState method to overwrite returnTo', async () => { + const res = await getResponse({ + url: '/api/auth/login', + loginOpts: { + returnTo: '/profile', + getLoginState() { + return { foo: 'bar', returnTo: '/bar' }; + } + } + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + foo: 'bar', + returnTo: '/bar' + }); }); - }); - test('should prefer organization auth param to config', async () => { - const baseUrl = await setup( - { ...withoutApi, organization: 'foo' }, - { loginOptions: { authorizationParams: { organization: 'bar' } } } - ); - const cookieJar = new CookieJar(); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); - - expect(statusCode).toBe(302); - expect(urlParse(headers.location, true)).toMatchObject({ - query: { - organization: 'bar' - } + test('should allow the returnTo url to be provided in the querystring', async () => { + const res = await getResponse({ + url: '/api/auth/login?returnTo=/from-query' + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toEqual('http://www.acme.com/from-query'); }); - }); - test('should allow adding custom data to the state', async () => { - const loginOptions = { - getLoginState: (): Record => { - return { - foo: 'bar' - }; - } - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login', { cookieJar }); - - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; - - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - foo: 'bar', - returnTo: 'http://www.acme.com/' + test('should take the first returnTo url provided in the querystring', async () => { + const res = await getResponse({ + url: '/api/auth/login?returnTo=/foo&returnTo=bar' + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toEqual('http://www.acme.com/foo'); }); - }); - test('should merge returnTo and state', async () => { - const loginOptions = { - returnTo: '/profile', - getLoginState: (): Record => { - return { - foo: 'bar' - }; - } - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login', { cookieJar }); - - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; - - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - foo: 'bar', - returnTo: '/profile' + test('should not allow absolute urls to be provided in the querystring', async () => { + const res = await getResponse({ + url: '/api/auth/login?returnTo=https://evil.com' + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toBeUndefined(); }); - }); - test('should allow the getState method to overwrite returnTo', async () => { - const loginOptions = { - returnTo: '/profile', - getLoginState: (): Record => { - return { - foo: 'bar', - returnTo: '/foo' - }; - } - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login', { cookieJar }); + test('should allow absolute urls in params of returnTo urls', async () => { + const res = await getResponse({ + url: '/api/auth/login', + loginOpts: { returnTo: 'https://google.com' } + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toBe('https://google.com'); + }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + test('should redirect relative to the redirect_uri over the base url', async () => { + const loginOpts = { + authorizationParams: { + redirect_uri: 'https://other-org.acme.com/api/auth/callback' + } + }; + const res = await getResponse({ + url: '/api/auth/login?returnTo=/bar', + loginOpts + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toBe('https://other-org.acme.com/bar'); + }); + + test('should allow the returnTo to be be overwritten by getState() when provided in the querystring', async () => { + const res = await getResponse({ + url: '/api/auth/login?returnTo=/foo', + loginOpts: { + getLoginState() { + return { returnTo: '/bar' }; + } + } + }); + const { value: state } = res.cookies.get('state'); + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toBe('/bar'); + }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - foo: 'bar', - returnTo: '/foo' + test('should redirect to the identity provider with scope and audience', async () => { + const res = await getResponse({ + config: { authorizationParams: { scope: 'openid profile foobar', audience: 'https://api.acme.com/foo' } }, + url: '/api/auth/login' + }); + expect(res.status).toBe(302); + expect(urlParse(res.headers.get('location'), true).query).toMatchObject({ + scope: 'openid profile foobar', + audience: 'https://api.acme.com/foo' + }); }); }); - test('should allow the returnTo url to be provided in the querystring', async () => { - const loginOptions = { - returnTo: '/profile' - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login?returnTo=/foo', { cookieJar }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + describe('page router', () => { + afterEach(teardown); + + test('should create a state', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login', { cookieJar }); + + expect(cookieJar.getCookiesSync(baseUrl)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: 'nonce', + value: expect.any(String), + path: '/', + sameSite: 'lax' + }), + expect.objectContaining({ + key: 'state', + value: expect.any(String), + path: '/', + sameSite: 'lax' + }), + expect.objectContaining({ + key: 'code_verifier', + value: expect.any(String), + path: '/', + sameSite: 'lax' + }) + ]) + ); + }); + + test('should add returnTo to the state', async () => { + const baseUrl = await setup(withoutApi, { loginOptions: { returnTo: '/custom-url' } }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login', { cookieJar }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - returnTo: new URL('/foo', withoutApi.baseURL).toString() + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + expect(state).toBeTruthy(); + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState?.returnTo).toEqual('/custom-url'); }); - }); - test('should take the first returnTo url provided in the querystring', async () => { - const loginOptions = { - returnTo: '/profile' - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login?returnTo=/foo&returnTo=/bar', { cookieJar }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + test('should redirect to the identity provider', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = new CookieJar(); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); + + expect(statusCode).toBe(302); + + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + expect(urlParse(headers.location, true)).toMatchObject({ + protocol: 'https:', + host: 'acme.auth0.local', + hash: null, + query: { + client_id: '__test_client_id__', + scope: 'openid profile email', + response_type: 'code', + redirect_uri: 'http://www.acme.com/api/auth/callback', + nonce: expect.any(String), + state: state.split('.')[0], + code_challenge: expect.any(String), + code_challenge_method: 'S256' + }, + pathname: '/authorize' + }); + }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - returnTo: new URL('/foo', withoutApi.baseURL).toString() + test('should allow sending custom parameters to the authorization server', async () => { + const loginOptions = { + authorizationParams: { + max_age: 123, + login_hint: 'foo@acme.com', + ui_locales: 'nl', + scope: 'some other scope openid', + foo: 'bar', + organization: 'foo', + invitation: 'bar' + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); + + expect(statusCode).toBe(302); + expect(urlParse(headers.location, true)).toMatchObject({ + query: { + ...loginOptions.authorizationParams, + max_age: '123' + } + }); }); - }); - test('should not allow absolute urls to be provided in the querystring', async () => { - const loginOptions = { - returnTo: '/default-redirect' - }; - const baseUrl = await setup(withoutApi, { loginOptions }); + test('should pass organization config to the authorization server', async () => { + const baseUrl = await setup({ ...withoutApi, organization: 'foo' }); + const cookieJar = new CookieJar(); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); + + expect(statusCode).toBe(302); + expect(urlParse(headers.location, true)).toMatchObject({ + query: { + organization: 'foo' + } + }); + }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login?returnTo=https://www.google.com', { cookieJar }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + test('should prefer organization auth param to config', async () => { + const baseUrl = await setup( + { ...withoutApi, organization: 'foo' }, + { loginOptions: { authorizationParams: { organization: 'bar' } } } + ); + const cookieJar = new CookieJar(); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/login', { cookieJar, fullResponse: true }); + + expect(statusCode).toBe(302); + expect(urlParse(headers.location, true)).toMatchObject({ + query: { + organization: 'bar' + } + }); + }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({}); - }); + test('should allow adding custom data to the state', async () => { + const loginOptions = { + getLoginState: (): Record => { + return { + foo: 'bar' + }; + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login', { cookieJar }); + + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + foo: 'bar', + returnTo: 'http://www.acme.com/' + }); + }); - test('should allow absolute urls in params of returnTo urls', async () => { - const loginOptions = { - returnTo: '/default-redirect' - }; - const baseUrl = await setup(withoutApi, { loginOptions }); + test('should merge returnTo and state', async () => { + const loginOptions = { + returnTo: '/profile', + getLoginState: (): Record => { + return { + foo: 'bar' + }; + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login', { cookieJar }); + + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + foo: 'bar', + returnTo: '/profile' + }); + }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login?returnTo=/foo?url=https://www.google.com', { cookieJar }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + test('should allow the getState method to overwrite returnTo', async () => { + const loginOptions = { + returnTo: '/profile', + getLoginState: (): Record => { + return { + foo: 'bar', + returnTo: '/foo' + }; + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login', { cookieJar }); + + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + foo: 'bar', + returnTo: '/foo' + }); + }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - returnTo: new URL('/foo?url=https://www.google.com', withoutApi.baseURL).toString() + test('should allow the returnTo url to be provided in the querystring', async () => { + const loginOptions = { + returnTo: '/profile' + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=/foo', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: new URL('/foo', withoutApi.baseURL).toString() + }); }); - }); - test('should redirect relative to the redirect_uri over the base url', async () => { - const loginOptions = { - returnTo: '/default-redirect', - authorizationParams: { - redirect_uri: 'https://other-org.acme.com/api/auth/callback' - } - }; - const baseUrl = await setup(withoutApi, { loginOptions }); + test('should take the first returnTo url provided in the querystring', async () => { + const loginOptions = { + returnTo: '/profile' + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=/foo&returnTo=/bar', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: new URL('/foo', withoutApi.baseURL).toString() + }); + }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login?returnTo=/foo', { cookieJar }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + test('should not allow absolute urls to be provided in the querystring', async () => { + const loginOptions = { + returnTo: '/default-redirect' + }; + const baseUrl = await setup(withoutApi, { loginOptions }); - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - returnTo: 'https://other-org.acme.com/foo' + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=https://www.google.com', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({}); }); - }); - test('should allow the returnTo to be be overwritten by getState() when provided in the querystring', async () => { - const loginOptions = { - returnTo: '/profile', - getLoginState: (): Record => { - return { - returnTo: '/foo' - }; - } - }; - const baseUrl = await setup(withoutApi, { loginOptions }); - const cookieJar = new CookieJar(); - await get(baseUrl, '/api/auth/login', { cookieJar }); - const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; - - const decodedState = decodeState(state.split('.')[0]); - expect(decodedState).toEqual({ - returnTo: '/foo' + test('should allow absolute urls in params of returnTo urls', async () => { + const loginOptions = { + returnTo: '/default-redirect' + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=/foo?url=https://www.google.com', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: new URL('/foo?url=https://www.google.com', withoutApi.baseURL).toString() + }); + }); + + test('should redirect relative to the redirect_uri over the base url', async () => { + const loginOptions = { + returnTo: '/default-redirect', + authorizationParams: { + redirect_uri: 'https://other-org.acme.com/api/auth/callback' + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=/foo', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: 'https://other-org.acme.com/foo' + }); + }); + + test('should allow the returnTo to be be overwritten by getState() when provided in the querystring', async () => { + const loginOptions = { + returnTo: '/profile', + getLoginState: (): Record => { + return { + returnTo: '/foo' + }; + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: '/foo' + }); }); - }); - test('should redirect to the identity provider with scope and audience', async () => { - const baseUrl = await setup(withApi); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/login', { fullResponse: true }); + test('should redirect to the identity provider with scope and audience', async () => { + const baseUrl = await setup(withApi); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/login', { fullResponse: true }); - expect(statusCode).toBe(302); + expect(statusCode).toBe(302); - expect(urlParse(headers.location, true).query).toMatchObject({ - scope: 'openid profile read:customer', - audience: 'https://api.acme.com' + expect(urlParse(headers.location, true).query).toMatchObject({ + scope: 'openid profile read:customer', + audience: 'https://api.acme.com' + }); }); }); }); diff --git a/tests/handlers/logout.test.ts b/tests/handlers/logout.test.ts index b043e7dff..9b4810029 100644 --- a/tests/handlers/logout.test.ts +++ b/tests/handlers/logout.test.ts @@ -3,137 +3,227 @@ import { parse as parseUrl } from 'url'; import { withoutApi } from '../fixtures/default-settings'; import { get } from '../auth0-session/fixtures/helpers'; import { setup, teardown, login } from '../fixtures/setup'; +import { getResponse, login as appRouterLogin } from '../fixtures/app-router-helpers'; describe('logout handler', () => { - afterEach(teardown); - - test('should redirect to auth0', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); - - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/logout', { - cookieJar, - fullResponse: true - }); - - expect(statusCode).toBe(302); - expect(parseUrl(headers['location'], true)).toMatchObject({ - protocol: 'https:', - host: 'acme.auth0.local', - query: { - returnTo: 'http://www.acme.com', - client_id: '__test_client_id__' - }, - pathname: '/v2/logout' - }); - }); - - test('should pass logout params to auth0', async () => { - const baseUrl = await setup(withoutApi, { logoutOptions: { logoutParams: { foo: 'bar' } } }); - const cookieJar = await login(baseUrl); - - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/logout', { - cookieJar, - fullResponse: true + describe('app router', () => { + test('should redirect to auth0', async () => { + const loginRes = await appRouterLogin(); + const cookies = { appSession: loginRes.cookies.get('appSession').value }; + const res = await getResponse({ url: '/api/auth/logout', cookies }); + expect(res.status).toBe(302); + expect(parseUrl(res.headers.get('location'), true)).toMatchObject({ + protocol: 'https:', + host: 'acme.auth0.local', + query: { + returnTo: 'http://www.acme.com', + client_id: '__test_client_id__' + }, + pathname: '/v2/logout' + }); }); - expect(statusCode).toBe(302); - expect(parseUrl(headers['location'], true)).toMatchObject({ - protocol: 'https:', - host: 'acme.auth0.local', - query: { + test('should pass logout params to auth0', async () => { + const loginRes = await appRouterLogin(); + const cookies = { appSession: loginRes.cookies.get('appSession').value }; + const res = await getResponse({ + url: '/api/auth/logout', + cookies, + logoutOpts: { logoutParams: { foo: 'bar' } } + }); + expect(res.status).toBe(302); + expect(parseUrl(res.headers.get('location'), true).query).toMatchObject({ returnTo: 'http://www.acme.com', client_id: '__test_client_id__', foo: 'bar' - }, - pathname: '/v2/logout' + }); }); - }); - - test('should return to the custom path', async () => { - const customReturnTo = 'https://www.foo.bar'; - const baseUrl = await setup(withoutApi, { - logoutOptions: { returnTo: customReturnTo } - }); - const cookieJar = await login(baseUrl); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/logout', { - cookieJar, - fullResponse: true + test('should return to the custom path', async () => { + const loginRes = await appRouterLogin(); + const cookies = { appSession: loginRes.cookies.get('appSession').value }; + const res = await getResponse({ + url: '/api/auth/logout', + cookies, + logoutOpts: { returnTo: 'https://www.google.com' } + }); + expect(res.status).toBe(302); + expect(parseUrl(res.headers.get('location'), true).query).toMatchObject({ + returnTo: 'https://www.google.com' + }); }); - expect(statusCode).toBe(302); - expect(parseUrl(headers['location'], true).query).toMatchObject({ - returnTo: 'https://www.foo.bar' + test('should use end_session_endpoint when configured', async () => { + const loginRes = await appRouterLogin(); + const cookies = { appSession: loginRes.cookies.get('appSession').value }; + const res = await getResponse({ + url: '/api/auth/logout', + cookies, + config: { auth0Logout: false }, + discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + }); + expect(res.status).toBe(302); + expect(parseUrl(res.headers.get('location'))).toMatchObject({ + host: 'my-end-session-endpoint', + pathname: '/logout' + }); }); - }); - test('should use end_session_endpoint when configured', async () => { - const baseUrl = await setup( - { ...withoutApi, auth0Logout: false }, - { + test('should use auth0 logout by default even when end_session_endpoint is discovered', async () => { + const loginRes = await appRouterLogin(); + const cookies = { appSession: loginRes.cookies.get('appSession').value }; + const res = await getResponse({ + url: '/api/auth/logout', + cookies, discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } - } - ); - const cookieJar = await login(baseUrl); - - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/logout', { - cookieJar, - fullResponse: true + }); + expect(res.status).toBe(302); + expect(parseUrl(res.headers.get('location'))).toMatchObject({ + host: 'acme.auth0.local', + pathname: '/v2/logout' + }); }); - expect(statusCode).toBe(302); - expect(parseUrl(headers['location'])).toMatchObject({ - host: 'my-end-session-endpoint', - pathname: '/logout' + test('should delete the session', async () => { + const loginRes = await appRouterLogin(); + const cookies = { appSession: loginRes.cookies.get('appSession').value }; + const res = await getResponse({ url: '/api/auth/logout', cookies }); + expect(res.status).toBe(302); + expect(new Date(res.cookies.get('appSession').expires).getTime()).toBe(0); }); }); - test('should use auth0 logout by default even when end_session_endpoint is discovered', async () => { - const baseUrl = await setup(withoutApi, { - discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + describe('page router', () => { + afterEach(teardown); + + test('should redirect to auth0', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(statusCode).toBe(302); + expect(parseUrl(headers['location'], true)).toMatchObject({ + protocol: 'https:', + host: 'acme.auth0.local', + query: { + returnTo: 'http://www.acme.com', + client_id: '__test_client_id__' + }, + pathname: '/v2/logout' + }); }); - const cookieJar = await login(baseUrl); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/api/auth/logout', { - cookieJar, - fullResponse: true + test('should pass logout params to auth0', async () => { + const baseUrl = await setup(withoutApi, { logoutOptions: { logoutParams: { foo: 'bar' } } }); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(statusCode).toBe(302); + expect(parseUrl(headers['location'], true)).toMatchObject({ + protocol: 'https:', + host: 'acme.auth0.local', + query: { + returnTo: 'http://www.acme.com', + client_id: '__test_client_id__', + foo: 'bar' + }, + pathname: '/v2/logout' + }); }); - expect(statusCode).toBe(302); - expect(parseUrl(headers['location'])).toMatchObject({ - host: 'acme.auth0.local', - pathname: '/v2/logout' + test('should return to the custom path', async () => { + const customReturnTo = 'https://www.foo.bar'; + const baseUrl = await setup(withoutApi, { + logoutOptions: { returnTo: customReturnTo } + }); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(statusCode).toBe(302); + expect(parseUrl(headers['location'], true).query).toMatchObject({ + returnTo: 'https://www.foo.bar' + }); }); - }); - test('should delete the session', async () => { - const baseUrl = await setup(withoutApi, { - discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + test('should use end_session_endpoint when configured', async () => { + const baseUrl = await setup( + { ...withoutApi, auth0Logout: false }, + { + discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + } + ); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(statusCode).toBe(302); + expect(parseUrl(headers['location'])).toMatchObject({ + host: 'my-end-session-endpoint', + pathname: '/logout' + }); }); - const cookieJar = await login(baseUrl); - const { - res: { headers } - } = await get(baseUrl, '/api/auth/logout', { - cookieJar, - fullResponse: true + test('should use auth0 logout by default even when end_session_endpoint is discovered', async () => { + const baseUrl = await setup(withoutApi, { + discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + }); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(statusCode).toBe(302); + expect(parseUrl(headers['location'])).toMatchObject({ + host: 'acme.auth0.local', + pathname: '/v2/logout' + }); }); - expect(parse(headers['set-cookie'][0])).toMatchObject({ - appSession: '', - 'Max-Age': '0', - Path: '/' + test('should delete the session', async () => { + const baseUrl = await setup(withoutApi, { + discoveryOptions: { end_session_endpoint: 'https://my-end-session-endpoint/logout' } + }); + const cookieJar = await login(baseUrl); + + const { + res: { headers } + } = await get(baseUrl, '/api/auth/logout', { + cookieJar, + fullResponse: true + }); + + expect(parse(headers['set-cookie'][0])).toMatchObject({ + appSession: '', + 'Max-Age': '0', + Path: '/' + }); }); }); }); diff --git a/tests/handlers/profile.test.ts b/tests/handlers/profile.test.ts index 1c9f9e597..55d0be33d 100644 --- a/tests/handlers/profile.test.ts +++ b/tests/handlers/profile.test.ts @@ -1,145 +1,323 @@ import nock from 'nock'; +import { cookies } from 'next/headers'; import { withApi, withoutApi } from '../fixtures/default-settings'; import { refreshTokenRotationExchange, userInfo } from '../fixtures/oidc-nocks'; import { get } from '../auth0-session/fixtures/helpers'; import { setup, teardown, login } from '../fixtures/setup'; import { Session, AfterCallbackPageRoute } from '../../src'; import { makeIdToken } from '../auth0-session/fixtures/cert'; +import { + getResponse, + login as appRouterLogin, + getSession as appRouterGetSession +} from '../fixtures/app-router-helpers'; +import { mocked } from 'ts-jest/utils'; +import { NextRequest } from 'next/server'; + +jest.mock('next/headers'); describe('profile handler', () => { - afterEach(teardown); + describe('app router', () => { + test('should be empty when not logged in', async () => { + await expect(getResponse({ url: '/api/auth/me' })).resolves.toMatchObject({ status: 204 }); + }); - test('should throw an error when not logged in', async () => { - const baseUrl = await setup(withoutApi); + test('should return the profile when logged in', async () => { + const loginRes = await appRouterLogin(); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ nickname: '__test_nickname__', sub: '__test_sub__' }); + }); - await expect(get(baseUrl, '/api/auth/me')).resolves.toBe(''); - }); + test('should not allow caching the profile response', async () => { + const loginRes = await appRouterLogin(); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value } + }); + expect(res.headers.get('cache-control')).toBe('no-store'); + }); - test('should return the profile when logged in', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); + test('should not allow caching the profile response when refetch is true', async () => { + const loginRes = await appRouterLogin(); + mocked(cookies).mockImplementation(() => loginRes.cookies); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { refetch: true } + }); + expect(res.headers.get('cache-control')).toBe('no-store'); + }); - const profile = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(profile).toStrictEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - }); + test('should throw if re-fetching with no access token', async () => { + const loginRes = await appRouterLogin({ + callbackOpts: { + afterCallback(_req: any, session: any): Session { + delete session.accessToken; + return session; + } + } + }); + mocked(cookies).mockImplementation(() => loginRes.cookies); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { refetch: true } + }); + expect(res.status).toBe(500); + expect(res.statusText).toMatch(/The user does not have a valid access token/); + }); - test('should not allow caching the profile response', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); + test('should refetch the user and update the session', async () => { + const loginRes = await appRouterLogin(); + mocked(cookies).mockImplementation(() => loginRes.cookies); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { refetch: true }, + userInfoPayload: { foo: 'bar' } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ foo: 'bar' }); + }); - const { res } = await get(baseUrl, '/api/auth/me', { cookieJar, fullResponse: true }); - expect(res.headers['cache-control']).toEqual('no-store'); - }); + test("should refetch the user and fail if it can't get an access token", async () => { + const loginRes = await appRouterLogin({ + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + session.accessTokenExpiresAt = -60; + return session; + } + } + }); + mocked(cookies).mockImplementation(() => loginRes.cookies); + nock.cleanAll(); + nock(`${withApi.issuerBaseURL}`) + .post('/oauth/token', `grant_type=refresh_token&refresh_token=GEbRxBN...edjnXbL`) + .reply(200, { + id_token: await makeIdToken({ iss: 'https://acme.auth0.local/' }), + token_type: 'Bearer', + expires_in: 750, + scope: 'read:foo write:foo' + }); - test('should not allow caching the profile response when refetch is true', async () => { - const baseUrl = await setup(withoutApi, { profileOptions: { refetch: true } }); - const cookieJar = await login(baseUrl); + await expect( + getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { refetch: true }, + userInfoPayload: { foo: 'bar' }, + clearNock: false, + userInfoToken: 'new-access-token' + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/No access token available to refetch the profile/) + }); + }); - const { res } = await get(baseUrl, '/api/auth/me', { cookieJar, fullResponse: true }); - expect(res.headers['cache-control']).toEqual('no-store'); - }); + test('should refetch the user and preserve new tokens', async () => { + const loginRes = await appRouterLogin({ + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + session.accessTokenExpiresAt = -60; + return session; + } + } + }); + mocked(cookies).mockImplementation(() => loginRes.cookies); + nock.cleanAll(); + await refreshTokenRotationExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-access-token', 'new-refresh-token'); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { refetch: true }, + userInfoPayload: { foo: 'bar' }, + clearNock: false, + userInfoToken: 'new-access-token' + }); + expect(res.status).toBe(200); + await expect(appRouterGetSession(withApi, res)).resolves.toMatchObject({ + user: expect.objectContaining({ foo: 'bar' }), + accessToken: 'new-access-token', + refreshToken: 'new-refresh-token' + }); + await expect(res.json()).resolves.toMatchObject({ foo: 'bar' }); + }); + + test('should update the session in the afterRefetch hook', async () => { + const loginRes = await appRouterLogin(); + const res = await getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { + refetch: true, + afterRefetch(_req: NextRequest, session: Session) { + return { ...session, user: { ...session.user, foo: 'baz' } }; + } + } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ foo: 'baz' }); + }); - test('should throw if re-fetching with no access token', async () => { - const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { - delete session.accessToken; - return session; - }; - const baseUrl = await setup(withoutApi, { profileOptions: { refetch: true }, callbackOptions: { afterCallback } }); - const cookieJar = await login(baseUrl); - - await expect(get(baseUrl, '/api/auth/me', { cookieJar })).rejects.toThrow( - 'The user does not have a valid access token.' - ); + test('should throw from the afterRefetch hook', async () => { + const loginRes = await appRouterLogin(); + await expect( + getResponse({ + url: '/api/auth/me', + cookies: { appSession: loginRes.cookies.get('appSession').value }, + profileOpts: { + refetch: true, + afterRefetch() { + throw new Error('some validation error'); + } + } + }) + ).resolves.toMatchObject({ status: 500, statusText: expect.stringMatching(/some validation error/) }); + }); }); + describe('page router', () => { + afterEach(teardown); - test('should refetch the user and update the session', async () => { - const baseUrl = await setup(withoutApi, { profileOptions: { refetch: true }, userInfoPayload: { foo: 'bar' } }); - const cookieJar = await login(baseUrl); + test('should throw an error when not logged in', async () => { + const baseUrl = await setup(withoutApi); - const profile = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(profile).toMatchObject({ foo: 'bar', nickname: '__test_nickname__', sub: '__test_sub__' }); - // check that the session is saved - userInfo(withoutApi, 'eyJz93a...k4laUWw', {}); - const profile2 = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(profile2).toMatchObject({ foo: 'bar', nickname: '__test_nickname__', sub: '__test_sub__' }); - }); + await expect(get(baseUrl, '/api/auth/me')).resolves.toBe(''); + }); - test("should refetch the user and fail if it can't get an access token", async () => { - const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { - session.accessTokenExpiresAt = -60; - return session; - }; - const baseUrl = await setup(withoutApi, { - profileOptions: { refetch: true }, - userInfoPayload: { foo: 'bar' }, - callbackOptions: { - afterCallback - } - }); - const cookieJar = await login(baseUrl); - - nock(`${withoutApi.issuerBaseURL}`) - .post('/oauth/token', `grant_type=refresh_token&refresh_token=GEbRxBN...edjnXbL`) - .reply(200, { - id_token: await makeIdToken({ iss: 'https://acme.auth0.local/' }), - token_type: 'Bearer', - expires_in: 750, - scope: 'read:foo write:foo' - }); - await expect(get(baseUrl, '/api/auth/me', { cookieJar })).rejects.toThrow( - 'No access token available to refetch the profile' - ); - }); + test('should return the profile when logged in', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); - test('should refetch the user and preserve new tokens', async () => { - const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { - session.accessTokenExpiresAt = -60; - return session; - }; - const baseUrl = await setup(withApi, { - profileOptions: { refetch: true }, - userInfoPayload: { foo: 'bar' }, - callbackOptions: { - afterCallback - }, - userInfoToken: 'new-access-token' - }); - await refreshTokenRotationExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-access-token', 'new-refresh-token'); - const cookieJar = await login(baseUrl); - const profile = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(profile).toMatchObject({ foo: 'bar' }); - const session = await get(baseUrl, '/api/session', { cookieJar }); - expect(session.accessToken).toEqual('new-access-token'); - expect(session.refreshToken).toEqual('new-refresh-token'); - }); + const profile = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(profile).toStrictEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + }); + + test('should not allow caching the profile response', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + + const { res } = await get(baseUrl, '/api/auth/me', { cookieJar, fullResponse: true }); + expect(res.headers['cache-control']).toEqual('no-store'); + }); + + test('should not allow caching the profile response when refetch is true', async () => { + const baseUrl = await setup(withoutApi, { profileOptions: { refetch: true } }); + const cookieJar = await login(baseUrl); + + const { res } = await get(baseUrl, '/api/auth/me', { cookieJar, fullResponse: true }); + expect(res.headers['cache-control']).toEqual('no-store'); + }); + + test('should throw if re-fetching with no access token', async () => { + const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { + delete session.accessToken; + return session; + }; + const baseUrl = await setup(withoutApi, { + profileOptions: { refetch: true }, + callbackOptions: { afterCallback } + }); + const cookieJar = await login(baseUrl); + + await expect(get(baseUrl, '/api/auth/me', { cookieJar })).rejects.toThrow( + 'The user does not have a valid access token.' + ); + }); + + test('should refetch the user and update the session', async () => { + const baseUrl = await setup(withoutApi, { profileOptions: { refetch: true }, userInfoPayload: { foo: 'bar' } }); + const cookieJar = await login(baseUrl); + + const profile = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(profile).toMatchObject({ foo: 'bar', nickname: '__test_nickname__', sub: '__test_sub__' }); + // check that the session is saved + userInfo(withoutApi, 'eyJz93a...k4laUWw', {}); + const profile2 = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(profile2).toMatchObject({ foo: 'bar', nickname: '__test_nickname__', sub: '__test_sub__' }); + }); - test('should update the session in the afterRefetch hook', async () => { - const baseUrl = await setup(withoutApi, { - profileOptions: { - refetch: true, - afterRefetch(_req, _res, session) { - session.user.foo = 'bar'; - return session; + test("should refetch the user and fail if it can't get an access token", async () => { + const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { + session.accessTokenExpiresAt = -60; + return session; + }; + const baseUrl = await setup(withoutApi, { + profileOptions: { refetch: true }, + userInfoPayload: { foo: 'bar' }, + callbackOptions: { + afterCallback } - } + }); + const cookieJar = await login(baseUrl); + + nock(`${withoutApi.issuerBaseURL}`) + .post('/oauth/token', `grant_type=refresh_token&refresh_token=GEbRxBN...edjnXbL`) + .reply(200, { + id_token: await makeIdToken({ iss: 'https://acme.auth0.local/' }), + token_type: 'Bearer', + expires_in: 750, + scope: 'read:foo write:foo' + }); + await expect(get(baseUrl, '/api/auth/me', { cookieJar })).rejects.toThrow( + 'No access token available to refetch the profile' + ); }); - const cookieJar = await login(baseUrl); - const user = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(user.foo).toEqual('bar'); - }); + test('should refetch the user and preserve new tokens', async () => { + const afterCallback: AfterCallbackPageRoute = (_req, _res, session: Session): Session => { + session.accessTokenExpiresAt = -60; + return session; + }; + const baseUrl = await setup(withApi, { + profileOptions: { refetch: true }, + userInfoPayload: { foo: 'bar' }, + callbackOptions: { + afterCallback + }, + userInfoToken: 'new-access-token' + }); + await refreshTokenRotationExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-access-token', 'new-refresh-token'); + const cookieJar = await login(baseUrl); + const profile = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(profile).toMatchObject({ foo: 'bar' }); + const session = await get(baseUrl, '/api/session', { cookieJar }); + expect(session.accessToken).toEqual('new-access-token'); + expect(session.refreshToken).toEqual('new-refresh-token'); + }); - test('should throw from the afterRefetch hook', async () => { - const baseUrl = await setup(withoutApi, { - profileOptions: { - refetch: true, - afterRefetch() { - throw new Error('some validation error'); + test('should update the session in the afterRefetch hook', async () => { + const baseUrl = await setup(withoutApi, { + profileOptions: { + refetch: true, + afterRefetch(_req, _res, session) { + session.user.foo = 'bar'; + return session; + } } - } + }); + const cookieJar = await login(baseUrl); + + const user = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(user.foo).toEqual('bar'); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/auth/me', { cookieJar })).rejects.toThrowError('some validation error'); + test('should throw from the afterRefetch hook', async () => { + const baseUrl = await setup(withoutApi, { + profileOptions: { + refetch: true, + afterRefetch() { + throw new Error('some validation error'); + } + } + }); + const cookieJar = await login(baseUrl); + + await expect(get(baseUrl, '/api/auth/me', { cookieJar })).rejects.toThrowError('some validation error'); + }); }); }); From d8ec9c2d49d25d0e4da02a85c9b4c48dd8bbeddc Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Mon, 5 Jun 2023 10:59:04 +0100 Subject: [PATCH 30/61] Add tests for getting the session and AT --- src/session/get-session.ts | 10 +- tests/fixtures/app-router-helpers.ts | 11 +- tests/handlers/profile.test.ts | 9 +- tests/session/get-access-token.test.ts | 881 +++++++++++++++++-------- 4 files changed, 608 insertions(+), 303 deletions(-) diff --git a/src/session/get-session.ts b/src/session/get-session.ts index 5da1e74a2..ccedb0536 100644 --- a/src/session/get-session.ts +++ b/src/session/get-session.ts @@ -1,5 +1,6 @@ import { IncomingMessage, ServerResponse } from 'http'; import { NextApiRequest, NextApiResponse } from 'next'; +import { NextRequest, NextResponse } from 'next/server'; import { SessionCache, Session, get } from '../session'; /** @@ -8,15 +9,18 @@ import { SessionCache, Session, get } from '../session'; * @category Server */ export type GetSession = ( - req?: IncomingMessage | NextApiRequest, - res?: ServerResponse | NextApiResponse + req?: IncomingMessage | NextApiRequest | NextRequest, + res?: ServerResponse | NextApiResponse | NextResponse ) => Promise; /** * @ignore */ export default function sessionFactory(sessionCache: SessionCache) { - return async (req?: IncomingMessage | NextApiRequest, res?: ServerResponse | NextApiResponse) => { + return async ( + req?: IncomingMessage | NextApiRequest | NextRequest, + res?: ServerResponse | NextApiResponse | NextResponse + ) => { const [session] = await get({ req, res, sessionCache }); return session; }; diff --git a/tests/fixtures/app-router-helpers.ts b/tests/fixtures/app-router-helpers.ts index fd79c7584..7e291f754 100644 --- a/tests/fixtures/app-router-helpers.ts +++ b/tests/fixtures/app-router-helpers.ts @@ -1,5 +1,6 @@ import nock from 'nock'; import { + Auth0Server, CallbackOptions, Claims, ConfigParameters, @@ -31,8 +32,11 @@ export type GetResponseOpts = { profileOpts?: ProfileOptions; extraHandlers?: any; clearNock?: boolean; + auth0Instance?: Auth0Server; }; +export type LoginOpts = Omit; + export const getResponse = async ({ url, config, @@ -46,13 +50,14 @@ export const getResponse = async ({ logoutOpts, profileOpts, extraHandlers, - clearNock = true + clearNock = true, + auth0Instance }: GetResponseOpts) => { const opts = { ...withApi, ...config }; clearNock && nock.cleanAll(); await setupNock(opts, { idTokenClaims, discoveryOptions, userInfoPayload, userInfoToken }); const auth0 = url.split('?')[0].split('/').slice(3); - const instance = initAuth0(opts); + const instance = auth0Instance || initAuth0(opts); const handleAuth = instance.handleAuth({ ...(callbackOpts && { callback: instance.handleCallback(callbackOpts) }), ...(loginOpts && { login: instance.handleLogin(loginOpts) }), @@ -84,7 +89,7 @@ export const getSession = async (config: any, res: NextResponse) => { return session; }; -export const login = async (opts: Omit = {}) => { +export const login = async (opts: LoginOpts = {}) => { const state = encodeState({ returnTo: '/' }); const res = await getResponse({ ...opts, diff --git a/tests/handlers/profile.test.ts b/tests/handlers/profile.test.ts index 55d0be33d..c7ea4e3ed 100644 --- a/tests/handlers/profile.test.ts +++ b/tests/handlers/profile.test.ts @@ -1,5 +1,4 @@ import nock from 'nock'; -import { cookies } from 'next/headers'; import { withApi, withoutApi } from '../fixtures/default-settings'; import { refreshTokenRotationExchange, userInfo } from '../fixtures/oidc-nocks'; import { get } from '../auth0-session/fixtures/helpers'; @@ -11,7 +10,6 @@ import { login as appRouterLogin, getSession as appRouterGetSession } from '../fixtures/app-router-helpers'; -import { mocked } from 'ts-jest/utils'; import { NextRequest } from 'next/server'; jest.mock('next/headers'); @@ -43,7 +41,6 @@ describe('profile handler', () => { test('should not allow caching the profile response when refetch is true', async () => { const loginRes = await appRouterLogin(); - mocked(cookies).mockImplementation(() => loginRes.cookies); const res = await getResponse({ url: '/api/auth/me', cookies: { appSession: loginRes.cookies.get('appSession').value }, @@ -61,7 +58,6 @@ describe('profile handler', () => { } } }); - mocked(cookies).mockImplementation(() => loginRes.cookies); const res = await getResponse({ url: '/api/auth/me', cookies: { appSession: loginRes.cookies.get('appSession').value }, @@ -73,7 +69,6 @@ describe('profile handler', () => { test('should refetch the user and update the session', async () => { const loginRes = await appRouterLogin(); - mocked(cookies).mockImplementation(() => loginRes.cookies); const res = await getResponse({ url: '/api/auth/me', cookies: { appSession: loginRes.cookies.get('appSession').value }, @@ -93,7 +88,7 @@ describe('profile handler', () => { } } }); - mocked(cookies).mockImplementation(() => loginRes.cookies); + //mocked(cookies).mockImplementation(() => loginRes.cookies); nock.cleanAll(); nock(`${withApi.issuerBaseURL}`) .post('/oauth/token', `grant_type=refresh_token&refresh_token=GEbRxBN...edjnXbL`) @@ -128,7 +123,7 @@ describe('profile handler', () => { } } }); - mocked(cookies).mockImplementation(() => loginRes.cookies); + //mocked(cookies).mockImplementation(() => loginRes.cookies); nock.cleanAll(); await refreshTokenRotationExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-access-token', 'new-refresh-token'); const res = await getResponse({ diff --git a/tests/session/get-access-token.test.ts b/tests/session/get-access-token.test.ts index 6f615b9a7..54b535a61 100644 --- a/tests/session/get-access-token.test.ts +++ b/tests/session/get-access-token.test.ts @@ -1,347 +1,648 @@ import { NextApiRequest, NextApiResponse } from 'next'; +import { NextRequest, NextResponse } from 'next/server'; +import { cookies as nextCookies } from 'next/headers'; +import nock from 'nock'; +import { mocked } from 'ts-jest/utils'; import { login, setup, teardown } from '../fixtures/setup'; import { withApi } from '../fixtures/default-settings'; import { get } from '../auth0-session/fixtures/helpers'; -import { Session } from '../../src'; +import { AccessTokenRequest, initAuth0, Session } from '../../src'; import { failedRefreshTokenExchange, refreshTokenExchange, refreshTokenRotationExchange } from '../fixtures/oidc-nocks'; import { makeIdToken } from '../auth0-session/fixtures/cert'; -import nock from 'nock'; +import { getResponse, GetResponseOpts, LoginOpts, login as appRouterLogin } from '../fixtures/app-router-helpers'; + +jest.mock('next/headers'); + +const getAccessTokenResponse = async ({ + authenticated = false, + getResOpts = {}, + loginOpts = {}, + getAccessTokenOpts, + userRsc +}: { + authenticated?: boolean; + getResOpts?: Omit; + loginOpts?: LoginOpts; + getAccessTokenOpts?: AccessTokenRequest; + userRsc?: boolean; +} = {}) => { + const auth0Instance = initAuth0(withApi); + let cookies: { appSession?: string } = {}; + if (authenticated) { + const loginRes = await appRouterLogin(loginOpts); + cookies.appSession = loginRes.cookies.get('appSession').value; + if (userRsc) { + mocked(nextCookies).mockImplementation(() => loginRes.cookies); + } + } + await refreshTokenExchange( + withApi, + 'GEbRxBN...edjnXbL', + { + email: 'john@test.com', + name: 'john doe', + sub: '123' + }, + 'new-token' + ); + return getResponse({ + auth0Instance, + url: userRsc ? '/api/auth/access-token-rsc' : '/api/auth/access-token', + extraHandlers: { + async 'access-token'(req: NextRequest) { + const res = new NextResponse(); + const at = await auth0Instance.getAccessToken(req, res, getAccessTokenOpts); + const session = await auth0Instance.getSession(req, res); + return NextResponse.json({ at, session }, res); + }, + async 'access-token-rsc'() { + const at = await auth0Instance.getAccessToken(getAccessTokenOpts); + const session = await auth0Instance.getSession(); + return NextResponse.json({ at, session }); + } + }, + cookies, + clearNock: false, + ...getResOpts + }).finally(() => nock.cleanAll()); +}; describe('get access token', () => { - afterEach(teardown); + describe('app router', () => { + test('should fail if the session is missing', async () => { + await expect(getAccessTokenResponse()).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/The user does not have a valid session/) + }); + }); - test('should fail if the session is missing', async () => { - const baseUrl = await setup(withApi); + test('should fail if access_token and refresh_token are missing', async () => { + await expect( + getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + delete session.accessToken; + delete session.refreshToken; + return session; + } + } + } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/The user does not have a valid access token/) + }); + }); - await expect(get(baseUrl, '/api/access-token')).rejects.toThrow('The user does not have a valid session.'); - }); + test('should fail if access_token expiry is missing', async () => { + await expect( + getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + delete session.accessTokenExpiresAt; + return session; + } + } + } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/Expiration information for the access token is not available/) + }); + }); - test('should fail if access_token and refresh_token are missing', async () => { - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - delete session.accessToken; - delete session.refreshToken; - return session; + test('should fail if access_token scope is missing', async () => { + await expect( + getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + delete session.accessTokenScope; + return session; + } + } + }, + getAccessTokenOpts: { + scopes: ['read:foo'] + } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/An access token with the requested scopes could not be provided/) + }); + }); + + test("should fail if access_token scope doesn't match requested scope", async () => { + await expect( + getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + return { ...session, accessTokenScope: 'read:bar' }; + } + } + }, + getAccessTokenOpts: { + scopes: ['read:foo'] + } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/Could not retrieve an access token with scopes "read:foo"/) + }); + }); + + test('should fail if the access token is expired', async () => { + await expect( + getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + delete session.refreshToken; + return { ...session, accessTokenExpiresAt: -60 }; + } + } + } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching(/The access token expired and a refresh token is not available/) + }); + }); + + test('should fail if you try to refresh the access token without a refresh token', async () => { + await expect( + getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + delete session.refreshToken; + return session; + } + } + }, + getAccessTokenOpts: { refresh: true } + }) + ).resolves.toMatchObject({ + status: 500, + statusText: expect.stringMatching( + /A refresh token is required to refresh the access token, but none is present/ + ) + }); + }); + + test('should return an access token', async () => { + const res = await getAccessTokenResponse({ + authenticated: true + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'eyJz93a...k4laUWw' } + }); + }); + + test('should retrieve a new access token if the old one is expired and update the profile', async () => { + const res = await getAccessTokenResponse({ + authenticated: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + return { ...session, accessTokenExpiresAt: -60 }; + } + } } - } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'new-token' }, + session: expect.objectContaining({ user: expect.objectContaining({ email: 'john@test.com' }) }) + }); + }); + + test('should retrieve a new access token if force refresh is set', async () => { + const res = await getAccessTokenResponse({ + authenticated: true, + getAccessTokenOpts: { refresh: true } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'new-token' }, + session: expect.objectContaining({ user: expect.objectContaining({ email: 'john@test.com' }) }) + }); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'The user does not have a valid access token.' - ); - }); - test('should fail if access_token expiry is missing', async () => { - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - delete session.accessTokenExpiresAt; - return session; + test('should retrieve a new access token and update the session based on afterRefresh', async () => { + const res = await getAccessTokenResponse({ + authenticated: true, + getAccessTokenOpts: { + refresh: true, + afterRefresh(_req, _res, session) { + return { ...session, foo: 'baz' }; + } } - } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'new-token' }, + session: expect.objectContaining({ foo: 'baz' }) + }); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'Expiration information for the access token is not available. The user will need to sign in again.' - ); }); - test('should fail if access_token scope is missing', async () => { - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - delete session.accessTokenScope; - return session; + describe('app router (server component)', () => { + test('should return an access token', async () => { + const res = await getAccessTokenResponse({ + authenticated: true, + userRsc: true + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'eyJz93a...k4laUWw' } + }); + }); + + test('should retrieve a new access token if the old one is expired and update the profile', async () => { + const res = await getAccessTokenResponse({ + authenticated: true, + userRsc: true, + loginOpts: { + callbackOpts: { + afterCallback(_req: NextRequest, session: Session) { + return { ...session, accessTokenExpiresAt: -60 }; + } + } } - }, - getAccessTokenOptions: { - scopes: ['read:foo'] - } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'new-token' }, + session: expect.objectContaining({ user: expect.objectContaining({ email: 'john@test.com' }) }) + }); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'An access token with the requested scopes could not be provided. The user will need to sign in again.' - ); - }); - test("should fail if access_token scope doesn't match requested scope", async () => { - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - session.accessTokenScope = 'read:bar'; - return session; + test('should retrieve a new access token and update the session based on afterRefresh', async () => { + const res = await getAccessTokenResponse({ + authenticated: true, + userRsc: true, + getAccessTokenOpts: { + refresh: true, + afterRefresh(_req, _res, session) { + return { ...session, foo: 'baz' }; + } } - }, - getAccessTokenOptions: { - scopes: ['read:foo'] - } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toMatchObject({ + at: { accessToken: 'new-token' }, + session: expect.objectContaining({ foo: 'baz' }) + }); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'Could not retrieve an access token with scopes "read:foo". The user will need to sign in again.' - ); }); - test('should fail if the access token is expired', async () => { - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - delete session.refreshToken; - session.accessTokenExpiresAt = -60; - return session; + describe('page router', () => { + afterEach(teardown); + + test('should fail if the session is missing', async () => { + const baseUrl = await setup(withApi); + + await expect(get(baseUrl, '/api/access-token')).rejects.toThrow('The user does not have a valid session.'); + }); + + test('should fail if access_token and refresh_token are missing', async () => { + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + delete session.accessToken; + delete session.refreshToken; + return session; + } } - } + }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'The user does not have a valid access token.' + ); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'The access token expired and a refresh token is not available. The user will need to sign in again.' - ); - }); - test('should fail if you try to refresh the access token without a refresh token', async () => { - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - delete session.refreshToken; - return session; + test('should fail if access_token expiry is missing', async () => { + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + delete session.accessTokenExpiresAt; + return session; + } } - }, - getAccessTokenOptions: { refresh: true } + }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'Expiration information for the access token is not available. The user will need to sign in again.' + ); }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'A refresh token is required to refresh the access token, but none is present.' - ); - }); - test('should return an access token', async () => { - const baseUrl = await setup(withApi); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('eyJz93a...k4laUWw'); - }); + test('should fail if access_token scope is missing', async () => { + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + delete session.accessTokenScope; + return session; + } + }, + getAccessTokenOptions: { + scopes: ['read:foo'] + } + }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'An access token with the requested scopes could not be provided. The user will need to sign in again.' + ); + }); - test('should retrieve a new access token if the old one is expired and update the profile', async () => { - await refreshTokenExchange( - withApi, - 'GEbRxBN...edjnXbL', - { - email: 'john@test.com', - name: 'john doe', - sub: '123' - }, - 'new-token' - ); - const baseUrl = await setup(withApi, { - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - session.accessTokenExpiresAt = -60; - return session; + test("should fail if access_token scope doesn't match requested scope", async () => { + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + session.accessTokenScope = 'read:bar'; + return session; + } + }, + getAccessTokenOptions: { + scopes: ['read:foo'] } - } + }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'Could not retrieve an access token with scopes "read:foo". The user will need to sign in again.' + ); }); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - const { refreshToken } = await get(baseUrl, '/api/session', { cookieJar }); - expect(refreshToken).toEqual('GEbRxBN...edjnXbL'); - }); - test('should retrieve a new access token if force refresh is set', async () => { - await refreshTokenExchange( - withApi, - 'GEbRxBN...edjnXbL', - { - email: 'john@test.com', - name: 'john doe', - sub: '123' - }, - 'new-token' - ); - const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - const { refreshToken } = await get(baseUrl, '/api/session', { cookieJar }); - expect(refreshToken).toEqual('GEbRxBN...edjnXbL'); - }); + test('should fail if the access token is expired', async () => { + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + delete session.refreshToken; + session.accessTokenExpiresAt = -60; + return session; + } + } + }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'The access token expired and a refresh token is not available. The user will need to sign in again.' + ); + }); - test('should fail when refresh grant fails', async () => { - await failedRefreshTokenExchange(withApi, 'GEbRxBN...edjnXbL', {}, 500); - const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'The request to refresh the access token failed. CAUSE: expected 200 OK, got: 500 Internal Server Error' - ); - }); + test('should fail if you try to refresh the access token without a refresh token', async () => { + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + delete session.refreshToken; + return session; + } + }, + getAccessTokenOptions: { refresh: true } + }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'A refresh token is required to refresh the access token, but none is present.' + ); + }); - test('should fail when refresh grant fails with oauth error', async () => { - await failedRefreshTokenExchange( - withApi, - 'GEbRxBN...edjnXbL', - { error: 'invalid_grant', error_description: 'Unknown or invalid refresh token.' }, - 401 - ); - const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'The request to refresh the access token failed. CAUSE: invalid_grant (Unknown or invalid refresh token.)' - ); - }); + test('should return an access token', async () => { + const baseUrl = await setup(withApi); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('eyJz93a...k4laUWw'); + }); - test('should escape oauth error', async () => { - await failedRefreshTokenExchange( - withApi, - 'GEbRxBN...edjnXbL', - { error: '', error_description: '' }, - 401 - ); - const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); - const cookieJar = await login(baseUrl); - await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( - 'The request to refresh the access token failed. CAUSE: <script>alert(1)</script> (<script>alert(2)</script>)' - ); - }); + test('should retrieve a new access token if the old one is expired and update the profile', async () => { + await refreshTokenExchange( + withApi, + 'GEbRxBN...edjnXbL', + { + email: 'john@test.com', + name: 'john doe', + sub: '123' + }, + 'new-token' + ); + const baseUrl = await setup(withApi, { + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + session.accessTokenExpiresAt = -60; + return session; + } + } + }); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + const { refreshToken } = await get(baseUrl, '/api/session', { cookieJar }); + expect(refreshToken).toEqual('GEbRxBN...edjnXbL'); + }); - test('should retrieve a new access token and rotate the refresh token', async () => { - await refreshTokenRotationExchange( - withApi, - 'GEbRxBN...edjnXbL', - { - email: 'john@test.com', - name: 'john doe', - sub: '123' - }, - 'new-token', - 'new-refresh-token' - ); - const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - const { refreshToken } = await get(baseUrl, '/api/session', { cookieJar }); - expect(refreshToken).toEqual('new-refresh-token'); - }); + test('should retrieve a new access token if force refresh is set', async () => { + await refreshTokenExchange( + withApi, + 'GEbRxBN...edjnXbL', + { + email: 'john@test.com', + name: 'john doe', + sub: '123' + }, + 'new-token' + ); + const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + const { refreshToken } = await get(baseUrl, '/api/session', { cookieJar }); + expect(refreshToken).toEqual('GEbRxBN...edjnXbL'); + }); - test('should return an access token with the given scopes', async () => { - const baseUrl = await setup(withApi, { getAccessTokenOptions: { scopes: ['read:foo'] } }); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('eyJz93a...k4laUWw'); - }); + test('should fail when refresh grant fails', async () => { + await failedRefreshTokenExchange(withApi, 'GEbRxBN...edjnXbL', {}, 500); + const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'The request to refresh the access token failed. CAUSE: expected 200 OK, got: 500 Internal Server Error' + ); + }); - test('should not overwrite custom session properties when applying a new access token', async () => { - await refreshTokenExchange( - withApi, - 'GEbRxBN...edjnXbL', - { - email: 'john@test.com', - name: 'john doe', - sub: '123' - }, - 'new-token' - ); - const baseUrl = await setup(withApi, { - getAccessTokenOptions: { refresh: true }, - callbackOptions: { - afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { - session.foo = 'bar'; - session.user.bar = 'baz'; - return session; - } - } + test('should fail when refresh grant fails with oauth error', async () => { + await failedRefreshTokenExchange( + withApi, + 'GEbRxBN...edjnXbL', + { error: 'invalid_grant', error_description: 'Unknown or invalid refresh token.' }, + 401 + ); + const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'The request to refresh the access token failed. CAUSE: invalid_grant (Unknown or invalid refresh token.)' + ); }); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - const session = await get(baseUrl, '/api/session', { cookieJar }); - expect(session).toMatchObject({ - foo: 'bar', - accessToken: 'new-token', - refreshToken: 'GEbRxBN...edjnXbL', - user: { - nickname: '__test_nickname__', - email: 'john@test.com', - name: 'john doe', - sub: '123', - bar: 'baz' - } + + test('should escape oauth error', async () => { + await failedRefreshTokenExchange( + withApi, + 'GEbRxBN...edjnXbL', + { error: '', error_description: '' }, + 401 + ); + const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); + const cookieJar = await login(baseUrl); + await expect(get(baseUrl, '/api/access-token', { cookieJar })).rejects.toThrow( + 'The request to refresh the access token failed. CAUSE: <script>alert(1)</script> (<script>alert(2)</script>)' + ); }); - }); - test('should retrieve a new access token and update the session based on afterRefresh', async () => { - await refreshTokenExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-token'); - const baseUrl = await setup(withApi, { - getAccessTokenOptions: { - refresh: true, - afterRefresh(_req, _res, session) { - delete session.accessTokenScope; - return session as Session; - } - } + test('should retrieve a new access token and rotate the refresh token', async () => { + await refreshTokenRotationExchange( + withApi, + 'GEbRxBN...edjnXbL', + { + email: 'john@test.com', + name: 'john doe', + sub: '123' + }, + 'new-token', + 'new-refresh-token' + ); + const baseUrl = await setup(withApi, { getAccessTokenOptions: { refresh: true } }); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + const { refreshToken } = await get(baseUrl, '/api/session', { cookieJar }); + expect(refreshToken).toEqual('new-refresh-token'); }); - const cookieJar = await login(baseUrl); - const { accessTokenScope } = await get(baseUrl, '/api/session', { cookieJar }); - expect(accessTokenScope).not.toBeUndefined(); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - const { accessTokenScope: newAccessTokenScope } = await get(baseUrl, '/api/session', { - cookieJar + + test('should return an access token with the given scopes', async () => { + const baseUrl = await setup(withApi, { getAccessTokenOptions: { scopes: ['read:foo'] } }); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('eyJz93a...k4laUWw'); }); - expect(newAccessTokenScope).toBeUndefined(); - }); - test('should retrieve a new access token and update the session based on the storeIDToken config', async () => { - await refreshTokenExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-token'); - const baseUrl = await setup( - { ...withApi, session: { storeIDToken: false } }, - { + test('should not overwrite custom session properties when applying a new access token', async () => { + await refreshTokenExchange( + withApi, + 'GEbRxBN...edjnXbL', + { + email: 'john@test.com', + name: 'john doe', + sub: '123' + }, + 'new-token' + ); + const baseUrl = await setup(withApi, { + getAccessTokenOptions: { refresh: true }, + callbackOptions: { + afterCallback: (_req: NextApiRequest, _res: NextApiResponse, session: Session): Session => { + session.foo = 'bar'; + session.user.bar = 'baz'; + return session; + } + } + }); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + const session = await get(baseUrl, '/api/session', { cookieJar }); + expect(session).toMatchObject({ + foo: 'bar', + accessToken: 'new-token', + refreshToken: 'GEbRxBN...edjnXbL', + user: { + nickname: '__test_nickname__', + email: 'john@test.com', + name: 'john doe', + sub: '123', + bar: 'baz' + } + }); + }); + + test('should retrieve a new access token and update the session based on afterRefresh', async () => { + await refreshTokenExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-token'); + const baseUrl = await setup(withApi, { getAccessTokenOptions: { - refresh: true + refresh: true, + afterRefresh(_req, _res, session) { + delete session.accessTokenScope; + return session as Session; + } } - } - ); - const cookieJar = await login(baseUrl); - const session = await get(baseUrl, '/api/session', { cookieJar }); - expect(session.idToken).toBeUndefined(); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - const newSession = await get(baseUrl, '/api/session', { - cookieJar - }); - expect(newSession.idToken).toBeUndefined(); - }); + }); + const cookieJar = await login(baseUrl); + const { accessTokenScope } = await get(baseUrl, '/api/session', { cookieJar }); + expect(accessTokenScope).not.toBeUndefined(); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + const { accessTokenScope: newAccessTokenScope } = await get(baseUrl, '/api/session', { + cookieJar + }); + expect(newAccessTokenScope).toBeUndefined(); + }); - test('should pass custom auth params in refresh grant request body', async () => { - const idToken = await makeIdToken({ - iss: `${withApi.issuerBaseURL}/`, - aud: withApi.clientID, - email: 'john@test.com', - name: 'john doe', - sub: '123' + test('should retrieve a new access token and update the session based on the storeIDToken config', async () => { + await refreshTokenExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-token'); + const baseUrl = await setup( + { ...withApi, session: { storeIDToken: false } }, + { + getAccessTokenOptions: { + refresh: true + } + } + ); + const cookieJar = await login(baseUrl); + const session = await get(baseUrl, '/api/session', { cookieJar }); + expect(session.idToken).toBeUndefined(); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + const newSession = await get(baseUrl, '/api/session', { + cookieJar + }); + expect(newSession.idToken).toBeUndefined(); }); - const spy = jest.fn(); - nock(`${withApi.issuerBaseURL}`) - .post('/oauth/token', /grant_type=refresh_token/) - .reply(200, (_, body) => { - spy(body); - return { - access_token: 'new-token', - id_token: idToken, - token_type: 'Bearer', - expires_in: 750, - scope: 'read:foo write:foo' - }; + test('should pass custom auth params in refresh grant request body', async () => { + const idToken = await makeIdToken({ + iss: `${withApi.issuerBaseURL}/`, + aud: withApi.clientID, + email: 'john@test.com', + name: 'john doe', + sub: '123' }); - const baseUrl = await setup(withApi, { - getAccessTokenOptions: { refresh: true, authorizationParams: { baz: 'qux' } } + const spy = jest.fn(); + nock(`${withApi.issuerBaseURL}`) + .post('/oauth/token', /grant_type=refresh_token/) + .reply(200, (_, body) => { + spy(body); + return { + access_token: 'new-token', + id_token: idToken, + token_type: 'Bearer', + expires_in: 750, + scope: 'read:foo write:foo' + }; + }); + + const baseUrl = await setup(withApi, { + getAccessTokenOptions: { refresh: true, authorizationParams: { baz: 'qux' } } + }); + const cookieJar = await login(baseUrl); + const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); + expect(accessToken).toEqual('new-token'); + expect(spy).toHaveBeenCalledWith(expect.stringContaining('baz=qux')); }); - const cookieJar = await login(baseUrl); - const { accessToken } = await get(baseUrl, '/api/access-token', { cookieJar }); - expect(accessToken).toEqual('new-token'); - expect(spy).toHaveBeenCalledWith(expect.stringContaining('baz=qux')); }); }); From 63202c0fc2a63de47debfd7f867eb184be5ca23e Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Mon, 5 Jun 2023 14:02:03 +0100 Subject: [PATCH 31/61] More app router tests --- src/helpers/with-api-auth-required.ts | 14 +- src/session/touch-session.ts | 5 +- src/session/update-session.ts | 7 +- tests/handlers/callback.test.ts | 27 ++++ tests/handlers/profile.test.ts | 2 - tests/helpers/with-api-auth-required.test.ts | 84 ++++++++++-- tests/session/cache.test.ts | 21 +++ tests/session/touch-session.test.ts | 109 +++++++++++----- tests/session/update-session.test.ts | 130 +++++++++++++------ 9 files changed, 306 insertions(+), 93 deletions(-) diff --git a/src/helpers/with-api-auth-required.ts b/src/helpers/with-api-auth-required.ts index f816de7e5..f0d343b91 100644 --- a/src/helpers/with-api-auth-required.ts +++ b/src/helpers/with-api-auth-required.ts @@ -20,7 +20,7 @@ export type AppRouteHandlerFn = ( * dynamic route). */ ctx: AppRouteHandlerFnContext -) => Promise | Response; +) => Promise | NextResponse; /** * Wrap an API route to check that the user has a valid session. If they're not logged in the @@ -66,15 +66,21 @@ export default function withApiAuthFactory(sessionCache: SessionCache): WithApiA const appRouteHandlerFactory = (sessionCache: SessionCache): WithApiAuthRequiredAppRoute => (apiRoute) => - async (req, params): Promise => { - const [session] = await get({ sessionCache }); + async (req, params): Promise => { + const res = new NextResponse(); + const [session] = await get({ sessionCache, req, res }); if (!session || !session.user) { return NextResponse.json( { error: 'not_authenticated', description: 'The user does not have an active session or is not authenticated' }, { status: 401 } ); } - return apiRoute(req, params); + let apiRes: NextResponse | Response = await apiRoute(req, params); + let nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes); + for (const cookie of res.cookies.getAll()) { + nextApiRes.cookies.set(cookie); + } + return nextApiRes; }; const pageRouteHandlerFactory = diff --git a/src/session/touch-session.ts b/src/session/touch-session.ts index 211283162..06915a77d 100644 --- a/src/session/touch-session.ts +++ b/src/session/touch-session.ts @@ -1,5 +1,6 @@ import { IncomingMessage, ServerResponse } from 'http'; import { NextApiRequest, NextApiResponse } from 'next'; +import { NextRequest, NextResponse } from 'next/server'; import { get, set, SessionCache } from '../session'; /** @@ -20,8 +21,8 @@ import { get, set, SessionCache } from '../session'; * @category Server */ export type TouchSession = ( - req?: IncomingMessage | NextApiRequest, - res?: ServerResponse | NextApiResponse + req?: IncomingMessage | NextApiRequest | NextRequest, + res?: ServerResponse | NextApiResponse | NextResponse ) => Promise; /** diff --git a/src/session/update-session.ts b/src/session/update-session.ts index 11804dd10..147c7e4e7 100644 --- a/src/session/update-session.ts +++ b/src/session/update-session.ts @@ -1,5 +1,6 @@ import { IncomingMessage, ServerResponse } from 'http'; import { NextApiRequest, NextApiResponse } from 'next'; +import { NextRequest, NextResponse } from 'next/server'; import { get, set, Session, SessionCache } from '../session'; /** @@ -24,8 +25,8 @@ import { get, set, Session, SessionCache } from '../session'; * @category Server */ export type UpdateSession = ( - req?: IncomingMessage | NextApiRequest | Session, - res?: ServerResponse | NextApiResponse, + req?: IncomingMessage | NextApiRequest | NextRequest | Session, + res?: ServerResponse | NextApiResponse | NextResponse, user?: Session ) => Promise; @@ -35,7 +36,7 @@ export type UpdateSession = ( export default function updateSessionFactory(sessionCache: SessionCache): UpdateSession { return async (reqOrSession, res, newSession) => { const session = (res ? newSession : reqOrSession) as Session | undefined; - const req = (res ? reqOrSession : undefined) as IncomingMessage | NextApiRequest | undefined; + const req = (res ? reqOrSession : undefined) as IncomingMessage | NextApiRequest | NextRequest | undefined; const [prevSession, iat] = await get({ sessionCache, req, res }); if (!prevSession || !session || !session.user) { diff --git a/tests/handlers/callback.test.ts b/tests/handlers/callback.test.ts index 6606eff1a..da7ea91db 100644 --- a/tests/handlers/callback.test.ts +++ b/tests/handlers/callback.test.ts @@ -679,5 +679,32 @@ describe('callback handler', () => { expect(res.statusCode).toBe(302); expect(spy).toHaveBeenCalledWith(expect.stringContaining('foo=bar')); }); + + test('redirects from afterCallback hook', async () => { + const afterCallback = (_req: NextApiRequest, res: NextApiResponse): undefined => { + res.writeHead(302, '', { location: '/foo' }); + res.end(); + return; + }; + const baseUrl = await setup(withApi, { callbackOptions: { afterCallback } }); + const state = encodeState({ returnTo: baseUrl }); + const cookieJar = await toSignedCookieJar( + { + state, + nonce: '__test_nonce__' + }, + baseUrl + ); + const { res } = await callback( + baseUrl, + { + state, + code: 'code' + }, + cookieJar + ); + expect(res.statusCode).toBe(302); + expect(res.headers.location).toBe('/foo'); + }); }); }); diff --git a/tests/handlers/profile.test.ts b/tests/handlers/profile.test.ts index c7ea4e3ed..cdae40721 100644 --- a/tests/handlers/profile.test.ts +++ b/tests/handlers/profile.test.ts @@ -88,7 +88,6 @@ describe('profile handler', () => { } } }); - //mocked(cookies).mockImplementation(() => loginRes.cookies); nock.cleanAll(); nock(`${withApi.issuerBaseURL}`) .post('/oauth/token', `grant_type=refresh_token&refresh_token=GEbRxBN...edjnXbL`) @@ -123,7 +122,6 @@ describe('profile handler', () => { } } }); - //mocked(cookies).mockImplementation(() => loginRes.cookies); nock.cleanAll(); await refreshTokenRotationExchange(withApi, 'GEbRxBN...edjnXbL', {}, 'new-access-token', 'new-refresh-token'); const res = await getResponse({ diff --git a/tests/helpers/with-api-auth-required.test.ts b/tests/helpers/with-api-auth-required.test.ts index 2079d1b4c..68ba403ad 100644 --- a/tests/helpers/with-api-auth-required.test.ts +++ b/tests/helpers/with-api-auth-required.test.ts @@ -1,23 +1,79 @@ +import { NextRequest, NextResponse } from 'next/server'; import { login, setup, teardown } from '../fixtures/setup'; -import { withoutApi } from '../fixtures/default-settings'; +import { withApi, withoutApi } from '../fixtures/default-settings'; import { get } from '../auth0-session/fixtures/helpers'; +import { getResponse, login as appRouterLogin, getSession } from '../fixtures/app-router-helpers'; +import { initAuth0 } from '../../src'; describe('with-api-auth-required', () => { - afterEach(teardown); + describe('app router', () => { + const getApiResponse = (opts?: any) => { + const auth0Instance = initAuth0(withApi); + return getResponse({ + url: '/api/auth/protected', + auth0Instance, + extraHandlers: { + protected(req: NextRequest, ctx: { params: Record }) { + return auth0Instance.withApiAuthRequired(() => { + return NextResponse.json({ foo: 'bar' }); + })(req, ctx); + }, + 'protected-returns-response'(req: NextRequest, ctx: { params: Record }) { + return auth0Instance.withApiAuthRequired((_req: NextRequest) => { + // @ts-expect-error This is not in lib/dom right now. + return Response.json({ foo: 'bar' }); + })(req, ctx); + } + }, + ...opts + }); + }; - test('protect an api route', async () => { - const baseUrl = await setup(withoutApi); - await expect(get(baseUrl, '/api/protected')).rejects.toThrow('Unauthorized'); + test('protect an api route', async () => { + await expect(getApiResponse()).resolves.toMatchObject({ status: 401 }); + }); + + test('allow access to an api route with a valid session', async () => { + const loginRes = await appRouterLogin(); + const res = await getApiResponse({ cookies: { appSession: loginRes.cookies.get('appSession').value } }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toEqual({ foo: 'bar' }); + await expect(getSession(withApi, res)).resolves.toMatchObject({ + user: expect.objectContaining({ sub: '__test_sub__' }) + }); + }); + + test('allow access to an api route that returns a basic response with a valid session', async () => { + const loginRes = await appRouterLogin(); + const res = await getApiResponse({ + url: '/api/auth/protected-returns-response', + cookies: { appSession: loginRes.cookies.get('appSession').value } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toEqual({ foo: 'bar' }); + await expect(getSession(withApi, res)).resolves.toMatchObject({ + user: expect.objectContaining({ sub: '__test_sub__' }) + }); + }); }); - test('allow access to an api route with a valid session', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); - const { - res: { statusCode }, - data - } = await get(baseUrl, '/api/protected', { cookieJar, fullResponse: true }); - expect(statusCode).toBe(200); - expect(data).toEqual({ foo: 'bar' }); + describe('page router', () => { + afterEach(teardown); + + test('protect an api route', async () => { + const baseUrl = await setup(withoutApi); + await expect(get(baseUrl, '/api/protected')).rejects.toThrow('Unauthorized'); + }); + + test('allow access to an api route with a valid session', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + const { + res: { statusCode }, + data + } = await get(baseUrl, '/api/protected', { cookieJar, fullResponse: true }); + expect(statusCode).toBe(200); + expect(data).toEqual({ foo: 'bar' }); + }); }); }); diff --git a/tests/session/cache.test.ts b/tests/session/cache.test.ts index 66d4f7d0d..aa50b4b57 100644 --- a/tests/session/cache.test.ts +++ b/tests/session/cache.test.ts @@ -2,6 +2,7 @@ import { IncomingMessage, ServerResponse } from 'http'; import { Socket } from 'net'; import { mocked } from 'ts-jest/utils'; import { StatelessSession, getConfig } from '../../src/auth0-session'; +import { get, set } from '../../src/session/cache'; import { ConfigParameters, Session, SessionCache } from '../../src'; import { withoutApi } from '../fixtures/default-settings'; @@ -87,4 +88,24 @@ describe('SessionCache', () => { expect(sessionStore.read).toHaveBeenCalledTimes(1); expect(sessionStore.save).toHaveBeenCalledTimes(1); }); + + test('should save the session on read and update with a rolling session from RSC', async () => { + sessionStore.read = jest.fn().mockResolvedValue([{ user: { sub: '__test_user__' } }, 500]); + expect((await get({ sessionCache: cache }))[0]?.user).toEqual({ sub: '__test_user__' }); + await set({ sessionCache: cache, session: new Session({ sub: '__new_user__' }) }); + // Note: the cache is not updated from a RSC as there is no request context to cache against + expect((await get({ sessionCache: cache }))[0]?.user).toEqual({ sub: '__test_user__' }); + expect(sessionStore.read).toHaveBeenCalledTimes(2); + expect(sessionStore.save).toHaveBeenCalledTimes(3); + }); + + test('should save the session only on update without a rolling session from RSC', async () => { + setup({ ...withoutApi, session: { rolling: false } }); + sessionStore.read = jest.fn().mockResolvedValue([{ user: { sub: '__test_user__' } }, 500]); + expect((await get({ sessionCache: cache }))[0]?.user).toEqual({ sub: '__test_user__' }); + await set({ session: new Session({ sub: '__new_user__' }), sessionCache: cache }); + expect((await get({ sessionCache: cache }))[0]?.user).toEqual({ sub: '__test_user__' }); + expect(sessionStore.read).toHaveBeenCalledTimes(2); + expect(sessionStore.save).toHaveBeenCalledTimes(1); + }); }); diff --git a/tests/session/touch-session.test.ts b/tests/session/touch-session.test.ts index 733fb8dad..c5c0b726d 100644 --- a/tests/session/touch-session.test.ts +++ b/tests/session/touch-session.test.ts @@ -1,45 +1,92 @@ import { login, setup, teardown } from '../fixtures/setup'; import { withoutApi } from '../fixtures/default-settings'; import { get } from '../auth0-session/fixtures/helpers'; +import { getResponse, login as appRouterLogin } from '../fixtures/app-router-helpers'; +import { NextRequest, NextResponse } from 'next/server'; +import { initAuth0 } from '../../src'; describe('touch-session', () => { - afterEach(teardown); + describe('app router', () => { + test('should not update the session when getting the session', async () => { + const config = { ...withoutApi, session: { autoSave: false } }; + const auth0Instance = initAuth0(config); + const loginRes = await appRouterLogin({ config }); + const res = await getResponse({ + url: '/api/auth/session', + config, + auth0Instance, + cookies: { appSession: loginRes.cookies.get('appSession').value }, + extraHandlers: { + async session(req: NextRequest) { + const res = new NextResponse(); + await auth0Instance.getSession(req, res); + return res; + } + } + }); + expect(res.headers.get('set-cookie')).toBeNull(); + }); - test('should not update the session when getting the session', async () => { - const baseUrl = await setup({ - ...withoutApi, - session: { - autoSave: false - } + test('should update the session when calling touchSession', async () => { + const config = { ...withoutApi, session: { autoSave: false } }; + const auth0Instance = initAuth0(config); + const loginRes = await appRouterLogin({ config }); + const res = await getResponse({ + url: '/api/auth/session', + config, + auth0Instance, + cookies: { appSession: loginRes.cookies.get('appSession').value }, + extraHandlers: { + async session(req: NextRequest) { + const res = new NextResponse(); + await auth0Instance.getSession(req, res); + await auth0Instance.touchSession(req, res); + return res; + } + } + }); + expect(res.headers.get('set-cookie')).not.toBeNull(); }); - const cookieJar = await login(baseUrl); - const [authCookie] = await cookieJar.getCookies(baseUrl); - await get(baseUrl, '/api/auth/me', { cookieJar }); - const [updatedAuthCookie] = await cookieJar.getCookies(baseUrl); - expect(updatedAuthCookie).toEqual(authCookie); }); + describe('page router', () => { + afterEach(teardown); - test('should update the session when calling touchSession', async () => { - const baseUrl = await setup({ - ...withoutApi, - session: { - autoSave: false - } + test('should not update the session when getting the session', async () => { + const baseUrl = await setup({ + ...withoutApi, + session: { + autoSave: false + } + }); + const cookieJar = await login(baseUrl); + const [authCookie] = await cookieJar.getCookies(baseUrl); + await get(baseUrl, '/api/auth/me', { cookieJar }); + const [updatedAuthCookie] = await cookieJar.getCookies(baseUrl); + expect(updatedAuthCookie).toEqual(authCookie); + }); + + test('should update the session when calling touchSession', async () => { + const baseUrl = await setup({ + ...withoutApi, + session: { + autoSave: false + } + }); + const cookieJar = await login(baseUrl); + const [authCookie] = await cookieJar.getCookies(baseUrl); + await get(baseUrl, '/api/touch-session', { cookieJar }); + const [updatedAuthCookie] = await cookieJar.getCookies(baseUrl); + expect(updatedAuthCookie).not.toEqual(authCookie); }); - const cookieJar = await login(baseUrl); - const [authCookie] = await cookieJar.getCookies(baseUrl); - await get(baseUrl, '/api/touch-session', { cookieJar }); - const [updatedAuthCookie] = await cookieJar.getCookies(baseUrl); - expect(updatedAuthCookie).not.toEqual(authCookie); - }); - test('should not throw when there is no session', async () => { - const baseUrl = await setup({ - ...withoutApi, - session: { - autoSave: false - } + test('should not throw when there is no session', async () => { + const baseUrl = await setup({ + ...withoutApi, + session: { + autoSave: false + } + }); + await expect(get(baseUrl, '/api/touch-session')).resolves.not.toThrow(); }); - await expect(get(baseUrl, '/api/touch-session')).resolves.not.toThrow(); }); }); diff --git a/tests/session/update-session.test.ts b/tests/session/update-session.test.ts index 4d8de02d2..2091ded5d 100644 --- a/tests/session/update-session.test.ts +++ b/tests/session/update-session.test.ts @@ -1,49 +1,105 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { cookies as nextCookies } from 'next/headers'; +import { mocked } from 'ts-jest/utils'; +import { CookieJar } from 'tough-cookie'; import { login, setup, teardown } from '../fixtures/setup'; -import { withoutApi } from '../fixtures/default-settings'; +import { withApi, withoutApi } from '../fixtures/default-settings'; import { get, post } from '../auth0-session/fixtures/helpers'; -import { CookieJar } from 'tough-cookie'; +import { getResponse, login as appRouterLogin, getSession } from '../fixtures/app-router-helpers'; +import { initAuth0 } from '../../src'; + +jest.mock('next/headers'); describe('update-user', () => { - afterEach(teardown); + describe('app router', () => { + test('should update session', async () => { + const loginRes = await appRouterLogin(); + const auth0Instance = initAuth0(withApi); + const res = await getResponse({ + url: '/api/auth/update', + auth0Instance, + cookies: { appSession: loginRes.cookies.get('appSession').value }, + extraHandlers: { + async update(req: NextRequest) { + const res = new NextResponse(); + const session = await auth0Instance.getSession(req, res); + await auth0Instance.updateSession(req, res, { ...session, user: { ...session?.user, foo: 'bar' } }); + return res; + } + } + }); + expect(res.status).toBe(200); + await expect(getSession(withApi, res)).resolves.toMatchObject({ user: expect.objectContaining({ foo: 'bar' }) }); + }); - test('should update session', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); - const user = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - await post(baseUrl, '/api/update-session', { cookieJar, body: { session: { foo: 'bar' } } }); - const updatedSession = await get(baseUrl, '/api/session', { cookieJar }); - expect(updatedSession).toMatchObject({ - foo: 'bar', - user: expect.objectContaining({ nickname: '__test_nickname__', sub: '__test_sub__' }) + test('should update session from a server component', async () => { + const loginRes = await appRouterLogin(); + // Note: An updated session from a React Server Component will not persist + // because you can't write to a cookie from a RSC in the current version of Next.js + // This test passes because we're mocking the dynamic `cookies` function. + mocked(nextCookies).mockImplementation(() => loginRes.cookies); + const auth0Instance = initAuth0(withApi); + const res = await getResponse({ + url: '/api/auth/update', + auth0Instance, + cookies: { appSession: loginRes.cookies.get('appSession').value }, + extraHandlers: { + async update() { + // const res = new NextResponse(); + const session = await auth0Instance.getSession(); + await auth0Instance.updateSession({ ...session, user: { ...session?.user, foo: 'bar' } }); + return new NextResponse(); + } + } + }); + expect(res.status).toBe(200); + await expect(getSession(withApi, loginRes)).resolves.toMatchObject({ + user: expect.objectContaining({ foo: 'bar' }) + }); }); }); + describe('page router', () => { + afterEach(teardown); - test('should ignore updates if session is not defined', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); - const user = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - await post(baseUrl, '/api/update-session', { cookieJar, body: { session: undefined } }); - const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(updatedUser).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - }); + test('should update session', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + const user = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + await post(baseUrl, '/api/update-session', { cookieJar, body: { session: { foo: 'bar' } } }); + const updatedSession = await get(baseUrl, '/api/session', { cookieJar }); + expect(updatedSession).toMatchObject({ + foo: 'bar', + user: expect.objectContaining({ nickname: '__test_nickname__', sub: '__test_sub__' }) + }); + }); - test('should ignore updates if user is not logged in', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = new CookieJar(); - await expect(get(baseUrl, '/api/auth/me', { cookieJar })).resolves.toBe(''); - await post(baseUrl, '/api/update-session', { body: { session: { sub: 'foo' } }, cookieJar }); - await expect(get(baseUrl, '/api/auth/me', { cookieJar })).resolves.toBe(''); - }); + test('should ignore updates if session is not defined', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + const user = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + await post(baseUrl, '/api/update-session', { cookieJar, body: { session: undefined } }); + const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(updatedUser).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + }); - test('should ignore updates if user is not defined in update', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); - const user = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); - await post(baseUrl, '/api/update-session', { cookieJar, body: { session: { user: undefined } } }); - const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); - expect(updatedUser).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + test('should ignore updates if user is not logged in', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = new CookieJar(); + await expect(get(baseUrl, '/api/auth/me', { cookieJar })).resolves.toBe(''); + await post(baseUrl, '/api/update-session', { body: { session: { sub: 'foo' } }, cookieJar }); + await expect(get(baseUrl, '/api/auth/me', { cookieJar })).resolves.toBe(''); + }); + + test('should ignore updates if user is not defined in update', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + const user = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(user).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + await post(baseUrl, '/api/update-session', { cookieJar, body: { session: { user: undefined } } }); + const updatedUser = await get(baseUrl, '/api/auth/me', { cookieJar }); + expect(updatedUser).toEqual({ nickname: '__test_nickname__', sub: '__test_sub__' }); + }); }); }); From 3d2ed548d0ecf89a77040d8e2b955412cbcada7e Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Mon, 5 Jun 2023 17:24:23 +0100 Subject: [PATCH 32/61] More app router tests --- src/helpers/with-middleware-auth-required.ts | 1 - src/http/auth0-next-api-request.ts | 1 + src/http/auth0-next-request.ts | 1 + src/http/auth0-next-response.ts | 1 + src/http/index.ts | 4 +- src/session/cache.ts | 13 +- tests/helpers/with-api-auth-required.test.ts | 23 ++ .../with-middleware-auth-required.test.ts | 14 + tests/helpers/with-page-auth-required.test.ts | 314 +++++++++++------- tests/http/auth0-next-request.test.ts | 2 +- .../http/auth0-next-response-cookies.test.ts | 43 +++ tests/http/auth0-next-response.test.ts | 2 +- 12 files changed, 283 insertions(+), 136 deletions(-) create mode 100644 tests/http/auth0-next-response-cookies.test.ts diff --git a/src/helpers/with-middleware-auth-required.ts b/src/helpers/with-middleware-auth-required.ts index 924bc7848..a32767378 100644 --- a/src/helpers/with-middleware-auth-required.ts +++ b/src/helpers/with-middleware-auth-required.ts @@ -86,7 +86,6 @@ export default function withMiddlewareAuthRequiredFactory( const cookies = headers.get('set-cookie')?.split(', ') || []; const authCookies = authRes.headers.get('set-cookie')?.split(', ') || []; if (cookies.length || authCookies.length) { - // TODO: Should Auth0NextResponse keep existing headers? headers.set('set-cookie', [...authCookies, ...cookies].join(', ')); } return NextResponse.next({ ...res, status: res.status, headers }); diff --git a/src/http/auth0-next-api-request.ts b/src/http/auth0-next-api-request.ts index 9e3d761dc..6814cb319 100644 --- a/src/http/auth0-next-api-request.ts +++ b/src/http/auth0-next-api-request.ts @@ -3,6 +3,7 @@ import { NextApiRequest } from 'next'; export default class Auth0NextApiRequest extends Auth0Request { public constructor(req: NextApiRequest) { + /* c8 ignore next */ super(req); } diff --git a/src/http/auth0-next-request.ts b/src/http/auth0-next-request.ts index d226e75e3..f750e9a90 100644 --- a/src/http/auth0-next-request.ts +++ b/src/http/auth0-next-request.ts @@ -3,6 +3,7 @@ import { NextRequest } from 'next/server'; export default class Auth0NextRequest extends Auth0Request { public constructor(req: NextRequest) { + /* c8 ignore next */ super(req); } diff --git a/src/http/auth0-next-response.ts b/src/http/auth0-next-response.ts index dc9bbabaf..c70772ace 100644 --- a/src/http/auth0-next-response.ts +++ b/src/http/auth0-next-response.ts @@ -4,6 +4,7 @@ import { Auth0Response } from '../auth0-session/http'; export default class Auth0NextResponse extends Auth0Response { public constructor(res: NextResponse) { + /* c8 ignore next */ super(res); } diff --git a/src/http/index.ts b/src/http/index.ts index e3910c789..25f8d76b2 100644 --- a/src/http/index.ts +++ b/src/http/index.ts @@ -2,5 +2,5 @@ export { default as Auth0NextApiRequest } from './auth0-next-api-request'; export { default as Auth0NextApiResponse } from './auth0-next-api-response'; export { default as Auth0NextRequest } from './auth0-next-request'; export { default as Auth0NextResponse } from './auth0-next-response'; -export { default as Auth0NextDynamicFunctionsRequest } from './auth0-next-request-cookies'; -export { default as Auth0NextDynamicFunctionsResponse } from './auth0-next-response-cookies'; +export { default as Auth0NextRequestCookies } from './auth0-next-request-cookies'; +export { default as Auth0NextResponseCookies } from './auth0-next-response-cookies'; diff --git a/src/session/cache.ts b/src/session/cache.ts index f5f0709e7..97409fefa 100644 --- a/src/session/cache.ts +++ b/src/session/cache.ts @@ -8,8 +8,8 @@ import { NodeRequest, NodeResponse } from '../auth0-session/http'; import { Auth0NextApiRequest, Auth0NextApiResponse, - Auth0NextDynamicFunctionsRequest, - Auth0NextDynamicFunctionsResponse, + Auth0NextRequestCookies, + Auth0NextResponseCookies, Auth0NextRequest, Auth0NextResponse } from '../http'; @@ -118,7 +118,7 @@ export const get = async ({ session: { rolling, autoSave } } } = sessionCache; - const auth0Req = new Auth0NextDynamicFunctionsRequest(); + const auth0Req = new Auth0NextRequestCookies(); const [session, iat] = await sessionStore.read(auth0Req); if (rolling && autoSave) { await set({ session, sessionCache, iat }); @@ -143,10 +143,5 @@ export const set = async ({ return sessionCache.set(req, res, session); } const { sessionStore } = sessionCache; - await sessionStore.save( - new Auth0NextDynamicFunctionsRequest(), - new Auth0NextDynamicFunctionsResponse(), - session, - iat - ); + await sessionStore.save(new Auth0NextRequestCookies(), new Auth0NextResponseCookies(), session, iat); }; diff --git a/tests/helpers/with-api-auth-required.test.ts b/tests/helpers/with-api-auth-required.test.ts index 68ba403ad..89782987b 100644 --- a/tests/helpers/with-api-auth-required.test.ts +++ b/tests/helpers/with-api-auth-required.test.ts @@ -23,6 +23,14 @@ describe('with-api-auth-required', () => { // @ts-expect-error This is not in lib/dom right now. return Response.json({ foo: 'bar' }); })(req, ctx); + }, + 'protected-updates-headers'(req: NextRequest, ctx: { params: Record }) { + return auth0Instance.withApiAuthRequired((_req: NextRequest) => { + const res = NextResponse.json({ foo: 'bar' }); + res.cookies.set('foo', 'bar'); + res.headers.set('baz', 'bar'); + return res; + })(req, ctx); } }, ...opts @@ -55,6 +63,21 @@ describe('with-api-auth-required', () => { user: expect.objectContaining({ sub: '__test_sub__' }) }); }); + + test('allow access to an api route that updates the cookies', async () => { + const loginRes = await appRouterLogin(); + const res = await getApiResponse({ + url: '/api/auth/protected-updates-headers', + cookies: { appSession: loginRes.cookies.get('appSession').value } + }); + expect(res.status).toBe(200); + await expect(res.json()).resolves.toEqual({ foo: 'bar' }); + await expect(getSession(withApi, res)).resolves.toMatchObject({ + user: expect.objectContaining({ sub: '__test_sub__' }) + }); + expect(res.cookies.get('foo').value).toBe('bar'); + expect(res.headers.get('baz')).toBe('bar'); + }); }); describe('page router', () => { diff --git a/tests/helpers/with-middleware-auth-required.test.ts b/tests/helpers/with-middleware-auth-required.test.ts index ded9742bd..e160156e7 100644 --- a/tests/helpers/with-middleware-auth-required.test.ts +++ b/tests/helpers/with-middleware-auth-required.test.ts @@ -208,4 +208,18 @@ describe('with-middleware-auth-required', () => { expect(res.status).toEqual(200); expect(res.headers.get('set-cookie')).toBeNull(); }); + + test('should set a custom header', async () => { + const middleware = () => { + const res = NextResponse.next(); + res.headers.set('foo', 'bar'); + return res; + }; + const res = await setup({ + user: { name: 'dave' }, + middleware + }); + expect(res.status).toEqual(200); + expect(res.headers.get('foo')).toBe('bar'); + }); }); diff --git a/tests/helpers/with-page-auth-required.test.ts b/tests/helpers/with-page-auth-required.test.ts index 732eb42d8..985f0f903 100644 --- a/tests/helpers/with-page-auth-required.test.ts +++ b/tests/helpers/with-page-auth-required.test.ts @@ -1,150 +1,220 @@ +import React from 'react'; +import ReactDOMServer from 'react-dom/server'; +import { cookies as nextCookies } from 'next/headers'; +import * as navigation from 'next/navigation'; +import { NextResponse } from 'next/server'; +import { mocked } from 'ts-jest/utils'; import { URL } from 'url'; import { login, setup, teardown } from '../fixtures/setup'; -import { withoutApi } from '../fixtures/default-settings'; +import { login as appRouterLogin } from '../fixtures/app-router-helpers'; +import { withApi, withoutApi } from '../fixtures/default-settings'; import { get } from '../auth0-session/fixtures/helpers'; +import { initAuth0 } from '../../src'; + +jest.mock('next/headers'); +jest.mock('next/navigation', () => { + const navigation = jest.requireActual('next/navigation'); + return { + ...navigation, + redirect: jest.fn(navigation.redirect) + }; +}); describe('with-page-auth-required ssr', () => { - afterEach(teardown); - - test('protect a page', async () => { - const baseUrl = await setup(withoutApi); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/protected', { fullResponse: true }); - expect(statusCode).toBe(307); - expect(decodeURIComponent(headers.location)).toBe('/api/auth/login?returnTo=/protected'); - }); + describe('app route', () => { + const getPageResponse = ({ config, cookies, returnTo, loginRes }: any = {}) => { + const res = loginRes || new NextResponse(); + mocked(nextCookies).mockImplementation(() => res.cookies as any); + const opts = { ...withApi, ...config }; + const instance = initAuth0(opts); + let headers = new Headers(); + if (cookies) { + headers.set( + 'Cookie', + Object.entries(cookies) + .map(([k, v]) => `${k}=${v}`) + .join('; ') + ); + } + const handler = instance.withPageAuthRequired(() => Promise.resolve(React.createElement('div', {}, 'foo')), { + returnTo + }); + return handler({}); + }; - test('allow access to a page with a valid session', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); + test('protect a page', async () => { + jest.spyOn(navigation, 'redirect'); + await expect(getPageResponse({})).rejects.toThrowError('NEXT_REDIRECT'); + expect(navigation.redirect).toHaveBeenCalledWith('/api/auth/login'); + }); - const { - res: { statusCode }, - data - } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); - expect(statusCode).toBe(200); - expect(data).toMatch(/Protected Page.*__test_sub__/); - }); + test('protect a page and redirect to returnTo option', async () => { + jest.spyOn(navigation, 'redirect'); + await expect(getPageResponse({ returnTo: '/foo' })).rejects.toThrowError('NEXT_REDIRECT'); + expect(navigation.redirect).toHaveBeenCalledWith('/api/auth/login?returnTo=/foo'); + }); - test('accept a custom returnTo url', async () => { - const baseUrl = await setup(withoutApi, { withPageAuthRequiredOptions: { returnTo: '/foo' } }); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/protected', { fullResponse: true }); - expect(statusCode).toBe(307); - expect(decodeURIComponent(headers.location)).toBe('/api/auth/login?returnTo=/foo'); - }); + test('allow access to a page with a valid session', async () => { + const loginRes = await appRouterLogin(); - test('accept custom server-side props', async () => { - const spy = jest.fn().mockReturnValue({ props: {} }); - const baseUrl = await setup(withoutApi, { - withPageAuthRequiredOptions: { - getServerSideProps: spy - } + const loginCookie = loginRes.cookies.get('appSession'); + const res = await getPageResponse({ loginRes }); + expect(ReactDOMServer.renderToString(res)).toBe('
    foo
    '); + expect(loginRes.cookies.get('appSession')).toBeDefined(); + expect(loginRes.cookies.get('appSession')).not.toEqual(loginCookie); + }); + + test('use a custom login url', async () => { + await expect( + getPageResponse({ config: { routes: { ...withApi.routes, login: '/api/auth/custom-login' } } }) + ).rejects.toThrowError('NEXT_REDIRECT'); + expect(navigation.redirect).toHaveBeenCalledWith('/api/auth/custom-login'); }); - const cookieJar = await login(baseUrl); - const { - res: { statusCode } - } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); - expect(statusCode).toBe(200); - expect(spy).toHaveBeenCalledWith(expect.objectContaining({ req: expect.anything(), res: expect.anything() })); }); - test('allow to override the user prop', async () => { - const baseUrl = await setup(withoutApi, { - withPageAuthRequiredOptions: { - async getServerSideProps() { - return { props: { user: { sub: 'foo' } } }; + describe('page route', () => { + afterEach(teardown); + + test('protect a page', async () => { + const baseUrl = await setup(withoutApi); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/protected', { fullResponse: true }); + expect(statusCode).toBe(307); + expect(decodeURIComponent(headers.location)).toBe('/api/auth/login?returnTo=/protected'); + }); + + test('allow access to a page with a valid session', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode }, + data + } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); + expect(statusCode).toBe(200); + expect(data).toMatch(/Protected Page.*__test_sub__/); + }); + + test('accept a custom returnTo url', async () => { + const baseUrl = await setup(withoutApi, { withPageAuthRequiredOptions: { returnTo: '/foo' } }); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/protected', { fullResponse: true }); + expect(statusCode).toBe(307); + expect(decodeURIComponent(headers.location)).toBe('/api/auth/login?returnTo=/foo'); + }); + + test('accept custom server-side props', async () => { + const spy = jest.fn().mockReturnValue({ props: {} }); + const baseUrl = await setup(withoutApi, { + withPageAuthRequiredOptions: { + getServerSideProps: spy } - } + }); + const cookieJar = await login(baseUrl); + const { + res: { statusCode } + } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); + expect(statusCode).toBe(200); + expect(spy).toHaveBeenCalledWith(expect.objectContaining({ req: expect.anything(), res: expect.anything() })); }); - const cookieJar = await login(baseUrl); - const { data } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); - expect(data).toMatch(/Protected Page.*foo/); - }); - test('allow to override the user prop when using aync props', async () => { - const baseUrl = await setup(withoutApi, { - withPageAuthRequiredOptions: { - async getServerSideProps() { - return { props: Promise.resolve({ user: { sub: 'foo' } }) }; + test('allow to override the user prop', async () => { + const baseUrl = await setup(withoutApi, { + withPageAuthRequiredOptions: { + async getServerSideProps() { + return { props: { user: { sub: 'foo' } } }; + } } - } + }); + const cookieJar = await login(baseUrl); + const { data } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); + expect(data).toMatch(/Protected Page.*foo/); }); - const cookieJar = await login(baseUrl); - const { data } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); - expect(data).toMatch(/Protected Page.*foo/); - }); - test('use a custom login url', async () => { - process.env.NEXT_PUBLIC_AUTH0_LOGIN = '/api/foo'; - const baseUrl = await setup(withoutApi); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/protected', { fullResponse: true }); - expect(statusCode).toBe(307); - expect(decodeURIComponent(headers.location)).toBe('/api/foo?returnTo=/protected'); - delete process.env.NEXT_PUBLIC_AUTH0_LOGIN; - }); + test('allow to override the user prop when using async props', async () => { + const baseUrl = await setup(withoutApi, { + withPageAuthRequiredOptions: { + async getServerSideProps() { + return { props: Promise.resolve({ user: { sub: 'foo' } }) }; + } + } + }); + const cookieJar = await login(baseUrl); + const { data } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); + expect(data).toMatch(/Protected Page.*foo/); + }); - test('is a no-op when invoked as a client-side protection from the server', async () => { - const baseUrl = await setup(withoutApi); - const cookieJar = await login(baseUrl); - const { - res: { statusCode } - } = await get(baseUrl, '/csr-protected', { cookieJar, fullResponse: true }); - expect(statusCode).toBe(200); - }); + test('use a custom login url', async () => { + process.env.NEXT_PUBLIC_AUTH0_LOGIN = '/api/foo'; + const baseUrl = await setup(withoutApi); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/protected', { fullResponse: true }); + expect(statusCode).toBe(307); + expect(decodeURIComponent(headers.location)).toBe('/api/foo?returnTo=/protected'); + delete process.env.NEXT_PUBLIC_AUTH0_LOGIN; + }); - test('should preserve multiple query params in the returnTo URL', async () => { - const baseUrl = await setup(withoutApi, { withPageAuthRequiredOptions: { returnTo: '/foo?bar=baz&qux=quux' } }); - const { - res: { statusCode, headers } - } = await get(baseUrl, '/protected', { fullResponse: true }); - expect(statusCode).toBe(307); - const url = new URL(headers.location, baseUrl); - expect(url.searchParams.get('returnTo')).toEqual('/foo?bar=baz&qux=quux'); - }); + test('is a no-op when invoked as a client-side protection from the server', async () => { + const baseUrl = await setup(withoutApi); + const cookieJar = await login(baseUrl); + const { + res: { statusCode } + } = await get(baseUrl, '/csr-protected', { cookieJar, fullResponse: true }); + expect(statusCode).toBe(200); + }); - test('allow access to a page with a valid session and async props', async () => { - const baseUrl = await setup(withoutApi, { - withPageAuthRequiredOptions: { - getServerSideProps() { - return Promise.resolve({ props: Promise.resolve({}) }); + test('should preserve multiple query params in the returnTo URL', async () => { + const baseUrl = await setup(withoutApi, { withPageAuthRequiredOptions: { returnTo: '/foo?bar=baz&qux=quux' } }); + const { + res: { statusCode, headers } + } = await get(baseUrl, '/protected', { fullResponse: true }); + expect(statusCode).toBe(307); + const url = new URL(headers.location, baseUrl); + expect(url.searchParams.get('returnTo')).toEqual('/foo?bar=baz&qux=quux'); + }); + + test('allow access to a page with a valid session and async props', async () => { + const baseUrl = await setup(withoutApi, { + withPageAuthRequiredOptions: { + getServerSideProps() { + return Promise.resolve({ props: Promise.resolve({}) }); + } } - } + }); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode, headers }, + data + } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); + expect(statusCode).toBe(200); + expect(data).toMatch(/Protected Page.*__test_sub__/); + const [cookie] = headers['set-cookie']; + expect(cookie).toMatch(/^appSession=/); }); - const cookieJar = await login(baseUrl); - - const { - res: { statusCode, headers }, - data - } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); - expect(statusCode).toBe(200); - expect(data).toMatch(/Protected Page.*__test_sub__/); - const [cookie] = headers['set-cookie']; - expect(cookie).toMatch(/^appSession=/); - }); - test('save session when getServerSideProps completes async', async () => { - const baseUrl = await setup(withoutApi, { - withPageAuthRequiredOptions: { - async getServerSideProps(ctx) { - await Promise.resolve(); - const session = await (global as any).getSession(ctx.req, ctx.res); - await (global as any).updateSession(ctx.req, ctx.res, { ...session, test: 'Hello World!' }); - return { props: {} }; + test('save session when getServerSideProps completes async', async () => { + const baseUrl = await setup(withoutApi, { + withPageAuthRequiredOptions: { + async getServerSideProps(ctx) { + await Promise.resolve(); + const session = await (global as any).getSession(ctx.req, ctx.res); + await (global as any).updateSession(ctx.req, ctx.res, { ...session, test: 'Hello World!' }); + return { props: {} }; + } } - } + }); + const cookieJar = await login(baseUrl); + + const { + res: { statusCode } + } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); + expect(statusCode).toBe(200); + const session = await get(baseUrl, '/api/session', { cookieJar }); + expect(session.test).toBe('Hello World!'); }); - const cookieJar = await login(baseUrl); - - const { - res: { statusCode } - } = await get(baseUrl, '/protected', { cookieJar, fullResponse: true }); - expect(statusCode).toBe(200); - const session = await get(baseUrl, '/api/session', { cookieJar }); - expect(session.test).toBe('Hello World!'); }); }); diff --git a/tests/http/auth0-next-request.test.ts b/tests/http/auth0-next-request.test.ts index c88c5cf7e..dc16abc3c 100644 --- a/tests/http/auth0-next-request.test.ts +++ b/tests/http/auth0-next-request.test.ts @@ -8,7 +8,7 @@ const setup = (reqInit?: RequestInit): [NextRequest, NextResponse] => { return [new NextRequest(new URL('http://example.com'), reqInit), NextResponse.next()]; }; -describe('cookie', () => { +describe('auth0-next-request', () => { it('should get all cookies', async () => { const [req] = setup({ headers: { cookie: 'foo=bar; bar=baz;' } }); expect(new Auth0NextRequest(req).getCookies()).toMatchObject({ foo: 'bar', bar: 'baz' }); diff --git a/tests/http/auth0-next-response-cookies.test.ts b/tests/http/auth0-next-response-cookies.test.ts new file mode 100644 index 000000000..d78162b99 --- /dev/null +++ b/tests/http/auth0-next-response-cookies.test.ts @@ -0,0 +1,43 @@ +import { cookies as nextCookies } from 'next/headers'; +import { mocked } from 'ts-jest/utils'; +import { Auth0NextResponseCookies } from '../../src/http'; +import { NextResponse } from 'next/server'; + +jest.mock('next/headers'); + +describe('auth0-next-response', () => { + it('should set a cookie', async () => { + const cookies = new Auth0NextResponseCookies(); + const res = new NextResponse(); + mocked(nextCookies).mockImplementation(() => res.cookies as any); + cookies.setCookie('foo', 'bar'); + expect(res.cookies.get('foo')?.value).toEqual('bar'); + }); + + it("should warn if cookie can't be set", async () => { + jest.spyOn(console, 'warn'); + const cookies = new Auth0NextResponseCookies(); + const res = new NextResponse(); + mocked(nextCookies).mockImplementation( + () => + ({ + set() { + throw new Error(); + } + } as any) + ); + cookies.setCookie('foo', 'bar'); + expect(console.warn).toHaveBeenCalledWith( + expect.stringMatching(/cant set cookies in app dir pages or server components/) + ); + expect(res.cookies.get('foo')).toBeUndefined(); + }); + + it('should delete cookies', async () => { + const cookies = new Auth0NextResponseCookies(); + const res = new NextResponse(); + mocked(nextCookies).mockImplementation(() => res.cookies as any); + cookies.clearCookie('foo'); + expect(res.headers.get('set-cookie')).toEqual('foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT'); + }); +}); diff --git a/tests/http/auth0-next-response.test.ts b/tests/http/auth0-next-response.test.ts index 8b60bb9d6..20b275c0b 100644 --- a/tests/http/auth0-next-response.test.ts +++ b/tests/http/auth0-next-response.test.ts @@ -8,7 +8,7 @@ const setup = (reqInit?: RequestInit): [NextRequest, NextResponse] => { return [new NextRequest(new URL('http://example.com'), reqInit), NextResponse.next()]; }; -describe('cookie', () => { +describe('auth0-next-response', () => { it('should set a cookie', async () => { const [, res] = setup(); const auth0Res = new Auth0NextResponse(res); From cfbfad12ac2162a1b5abc9950f40c17437661e2d Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 6 Jun 2023 09:26:26 +0100 Subject: [PATCH 33/61] Simplify running example app --- CONTRIBUTING.md | 12 ------------ example-app/lib/auth0.ts | 2 +- example-app/server.js | 6 +++++- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2b47aab2..c24337e9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,18 +23,6 @@ Please read [Auth0's contribution guidelines](https://github.com/auth0/open-sour ## Running examples against a mock openid provider -Your env vars in `/example-app/.env.local` should look like - -```bash -AUTH0_SECRET=#ANY LONG RANDOM VALUE -AUTH0_ISSUER_BASE_URL=http://localhost:3000/oidc -AUTH0_BASE_URL=http://localhost:3000 -AUTH0_CLIENT_ID=testing -AUTH0_CLIENT_SECRET=testing -``` - -Then run one of the commands: - - `start:example-local`: Run the example app with a mock openid provider - `test:example-local`: Run the E2E tests with a mock openid provider - `test:example-local:watch`: Run the E2E tests with a mock openid provider and watch for changes diff --git a/example-app/lib/auth0.ts b/example-app/lib/auth0.ts index c4c70920b..dc32a5435 100644 --- a/example-app/lib/auth0.ts +++ b/example-app/lib/auth0.ts @@ -1,7 +1,7 @@ import { initAuth0, Auth0Server } from '@auth0/nextjs-auth0'; export const pageRouterAuth: Auth0Server = initAuth0({ - auth0Logout: process.env.USE_AUTH0 ? true : false, + auth0Logout: !(process.env.AUTH0_ISSUER_BASE_URL as string).startsWith('http://localhost'), routes: { login: '/api/page-router-auth/login', callback: '/api/page-router-auth/callback', diff --git a/example-app/server.js b/example-app/server.js index 8340cec37..136f2445e 100644 --- a/example-app/server.js +++ b/example-app/server.js @@ -2,10 +2,14 @@ const express = require('express'); const next = require('next'); const oidc = require('../scripts/oidc-provider'); -const port = process.env.PORT || 3000; +const port = +(process.env.PORT || 3000); const app = next({ dev: true, hostname: 'localhost', port }); const handle = app.getRequestHandler(); +process.env.AUTH0_ISSUER_BASE_URL = `http://localhost:${port}/oidc`; +process.env.AUTH0_CLIENT_ID = 'testing'; +process.env.AUTH0_CLIENT_SECRET = 'testing'; + app .prepare() .then(() => { From aad1bf74fe430f441a6893a9a055fdd9db1d6da9 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 6 Jun 2023 11:50:36 +0100 Subject: [PATCH 34/61] Fix client withPageX types and add logout e2e tests for app dir --- cypress/e2e/smoke.cy.ts | 12 ++++++++++++ example-app/app/client-component.tsx | 2 +- example-app/app/profile/page.tsx | 12 +++++++++++- example-app/app/server-component.tsx | 2 +- src/client/index.ts | 3 +-- src/client/with-page-auth-required.tsx | 9 +-------- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/cypress/e2e/smoke.cy.ts b/cypress/e2e/smoke.cy.ts index d304fe634..46c59e16b 100644 --- a/cypress/e2e/smoke.cy.ts +++ b/cypress/e2e/smoke.cy.ts @@ -107,5 +107,17 @@ describe('smoke tests', () => { cy.url().should('eq', `${Cypress.config().baseUrl}/profile-api`); cy.get('[data-testid=profile-api]').contains(EMAIL); }); + + it('should logout and return to the index page', () => { + cy.visit('/profile'); + login(); + cy.url().should('eq', `${Cypress.config().baseUrl}/profile`); + cy.get('[data-testid=logout]').click(); + if (!useAuth0) { + cy.get('[name=logout]').click(); + } + cy.url().should('eq', `${Cypress.config().baseUrl}/`); + cy.get('[data-testid=login]').should('exist'); + }); }); }); diff --git a/example-app/app/client-component.tsx b/example-app/app/client-component.tsx index b21a8cc83..80f5e6b3c 100644 --- a/example-app/app/client-component.tsx +++ b/example-app/app/client-component.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useUser } from '../../client'; +import { useUser } from '@auth0/nextjs-auth0/client'; export default function ClientComponent() { const { user } = useUser(); diff --git a/example-app/app/profile/page.tsx b/example-app/app/profile/page.tsx index bbec98035..99030943b 100644 --- a/example-app/app/profile/page.tsx +++ b/example-app/app/profile/page.tsx @@ -1,10 +1,20 @@ -import React from 'react'; +import React, { cache } from 'react'; +import { cookies } from 'next/headers'; import { getSession, withPageAuthRequired } from '@auth0/nextjs-auth0'; import ServerComponent from '@/app/server-component'; import ClientComponent from '@/app/client-component'; +const getCookies = cache(() => cookies().getAll()); + +const foo = new Map(); + export default withPageAuthRequired( async function Page() { + console.log(getCookies()); + + foo.set(cookies(), new Date()); + console.log(foo); + const session = await getSession(); return ( diff --git a/example-app/app/server-component.tsx b/example-app/app/server-component.tsx index 8df94dac1..520d27669 100644 --- a/example-app/app/server-component.tsx +++ b/example-app/app/server-component.tsx @@ -1,4 +1,4 @@ -import { getSession, getAccessToken } from '../../dist'; +import { getSession, getAccessToken } from '@auth0/nextjs-auth0'; export default async function ServerComponent() { const session = await getSession(); diff --git a/src/client/index.ts b/src/client/index.ts index 23960346e..3d4747170 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -10,8 +10,7 @@ import { import { default as withPageAuthRequired, WithPageAuthRequired, - WithPageAuthRequiredProps, WithPageAuthRequiredOptions } from './with-page-auth-required'; export { UserProvider, UserProviderProps, UserProfile, UserContext, RequestError, useUser }; -export { withPageAuthRequired, WithPageAuthRequired, WithPageAuthRequiredProps, WithPageAuthRequiredOptions }; +export { withPageAuthRequired, WithPageAuthRequired, WithPageAuthRequiredOptions }; diff --git a/src/client/with-page-auth-required.tsx b/src/client/with-page-auth-required.tsx index 9b86015af..ee7837e25 100644 --- a/src/client/with-page-auth-required.tsx +++ b/src/client/with-page-auth-required.tsx @@ -52,13 +52,6 @@ export interface WithPageAuthRequiredOptions { onError?: (error: Error) => JSX.Element; } -/** - * @ignore - */ -export interface WithPageAuthRequiredProps { - [key: string]: any; -} - export interface UserProps { user: UserProfile; } @@ -73,7 +66,7 @@ export interface UserProps { * * @category Client */ -export type WithPageAuthRequired =

    ( +export type WithPageAuthRequired =

    ( Component: ComponentType

    , options?: WithPageAuthRequiredOptions ) => React.FC

    ; From 71c629c0829fd28f333aaf5ae9e5bea95eb71a9e Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 6 Jun 2023 12:15:09 +0100 Subject: [PATCH 35/61] Fix lint errors --- src/handlers/auth.ts | 3 ++- src/handlers/callback.ts | 2 +- src/handlers/login.ts | 1 + src/handlers/router-helpers.ts | 7 +++++-- src/helpers/with-api-auth-required.ts | 4 ++-- src/http/auth0-next-response-cookies.ts | 3 +++ src/session/get-access-token.ts | 4 +++- 7 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/handlers/auth.ts b/src/handlers/auth.ts index c7d2b380f..409ebce06 100644 --- a/src/handlers/auth.ts +++ b/src/handlers/auth.ts @@ -90,7 +90,8 @@ type ErrorHandlers = { * * @category Server */ -export type HandleAuth = (userHandlers?: Handlers) => NextApiHandler | AppRouteHandlerFn | any; // any is required for app router ts check +// any is required for app router ts check +export type HandleAuth = (userHandlers?: Handlers) => NextApiHandler | AppRouteHandlerFn | any; /** * Error handler for the default auth routes. diff --git a/src/handlers/callback.ts b/src/handlers/callback.ts index a31112633..7dc67ae99 100644 --- a/src/handlers/callback.ts +++ b/src/handlers/callback.ts @@ -224,7 +224,7 @@ const applyOptions = ( options: CallbackOptions, config: NextConfig ) => { - let opts = { ...options }; + const opts = { ...options }; const idTokenValidator = (afterCallback?: AfterCallback, organization?: string): BaseAfterCallback => (session, state) => { diff --git a/src/handlers/login.ts b/src/handlers/login.ts index ec656094f..172c4fac8 100644 --- a/src/handlers/login.ts +++ b/src/handlers/login.ts @@ -263,6 +263,7 @@ const applyOptions = ( ): BaseLoginOptions => { let opts: BaseLoginOptions; let getLoginState: GetLoginState | undefined; + // eslint-disable-next-line prefer-const ({ getLoginState, ...opts } = options); if (dangerousReturnTo) { const safeBaseUrl = new URL(options.authorizationParams?.redirect_uri || baseConfig.baseURL); diff --git a/src/handlers/router-helpers.ts b/src/handlers/router-helpers.ts index c4a9aff2c..39edf0aed 100644 --- a/src/handlers/router-helpers.ts +++ b/src/handlers/router-helpers.ts @@ -56,7 +56,10 @@ export type Handler = { }; export const getHandler = - (appRouteHandler: AppRouteHandlerFn, pageRouteHandler: PageRouteHandlerFn) => + >( + appRouteHandler: AppRouteHandlerFn, + pageRouteHandler: PageRouteHandlerFn + ) => ( reqOrOptions: NextApiRequest | NextRequest | Opts, resOrCtx: NextApiResponse | AppRouteHandlerFnContext, @@ -66,7 +69,7 @@ export const getHandler = return appRouteHandler(reqOrOptions, resOrCtx as AppRouteHandlerFnContext, options); } if ('socket' in reqOrOptions) { - return pageRouteHandler(reqOrOptions, resOrCtx as NextApiResponse, options); + return pageRouteHandler(reqOrOptions as NextApiRequest, resOrCtx as NextApiResponse, options); } return (req: NextApiRequest | NextRequest, resOrCtxInner: NextApiResponse | AppRouteHandlerFnContext) => { const opts = typeof reqOrOptions === 'function' ? (reqOrOptions as OptionsProvider)(req) : reqOrOptions; diff --git a/src/helpers/with-api-auth-required.ts b/src/helpers/with-api-auth-required.ts index f0d343b91..d26e5da34 100644 --- a/src/helpers/with-api-auth-required.ts +++ b/src/helpers/with-api-auth-required.ts @@ -75,8 +75,8 @@ const appRouteHandlerFactory = { status: 401 } ); } - let apiRes: NextResponse | Response = await apiRoute(req, params); - let nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes); + const apiRes: NextResponse | Response = await apiRoute(req, params); + const nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes); for (const cookie of res.cookies.getAll()) { nextApiRes.cookies.set(cookie); } diff --git a/src/http/auth0-next-response-cookies.ts b/src/http/auth0-next-response-cookies.ts index 1e90e4954..ab119527b 100644 --- a/src/http/auth0-next-response-cookies.ts +++ b/src/http/auth0-next-response-cookies.ts @@ -10,6 +10,8 @@ export default class Auth0NextResponseCookies extends Auth0ResponseCookies { public setCookie(name: string, value: string, options?: CookieSerializeOptions) { const cookieSetter = cookies(); try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore see: https://github.com/vercel/next.js/pull/50052 cookieSetter.set({ ...options, name, value }); } catch (_) { console.warn('cant set cookies in app dir pages or server components'); @@ -18,6 +20,7 @@ export default class Auth0NextResponseCookies extends Auth0ResponseCookies { public clearCookie(name: string, options?: CookieSerializeOptions) { const cookieSetter = cookies(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore see: https://github.com/vercel/next.js/pull/50052 cookieSetter.delete({ ...options, name, value: '' }); } diff --git a/src/session/get-access-token.ts b/src/session/get-access-token.ts index 184774d69..b0bf3b58e 100644 --- a/src/session/get-access-token.ts +++ b/src/session/get-access-token.ts @@ -110,7 +110,9 @@ export default function accessTokenFactory( const options = (res ? accessTokenRequest : reqOrOpts) as AccessTokenRequest | undefined; const req = (res ? reqOrOpts : undefined) as IncomingMessage | NextApiRequest | undefined; - let [session, iat] = await get({ sessionCache, req, res }); + const parts = await get({ sessionCache, req, res }); + let [session] = parts; + const [, iat] = parts; if (!session) { throw new AccessTokenError(AccessTokenErrorCode.MISSING_SESSION, 'The user does not have a valid session.'); } From 372cd5c51572a0c931e6153d28cbec37ed20f4e7 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 6 Jun 2023 12:28:59 +0100 Subject: [PATCH 36/61] remove test code form example app --- example-app/app/profile/page.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/example-app/app/profile/page.tsx b/example-app/app/profile/page.tsx index 99030943b..bbec98035 100644 --- a/example-app/app/profile/page.tsx +++ b/example-app/app/profile/page.tsx @@ -1,20 +1,10 @@ -import React, { cache } from 'react'; -import { cookies } from 'next/headers'; +import React from 'react'; import { getSession, withPageAuthRequired } from '@auth0/nextjs-auth0'; import ServerComponent from '@/app/server-component'; import ClientComponent from '@/app/client-component'; -const getCookies = cache(() => cookies().getAll()); - -const foo = new Map(); - export default withPageAuthRequired( async function Page() { - console.log(getCookies()); - - foo.set(cookies(), new Date()); - console.log(foo); - const session = await getSession(); return ( From 9115173fa718b18c3716b49ca2372574330517c0 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Tue, 6 Jun 2023 13:48:12 +0100 Subject: [PATCH 37/61] Type improvements and migration guide --- README.md | 4 +- V3_MIGRATION_GUIDE.md | 52 +++++++++++++++++++++++--- example-app/public/next.svg | 1 - example-app/public/vercel.svg | 1 - package.json | 2 +- src/auth0-session/handlers/callback.ts | 4 +- src/auth0-session/handlers/logout.ts | 2 +- src/auth0-session/http/node-request.ts | 2 +- src/auth0-session/session-cache.ts | 2 +- src/session/get-access-token.ts | 10 +++-- src/session/get-session.ts | 10 ++--- src/session/touch-session.ts | 5 +-- src/session/update-session.ts | 10 +++-- 13 files changed, 72 insertions(+), 33 deletions(-) delete mode 100644 example-app/public/next.svg delete mode 100644 example-app/public/vercel.svg diff --git a/README.md b/README.md index c759a576b..7d1a2c441 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ The Auth0 Next.js SDK is a library for implementing user authentication in Next.js applications. +> :warning: Please be aware that v3 is currently in [**Beta**](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages). Whilst we encourage you to test the update within your applications, we do no recommend using this version in production yet. + ![Release](https://img.shields.io/npm/v/@auth0/nextjs-auth0) [![Coverage](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=coverage&query=jest.coverageThreshold.global.lines&suffix=%25&url=https%3A%2F%2Fraw.githubusercontent.com%2Fauth0%2Fnextjs-auth0%2Fmain%2Fpackage.json)](https://github.com/auth0/nextjs-auth0/blob/main/package.json#L147) ![Downloads](https://img.shields.io/npm/dw/@auth0/nextjs-auth0) @@ -28,7 +30,7 @@ The Auth0 Next.js SDK is a library for implementing user authentication in Next. Using [npm](https://npmjs.org): ```sh -npm install @auth0/nextjs-auth0 +npm install @auth0/nextjs-auth0@beta ``` This library supports the following tooling versions: diff --git a/V3_MIGRATION_GUIDE.md b/V3_MIGRATION_GUIDE.md index d9a9daaae..be7c0e520 100644 --- a/V3_MIGRATION_GUIDE.md +++ b/V3_MIGRATION_GUIDE.md @@ -2,16 +2,56 @@ Guide to migrating from `2.x` to `3.x` -- [Node 14 is no longer supported](#node-14-is-no-longer-supported) +## Node 16 or newer is required +Node 16 LTS and newer LTS releases are supported. -## Node 14 is no longer supported +## TypeScript changes -Node 16 LTS and newer LTS releases are supported. +All the server functions of this SDK now support the App Router in addition to the Page Router. + +As a result of this, the type signatures of these functions have been overloaded to include the App Router signatures. + +So in some places, TypeScript may require help inferring the types of request and response. e.g. + +### Before + +```ts +import { withApiAuthRequired } from '@auth0/nextjs-auth0' + +export default withApiAuthRequired(async function handler(req, res) { + res.status(200).json({}); +}); +``` + +### After + +```ts +import { NextApiRequest, NextApiResponse } from 'next'; +import { withApiAuthRequired } from '@auth0/nextjs-auth0' + +export default withApiAuthRequired(async function handler(req: NextApiRequest, res: NextApiResponse) { + res.status(200).json({}); +}); +``` + + +## The `/401` handler has been removed + +As of Next.js 13.1, you can now return responses from Middleware so the Unauthorized handler has been removed in favour of an Unauthorized response. +If you want to protect an API with `withMiddlewareAuthRequired` you will need a minimum of Next.js 13.1, or add the 401 back yourself. e.g. -## How Hooks +```ts +import { handleAuth } from '@auth0/nextjs-auth0'; -## unauthorized handler been removed +export default handleAuth({ + '401'(_req, res) { + res.status(401).json({ + error: 'not_authenticated', + description: 'The user does not have an active session or is not authenticated' + }); + } +}); -need next vX for middleware support of APIs +``` diff --git a/example-app/public/next.svg b/example-app/public/next.svg deleted file mode 100644 index 5174b28c5..000000000 --- a/example-app/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/example-app/public/vercel.svg b/example-app/public/vercel.svg deleted file mode 100644 index d2f842227..000000000 --- a/example-app/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/package.json b/package.json index 52df52df9..a15cb8b6a 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "url-join": "^4.0.1" }, "peerDependencies": { - "next": ">=10" + "next": ">=16" }, "jest": { "testEnvironment": "jest-environment-node-single-context", diff --git a/src/auth0-session/handlers/callback.ts b/src/auth0-session/handlers/callback.ts index 604939f38..f4b5fbc55 100644 --- a/src/auth0-session/handlers/callback.ts +++ b/src/auth0-session/handlers/callback.ts @@ -1,3 +1,4 @@ +import type { IncomingMessage } from 'http'; import urlJoin from 'url-join'; import createHttpError from 'http-errors'; import { errors } from 'openid-client'; @@ -14,7 +15,6 @@ import { MissingStateParamError } from '../utils/errors'; import { Auth0Request, Auth0Response } from '../http'; -import { IncomingMessage } from 'http'; function getRedirectUri(config: Config): string { return urlJoin(config.baseURL, config.routes.callback); @@ -37,7 +37,7 @@ export type HandleCallback = (req: Auth0Request, res: Auth0Response, options?: C export default function callbackHandlerFactory( config: Config, getClient: ClientFactory, - sessionCache: SessionCache, + sessionCache: SessionCache, transientCookieHandler: TransientStore ): HandleCallback { return async (req, res, options) => { diff --git a/src/auth0-session/handlers/logout.ts b/src/auth0-session/handlers/logout.ts index 7a029fb53..ec42bf131 100644 --- a/src/auth0-session/handlers/logout.ts +++ b/src/auth0-session/handlers/logout.ts @@ -13,7 +13,7 @@ export type HandleLogout = (req: Auth0Request, res: Auth0Response, options?: Log export default function logoutHandlerFactory( config: Config, getClient: ClientFactory, - sessionCache: SessionCache + sessionCache: SessionCache ): HandleLogout { return async (req, res, options = {}) => { let returnURL = options.returnTo || config.routes.postLogoutRedirect; diff --git a/src/auth0-session/http/node-request.ts b/src/auth0-session/http/node-request.ts index 943667b5b..d08241363 100644 --- a/src/auth0-session/http/node-request.ts +++ b/src/auth0-session/http/node-request.ts @@ -17,7 +17,7 @@ export default class NodeRequest extends Auth0Request { } public getBody() { - return (this.req as any).body as Record; + return (this.req as IncomingMessage & { body: Record }).body; } public getCookies(): Record { diff --git a/src/auth0-session/session-cache.ts b/src/auth0-session/session-cache.ts index d6670b3e9..bfa971937 100644 --- a/src/auth0-session/session-cache.ts +++ b/src/auth0-session/session-cache.ts @@ -1,6 +1,6 @@ import type { TokenSet } from 'openid-client'; -export interface SessionCache { +export interface SessionCache { create(req: Req, res: Res, session: Session): Promise; delete(req: Req, res: Res): Promise; isAuthenticated(req: Req, res: Res): Promise; diff --git a/src/session/get-access-token.ts b/src/session/get-access-token.ts index b0bf3b58e..d441a50bf 100644 --- a/src/session/get-access-token.ts +++ b/src/session/get-access-token.ts @@ -93,9 +93,11 @@ export interface GetAccessTokenResult { * @category Server */ export type GetAccessToken = ( - req?: IncomingMessage | NextApiRequest | NextRequest | AccessTokenRequest, - res?: ServerResponse | NextApiResponse | NextResponse, - accessTokenRequest?: AccessTokenRequest + ...args: + | [IncomingMessage, ServerResponse, AccessTokenRequest?] + | [NextApiRequest, NextApiResponse, AccessTokenRequest?] + | [NextRequest, NextResponse, AccessTokenRequest?] + | [AccessTokenRequest?] ) => Promise; /** @@ -106,7 +108,7 @@ export default function accessTokenFactory( getClient: ClientFactory, sessionCache: SessionCache ): GetAccessToken { - return async (reqOrOpts, res, accessTokenRequest): Promise => { + return async (reqOrOpts?, res?, accessTokenRequest?): Promise => { const options = (res ? accessTokenRequest : reqOrOpts) as AccessTokenRequest | undefined; const req = (res ? reqOrOpts : undefined) as IncomingMessage | NextApiRequest | undefined; diff --git a/src/session/get-session.ts b/src/session/get-session.ts index ccedb0536..05e98d7ea 100644 --- a/src/session/get-session.ts +++ b/src/session/get-session.ts @@ -9,18 +9,14 @@ import { SessionCache, Session, get } from '../session'; * @category Server */ export type GetSession = ( - req?: IncomingMessage | NextApiRequest | NextRequest, - res?: ServerResponse | NextApiResponse | NextResponse + ...args: [IncomingMessage, ServerResponse] | [NextApiRequest, NextApiResponse] | [NextRequest, NextResponse] | [] ) => Promise; /** * @ignore */ -export default function sessionFactory(sessionCache: SessionCache) { - return async ( - req?: IncomingMessage | NextApiRequest | NextRequest, - res?: ServerResponse | NextApiResponse | NextResponse - ) => { +export default function sessionFactory(sessionCache: SessionCache): GetSession { + return async (req?, res?) => { const [session] = await get({ req, res, sessionCache }); return session; }; diff --git a/src/session/touch-session.ts b/src/session/touch-session.ts index 06915a77d..06192b05c 100644 --- a/src/session/touch-session.ts +++ b/src/session/touch-session.ts @@ -21,15 +21,14 @@ import { get, set, SessionCache } from '../session'; * @category Server */ export type TouchSession = ( - req?: IncomingMessage | NextApiRequest | NextRequest, - res?: ServerResponse | NextApiResponse | NextResponse + ...args: [IncomingMessage, ServerResponse] | [NextApiRequest, NextApiResponse] | [NextRequest, NextResponse] | [] ) => Promise; /** * @ignore */ export default function touchSessionFactory(sessionCache: SessionCache): TouchSession { - return async (req, res) => { + return async (req?, res?) => { const [session, iat] = await get({ sessionCache, req, res }); if (!session) { return; diff --git a/src/session/update-session.ts b/src/session/update-session.ts index 147c7e4e7..99d19b0ad 100644 --- a/src/session/update-session.ts +++ b/src/session/update-session.ts @@ -25,16 +25,18 @@ import { get, set, Session, SessionCache } from '../session'; * @category Server */ export type UpdateSession = ( - req?: IncomingMessage | NextApiRequest | NextRequest | Session, - res?: ServerResponse | NextApiResponse | NextResponse, - user?: Session + ...args: + | [IncomingMessage, ServerResponse, Session] + | [NextApiRequest, NextApiResponse, Session] + | [NextRequest, NextResponse, Session] + | [Session] ) => Promise; /** * @ignore */ export default function updateSessionFactory(sessionCache: SessionCache): UpdateSession { - return async (reqOrSession, res, newSession) => { + return async (reqOrSession, res?, newSession?) => { const session = (res ? newSession : reqOrSession) as Session | undefined; const req = (res ? reqOrSession : undefined) as IncomingMessage | NextApiRequest | NextRequest | undefined; From 2cd89025d889e8ad9beb45bbc78ece7ee23fcd67 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Wed, 7 Jun 2023 12:08:34 +0100 Subject: [PATCH 38/61] revert peer deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a15cb8b6a..52df52df9 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "url-join": "^4.0.1" }, "peerDependencies": { - "next": ">=16" + "next": ">=10" }, "jest": { "testEnvironment": "jest-environment-node-single-context", From be11cc7395328137f9eaf058dc6f98742e491eca Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Wed, 7 Jun 2023 13:26:19 +0100 Subject: [PATCH 39/61] Update README/Examples, add fn option for returnTo --- EXAMPLES.md | 100 ++++++++++++--- README.md | 117 ++++++++++++++---- src/helpers/index.ts | 3 +- src/helpers/with-page-auth-required.ts | 69 +++++++++-- src/index.ts | 6 +- tests/fixtures/setup.ts | 4 +- tests/helpers/with-page-auth-required.test.ts | 19 ++- 7 files changed, 267 insertions(+), 51 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 08c27dbc8..1cfc03730 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,5 +1,11 @@ # Examples +**App Router** + +- [Basic Setup](#basic-setup) + +**Page Router** + - [Basic Setup](#basic-setup) - [Create your own instance of the SDK](#create-your-own-instance-of-the-sdk) - [Customize handlers behavior](#customize-handlers-behavior) @@ -15,7 +21,75 @@ See also the the [example app](./example-app). -## Basic Setup +## App Router + +### Basic Setup + +Configure the required options in an `.env.local` file in the root of your application: + +```sh +AUTH0_SECRET='LONG_RANDOM_VALUE' +AUTH0_BASE_URL='http://localhost:3000' +AUTH0_ISSUER_BASE_URL='https://your-tenant.auth0.com' +AUTH0_CLIENT_ID='CLIENT_ID' +AUTH0_CLIENT_SECRET='CLIENT_SECRET' +``` + +Create a [dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes) at `/app/api/auth/[auth0]/route.js`. + +```js +import { handleAuth } from '@auth0/nextjs-auth0'; + +export const GET = handleAuth(); +``` + +This will create the following urls: `/api/auth/login`, `/api/auth/callback`, `/api/auth/logout` and `/api/auth/me`. + +Wrap your `app/layout.jsx` component in the `UserProvider` component. + +```jsx +// app/layout.jsx +import React from 'react'; +import { UserProvider } from '@auth0/nextjs-auth0/client'; + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ); +} +``` + +Check the user's authentication state and log them in or out from the front end using the `useUser` hook. + +```jsx +// app/page.jsx +'use client'; +import { useUser } from '@auth0/nextjs-auth0/client'; + +export default function Index() { + const { user, error, isLoading } = useUser(); + + if (isLoading) return

    Loading...
    ; + if (error) return
    {error.message}
    ; + + if (user) { + return ( +
    + Welcome {user.name}! Logout +
    + ); + } + return Login; +}; +``` + +## Page Router + +### Basic Setup Configure the required options in an `.env.local` file in the root of your application: @@ -63,7 +137,7 @@ Check the user's authentication state and log them in or out from the front end // pages/index.jsx import { useUser } from '@auth0/nextjs-auth0/client'; -export default () => { +export default function Index() { const { user, error, isLoading } = useUser(); if (isLoading) return
    Loading...
    ; @@ -80,7 +154,7 @@ export default () => { }; ``` -## Create your own instance of the SDK +### Create your own instance of the SDK When you use the named exports, the SDK creates an instance of the SDK for you and configures it with the provided environment variables. @@ -153,7 +227,7 @@ export default auth0.handleAuth({ }); ``` -## Customize handlers behavior +### Customize handlers behavior Pass custom parameters to the auth handlers or add your own logging and error handling. @@ -189,7 +263,7 @@ export default handleAuth({ }); ``` -## Use custom auth urls +### Use custom auth urls Instead of (or in addition to) creating `/pages/api/auth/[auth0].js` to handle all requests, you can create them individually at different urls. @@ -215,7 +289,7 @@ export default () => Login; > Note: If you customize the login url you will need to set the environment variable `NEXT_PUBLIC_AUTH0_LOGIN` to this custom value for `withPageAuthRequired` to work correctly. And if you customize the profile url, you will need to set the `NEXT_PUBLIC_AUTH0_PROFILE` environment variable to this custom value for the `useUser` hook to work properly. -## Protecting a Server-Side Rendered (SSR) Page +### Protecting a Server-Side Rendered (SSR) Page Requests to `/pages/profile` without a valid session cookie will be redirected to the login page. @@ -234,7 +308,7 @@ export const getServerSideProps = withPageAuthRequired(); See a running example of an [SSR protected page](./example-app/pages/profile-ssr.tsx) in the example app or refer to the full list of configuration options for `withPageAuthRequired` [here](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequiredoptions). -## Protecting a Client-Side Rendered (CSR) Page +### Protecting a Client-Side Rendered (CSR) Page Requests to `/pages/profile` without a valid session cookie will be redirected to the login page. @@ -249,7 +323,7 @@ export default withPageAuthRequired(function Profile({ user }) { See a running example of a [CSR protected page](./example-app/pages/profile.tsx) in the example app. -## Protect an API Route +### Protect an API Route Requests to `/pages/api/protected` without a valid session cookie will fail with `401`. @@ -286,7 +360,7 @@ export default withPageAuthRequired(function Products() { See a running example in the example app, the [protected API route](./example-app/pages/api/shows.ts) and the [frontend code to access the protected API](./example-app/pages/shows.tsx). -## Protecting pages with Middleware +### Protecting pages with Middleware Protect your pages with Next.js Middleware. @@ -346,7 +420,7 @@ export default auth0.withMiddlewareAuthRequired(async function middleware(req) { }); ``` -## Access an External API from an API Route +### Access an External API from an API Route Get an access token by providing your API's audience and scopes. You can pass them directly to the `handlelogin` method, or use environment variables instead. @@ -388,8 +462,6 @@ export default withApiAuthRequired(async function products(req, res) { }); ``` -See a running example of the [API route acting as a proxy to an External API](./example-app/pages/api/shows.ts) in the example app. - ### Getting a Refresh Token - Include the `offline_access` scope your configuration (or `AUTH0_SCOPE`) @@ -417,7 +489,7 @@ Users can then sign up using the signup handler. Sign up ``` -## Use with Base Path and Internationalized Routing +### Use with Base Path and Internationalized Routing With Next.js you can deploy a Next.js application under a sub-path of a domain using [Base Path](https://nextjs.org/docs/api-reference/next.config.js/basepath) and serve internationalized (i18n) routes using [Internationalized Routing](https://nextjs.org/docs/advanced-features/i18n-routing). @@ -468,7 +540,7 @@ export const getServerSideProps = (ctx) => { }; ``` -## Use a custom session store +### Use a custom session store You need to create your own instance of the SDK in code, so you can pass an instance of your session store to the SDK's configuration. diff --git a/README.md b/README.md index 7d1a2c441..bc97f4876 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ The Auth0 Next.js SDK is a library for implementing user authentication in Next.js applications. -> :warning: Please be aware that v3 is currently in [**Beta**](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages). Whilst we encourage you to test the update within your applications, we do no recommend using this version in production yet. +> :warning: Please be aware that v3 is currently in [**Beta**](https://auth0.com/docs/troubleshoot/product-lifecycle/product-release-stages). Whilst we encourage you to test the update within your applications, we do not recommend using this version in production yet. -![Release](https://img.shields.io/npm/v/@auth0/nextjs-auth0) +![Release](https://img.shields.io/npm/v/@auth0/nextjs-auth0/beta) [![Coverage](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=coverage&query=jest.coverageThreshold.global.lines&suffix=%25&url=https%3A%2F%2Fraw.githubusercontent.com%2Fauth0%2Fnextjs-auth0%2Fmain%2Fpackage.json)](https://github.com/auth0/nextjs-auth0/blob/main/package.json#L147) -![Downloads](https://img.shields.io/npm/dw/@auth0/nextjs-auth0) +![Downloads](https://img.shields.io/npm/dw/@auth0/nextjs-auth0/beta) [![License](https://img.shields.io/:license-mit-blue.svg?style=flat)](https://opensource.org/licenses/MIT) -![CircleCI](https://img.shields.io/circleci/build/github/auth0/nextjs-auth0) +![CircleCI](https://img.shields.io/circleci/build/github/auth0/nextjs-auth0/beta) 📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started)- 💻 [API Reference](#api-reference) - 💬 [Feedback](#feedback) @@ -33,10 +33,7 @@ Using [npm](https://npmjs.org): npm install @auth0/nextjs-auth0@beta ``` -This library supports the following tooling versions: - -- Node.js: 12 LTS and newer LTS releases are supported. -- Next.js: `>=10` +This library requires Node.js 16 LTS and newer LTS versions. ### Auth0 Configuration @@ -85,12 +82,22 @@ You can see a full list of Auth0 configuration options in the ["Configuration pr > For more details about loading environment variables in Next.js, visit the ["Environment Variables"](https://nextjs.org/docs/basic-features/environment-variables) document. -#### Add the Dynamic API Route +Add `handleAuth()` to your app, which creates the following route handlers under the hood that perform different parts of the authentication flow: + +- `/api/auth/login`: Your Next.js application redirects users to your identity provider for them to log in (you can optionally pass a `returnTo` parameter to return to a custom relative URL after login, for example `/api/auth/login?returnTo=/profile`). +- `/api/auth/callback`: Your identity provider redirects users to this route after they successfully log in. +- `/api/auth/logout`: Your Next.js application logs out the user. +- `/api/auth/me`: You can fetch user profile information in JSON format. + +> Note: `handleAuth` requires Node.js and so will not work on Cloudflare Workers or Vercel Edge Runtime. -Go to your Next.js application and create a [catch-all, dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes#optional-catch-all-api-routes) under the `/pages/api` directory: +#### Page Router -- Create an `auth` directory under the `/pages/api/` directory. +##### Add the Dynamic API Route + +Create a [catch-all, dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes#optional-catch-all-api-routes) under the `/pages/api` directory: +- Create an `auth` directory under the `/pages/api/` directory. - Create a `[auth0].js` file under the newly created `auth` directory. The path to your dynamic API route file would be `/pages/api/auth/[auth0].js`. Populate that file as follows: @@ -101,21 +108,77 @@ import { handleAuth } from '@auth0/nextjs-auth0'; export default handleAuth(); ``` -Executing `handleAuth()` creates the following route handlers under the hood that perform different parts of the authentication flow: +##### Add the UserProvider to Custom App -- `/api/auth/login`: Your Next.js application redirects users to your identity provider for them to log in (you can optionally pass a `returnTo` parameter to return to a custom relative URL after login, for example `/api/auth/login?returnTo=/profile`). -- `/api/auth/callback`: Your identity provider redirects users to this route after they successfully log in. -- `/api/auth/logout`: Your Next.js application logs out the user. -- `/api/auth/me`: You can fetch user profile information in JSON format. +Wrap your `pages/_app.js` component with the `UserProvider` component: -> Note: `handleAuth` requires Node.js and so will not work on Cloudflare Workers or Vercel Edge Runtime. +```jsx +// pages/_app.js +import React from 'react'; +import { UserProvider } from '@auth0/nextjs-auth0/client'; + +export default function App({ Component, pageProps }) { + return ( + + + + ); +} +``` -#### Add the UserProvider to Custom App +##### Consume Authentication -Wrap your `pages/_app.js` component with the `UserProvider` component: +You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined. You can also log in or log out your users from the frontend layer of your Next.js application by redirecting them to the appropriate automatically-generated route: ```jsx -// pages/_app.js +// pages/index.js +import { useUser } from '@auth0/nextjs-auth0/client'; + +export default function Index() { + const { user, error, isLoading } = useUser(); + + if (isLoading) return
    Loading...
    ; + if (error) return
    {error.message}
    ; + + if (user) { + return ( +
    + Welcome {user.name}! Logout +
    + ); + } + + return Login; +} +``` + +> Next linting rules might suggest using the `Link` component instead of an anchor tag. The `Link` component is meant to perform [client-side transitions between pages](https://nextjs.org/docs/api-reference/next/link). As the links point to an API route and not to a page, you should keep them as anchor tags. + +#### App Router + +##### Add the Dynamic API Route + +Create a [catch-all, dynamic API route handler](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) under the `/app/api` directory (strictly speaking you do not need to put API routes under `/api` but we maintain the convention for simplicity): + +- Create an `auth` directory under the `/app/api/` directory. +- Create a `[auth0]` directory under the newly created `auth` directory. +- Create a `route.js` file under the newly created `[auth0]` directory. + +The path to your dynamic API route file would be `/app/api/auth/[auth0]/route.js`. Populate that file as follows: + +```js +import { handleAuth } from '@auth0/nextjs-auth0'; + +export const GET = handleAuth(); +``` + + +##### Add the UserProvider to Custom App + +Wrap your `app/layout.js` component with the `UserProvider` component: + +```jsx +// app/layout.js import React from 'react'; import { UserProvider } from '@auth0/nextjs-auth0/client'; @@ -128,7 +191,7 @@ export default function App({ Component, pageProps }) { } ``` -#### Consume Authentication +##### Consume Authentication You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined. You can also log in or log out your users from the frontend layer of your Next.js application by redirecting them to the appropriate automatically-generated route: @@ -156,7 +219,17 @@ export default function Index() { > Next linting rules might suggest using the `Link` component instead of an anchor tag. The `Link` component is meant to perform [client-side transitions between pages](https://nextjs.org/docs/api-reference/next/link). As the links point to an API route and not to a page, you should keep them as anchor tags. -There are two additional ways to check for an authenticated user; one for Next.js pages using [withPageAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequired) and one for Next.js API routes using [withApiAuthRequired](https://auth0.github.io/nextjs-auth0/modules/helpers_with_api_auth_required.html#withapiauthrequired). +##### Important: Limitations of the App Directory + +At the time of writing, Server Components in the App Directory (including Pages and Layouts) _cannot_ write to a cookie. + +If you rely on Server Components to read and update your session you should be aware of the following: + +- If you have a rolling session (the default for this SDK), it will not be updated when the user visits your site. So their session expiration may revert to its absolute duration (7 days by default). +- If you refresh the access token, the new access token will not be persisted in the session. So subsequent attempts to get an access token will always result in refreshing the expired access token in the session. +- If you make any other updates to the session, they will not be persisted between requests. + +> The cookie will be updated from [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) and [route handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers). For other comprehensive examples, see the [EXAMPLES.md](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md) document. diff --git a/src/helpers/index.ts b/src/helpers/index.ts index b2024e2e5..ab0096bf9 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -3,6 +3,7 @@ export { default as withPageAuthRequiredFactory, GetServerSidePropsResultWithSession, WithPageAuthRequired, - WithPageAuthRequiredOptions, + WithPageAuthRequiredPageRouterOptions, + WithPageAuthRequiredAppRouterOptions, PageRoute } from './with-page-auth-required'; diff --git a/src/helpers/with-page-auth-required.ts b/src/helpers/with-page-auth-required.ts index 007bec8d3..bfae219d1 100644 --- a/src/helpers/with-page-auth-required.ts +++ b/src/helpers/with-page-auth-required.ts @@ -33,13 +33,17 @@ export type PageRoute = ( ctx: GetServerSidePropsContext ) => Promise>; +type AppRouterPageRouteOpts = { + params?: { slug: string }; + searchParams?: { [key: string]: string | string[] | undefined }; +}; + /** + * An app route that has been augmented with {@link WithPageAuthRequired}. + * * @category Server */ -export type AppRouterPageRoute = (obj: { - params?: { slug: string }; - searchParams?: { [key: string]: string | string[] | undefined }; -}) => Promise; +export type AppRouterPageRoute = (obj: AppRouterPageRouteOpts) => Promise; /** * If you have a custom returnTo url you should specify it in `returnTo`. @@ -72,7 +76,7 @@ export type AppRouterPageRoute = (obj: { * * @category Server */ -export type WithPageAuthRequiredOptions< +export type WithPageAuthRequiredPageRouterOptions< P extends { [key: string]: any } = { [key: string]: any }, Q extends ParsedUrlQuery = ParsedUrlQuery > = { @@ -104,10 +108,58 @@ type WithPageAuthRequiredPageRouter = < P extends { [key: string]: any } = { [key: string]: any }, Q extends ParsedUrlQuery = ParsedUrlQuery >( - opts?: WithPageAuthRequiredOptions + opts?: WithPageAuthRequiredPageRouterOptions ) => PageRoute; -type WithPageAuthRequiredAppRouter = (fn: AppRouterPageRoute, opts?: { returnTo?: string }) => AppRouterPageRoute; +/** + * @category Server + */ +export type WithPageAuthRequiredAppRouterOptions = { + returnTo?: string | ((obj: AppRouterPageRouteOpts) => Promise | string); +}; + +/** + * Wrap your Server Component with this method to make sure the user is authenticated before + * visiting the page. + * + * ```js + * // app/protected-page/page.js + * import { withPageAuthRequired } from '@auth0/nextjs-auth0'; + * + * export default function withPageAuthRequired(ProtectedPage() { + * return
    Protected content
    ; + * }, { returnTo: '/protected-page' }); + * ``` + * + * If the user visits `/protected-page` without a valid session, it will redirect the user to the + * login page. + * + * Note: Server Components are not aware of the req or the url of the page. So if you want the user to return to the + * page after login, you must specify the `returnTo` option. + * + * You can specify a function to `returnTo` that accepts the `params` (An object containing the dynamic + * route parameters) and `searchParams` (An object containing the search parameters of the current URL) + * argument from the page, to preserve dynamic routes and search params. + * + * ```js + * // app/protected-page/[slug]/page.js + * import { withPageAuthRequired } from '@auth0/nextjs-auth0'; + * + * export default function withPageAuthRequired(ProtectedPage() { + * return
    Protected content
    ; + * }, { + * returnTo({ params }) { + * return `/protected-page/${params.slug}` + * } + * }); + * ``` + * + * @category Server + */ +type WithPageAuthRequiredAppRouter = ( + fn: AppRouterPageRoute, + opts?: WithPageAuthRequiredAppRouterOptions +) => AppRouterPageRoute; export type WithPageAuthRequired = WithPageAuthRequiredPageRouter & WithPageAuthRequiredAppRouter; @@ -136,7 +188,8 @@ const appRouteHandlerFactory = const sessionCache = getSessionCache(); const [session] = await get({ sessionCache }); if (!session?.user) { - redirect(`${loginUrl}${opts.returnTo ? `?returnTo=${opts.returnTo}` : ''}`); + const returnTo = typeof opts.returnTo === 'function' ? await opts.returnTo(params) : opts.returnTo; + redirect(`${loginUrl}${opts.returnTo ? `?returnTo=${returnTo}` : ''}`); } return handler(params); }; diff --git a/src/index.ts b/src/index.ts index 58b74d6a0..a98b78a00 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,7 +54,8 @@ import { WithApiAuthRequired, WithPageAuthRequired, GetServerSidePropsResultWithSession, - WithPageAuthRequiredOptions, + WithPageAuthRequiredPageRouterOptions, + WithPageAuthRequiredAppRouterOptions, PageRoute } from './helpers'; import version from './version'; @@ -247,7 +248,8 @@ export { ProfileOptions, Handlers, GetServerSidePropsResultWithSession, - WithPageAuthRequiredOptions, + WithPageAuthRequiredPageRouterOptions, + WithPageAuthRequiredAppRouterOptions, PageRoute, WithApiAuthRequired, WithPageAuthRequired, diff --git a/tests/fixtures/setup.ts b/tests/fixtures/setup.ts index dcfd86ee1..6a47ba3f5 100644 --- a/tests/fixtures/setup.ts +++ b/tests/fixtures/setup.ts @@ -8,7 +8,7 @@ import { LoginOptions, LogoutOptions, ProfileOptions, - WithPageAuthRequiredOptions, + WithPageAuthRequiredPageRouterOptions, initAuth0, AccessTokenRequest, Claims, @@ -34,7 +34,7 @@ export type SetupOptions = { logoutOptions?: LogoutOptions; profileHandler?: HandleProfile; profileOptions?: ProfileOptions; - withPageAuthRequiredOptions?: WithPageAuthRequiredOptions; + withPageAuthRequiredOptions?: WithPageAuthRequiredPageRouterOptions; getAccessTokenOptions?: AccessTokenRequest; onError?: OnError; discoveryOptions?: Record; diff --git a/tests/helpers/with-page-auth-required.test.ts b/tests/helpers/with-page-auth-required.test.ts index 985f0f903..6ff93ce4a 100644 --- a/tests/helpers/with-page-auth-required.test.ts +++ b/tests/helpers/with-page-auth-required.test.ts @@ -22,7 +22,7 @@ jest.mock('next/navigation', () => { describe('with-page-auth-required ssr', () => { describe('app route', () => { - const getPageResponse = ({ config, cookies, returnTo, loginRes }: any = {}) => { + const getPageResponse = ({ config, cookies, returnTo, loginRes, params, searchParams }: any = {}) => { const res = loginRes || new NextResponse(); mocked(nextCookies).mockImplementation(() => res.cookies as any); const opts = { ...withApi, ...config }; @@ -39,7 +39,7 @@ describe('with-page-auth-required ssr', () => { const handler = instance.withPageAuthRequired(() => Promise.resolve(React.createElement('div', {}, 'foo')), { returnTo }); - return handler({}); + return handler({ params, searchParams }); }; test('protect a page', async () => { @@ -54,6 +54,21 @@ describe('with-page-auth-required ssr', () => { expect(navigation.redirect).toHaveBeenCalledWith('/api/auth/login?returnTo=/foo'); }); + test('protect a page and redirect to returnTo fn option', async () => { + jest.spyOn(navigation, 'redirect'); + await expect( + getPageResponse({ + returnTo({ params, searchParams }: any) { + const query = new URLSearchParams(searchParams).toString(); + return `/foo/${params.slug}${query ? `?${query}` : ''}`; + }, + params: { slug: 'bar' }, + searchParams: { foo: 'bar' } + }) + ).rejects.toThrowError('NEXT_REDIRECT'); + expect(navigation.redirect).toHaveBeenCalledWith('/api/auth/login?returnTo=/foo/bar?foo=bar'); + }); + test('allow access to a page with a valid session', async () => { const loginRes = await appRouterLogin(); From 32b0795cf586a76bdb754cabf64613772514a7f4 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Wed, 7 Jun 2023 13:35:51 +0100 Subject: [PATCH 40/61] Docs fixes --- EXAMPLES.md | 2 +- README.md | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 1cfc03730..470fe5bca 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -19,7 +19,7 @@ - [Use with Base Path and Internationalized Routing](#use-with-base-path-and-internationalized-routing) - [Use a custom session store](#use-a-custom-session-store) -See also the the [example app](./example-app). +See also the [example app](./example-app). ## App Router diff --git a/README.md b/README.md index bc97f4876..0884c0049 100644 --- a/README.md +++ b/README.md @@ -156,11 +156,14 @@ export default function Index() { #### App Router +> Important: You should understand the [limitations of the App Directory](#important-limitations-of-the-app-directory) before proceeding. + ##### Add the Dynamic API Route Create a [catch-all, dynamic API route handler](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) under the `/app/api` directory (strictly speaking you do not need to put API routes under `/api` but we maintain the convention for simplicity): -- Create an `auth` directory under the `/app/api/` directory. +- Create an `api` directory under the `/app/` directory. +- Create an `auth` directory under the newly created `/app/api/` directory. - Create a `[auth0]` directory under the newly created `auth` directory. - Create a `route.js` file under the newly created `[auth0]` directory. @@ -173,7 +176,7 @@ export const GET = handleAuth(); ``` -##### Add the UserProvider to Custom App +##### Add the UserProvider to your layout Wrap your `app/layout.js` component with the `UserProvider` component: @@ -182,10 +185,10 @@ Wrap your `app/layout.js` component with the `UserProvider` component: import React from 'react'; import { UserProvider } from '@auth0/nextjs-auth0/client'; -export default function App({ Component, pageProps }) { +export default function App({ children }) { return ( - + {children} ); } From 1fc6c580e22cb974ff7c1d97f9ffa07707e32844 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Wed, 7 Jun 2023 18:20:31 +0100 Subject: [PATCH 41/61] Tody up docs, types, exports & examples --- EXAMPLES.md | 140 ------------------------- README.md | 10 +- src/config.ts | 4 +- src/handlers/auth.ts | 26 +++++ src/handlers/callback.ts | 78 +++++++++++++- src/handlers/index.ts | 25 +++-- src/handlers/login.ts | 45 ++++++-- src/handlers/profile.ts | 21 ++++ src/helpers/index.ts | 14 ++- src/helpers/with-api-auth-required.ts | 49 ++++++++- src/helpers/with-page-auth-required.ts | 33 +++++- src/index.ts | 84 ++++++++------- src/session/get-access-token.ts | 15 +++ src/session/index.ts | 5 +- src/utils/errors.ts | 6 +- tests/fixtures/setup.ts | 6 +- tests/handlers/auth.test.ts | 2 +- 17 files changed, 338 insertions(+), 225 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 470fe5bca..62582e881 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,12 +1,5 @@ # Examples -**App Router** - -- [Basic Setup](#basic-setup) - -**Page Router** - -- [Basic Setup](#basic-setup) - [Create your own instance of the SDK](#create-your-own-instance-of-the-sdk) - [Customize handlers behavior](#customize-handlers-behavior) - [Use custom auth urls](#use-custom-auth-urls) @@ -21,139 +14,6 @@ See also the [example app](./example-app). -## App Router - -### Basic Setup - -Configure the required options in an `.env.local` file in the root of your application: - -```sh -AUTH0_SECRET='LONG_RANDOM_VALUE' -AUTH0_BASE_URL='http://localhost:3000' -AUTH0_ISSUER_BASE_URL='https://your-tenant.auth0.com' -AUTH0_CLIENT_ID='CLIENT_ID' -AUTH0_CLIENT_SECRET='CLIENT_SECRET' -``` - -Create a [dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes) at `/app/api/auth/[auth0]/route.js`. - -```js -import { handleAuth } from '@auth0/nextjs-auth0'; - -export const GET = handleAuth(); -``` - -This will create the following urls: `/api/auth/login`, `/api/auth/callback`, `/api/auth/logout` and `/api/auth/me`. - -Wrap your `app/layout.jsx` component in the `UserProvider` component. - -```jsx -// app/layout.jsx -import React from 'react'; -import { UserProvider } from '@auth0/nextjs-auth0/client'; - -export default function RootLayout({ children }) { - return ( - - - {children} - - - ); -} -``` - -Check the user's authentication state and log them in or out from the front end using the `useUser` hook. - -```jsx -// app/page.jsx -'use client'; -import { useUser } from '@auth0/nextjs-auth0/client'; - -export default function Index() { - const { user, error, isLoading } = useUser(); - - if (isLoading) return
    Loading...
    ; - if (error) return
    {error.message}
    ; - - if (user) { - return ( -
    - Welcome {user.name}! Logout -
    - ); - } - return Login; -}; -``` - -## Page Router - -### Basic Setup - -Configure the required options in an `.env.local` file in the root of your application: - -```sh -AUTH0_SECRET='LONG_RANDOM_VALUE' -AUTH0_BASE_URL='http://localhost:3000' -AUTH0_ISSUER_BASE_URL='https://your-tenant.auth0.com' -AUTH0_CLIENT_ID='CLIENT_ID' -AUTH0_CLIENT_SECRET='CLIENT_SECRET' -``` - -Create a [dynamic API route handler](https://nextjs.org/docs/api-routes/dynamic-api-routes) at `/pages/api/auth/[auth0].js`. - -```js -import { handleAuth } from '@auth0/nextjs-auth0'; - -export default handleAuth(); -``` - -This will create the following urls: `/api/auth/login`, `/api/auth/callback`, `/api/auth/logout` and `/api/auth/me`. - -Wrap your `pages/_app.jsx` component in the `UserProvider` component. - -```jsx -// pages/_app.jsx -import React from 'react'; -import { UserProvider } from '@auth0/nextjs-auth0/client'; - -export default function App({ Component, pageProps }) { - // You can optionally pass the `user` prop from pages that require server-side - // rendering to prepopulate the `useUser` hook. - const { user } = pageProps; - - return ( - - - - ); -} -``` - -Check the user's authentication state and log them in or out from the front end using the `useUser` hook. - -```jsx -// pages/index.jsx -import { useUser } from '@auth0/nextjs-auth0/client'; - -export default function Index() { - const { user, error, isLoading } = useUser(); - - if (isLoading) return
    Loading...
    ; - if (error) return
    {error.message}
    ; - - if (user) { - return ( -
    - Welcome {user.name}! Logout -
    - ); - } - return Login; -}; -``` - ### Create your own instance of the SDK When you use the named exports, the SDK creates an instance of the SDK for you and configures it with the provided environment variables. diff --git a/README.md b/README.md index 0884c0049..339acd9a8 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Create a [catch-all, dynamic API route handler](https://nextjs.org/docs/app/buil - Create a `[auth0]` directory under the newly created `auth` directory. - Create a `route.js` file under the newly created `[auth0]` directory. -The path to your dynamic API route file would be `/app/api/auth/[auth0]/route.js`. Populate that file as follows: +The path to your dynamic API route file will be `/app/api/auth/[auth0]/route.js`. Populate that file as follows: ```js import { handleAuth } from '@auth0/nextjs-auth0'; @@ -175,8 +175,7 @@ import { handleAuth } from '@auth0/nextjs-auth0'; export const GET = handleAuth(); ``` - -##### Add the UserProvider to your layout +##### Add the `UserProvider` to your layout Wrap your `app/layout.js` component with the `UserProvider` component: @@ -200,6 +199,7 @@ You can now determine if a user is authenticated by checking that the `user` obj ```jsx // pages/index.js +'use client'; import { useUser } from '@auth0/nextjs-auth0/client'; export default function Index() { @@ -226,13 +226,13 @@ export default function Index() { At the time of writing, Server Components in the App Directory (including Pages and Layouts) _cannot_ write to a cookie. -If you rely on Server Components to read and update your session you should be aware of the following: +If you rely on Server Components to read and update your session from a Server Component you should be aware of the following: - If you have a rolling session (the default for this SDK), it will not be updated when the user visits your site. So their session expiration may revert to its absolute duration (7 days by default). - If you refresh the access token, the new access token will not be persisted in the session. So subsequent attempts to get an access token will always result in refreshing the expired access token in the session. - If you make any other updates to the session, they will not be persisted between requests. -> The cookie will be updated from [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) and [route handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers). +> The cookie is updated from [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) and [route handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers). For other comprehensive examples, see the [EXAMPLES.md](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md) document. diff --git a/src/config.ts b/src/config.ts index 0a0ef1f91..231064ec5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -441,8 +441,8 @@ export interface NextConfig extends Pick { * * **IMPORTANT** If you use {@link InitAuth0}, you should *not* use the other named exports as they will use a different * instance of the SDK. Also note - this is for the server side part of the SDK - you will always use named exports for - * the front end components: {@Link UserProvider}, {@Link UseUser} and the - * front end version of {@Link WithPageAuthRequired} + * the front end components: {@link UserProvider}, {@link UseUser} and the + * front end version of {@link WithPageAuthRequired} * * @category Server */ diff --git a/src/handlers/auth.ts b/src/handlers/auth.ts index 409ebce06..2d2bcafa3 100644 --- a/src/handlers/auth.ts +++ b/src/handlers/auth.ts @@ -62,8 +62,14 @@ import { AppRouteHandlerFn, AppRouteHandlerFnContext, Handler } from './router-h */ export type Handlers = ApiHandlers | ErrorHandlers; +/** + * @ignore + */ type ApiHandlers = { [key: string]: Handler }; +/** + * @ignore + */ type ErrorHandlers = { onError?: PageRouterOnError | AppRouterOnError; }; @@ -71,6 +77,8 @@ type ErrorHandlers = { /** * The main way to use the server SDK. * + * *Page Router* + * * Simply set the environment variables per {@link ConfigParameters} then create the file * `pages/api/auth/[auth0].js`. For example: * @@ -80,6 +88,18 @@ type ErrorHandlers = { * * export default handleAuth(); * ``` + + * *App Router* + * + * Simply set the environment variables per {@link ConfigParameters} then create the file + * `app/api/auth/[auth0]/route.js`. For example: + * + * ```js + * // app/api/auth/[auth0]/route.js + * import { handleAuth } from '@auth0/nextjs-auth0'; + * + * export const GET = handleAuth(); + * ``` * * This will create 5 handlers for the following urls: * @@ -171,6 +191,9 @@ export default function handlerFactory({ }; } +/** + * @ignore + */ const appRouteHandlerFactory: (customHandlers: ApiHandlers, onError?: AppRouterOnError) => AppRouteHandlerFn = (customHandlers, onError) => async (req: NextRequest, ctx) => { const { params } = ctx; @@ -197,6 +220,9 @@ const appRouteHandlerFactory: (customHandlers: ApiHandlers, onError?: AppRouterO } }; +/** + * @ignore + */ const pageRouteHandlerFactory: (customHandlers: ApiHandlers, onError?: PageRouterOnError) => NextApiHandler = (customHandlers, onError) => async (req: NextApiRequest, res: NextApiResponse): Promise => { diff --git a/src/handlers/callback.ts b/src/handlers/callback.ts index 7dc67ae99..8a462b631 100644 --- a/src/handlers/callback.ts +++ b/src/handlers/callback.ts @@ -15,6 +15,11 @@ import { Auth0NextApiRequest, Auth0NextApiResponse, Auth0NextRequest, Auth0NextR import { LoginOptions } from './login'; import { AppRouteHandlerFnContext, AuthHandler, getHandler, Handler, OptionsProvider } from './router-helpers'; +/** + * afterCallback hook for page router {@link AfterCallbackPageRoute} and app router {@link AfterCallbackAppRoute} + */ +export type AfterCallback = AfterCallbackPageRoute | AfterCallbackAppRoute; + /** * Use this function for validating additional claims on the user's ID token or adding removing items from * the session after login. @@ -95,8 +100,6 @@ import { AppRouteHandlerFnContext, AuthHandler, getHandler, Handler, OptionsProv * * @category Server */ -export type AfterCallback = AfterCallbackPageRoute | AfterCallbackAppRoute; - export type AfterCallbackPageRoute = ( req: NextApiRequest, res: NextApiResponse, @@ -104,6 +107,71 @@ export type AfterCallbackPageRoute = ( state?: { [key: string]: any } ) => Promise | Session | undefined; +/** + * Use this function for validating additional claims on the user's ID token or adding removing items from + * the session after login. + * + * @example Validate additional claims + * + * ```js + * // app/api/auth/[auth0]/route.js + * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0'; + * import { redirect } from 'next/navigation'; + * + * const afterCallback = (req, session, state) => { + * if (session.user.isAdmin) { + * return session; + * } else { + * redirect('/unauthorized'); + * } + * }; + * + * export default handleAuth({ + * callback: handleCallback({ afterCallback }) + * }); + * ``` + * + * @example Modify the session after login + * + * ```js + * // pages/api/auth/[auth0].js + * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0'; + * import { NextResponse } from 'next/server'; + * + * const afterCallback = (req, session, state) => { + * session.user.customProperty = 'foo'; + * delete session.refreshToken; + * return session; + * }; + * + * export default handleAuth({ + * callback: handleCallback({ afterCallback }) + * }); + * ``` + * + * @example Redirect successful login based on claim + * + * ```js + * // pages/api/auth/[auth0].js + * import { handleAuth, handleCallback } from '@auth0/nextjs-auth0'; + * import { headers } from 'next/headers'; + * + * const afterCallback = (req, session, state) => { + * if (!session.user.isAdmin) { + * headers.set('location', '/admin'); + * } + * return session; + * }; + * + * export default handleAuth({ + * callback: handleCallback({ afterCallback }) + * }); + * ``` + * + * @throws {@link HandlerError} + * + * @category Server + */ export type AfterCallbackAppRoute = ( req: NextRequest, session: Session, @@ -252,6 +320,9 @@ const applyOptions = ( }; }; +/** + * @ignore + */ const appRouteHandlerFactory: ( handler: BaseHandleLogin, config: NextConfig @@ -267,6 +338,9 @@ const appRouteHandlerFactory: ( } }; +/** + * @ignore + */ const pageRouteHandlerFactory: ( handler: BaseHandleCallback, config: NextConfig diff --git a/src/handlers/index.ts b/src/handlers/index.ts index e9c6a243f..22f8b8a29 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -6,16 +6,23 @@ export { AfterCallbackPageRoute, AfterCallbackAppRoute } from './callback'; -export { default as loginHandler, HandleLogin, LoginOptions, GetLoginState } from './login'; +export { + default as loginHandler, + HandleLogin, + LoginOptions, + GetLoginState, + GetLoginStatePageRoute, + GetLoginStateAppRoute +} from './login'; export { default as logoutHandler, HandleLogout, LogoutOptions } from './logout'; -export { default as profileHandler, HandleProfile, ProfileOptions, AfterRefetch } from './profile'; export { - default as handlerFactory, - Handlers, - HandleAuth, - AppRouterOnError, - PageRouterOnError, - PageRouterOnError as OnError -} from './auth'; + default as profileHandler, + HandleProfile, + ProfileOptions, + AfterRefetch, + AfterRefetchPageRoute, + AfterRefetchAppRoute +} from './profile'; +export { default as handlerFactory, Handlers, HandleAuth, AppRouterOnError, PageRouterOnError } from './auth'; export { AppRouteHandlerFn } from './router-helpers'; export { AppRouteHandlerFnContext } from './router-helpers'; diff --git a/src/handlers/login.ts b/src/handlers/login.ts index 172c4fac8..c6fd989e4 100644 --- a/src/handlers/login.ts +++ b/src/handlers/login.ts @@ -12,6 +12,11 @@ import { HandlerErrorCause, LoginHandlerError } from '../utils/errors'; import { Auth0NextApiRequest, Auth0NextApiResponse, Auth0NextRequest, Auth0NextResponse } from '../http'; import { AppRouteHandlerFnContext, getHandler, OptionsProvider, Handler, AuthHandler } from './router-helpers'; +/** + * Get login state hook for page router {@link GetLoginStatePageRoute} and app router {@link GetLoginStateAppRoute}. + */ +export type GetLoginState = GetLoginStatePageRoute | GetLoginStateAppRoute; + /** * Use this to store additional state for the user before they visit the identity provider to log in. * @@ -24,22 +29,33 @@ import { AppRouteHandlerFnContext, getHandler, OptionsProvider, Handler, AuthHan * }; * * export default handleAuth({ - * async login(req, res) { - * try { - * await handleLogin(req, res, { getLoginState }); - * } catch (error) { - * res.status(error.status || 500).end(); - * } - * } + * login: handleLogin({ getLoginState }) * }); * ``` * * @category Server */ -export type GetLoginState = GetLoginStatePageRoute | GetLoginStateAppRoute; - export type GetLoginStatePageRoute = (req: NextApiRequest, options: LoginOptions) => { [key: string]: any }; -export type GetLoginStateAppRoute = (req: NextApiRequest, options: LoginOptions) => { [key: string]: any }; + +/** + * Use this to store additional state for the user before they visit the identity provider to log in. + * + * ```js + * // app/api/auth/[auth0]/route.js + * import { handleAuth, handleLogin } from '@auth0/nextjs-auth0'; + * + * const getLoginState = (req, loginOptions) => { + * return { basket_id: getBasketId(req) }; + * }; + * + * export default handleAuth({ + * login: handleLogin({ getLoginState }) + * }); + * ``` + * + * @category Server + */ +export type GetLoginStateAppRoute = (req: NextRequest, options: LoginOptions) => { [key: string]: any }; /** * Authorization params to pass to the login handler. @@ -254,6 +270,9 @@ export default function handleLoginFactory( return getHandler(appRouteHandler, pageRouteHandler) as HandleLogin; } +/** + * @ignore + */ const applyOptions = ( req: NextApiRequest | NextRequest, options: LoginOptions, @@ -282,6 +301,9 @@ const applyOptions = ( return opts; }; +/** + * @ignore + */ const appRouteHandlerFactory: ( handler: BaseHandleLogin, nextConfig: NextConfig, @@ -305,6 +327,9 @@ const appRouteHandlerFactory: ( } }; +/** + * @ignore + */ const pageRouteHandlerFactory: ( handler: BaseHandleLogin, nextConfig: NextConfig, diff --git a/src/handlers/profile.ts b/src/handlers/profile.ts index f9591f97f..cf65af102 100644 --- a/src/handlers/profile.ts +++ b/src/handlers/profile.ts @@ -6,14 +6,29 @@ import { assertReqRes } from '../utils/assert'; import { ProfileHandlerError, HandlerErrorCause } from '../utils/errors'; import { AppRouteHandlerFnContext, AuthHandler, getHandler, Handler, OptionsProvider } from './router-helpers'; +/** + * After refetch handler for page router {@link AfterRefetchPageRoute} and app router {@link AfterRefetchAppRoute}. + * + * @category Server + */ export type AfterRefetch = AfterRefetchPageRoute | AfterRefetchAppRoute; +/** + * After refetch handler for page router. + * + * @category Server + */ export type AfterRefetchPageRoute = ( req: NextApiRequest, res: NextApiResponse, session: Session ) => Promise | Session; +/** + * After refetch handler for app router. + * + * @category Server + */ export type AfterRefetchAppRoute = (req: NextRequest, session: Session) => Promise | Session; /** @@ -119,6 +134,9 @@ export default function profileHandler( return getHandler(appRouteHandler, pageRouteHandler) as HandleProfile; } +/** + * @ignore + */ const appRouteHandlerFactory: ( getClient: ClientFactory, getAccessToken: GetAccessToken, @@ -168,6 +186,9 @@ const appRouteHandlerFactory: ( } }; +/** + * @ignore + */ const pageRouteHandlerFactory: ( getClient: ClientFactory, getAccessToken: GetAccessToken, diff --git a/src/helpers/index.ts b/src/helpers/index.ts index ab0096bf9..14a6fcdb2 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,9 +1,19 @@ -export { default as withApiAuthRequiredFactory, WithApiAuthRequired } from './with-api-auth-required'; +export { + default as withApiAuthRequiredFactory, + WithApiAuthRequired, + AppRouteHandlerFn, + WithApiAuthRequiredAppRoute, + WithApiAuthRequiredPageRoute +} from './with-api-auth-required'; export { default as withPageAuthRequiredFactory, GetServerSidePropsResultWithSession, WithPageAuthRequired, WithPageAuthRequiredPageRouterOptions, WithPageAuthRequiredAppRouterOptions, - PageRoute + PageRoute, + AppRouterPageRouteOpts, + AppRouterPageRoute, + WithPageAuthRequiredPageRouter, + WithPageAuthRequiredAppRouter } from './with-page-auth-required'; diff --git a/src/helpers/with-api-auth-required.ts b/src/helpers/with-api-auth-required.ts index d26e5da34..b9d7a3c40 100644 --- a/src/helpers/with-api-auth-required.ts +++ b/src/helpers/with-api-auth-required.ts @@ -3,12 +3,23 @@ import { NextRequest, NextResponse } from 'next/server'; import { get, SessionCache } from '../session'; import { assertReqRes } from '../utils/assert'; +/** + * This contains `param`s, which is an object containing the dynamic route parameters for the current route. + * + * See https://nextjs.org/docs/app/api-reference/file-conventions/route#context-optional + * + * @category Server + */ export type AppRouteHandlerFnContext = { params?: Record; }; /** - * Handler function for app routes. + * Handler function for app directory api routes. + * + * See: https://nextjs.org/docs/app/api-reference/file-conventions/route + * + * @category Server */ export type AppRouteHandlerFn = ( /** @@ -23,7 +34,27 @@ export type AppRouteHandlerFn = ( ) => Promise | NextResponse; /** - * Wrap an API route to check that the user has a valid session. If they're not logged in the + * Wrap an app router API route to check that the user has a valid session. If they're not logged in the + * handler will return a 401 Unauthorized. + * + * ```js + * // app/protected-api/route.js + * import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0'; + * + * export default withApiAuthRequired(function Protected(req) { + * const session = getSession(); + * ... + * }); + * ``` + * + * If you visit `/protected-api` without a valid session cookie, you will get a 401 response. + * + * @category Server + */ +export type WithApiAuthRequiredAppRoute = (apiRoute: AppRouteHandlerFn) => AppRouteHandlerFn; + +/** + * Wrap a page router API route to check that the user has a valid session. If they're not logged in the * handler will return a 401 Unauthorized. * * ```js @@ -40,8 +71,14 @@ export type AppRouteHandlerFn = ( * * @category Server */ -export type WithApiAuthRequiredAppRoute = (apiRoute: AppRouteHandlerFn) => AppRouteHandlerFn; export type WithApiAuthRequiredPageRoute = (apiRoute: NextApiHandler) => NextApiHandler; + +/** + * Protects API routes for Page router pages {@link WithApiAuthRequiredPageRoute} or + * App router pages {@link WithApiAuthRequiredAppRoute} + * + * @category Server + */ export type WithApiAuthRequired = WithApiAuthRequiredAppRoute & WithApiAuthRequiredPageRoute; /** @@ -63,6 +100,9 @@ export default function withApiAuthFactory(sessionCache: SessionCache): WithApiA }; } +/** + * @ignore + */ const appRouteHandlerFactory = (sessionCache: SessionCache): WithApiAuthRequiredAppRoute => (apiRoute) => @@ -83,6 +123,9 @@ const appRouteHandlerFactory = return nextApiRes; }; +/** + * @ignore + */ const pageRouteHandlerFactory = (sessionCache: SessionCache): WithApiAuthRequiredPageRoute => (apiRoute) => diff --git a/src/helpers/with-page-auth-required.ts b/src/helpers/with-page-auth-required.ts index bfae219d1..b5061e38a 100644 --- a/src/helpers/with-page-auth-required.ts +++ b/src/helpers/with-page-auth-required.ts @@ -33,7 +33,12 @@ export type PageRoute = ( ctx: GetServerSidePropsContext ) => Promise>; -type AppRouterPageRouteOpts = { +/** + * Objects containing the route parameters and search parameters of th page. + * + * @category Server + */ +export type AppRouterPageRouteOpts = { params?: { slug: string }; searchParams?: { [key: string]: string | string[] | undefined }; }; @@ -104,7 +109,7 @@ export type WithPageAuthRequiredPageRouterOptions< * * @category Server */ -type WithPageAuthRequiredPageRouter = < +export type WithPageAuthRequiredPageRouter = < P extends { [key: string]: any } = { [key: string]: any }, Q extends ParsedUrlQuery = ParsedUrlQuery >( @@ -112,6 +117,9 @@ type WithPageAuthRequiredPageRouter = < ) => PageRoute; /** + * Specify the URL to `returnTo` - this is important in app router pages because the server component + * won't know the URL of the page. + * * @category Server */ export type WithPageAuthRequiredAppRouterOptions = { @@ -156,11 +164,17 @@ export type WithPageAuthRequiredAppRouterOptions = { * * @category Server */ -type WithPageAuthRequiredAppRouter = ( +export type WithPageAuthRequiredAppRouter = ( fn: AppRouterPageRoute, opts?: WithPageAuthRequiredAppRouterOptions ) => AppRouterPageRoute; +/** + * Protects Page router pages {@link WithPageAuthRequiredPageRouter} or + * App router pages {@link WithPageAuthRequiredAppRouter} + * + * @category Server + */ export type WithPageAuthRequired = WithPageAuthRequiredPageRouter & WithPageAuthRequiredAppRouter; /** @@ -173,14 +187,20 @@ export default function withPageAuthRequiredFactory( const appRouteHandler = appRouteHandlerFactory(loginUrl, getSessionCache); const pageRouteHandler = pageRouteHandlerFactory(loginUrl, getSessionCache); - return (fnOrOpts: any, opts?: any): any => { + return (( + fnOrOpts?: WithPageAuthRequiredPageRouterOptions | AppRouterPageRoute, + opts?: WithPageAuthRequiredAppRouterOptions + ) => { if (typeof fnOrOpts === 'function') { return appRouteHandler(fnOrOpts, opts); } return pageRouteHandler(fnOrOpts); - }; + }) as WithPageAuthRequired; } +/** + * @ignore + */ const appRouteHandlerFactory = (loginUrl: string, getSessionCache: () => SessionCache): WithPageAuthRequiredAppRouter => (handler, opts = {}) => @@ -194,6 +214,9 @@ const appRouteHandlerFactory = return handler(params); }; +/** + * @ignore + */ const pageRouteHandlerFactory = (loginUrl: string, getSessionCache: () => SessionCache): WithPageAuthRequiredPageRouter => ({ getServerSideProps, returnTo } = {}) => diff --git a/src/index.ts b/src/index.ts index a98b78a00..cfe0994ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,22 +16,11 @@ import { loginHandler, logoutHandler, profileHandler, - Handlers, HandleAuth, HandleLogin, HandleProfile, HandleLogout, - HandleCallback, - LoginOptions, - LogoutOptions, - GetLoginState, - ProfileOptions, - CallbackOptions, - AfterCallback, - AfterCallbackPageRoute, - AfterCallbackAppRoute, - AfterRefetch, - OnError + HandleCallback } from './handlers'; import { sessionFactory, @@ -40,9 +29,6 @@ import { GetSession, GetAccessToken, Session, - AccessTokenRequest, - GetAccessTokenResult, - Claims, touchSessionFactory, TouchSession, updateSessionFactory, @@ -52,11 +38,7 @@ import { withPageAuthRequiredFactory, withApiAuthRequiredFactory, WithApiAuthRequired, - WithPageAuthRequired, - GetServerSidePropsResultWithSession, - WithPageAuthRequiredPageRouterOptions, - WithPageAuthRequiredAppRouterOptions, - PageRoute + WithPageAuthRequired } from './helpers'; import version from './version'; import { getConfig, getLoginUrl, ConfigParameters } from './config'; @@ -231,6 +213,48 @@ export { ProfileHandlerError } from './utils/errors'; +export { + Handlers, + LoginOptions, + LogoutOptions, + GetLoginState, + GetLoginStatePageRoute, + GetLoginStateAppRoute, + ProfileOptions, + CallbackOptions, + AfterCallback, + AfterCallbackPageRoute, + AfterCallbackAppRoute, + AfterRefetch, + AfterRefetchPageRoute, + AfterRefetchAppRoute, + AppRouterOnError, + PageRouterOnError +} from './handlers'; + +export { + AppRouterPageRouteOpts, + AppRouterPageRoute, + WithPageAuthRequiredPageRouter, + WithPageAuthRequiredAppRouter, + GetServerSidePropsResultWithSession, + WithPageAuthRequiredPageRouterOptions, + WithPageAuthRequiredAppRouterOptions, + PageRoute, + AppRouteHandlerFn, + WithApiAuthRequiredAppRoute, + WithApiAuthRequiredPageRoute +} from './helpers'; + +export { + AccessTokenRequest, + GetAccessTokenResult, + Claims, + AfterRefresh, + AfterRefreshPageRoute, + AfterRefreshAppRoute +} from './session'; + export { MissingStateCookieError, MissingStateParamError, @@ -245,12 +269,6 @@ export { HandleProfile, HandleLogout, HandleCallback, - ProfileOptions, - Handlers, - GetServerSidePropsResultWithSession, - WithPageAuthRequiredPageRouterOptions, - WithPageAuthRequiredAppRouterOptions, - PageRoute, WithApiAuthRequired, WithPageAuthRequired, SessionCache, @@ -258,19 +276,7 @@ export { TouchSession, UpdateSession, GetAccessToken, - Session, - Claims, - AccessTokenRequest, - GetAccessTokenResult, - CallbackOptions, - AfterCallback, - AfterCallbackPageRoute, - AfterCallbackAppRoute, - AfterRefetch, - LoginOptions, - LogoutOptions, - GetLoginState, - OnError + Session }; export type SessionStore = GenericSessionStore; diff --git a/src/session/get-access-token.ts b/src/session/get-access-token.ts index d441a50bf..f02f371b5 100644 --- a/src/session/get-access-token.ts +++ b/src/session/get-access-token.ts @@ -8,14 +8,29 @@ import { Session, SessionCache, fromTokenSet, get, set } from '../session'; import { AuthorizationParameters, NextConfig } from '../config'; import { NextRequest, NextResponse } from 'next/server'; +/** + * After refresh handler for page router {@link AfterRefreshPageRoute} and app router {@link AfterRefreshAppRoute}. + * + * @category Server + */ export type AfterRefresh = AfterRefreshPageRoute | AfterRefreshAppRoute; +/** + * After refresh handler for page router. + * + * @category Server + */ export type AfterRefreshPageRoute = ( req: NextApiRequest | IncomingMessage, res: NextApiRequest | ServerResponse, session: Session ) => Promise | Session; +/** + * After refresh handler for app router. + * + * @category Server + */ export type AfterRefreshAppRoute = (session: Session) => Promise | Session; /** diff --git a/src/session/index.ts b/src/session/index.ts index dd50bb009..c18849590 100644 --- a/src/session/index.ts +++ b/src/session/index.ts @@ -4,7 +4,10 @@ export { default as accessTokenFactory, GetAccessToken, AccessTokenRequest, - GetAccessTokenResult + GetAccessTokenResult, + AfterRefresh, + AfterRefreshPageRoute, + AfterRefreshAppRoute } from './get-access-token'; export { default as SessionCache, get, set } from './cache'; export { default as touchSessionFactory, TouchSession } from './touch-session'; diff --git a/src/utils/errors.ts b/src/utils/errors.ts index fda7b003b..cc7aca6dd 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -48,7 +48,7 @@ export abstract class AuthError extends Error { /** * The underlying error, if any. * - * **IMPORTANT** When this error is from the Identity Provider ({@Link IdentityProviderError}) it can contain user + * **IMPORTANT** When this error is from the Identity Provider ({@link IdentityProviderError}) it can contain user * input and is only escaped using basic escaping for putting untrusted data directly into the HTML body. * * You should **not** render this error without using a templating engine that will properly escape it for other @@ -129,7 +129,7 @@ type HandlerErrorOptions = { * without using a templating engine that will properly escape it for other HTML contexts first. * * @see the {@link AuthError.cause | cause property} contains the underlying error. - * **IMPORTANT** When this error is from the Identity Provider ({@Link IdentityProviderError}) it can contain user + * **IMPORTANT** When this error is from the Identity Provider ({@link IdentityProviderError}) it can contain user * input and is only escaped using basic escaping for putting untrusted data directly into the HTML body. * You should **not** render this error without using a templating engine that will properly escape it for other * HTML contexts first. @@ -158,7 +158,7 @@ export class HandlerError extends AuthError { * without using a templating engine that will properly escape it for other HTML contexts first. * * @see the {@link AuthError.cause | cause property} contains the underlying error. - * **IMPORTANT** When this error is from the Identity Provider ({@Link IdentityProviderError}) it can contain user + * **IMPORTANT** When this error is from the Identity Provider ({@link IdentityProviderError}) it can contain user * input and is only escaped using basic escaping for putting untrusted data directly into the HTML body. * You should **not** render this error without using a templating engine that will properly escape it for other * HTML contexts first. diff --git a/tests/fixtures/setup.ts b/tests/fixtures/setup.ts index 6a47ba3f5..fa3bddb86 100644 --- a/tests/fixtures/setup.ts +++ b/tests/fixtures/setup.ts @@ -12,7 +12,7 @@ import { initAuth0, AccessTokenRequest, Claims, - OnError, + PageRouterOnError, HandleLogin, HandleLogout, HandleCallback, @@ -36,14 +36,14 @@ export type SetupOptions = { profileOptions?: ProfileOptions; withPageAuthRequiredOptions?: WithPageAuthRequiredPageRouterOptions; getAccessTokenOptions?: AccessTokenRequest; - onError?: OnError; + onError?: PageRouterOnError; discoveryOptions?: Record; userInfoPayload?: Record; userInfoToken?: string; asyncProps?: boolean; }; -export const defaultOnError: OnError = (_req, res, error) => { +export const defaultOnError: PageRouterOnError = (_req, res, error) => { res.statusMessage = error.message; res.status(error.status || 500).end(error.message); }; diff --git a/tests/handlers/auth.test.ts b/tests/handlers/auth.test.ts index 27981f54a..2f80610f8 100644 --- a/tests/handlers/auth.test.ts +++ b/tests/handlers/auth.test.ts @@ -3,7 +3,7 @@ import { ArgumentsOf } from 'ts-jest'; import { withoutApi } from '../fixtures/default-settings'; import { login, setup, teardown } from '../fixtures/setup'; import { get } from '../auth0-session/fixtures/helpers'; -import { initAuth0, OnError } from '../../src'; +import { initAuth0, PageRouterOnError as OnError } from '../../src'; import { LoginOptions, LogoutOptions, CallbackOptions, ProfileOptions } from '../../src/handlers'; import * as baseLoginHandler from '../../src/auth0-session/handlers/login'; import * as baseLogoutHandler from '../../src/auth0-session/handlers/logout'; From e506ef7db7178404cc17e649f3ee4549cecfdd02 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 8 Jun 2023 10:33:41 +0100 Subject: [PATCH 42/61] Add app router examples --- EXAMPLES.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 62582e881..b4077b78e 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -151,6 +151,8 @@ export default () => Login; ### Protecting a Server-Side Rendered (SSR) Page +#### Page Router + Requests to `/pages/profile` without a valid session cookie will be redirected to the login page. ```jsx @@ -166,7 +168,23 @@ export default function Profile({ user }) { export const getServerSideProps = withPageAuthRequired(); ``` -See a running example of an [SSR protected page](./example-app/pages/profile-ssr.tsx) in the example app or refer to the full list of configuration options for `withPageAuthRequired` [here](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequiredoptions). +See a running example of an [SSR protected page](./example-app/pages/page-router/profile-ssr.tsx) in the example app or refer to the full list of configuration options for `withPageAuthRequired` [here](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequiredoptions). + +#### App Router + +Requests to `/pages/profile` without a valid session cookie will be redirected to the login page. + +```jsx +// app/profile/page.js +import { withPageAuthRequired } from '@auth0/nextjs-auth0'; + +export default withPageAuthRequired(function Profile({ user }) { + return
    Hello {user.name}
    ; +}, { returnTo: '/profile' }) +// You need to provide a `returnTo` since Server Components aren't aware of the page's URL +``` + +See a running example of a [protected server component page](./example-app/app/profile/page.tsx) in the example app or more info [in the docs](./src/helpers/with-page-auth-required.ts#129). ### Protecting a Client-Side Rendered (CSR) Page @@ -185,7 +203,9 @@ See a running example of a [CSR protected page](./example-app/pages/profile.tsx) ### Protect an API Route -Requests to `/pages/api/protected` without a valid session cookie will fail with `401`. +#### Page Router + +Requests to `/api/protected` without a valid session cookie will fail with `401`. ```js // pages/api/protected.js @@ -217,8 +237,47 @@ export default withPageAuthRequired(function Products() { }); ``` -See a running example in the example app, the [protected API route](./example-app/pages/api/shows.ts) and -the [frontend code to access the protected API](./example-app/pages/shows.tsx). +See a running example in the example app, the [protected API route](./example-app/pages/api/page-router-profile.ts) and +the [frontend code to access the protected API](./example-app/pages/page-router/profile-api.tsx). + +#### App Router + +Requests to `/api/protected` without a valid session cookie will fail with `401`. + +```js +// app/api/protected/route.js +import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0'; + +export default withApiAuthRequired(async function myApiRoute(req) { + const res = new NextResponse(); + const { user } = await getSession(req, res); + return NextResponse.json({ protected: 'My Secret', id: user.sub }, res); +}); +``` + +Then you can access your API from the frontend with a valid session cookie. + +```jsx +// app/products/page.jsx +'use client' +import useSWR from 'swr'; +import { withPageAuthRequired } from '@auth0/nextjs-auth0/client'; + +const fetcher = async (uri) => { + const response = await fetch(uri); + return response.json(); +}; + +export default withPageAuthRequired(function Products() { + const { data, error } = useSWR('/api/protected', fetcher); + if (error) return
    oops... {error.message}
    ; + if (data === undefined) return
    Loading...
    ; + return
    {data.protected}
    ; +}); +``` + +See a running example in the example app, the [protected API route](./example-app/app/api/profile/route.ts) and +the [frontend code to access the protected API](./example-app/app/profile-api/page.tsx). ### Protecting pages with Middleware From 726fcc21e96737c15af01798f144212f20046a5b Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 8 Jun 2023 10:52:41 +0100 Subject: [PATCH 43/61] Warn once in development --- src/http/auth0-next-response-cookies.ts | 11 ++++++++++- .../http/auth0-next-response-cookies.test.ts | 19 ------------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/http/auth0-next-response-cookies.ts b/src/http/auth0-next-response-cookies.ts index ab119527b..3ed300d7d 100644 --- a/src/http/auth0-next-response-cookies.ts +++ b/src/http/auth0-next-response-cookies.ts @@ -2,6 +2,8 @@ import { cookies } from 'next/headers'; import type { CookieSerializeOptions } from 'cookie'; import { Auth0ResponseCookies } from '../auth0-session/http'; +let warned = false; + export default class Auth0NextResponseCookies extends Auth0ResponseCookies { public constructor() { super(); @@ -13,8 +15,15 @@ export default class Auth0NextResponseCookies extends Auth0ResponseCookies { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore see: https://github.com/vercel/next.js/pull/50052 cookieSetter.set({ ...options, name, value }); + /* c8 ignore next 6 */ } catch (_) { - console.warn('cant set cookies in app dir pages or server components'); + if (process.env.NODE_ENV === 'development' && !warned) { + console.warn( + 'nextjs-auth0 is attempting to set cookies from a server component,' + + 'see https://github.com/auth0/nextjs-auth0/tree/beta#important-limitations-of-the-app-directory' + ); + warned = true; + } } } diff --git a/tests/http/auth0-next-response-cookies.test.ts b/tests/http/auth0-next-response-cookies.test.ts index d78162b99..eeef8679b 100644 --- a/tests/http/auth0-next-response-cookies.test.ts +++ b/tests/http/auth0-next-response-cookies.test.ts @@ -14,25 +14,6 @@ describe('auth0-next-response', () => { expect(res.cookies.get('foo')?.value).toEqual('bar'); }); - it("should warn if cookie can't be set", async () => { - jest.spyOn(console, 'warn'); - const cookies = new Auth0NextResponseCookies(); - const res = new NextResponse(); - mocked(nextCookies).mockImplementation( - () => - ({ - set() { - throw new Error(); - } - } as any) - ); - cookies.setCookie('foo', 'bar'); - expect(console.warn).toHaveBeenCalledWith( - expect.stringMatching(/cant set cookies in app dir pages or server components/) - ); - expect(res.cookies.get('foo')).toBeUndefined(); - }); - it('should delete cookies', async () => { const cookies = new Auth0NextResponseCookies(); const res = new NextResponse(); From d139a80b2e5807a5c8bc476bc068585bbe686a13 Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 8 Jun 2023 12:09:13 +0100 Subject: [PATCH 44/61] fix coverage --- src/http/auth0-next-response-cookies.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/http/auth0-next-response-cookies.ts b/src/http/auth0-next-response-cookies.ts index 3ed300d7d..137488301 100644 --- a/src/http/auth0-next-response-cookies.ts +++ b/src/http/auth0-next-response-cookies.ts @@ -15,8 +15,8 @@ export default class Auth0NextResponseCookies extends Auth0ResponseCookies { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore see: https://github.com/vercel/next.js/pull/50052 cookieSetter.set({ ...options, name, value }); - /* c8 ignore next 6 */ - } catch (_) { + } catch (_) /* c8 ignore next */ { + /* c8 ignore next 8 */ if (process.env.NODE_ENV === 'development' && !warned) { console.warn( 'nextjs-auth0 is attempting to set cookies from a server component,' + From ce7dcdafe2079008cad1afdd0a2354288bec062d Mon Sep 17 00:00:00 2001 From: Adam Mcgrath Date: Thu, 8 Jun 2023 12:23:57 +0100 Subject: [PATCH 45/61] Release 3.0.0-beta.0 --- .circleci/config.yml | 3 +- CHANGELOG.md | 9 + docs/assets/search.js | 2 +- .../classes/client_use_user.RequestError.html | 14 +- .../http_auth0_next_api_request.default.html | 159 ++++++++++++++++ .../http_auth0_next_api_response.default.html | 133 ++++++++++++++ .../http_auth0_next_request.default.html | 159 ++++++++++++++++ ...tp_auth0_next_request_cookies.default.html | 121 +++++++++++++ .../http_auth0_next_response.default.html | 171 ++++++++++++++++++ ...p_auth0_next_response_cookies.default.html | 148 +++++++++++++++ docs/classes/session_session.default.html | 24 ++- .../utils_errors.AccessTokenError.html | 22 ++- docs/classes/utils_errors.AuthError.html | 22 ++- .../utils_errors.CallbackHandlerError.html | 26 ++- docs/classes/utils_errors.HandlerError.html | 24 ++- .../utils_errors.LoginHandlerError.html | 24 ++- .../utils_errors.LogoutHandlerError.html | 24 ++- .../utils_errors.ProfileHandlerError.html | 24 ++- .../utils_errors.AccessTokenErrorCode.html | 22 ++- docs/functions/client_use_user.default.html | 10 +- docs/functions/client_use_user.useUser.html | 10 +- docs/functions/edge.getSession-1.html | 12 +- docs/functions/edge.initAuth0-1.html | 10 +- .../edge.withMiddlewareAuthRequired-1.html | 10 +- .../handlers_router_helpers.getHandler.html | 112 ++++++++++++ ...helpers_testing.generateSessionCookie.html | 10 +- docs/functions/index._initAuth.html | 36 +++- docs/functions/index.getAccessToken-1.html | 40 +++- docs/functions/index.getSession-1.html | 38 +++- docs/functions/index.handleAuth-1.html | 43 ++++- docs/functions/index.handleCallback-1.html | 80 ++++++-- docs/functions/index.handleLogin-1.html | 80 ++++++-- docs/functions/index.handleLogout-1.html | 80 ++++++-- docs/functions/index.handleProfile-1.html | 80 ++++++-- docs/functions/index.initAuth0-1.html | 32 +++- docs/functions/index.updateSession-1.html | 40 +++- .../index.withApiAuthRequired-1.html | 53 +++++- .../index.withPageAuthRequired-1.html | 61 ++++++- docs/index.html | 16 ++ .../client_use_user.UserProfile.html | 26 ++- ...ent_with_page_auth_required.UserProps.html | 12 +- ..._required.WithPageAuthRequiredOptions.html | 16 +- .../config.AuthorizationParameters.html | 16 +- docs/interfaces/config.BaseConfig.html | 56 +++--- docs/interfaces/config.CookieConfig.html | 22 ++- docs/interfaces/config.NextConfig.html | 24 ++- docs/interfaces/config.SessionConfig.html | 28 ++- .../handlers_callback.CallbackOptions.html | 24 ++- .../handlers_login.AuthorizationParams.html | 22 ++- .../handlers_login.LoginOptions.html | 18 +- .../handlers_logout.LogoutOptions.html | 14 +- docs/interfaces/index.Auth0Server.html | 54 ++++-- ...n_get_access_token.AccessTokenRequest.html | 20 +- ...get_access_token.GetAccessTokenResult.html | 14 +- docs/interfaces/session_session.Claims.html | 10 +- docs/modules/client.html | 10 +- docs/modules/client_use_user.html | 10 +- .../client_with_page_auth_required.html | 10 +- docs/modules/config.html | 10 +- docs/modules/edge.html | 10 +- docs/modules/handlers.html | 65 ++++++- docs/modules/handlers_auth.html | 21 ++- docs/modules/handlers_callback.html | 19 +- docs/modules/handlers_login.html | 19 +- docs/modules/handlers_logout.html | 10 +- docs/modules/handlers_profile.html | 21 ++- docs/modules/handlers_router_helpers.html | 95 ++++++++++ docs/modules/helpers.html | 64 ++++++- docs/modules/helpers_testing.html | 10 +- .../helpers_with_api_auth_required.html | 22 ++- ...helpers_with_middleware_auth_required.html | 10 +- .../helpers_with_page_auth_required.html | 26 ++- docs/modules/http.html | 116 ++++++++++++ docs/modules/http_auth0_next_api_request.html | 80 ++++++++ .../modules/http_auth0_next_api_response.html | 80 ++++++++ docs/modules/http_auth0_next_request.html | 80 ++++++++ .../http_auth0_next_request_cookies.html | 80 ++++++++ docs/modules/http_auth0_next_response.html | 80 ++++++++ .../http_auth0_next_response_cookies.html | 80 ++++++++ docs/modules/index.html | 120 ++++++++++-- docs/modules/session.html | 25 ++- docs/modules/session_get_access_token.html | 21 ++- docs/modules/session_get_session.html | 10 +- docs/modules/session_session.html | 10 +- docs/modules/session_touch_session.html | 10 +- docs/modules/session_update_session.html | 10 +- docs/modules/utils_errors.html | 10 +- docs/modules/version.html | 10 +- docs/types/client_use_user.UserContext.html | 12 +- docs/types/client_use_user.UserProvider.html | 10 +- .../client_use_user.UserProviderProps.html | 10 +- ...ge_auth_required.WithPageAuthRequired.html | 12 +- docs/types/config.ConfigParameters.html | 14 +- docs/types/edge.Auth0Edge.html | 10 +- docs/types/edge.GetSession.html | 10 +- docs/types/edge.InitAuth0.html | 10 +- .../types/handlers_auth.AppRouterOnError.html | 93 ++++++++++ docs/types/handlers_auth.HandleAuth.html | 28 ++- docs/types/handlers_auth.Handlers.html | 15 +- ...l => handlers_auth.PageRouterOnError.html} | 23 ++- .../handlers_callback.AfterCallback.html | 55 ++---- ...ndlers_callback.AfterCallbackAppRoute.html | 119 ++++++++++++ ...dlers_callback.AfterCallbackPageRoute.html | 121 +++++++++++++ .../handlers_callback.CallbackHandler.html | 33 ++-- ...lers_callback.CallbackOptionsProvider.html | 29 ++- .../handlers_callback.HandleCallback.html | 83 ++------- docs/types/handlers_login.GetLoginState.html | 38 ++-- .../handlers_login.GetLoginStateAppRoute.html | 105 +++++++++++ ...handlers_login.GetLoginStatePageRoute.html | 105 +++++++++++ docs/types/handlers_login.HandleLogin.html | 83 ++------- docs/types/handlers_login.LoginHandler.html | 33 ++-- .../handlers_login.LoginOptionsProvider.html | 29 ++- docs/types/handlers_logout.HandleLogout.html | 81 ++------- docs/types/handlers_logout.LogoutHandler.html | 31 ++-- ...handlers_logout.LogoutOptionsProvider.html | 27 ++- docs/types/handlers_profile.AfterRefetch.html | 34 ++-- ...handlers_profile.AfterRefetchAppRoute.html | 99 ++++++++++ ...andlers_profile.AfterRefetchPageRoute.html | 101 +++++++++++ .../types/handlers_profile.HandleProfile.html | 83 ++------- .../handlers_profile.ProfileHandler.html | 33 ++-- .../handlers_profile.ProfileOptions.html | 12 +- ...ndlers_profile.ProfileOptionsProvider.html | 29 ++- ...lers_router_helpers.AppRouteHandlerFn.html | 106 +++++++++++ ...uter_helpers.AppRouteHandlerFnContext.html | 86 +++++++++ .../handlers_router_helpers.AuthHandler.html | 107 +++++++++++ .../handlers_router_helpers.Handler.html | 127 +++++++++++++ ...ndlers_router_helpers.OptionsProvider.html | 99 ++++++++++ ...ers_router_helpers.PageRouteHandlerFn.html | 106 +++++++++++ ...s_testing.GenerateSessionCookieConfig.html | 10 +- ...h_api_auth_required.AppRouteHandlerFn.html | 98 ++++++++++ ...uth_required.AppRouteHandlerFnContext.html | 88 +++++++++ ...api_auth_required.WithApiAuthRequired.html | 40 ++-- ..._required.WithApiAuthRequiredAppRoute.html | 99 ++++++++++ ...required.WithApiAuthRequiredPageRoute.html | 99 ++++++++++ ...h_required.WithMiddlewareAuthRequired.html | 10 +- ...page_auth_required.AppRouterPageRoute.html | 99 ++++++++++ ..._auth_required.AppRouterPageRouteOpts.html | 99 ++++++++++ ...d.GetServerSidePropsResultWithSession.html | 19 +- ...ers_with_page_auth_required.PageRoute.html | 25 ++- ...ge_auth_required.WithPageAuthRequired.html | 51 ++---- ...equired.WithPageAuthRequiredAppRouter.html | 113 ++++++++++++ ....WithPageAuthRequiredAppRouterOptions.html | 92 ++++++++++ ...quired.WithPageAuthRequiredPageRouter.html | 111 ++++++++++++ ...ithPageAuthRequiredPageRouterOptions.html} | 27 ++- docs/types/index.InitAuth0.html | 32 +++- docs/types/index.SessionStore.html | 32 +++- docs/types/index.SessionStorePayload.html | 32 +++- ...session_get_access_token.AfterRefresh.html | 34 ++-- ...get_access_token.AfterRefreshAppRoute.html | 96 ++++++++++ ...et_access_token.AfterRefreshPageRoute.html | 100 ++++++++++ ...ssion_get_access_token.GetAccessToken.html | 22 ++- .../types/session_get_session.GetSession.html | 18 +- .../session_touch_session.TouchSession.html | 18 +- .../session_update_session.UpdateSession.html | 20 +- docs/variables/version.default.html | 12 +- package-lock.json | 4 +- package.json | 2 +- src/version.ts | 2 +- 158 files changed, 6327 insertions(+), 1032 deletions(-) create mode 100644 docs/classes/http_auth0_next_api_request.default.html create mode 100644 docs/classes/http_auth0_next_api_response.default.html create mode 100644 docs/classes/http_auth0_next_request.default.html create mode 100644 docs/classes/http_auth0_next_request_cookies.default.html create mode 100644 docs/classes/http_auth0_next_response.default.html create mode 100644 docs/classes/http_auth0_next_response_cookies.default.html create mode 100644 docs/functions/handlers_router_helpers.getHandler.html create mode 100644 docs/modules/handlers_router_helpers.html create mode 100644 docs/modules/http.html create mode 100644 docs/modules/http_auth0_next_api_request.html create mode 100644 docs/modules/http_auth0_next_api_response.html create mode 100644 docs/modules/http_auth0_next_request.html create mode 100644 docs/modules/http_auth0_next_request_cookies.html create mode 100644 docs/modules/http_auth0_next_response.html create mode 100644 docs/modules/http_auth0_next_response_cookies.html create mode 100644 docs/types/handlers_auth.AppRouterOnError.html rename docs/types/{handlers_auth.OnError.html => handlers_auth.PageRouterOnError.html} (76%) create mode 100644 docs/types/handlers_callback.AfterCallbackAppRoute.html create mode 100644 docs/types/handlers_callback.AfterCallbackPageRoute.html create mode 100644 docs/types/handlers_login.GetLoginStateAppRoute.html create mode 100644 docs/types/handlers_login.GetLoginStatePageRoute.html create mode 100644 docs/types/handlers_profile.AfterRefetchAppRoute.html create mode 100644 docs/types/handlers_profile.AfterRefetchPageRoute.html create mode 100644 docs/types/handlers_router_helpers.AppRouteHandlerFn.html create mode 100644 docs/types/handlers_router_helpers.AppRouteHandlerFnContext.html create mode 100644 docs/types/handlers_router_helpers.AuthHandler.html create mode 100644 docs/types/handlers_router_helpers.Handler.html create mode 100644 docs/types/handlers_router_helpers.OptionsProvider.html create mode 100644 docs/types/handlers_router_helpers.PageRouteHandlerFn.html create mode 100644 docs/types/helpers_with_api_auth_required.AppRouteHandlerFn.html create mode 100644 docs/types/helpers_with_api_auth_required.AppRouteHandlerFnContext.html create mode 100644 docs/types/helpers_with_api_auth_required.WithApiAuthRequiredAppRoute.html create mode 100644 docs/types/helpers_with_api_auth_required.WithApiAuthRequiredPageRoute.html create mode 100644 docs/types/helpers_with_page_auth_required.AppRouterPageRoute.html create mode 100644 docs/types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html create mode 100644 docs/types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouter.html create mode 100644 docs/types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouterOptions.html create mode 100644 docs/types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouter.html rename docs/types/{helpers_with_page_auth_required.WithPageAuthRequiredOptions.html => helpers_with_page_auth_required.WithPageAuthRequiredPageRouterOptions.html} (73%) create mode 100644 docs/types/session_get_access_token.AfterRefreshAppRoute.html create mode 100644 docs/types/session_get_access_token.AfterRefreshPageRoute.html diff --git a/.circleci/config.yml b/.circleci/config.yml index 3286ccae5..3777fe028 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,6 +44,7 @@ workflows: context: - browserstack-env - ship/node-publish: + publish-command: npm publish --tag beta requires: - build context: @@ -52,4 +53,4 @@ workflows: filters: branches: only: - - main + - beta diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d12fa36d..3071916c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## [v3.0.0-beta.0](https://github.com/auth0/nextjs-auth0/tree/v3.0.0-beta.0) (2023-06-08) +[Full Changelog](https://github.com/auth0/nextjs-auth0/compare/v2.6.1...v3.0.0-beta.0) + +**Added** +- Support for the App Router. + +**⚠️ BREAKING CHANGES** +- Support for EOL Node versions 12 and 14 has been removed. See the [V3_MIGRATION_GUIDE.md](./V3_MIGRATION_GUIDE.md) for more details. + ## [v2.6.1](https://github.com/auth0/nextjs-auth0/tree/v2.6.1) (2023-06-06) [Full Changelog](https://github.com/auth0/nextjs-auth0/compare/v2.6.0...v2.6.1) diff --git a/docs/assets/search.js b/docs/assets/search.js index 3a76d4928..bb797c472 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = JSON.parse("{\"rows\":[{\"kind\":2,\"name\":\"client\",\"url\":\"modules/client.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"client/use-user\",\"url\":\"modules/client_use_user.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"UserProfile\",\"url\":\"interfaces/client_use_user.UserProfile.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":1024,\"name\":\"email\",\"url\":\"interfaces/client_use_user.UserProfile.html#email\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"email_verified\",\"url\":\"interfaces/client_use_user.UserProfile.html#email_verified\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"interfaces/client_use_user.UserProfile.html#name\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"nickname\",\"url\":\"interfaces/client_use_user.UserProfile.html#nickname\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"picture\",\"url\":\"interfaces/client_use_user.UserProfile.html#picture\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"sub\",\"url\":\"interfaces/client_use_user.UserProfile.html#sub\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"updated_at\",\"url\":\"interfaces/client_use_user.UserProfile.html#updated_at\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"org_id\",\"url\":\"interfaces/client_use_user.UserProfile.html#org_id\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":4194304,\"name\":\"UserContext\",\"url\":\"types/client_use_user.UserContext.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_use_user.UserContext.html#__type\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext\"},{\"kind\":1024,\"name\":\"user\",\"url\":\"types/client_use_user.UserContext.html#__type.user\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":1024,\"name\":\"error\",\"url\":\"types/client_use_user.UserContext.html#__type.error\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":1024,\"name\":\"isLoading\",\"url\":\"types/client_use_user.UserContext.html#__type.isLoading\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":1024,\"name\":\"checkSession\",\"url\":\"types/client_use_user.UserContext.html#__type.checkSession\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_use_user.UserContext.html#__type.checkSession.__type-1\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type.checkSession\"},{\"kind\":128,\"name\":\"RequestError\",\"url\":\"classes/client_use_user.RequestError.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/client_use_user.RequestError.html#constructor\",\"classes\":\"\",\"parent\":\"client/use-user.RequestError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/client_use_user.RequestError.html#status\",\"classes\":\"\",\"parent\":\"client/use-user.RequestError\"},{\"kind\":4194304,\"name\":\"UserProviderProps\",\"url\":\"types/client_use_user.UserProviderProps.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":64,\"name\":\"useUser\",\"url\":\"functions/client_use_user.useUser.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":4194304,\"name\":\"UserProvider\",\"url\":\"types/client_use_user.UserProvider.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_use_user.UserProvider.html#__type\",\"classes\":\"\",\"parent\":\"client/use-user.UserProvider\"},{\"kind\":64,\"name\":\"default\",\"url\":\"functions/client_use_user.default.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":2,\"name\":\"client/with-page-auth-required\",\"url\":\"modules/client_with_page_auth_required.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#returnTo\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":1024,\"name\":\"onRedirecting\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onRedirecting\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onRedirecting.__type-2\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions.onRedirecting\"},{\"kind\":1024,\"name\":\"onError\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onError\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onError.__type\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions.onError\"},{\"kind\":256,\"name\":\"UserProps\",\"url\":\"interfaces/client_with_page_auth_required.UserProps.html\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required\"},{\"kind\":1024,\"name\":\"user\",\"url\":\"interfaces/client_with_page_auth_required.UserProps.html#user\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.UserProps\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequired\",\"url\":\"types/client_with_page_auth_required.WithPageAuthRequired.html\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_with_page_auth_required.WithPageAuthRequired.html#__type\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequired\"},{\"kind\":2,\"name\":\"config\",\"url\":\"modules/config.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"BaseConfig\",\"url\":\"interfaces/config.BaseConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"secret\",\"url\":\"interfaces/config.BaseConfig.html#secret\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"session\",\"url\":\"interfaces/config.BaseConfig.html#session\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"auth0Logout\",\"url\":\"interfaces/config.BaseConfig.html#auth0Logout\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/config.BaseConfig.html#authorizationParams\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"baseURL\",\"url\":\"interfaces/config.BaseConfig.html#baseURL\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clientID\",\"url\":\"interfaces/config.BaseConfig.html#clientID\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clientSecret\",\"url\":\"interfaces/config.BaseConfig.html#clientSecret\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clockTolerance\",\"url\":\"interfaces/config.BaseConfig.html#clockTolerance\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"httpTimeout\",\"url\":\"interfaces/config.BaseConfig.html#httpTimeout\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"enableTelemetry\",\"url\":\"interfaces/config.BaseConfig.html#enableTelemetry\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"getLoginState\",\"url\":\"interfaces/config.BaseConfig.html#getLoginState\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.BaseConfig.html#getLoginState.__type\",\"classes\":\"\",\"parent\":\"config.BaseConfig.getLoginState\"},{\"kind\":1024,\"name\":\"identityClaimFilter\",\"url\":\"interfaces/config.BaseConfig.html#identityClaimFilter\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"idpLogout\",\"url\":\"interfaces/config.BaseConfig.html#idpLogout\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"idTokenSigningAlg\",\"url\":\"interfaces/config.BaseConfig.html#idTokenSigningAlg\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"issuerBaseURL\",\"url\":\"interfaces/config.BaseConfig.html#issuerBaseURL\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"legacySameSiteCookie\",\"url\":\"interfaces/config.BaseConfig.html#legacySameSiteCookie\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"routes\",\"url\":\"interfaces/config.BaseConfig.html#routes\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.BaseConfig.html#routes.__type-2\",\"classes\":\"\",\"parent\":\"config.BaseConfig.routes\"},{\"kind\":1024,\"name\":\"postLogoutRedirect\",\"url\":\"interfaces/config.BaseConfig.html#routes.__type-2.postLogoutRedirect\",\"classes\":\"\",\"parent\":\"config.BaseConfig.routes.__type\"},{\"kind\":1024,\"name\":\"callback\",\"url\":\"interfaces/config.BaseConfig.html#routes.__type-2.callback\",\"classes\":\"\",\"parent\":\"config.BaseConfig.routes.__type\"},{\"kind\":1024,\"name\":\"clientAssertionSigningKey\",\"url\":\"interfaces/config.BaseConfig.html#clientAssertionSigningKey\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clientAssertionSigningAlg\",\"url\":\"interfaces/config.BaseConfig.html#clientAssertionSigningAlg\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":256,\"name\":\"SessionConfig\",\"url\":\"interfaces/config.SessionConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"interfaces/config.SessionConfig.html#name\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"store\",\"url\":\"interfaces/config.SessionConfig.html#store\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"genId\",\"url\":\"interfaces/config.SessionConfig.html#genId\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.SessionConfig.html#genId.__type\",\"classes\":\"\",\"parent\":\"config.SessionConfig.genId\"},{\"kind\":1024,\"name\":\"rolling\",\"url\":\"interfaces/config.SessionConfig.html#rolling\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"rollingDuration\",\"url\":\"interfaces/config.SessionConfig.html#rollingDuration\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"absoluteDuration\",\"url\":\"interfaces/config.SessionConfig.html#absoluteDuration\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"autoSave\",\"url\":\"interfaces/config.SessionConfig.html#autoSave\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"storeIDToken\",\"url\":\"interfaces/config.SessionConfig.html#storeIDToken\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"cookie\",\"url\":\"interfaces/config.SessionConfig.html#cookie\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":256,\"name\":\"CookieConfig\",\"url\":\"interfaces/config.CookieConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"domain\",\"url\":\"interfaces/config.CookieConfig.html#domain\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"path\",\"url\":\"interfaces/config.CookieConfig.html#path\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"transient\",\"url\":\"interfaces/config.CookieConfig.html#transient\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"httpOnly\",\"url\":\"interfaces/config.CookieConfig.html#httpOnly\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"secure\",\"url\":\"interfaces/config.CookieConfig.html#secure\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"sameSite\",\"url\":\"interfaces/config.CookieConfig.html#sameSite\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":256,\"name\":\"AuthorizationParameters\",\"url\":\"interfaces/config.AuthorizationParameters.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"scope\",\"url\":\"interfaces/config.AuthorizationParameters.html#scope\",\"classes\":\"\",\"parent\":\"config.AuthorizationParameters\"},{\"kind\":1024,\"name\":\"response_mode\",\"url\":\"interfaces/config.AuthorizationParameters.html#response_mode\",\"classes\":\"\",\"parent\":\"config.AuthorizationParameters\"},{\"kind\":1024,\"name\":\"response_type\",\"url\":\"interfaces/config.AuthorizationParameters.html#response_type\",\"classes\":\"\",\"parent\":\"config.AuthorizationParameters\"},{\"kind\":256,\"name\":\"NextConfig\",\"url\":\"interfaces/config.NextConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"organization\",\"url\":\"interfaces/config.NextConfig.html#organization\",\"classes\":\"\",\"parent\":\"config.NextConfig\"},{\"kind\":1024,\"name\":\"routes\",\"url\":\"interfaces/config.NextConfig.html#routes\",\"classes\":\"\",\"parent\":\"config.NextConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.NextConfig.html#routes.__type\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes\"},{\"kind\":1024,\"name\":\"callback\",\"url\":\"interfaces/config.NextConfig.html#routes.__type.callback\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes.__type\"},{\"kind\":1024,\"name\":\"login\",\"url\":\"interfaces/config.NextConfig.html#routes.__type.login\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes.__type\"},{\"kind\":1024,\"name\":\"unauthorized\",\"url\":\"interfaces/config.NextConfig.html#routes.__type.unauthorized\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes.__type\"},{\"kind\":1024,\"name\":\"session\",\"url\":\"interfaces/config.NextConfig.html#session\",\"classes\":\"\",\"parent\":\"config.NextConfig\"},{\"kind\":1024,\"name\":\"identityClaimFilter\",\"url\":\"interfaces/config.NextConfig.html#identityClaimFilter\",\"classes\":\"tsd-is-inherited\",\"parent\":\"config.NextConfig\"},{\"kind\":4194304,\"name\":\"ConfigParameters\",\"url\":\"types/config.ConfigParameters.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":2,\"name\":\"edge\",\"url\":\"modules/edge.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"Auth0Edge\",\"url\":\"types/edge.Auth0Edge.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/edge.Auth0Edge.html#__type\",\"classes\":\"\",\"parent\":\"edge.Auth0Edge\"},{\"kind\":1024,\"name\":\"withMiddlewareAuthRequired\",\"url\":\"types/edge.Auth0Edge.html#__type.withMiddlewareAuthRequired\",\"classes\":\"\",\"parent\":\"edge.Auth0Edge.__type\"},{\"kind\":1024,\"name\":\"getSession\",\"url\":\"types/edge.Auth0Edge.html#__type.getSession\",\"classes\":\"\",\"parent\":\"edge.Auth0Edge.__type\"},{\"kind\":4194304,\"name\":\"GetSession\",\"url\":\"types/edge.GetSession.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/edge.GetSession.html#__type\",\"classes\":\"\",\"parent\":\"edge.GetSession\"},{\"kind\":4194304,\"name\":\"InitAuth0\",\"url\":\"types/edge.InitAuth0.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/edge.InitAuth0.html#__type\",\"classes\":\"\",\"parent\":\"edge.InitAuth0\"},{\"kind\":64,\"name\":\"initAuth0\",\"url\":\"functions/edge.initAuth0-1.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":64,\"name\":\"getSession\",\"url\":\"functions/edge.getSession-1.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":64,\"name\":\"withMiddlewareAuthRequired\",\"url\":\"functions/edge.withMiddlewareAuthRequired-1.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":2,\"name\":\"handlers/auth\",\"url\":\"modules/handlers_auth.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"Handlers\",\"url\":\"types/handlers_auth.Handlers.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":4194304,\"name\":\"HandleAuth\",\"url\":\"types/handlers_auth.HandleAuth.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_auth.HandleAuth.html#__type\",\"classes\":\"\",\"parent\":\"handlers/auth.HandleAuth\"},{\"kind\":4194304,\"name\":\"OnError\",\"url\":\"types/handlers_auth.OnError.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_auth.OnError.html#__type\",\"classes\":\"\",\"parent\":\"handlers/auth.OnError\"},{\"kind\":2,\"name\":\"handlers/callback\",\"url\":\"modules/handlers_callback.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AfterCallback\",\"url\":\"types/handlers_callback.AfterCallback.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_callback.AfterCallback.html#__type\",\"classes\":\"\",\"parent\":\"handlers/callback.AfterCallback\"},{\"kind\":256,\"name\":\"CallbackOptions\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":1024,\"name\":\"afterCallback\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#afterCallback\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":1024,\"name\":\"redirectUri\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#redirectUri\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":1024,\"name\":\"organization\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#organization\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#authorizationParams\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":4194304,\"name\":\"CallbackOptionsProvider\",\"url\":\"types/handlers_callback.CallbackOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_callback.CallbackOptionsProvider.html#__type\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptionsProvider\"},{\"kind\":4194304,\"name\":\"HandleCallback\",\"url\":\"types/handlers_callback.HandleCallback.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_callback.HandleCallback.html#__type\",\"classes\":\"\",\"parent\":\"handlers/callback.HandleCallback\"},{\"kind\":4194304,\"name\":\"CallbackHandler\",\"url\":\"types/handlers_callback.CallbackHandler.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_callback.CallbackHandler.html#__type\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackHandler\"},{\"kind\":2,\"name\":\"handlers\",\"url\":\"modules/handlers.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"handlers/login\",\"url\":\"modules/handlers_login.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GetLoginState\",\"url\":\"types/handlers_login.GetLoginState.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.GetLoginState.html#__type\",\"classes\":\"\",\"parent\":\"handlers/login.GetLoginState\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.GetLoginState.html#__type.__type-1.__type-2\",\"classes\":\"\",\"parent\":\"handlers/login.GetLoginState.__type.__type\"},{\"kind\":256,\"name\":\"AuthorizationParams\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":1024,\"name\":\"connection\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#connection\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"connection_scope\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#connection_scope\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"invitation\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#invitation\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"organization\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#organization\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"screen_hint\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#screen_hint\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":256,\"name\":\"LoginOptions\",\"url\":\"interfaces/handlers_login.LoginOptions.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/handlers_login.LoginOptions.html#authorizationParams\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptions\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"interfaces/handlers_login.LoginOptions.html#returnTo\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptions\"},{\"kind\":1024,\"name\":\"getLoginState\",\"url\":\"interfaces/handlers_login.LoginOptions.html#getLoginState\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptions\"},{\"kind\":4194304,\"name\":\"LoginOptionsProvider\",\"url\":\"types/handlers_login.LoginOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.LoginOptionsProvider.html#__type\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptionsProvider\"},{\"kind\":4194304,\"name\":\"HandleLogin\",\"url\":\"types/handlers_login.HandleLogin.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.HandleLogin.html#__type\",\"classes\":\"\",\"parent\":\"handlers/login.HandleLogin\"},{\"kind\":4194304,\"name\":\"LoginHandler\",\"url\":\"types/handlers_login.LoginHandler.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.LoginHandler.html#__type\",\"classes\":\"\",\"parent\":\"handlers/login.LoginHandler\"},{\"kind\":2,\"name\":\"handlers/logout\",\"url\":\"modules/handlers_logout.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"LogoutOptions\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html#returnTo\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptions\"},{\"kind\":1024,\"name\":\"logoutParams\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html#logoutParams\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptions\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html#logoutParams.__type\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptions.logoutParams\"},{\"kind\":4194304,\"name\":\"LogoutOptionsProvider\",\"url\":\"types/handlers_logout.LogoutOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_logout.LogoutOptionsProvider.html#__type\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptionsProvider\"},{\"kind\":4194304,\"name\":\"HandleLogout\",\"url\":\"types/handlers_logout.HandleLogout.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_logout.HandleLogout.html#__type\",\"classes\":\"\",\"parent\":\"handlers/logout.HandleLogout\"},{\"kind\":4194304,\"name\":\"LogoutHandler\",\"url\":\"types/handlers_logout.LogoutHandler.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_logout.LogoutHandler.html#__type\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutHandler\"},{\"kind\":2,\"name\":\"handlers/profile\",\"url\":\"modules/handlers_profile.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AfterRefetch\",\"url\":\"types/handlers_profile.AfterRefetch.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.AfterRefetch.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.AfterRefetch\"},{\"kind\":4194304,\"name\":\"ProfileOptions\",\"url\":\"types/handlers_profile.ProfileOptions.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.ProfileOptions.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptions\"},{\"kind\":1024,\"name\":\"refetch\",\"url\":\"types/handlers_profile.ProfileOptions.html#__type.refetch\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptions.__type\"},{\"kind\":1024,\"name\":\"afterRefetch\",\"url\":\"types/handlers_profile.ProfileOptions.html#__type.afterRefetch\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptions.__type\"},{\"kind\":4194304,\"name\":\"ProfileOptionsProvider\",\"url\":\"types/handlers_profile.ProfileOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.ProfileOptionsProvider.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptionsProvider\"},{\"kind\":4194304,\"name\":\"HandleProfile\",\"url\":\"types/handlers_profile.HandleProfile.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.HandleProfile.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.HandleProfile\"},{\"kind\":4194304,\"name\":\"ProfileHandler\",\"url\":\"types/handlers_profile.ProfileHandler.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.ProfileHandler.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileHandler\"},{\"kind\":2,\"name\":\"helpers\",\"url\":\"modules/helpers.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"helpers/testing\",\"url\":\"modules/helpers_testing.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GenerateSessionCookieConfig\",\"url\":\"types/helpers_testing.GenerateSessionCookieConfig.html\",\"classes\":\"\",\"parent\":\"helpers/testing\"},{\"kind\":64,\"name\":\"generateSessionCookie\",\"url\":\"functions/helpers_testing.generateSessionCookie.html\",\"classes\":\"\",\"parent\":\"helpers/testing\"},{\"kind\":2,\"name\":\"helpers/with-api-auth-required\",\"url\":\"modules/helpers_with_api_auth_required.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"WithApiAuthRequired\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequired.html\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequired.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required.WithApiAuthRequired\"},{\"kind\":2,\"name\":\"helpers/with-middleware-auth-required\",\"url\":\"modules/helpers_with_middleware_auth_required.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"WithMiddlewareAuthRequired\",\"url\":\"types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html\",\"classes\":\"\",\"parent\":\"helpers/with-middleware-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-middleware-auth-required.WithMiddlewareAuthRequired\"},{\"kind\":2,\"name\":\"helpers/with-page-auth-required\",\"url\":\"modules/helpers_with_page_auth_required.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GetServerSidePropsResultWithSession\",\"url\":\"types/helpers_with_page_auth_required.GetServerSidePropsResultWithSession.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":4194304,\"name\":\"PageRoute\",\"url\":\"types/helpers_with_page_auth_required.PageRoute.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.PageRoute.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.PageRoute\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredOptions.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredOptions.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":1024,\"name\":\"getServerSideProps\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredOptions.html#__type.getServerSideProps\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredOptions.__type\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredOptions.html#__type.returnTo\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredOptions.__type\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequired\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequired.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequired.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequired\"},{\"kind\":2,\"name\":\"index\",\"url\":\"modules/index.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"Auth0Server\",\"url\":\"interfaces/index.Auth0Server.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":1024,\"name\":\"getSession\",\"url\":\"interfaces/index.Auth0Server.html#getSession\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"touchSession\",\"url\":\"interfaces/index.Auth0Server.html#touchSession\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"updateSession\",\"url\":\"interfaces/index.Auth0Server.html#updateSession\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"getAccessToken\",\"url\":\"interfaces/index.Auth0Server.html#getAccessToken\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleLogin\",\"url\":\"interfaces/index.Auth0Server.html#handleLogin\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleCallback\",\"url\":\"interfaces/index.Auth0Server.html#handleCallback\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleLogout\",\"url\":\"interfaces/index.Auth0Server.html#handleLogout\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleProfile\",\"url\":\"interfaces/index.Auth0Server.html#handleProfile\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"withApiAuthRequired\",\"url\":\"interfaces/index.Auth0Server.html#withApiAuthRequired\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"withPageAuthRequired\",\"url\":\"interfaces/index.Auth0Server.html#withPageAuthRequired\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleAuth\",\"url\":\"interfaces/index.Auth0Server.html#handleAuth\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":4194304,\"name\":\"InitAuth0\",\"url\":\"types/index.InitAuth0.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/index.InitAuth0.html#__type\",\"classes\":\"\",\"parent\":\"index.InitAuth0\"},{\"kind\":64,\"name\":\"initAuth0\",\"url\":\"functions/index.initAuth0-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"_initAuth\",\"url\":\"functions/index._initAuth.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"getSession\",\"url\":\"functions/index.getSession-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"updateSession\",\"url\":\"functions/index.updateSession-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"getAccessToken\",\"url\":\"functions/index.getAccessToken-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"withApiAuthRequired\",\"url\":\"functions/index.withApiAuthRequired-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"withPageAuthRequired\",\"url\":\"functions/index.withPageAuthRequired-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleLogin\",\"url\":\"functions/index.handleLogin-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleLogout\",\"url\":\"functions/index.handleLogout-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleCallback\",\"url\":\"functions/index.handleCallback-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleProfile\",\"url\":\"functions/index.handleProfile-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleAuth\",\"url\":\"functions/index.handleAuth-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":4194304,\"name\":\"SessionStore\",\"url\":\"types/index.SessionStore.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":4194304,\"name\":\"SessionStorePayload\",\"url\":\"types/index.SessionStorePayload.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":2,\"name\":\"session/get-access-token\",\"url\":\"modules/session_get_access_token.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AfterRefresh\",\"url\":\"types/session_get_access_token.AfterRefresh.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_access_token.AfterRefresh.html#__type\",\"classes\":\"\",\"parent\":\"session/get-access-token.AfterRefresh\"},{\"kind\":256,\"name\":\"AccessTokenRequest\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":1024,\"name\":\"scopes\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#scopes\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":1024,\"name\":\"refresh\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#refresh\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":1024,\"name\":\"afterRefresh\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#afterRefresh\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#authorizationParams\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":256,\"name\":\"GetAccessTokenResult\",\"url\":\"interfaces/session_get_access_token.GetAccessTokenResult.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":1024,\"name\":\"accessToken\",\"url\":\"interfaces/session_get_access_token.GetAccessTokenResult.html#accessToken\",\"classes\":\"\",\"parent\":\"session/get-access-token.GetAccessTokenResult\"},{\"kind\":4194304,\"name\":\"GetAccessToken\",\"url\":\"types/session_get_access_token.GetAccessToken.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_access_token.GetAccessToken.html#__type\",\"classes\":\"\",\"parent\":\"session/get-access-token.GetAccessToken\"},{\"kind\":2,\"name\":\"session/get-session\",\"url\":\"modules/session_get_session.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GetSession\",\"url\":\"types/session_get_session.GetSession.html\",\"classes\":\"\",\"parent\":\"session/get-session\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_session.GetSession.html#__type\",\"classes\":\"\",\"parent\":\"session/get-session.GetSession\"},{\"kind\":2,\"name\":\"session\",\"url\":\"modules/session.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"session/session\",\"url\":\"modules/session_session.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"Claims\",\"url\":\"interfaces/session_session.Claims.html\",\"classes\":\"\",\"parent\":\"session/session\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/session_session.default.html\",\"classes\":\"\",\"parent\":\"session/session\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/session_session.default.html#constructor\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"user\",\"url\":\"classes/session_session.default.html#user\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"idToken\",\"url\":\"classes/session_session.default.html#idToken\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"accessToken\",\"url\":\"classes/session_session.default.html#accessToken\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"accessTokenScope\",\"url\":\"classes/session_session.default.html#accessTokenScope\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"accessTokenExpiresAt\",\"url\":\"classes/session_session.default.html#accessTokenExpiresAt\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"refreshToken\",\"url\":\"classes/session_session.default.html#refreshToken\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":2,\"name\":\"session/touch-session\",\"url\":\"modules/session_touch_session.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"TouchSession\",\"url\":\"types/session_touch_session.TouchSession.html\",\"classes\":\"\",\"parent\":\"session/touch-session\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_touch_session.TouchSession.html#__type\",\"classes\":\"\",\"parent\":\"session/touch-session.TouchSession\"},{\"kind\":2,\"name\":\"session/update-session\",\"url\":\"modules/session_update_session.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"UpdateSession\",\"url\":\"types/session_update_session.UpdateSession.html\",\"classes\":\"\",\"parent\":\"session/update-session\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_update_session.UpdateSession.html#__type\",\"classes\":\"\",\"parent\":\"session/update-session.UpdateSession\"},{\"kind\":2,\"name\":\"utils/errors\",\"url\":\"modules/utils_errors.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"AuthError\",\"url\":\"classes/utils_errors.AuthError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.AuthError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.AuthError.html#code\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.AuthError.html#name\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.AuthError.html#cause\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.AuthError.html#status\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":8,\"name\":\"AccessTokenErrorCode\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":16,\"name\":\"MISSING_SESSION\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#MISSING_SESSION\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"MISSING_ACCESS_TOKEN\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#MISSING_ACCESS_TOKEN\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"MISSING_REFRESH_TOKEN\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#MISSING_REFRESH_TOKEN\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"EXPIRED_ACCESS_TOKEN\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#EXPIRED_ACCESS_TOKEN\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"INSUFFICIENT_SCOPE\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#INSUFFICIENT_SCOPE\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"FAILED_REFRESH_GRANT\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#FAILED_REFRESH_GRANT\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":128,\"name\":\"AccessTokenError\",\"url\":\"classes/utils_errors.AccessTokenError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.AccessTokenError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.AccessTokenError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.AccessTokenError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.AccessTokenError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.AccessTokenError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":128,\"name\":\"HandlerError\",\"url\":\"classes/utils_errors.HandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.HandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.HandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.HandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.HandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.HandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":128,\"name\":\"CallbackHandlerError\",\"url\":\"classes/utils_errors.CallbackHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":128,\"name\":\"LoginHandlerError\",\"url\":\"classes/utils_errors.LoginHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LoginHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.LoginHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LoginHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.LoginHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.LoginHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.LoginHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":128,\"name\":\"LogoutHandlerError\",\"url\":\"classes/utils_errors.LogoutHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":128,\"name\":\"ProfileHandlerError\",\"url\":\"classes/utils_errors.ProfileHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":2,\"name\":\"version\",\"url\":\"modules/version.html\",\"classes\":\"\"},{\"kind\":32,\"name\":\"default\",\"url\":\"variables/version.default.html\",\"classes\":\"\",\"parent\":\"version\"},{\"kind\":8388608,\"name\":\"UserProvider\",\"url\":\"modules/client.html#UserProvider\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"UserProviderProps\",\"url\":\"modules/client.html#UserProviderProps\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"UserProfile\",\"url\":\"modules/client.html#UserProfile\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"RequestError\",\"url\":\"modules/client.html#RequestError\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"useUser\",\"url\":\"modules/client.html#useUser\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequired\",\"url\":\"modules/client.html#WithPageAuthRequired\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"modules/client.html#WithPageAuthRequiredOptions\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"WithMiddlewareAuthRequired\",\"url\":\"modules/edge.html#WithMiddlewareAuthRequired\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":8388608,\"name\":\"HandleCallback\",\"url\":\"modules/handlers.html#HandleCallback\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"CallbackOptions\",\"url\":\"modules/handlers.html#CallbackOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterCallback\",\"url\":\"modules/handlers.html#AfterCallback\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleLogin\",\"url\":\"modules/handlers.html#HandleLogin\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"LoginOptions\",\"url\":\"modules/handlers.html#LoginOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"GetLoginState\",\"url\":\"modules/handlers.html#GetLoginState\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleLogout\",\"url\":\"modules/handlers.html#HandleLogout\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"LogoutOptions\",\"url\":\"modules/handlers.html#LogoutOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleProfile\",\"url\":\"modules/handlers.html#HandleProfile\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"ProfileOptions\",\"url\":\"modules/handlers.html#ProfileOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterRefetch\",\"url\":\"modules/handlers.html#AfterRefetch\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"Handlers\",\"url\":\"modules/handlers.html#Handlers\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleAuth\",\"url\":\"modules/handlers.html#HandleAuth\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"OnError\",\"url\":\"modules/handlers.html#OnError\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequired\",\"url\":\"modules/helpers.html#WithApiAuthRequired\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"GetServerSidePropsResultWithSession\",\"url\":\"modules/helpers.html#GetServerSidePropsResultWithSession\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequired\",\"url\":\"modules/helpers.html#WithPageAuthRequired\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"modules/helpers.html#WithPageAuthRequiredOptions\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"PageRoute\",\"url\":\"modules/helpers.html#PageRoute\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"AuthError\",\"url\":\"modules/index.html#AuthError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AccessTokenErrorCode\",\"url\":\"modules/index.html#AccessTokenErrorCode\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AccessTokenError\",\"url\":\"modules/index.html#AccessTokenError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandlerError\",\"url\":\"modules/index.html#HandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"CallbackHandlerError\",\"url\":\"modules/index.html#CallbackHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LoginHandlerError\",\"url\":\"modules/index.html#LoginHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LogoutHandlerError\",\"url\":\"modules/index.html#LogoutHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"ProfileHandlerError\",\"url\":\"modules/index.html#ProfileHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"ConfigParameters\",\"url\":\"modules/index.html#ConfigParameters\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleAuth\",\"url\":\"modules/index.html#HandleAuth\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleLogin\",\"url\":\"modules/index.html#HandleLogin\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleProfile\",\"url\":\"modules/index.html#HandleProfile\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleLogout\",\"url\":\"modules/index.html#HandleLogout\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleCallback\",\"url\":\"modules/index.html#HandleCallback\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"ProfileOptions\",\"url\":\"modules/index.html#ProfileOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Handlers\",\"url\":\"modules/index.html#Handlers\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetServerSidePropsResultWithSession\",\"url\":\"modules/index.html#GetServerSidePropsResultWithSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"modules/index.html#WithPageAuthRequiredOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"PageRoute\",\"url\":\"modules/index.html#PageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequired\",\"url\":\"modules/index.html#WithApiAuthRequired\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequired\",\"url\":\"modules/index.html#WithPageAuthRequired\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetSession\",\"url\":\"modules/index.html#GetSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"TouchSession\",\"url\":\"modules/index.html#TouchSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"UpdateSession\",\"url\":\"modules/index.html#UpdateSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetAccessToken\",\"url\":\"modules/index.html#GetAccessToken\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Session\",\"url\":\"modules/index.html#Session\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Claims\",\"url\":\"modules/index.html#Claims\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AccessTokenRequest\",\"url\":\"modules/index.html#AccessTokenRequest\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetAccessTokenResult\",\"url\":\"modules/index.html#GetAccessTokenResult\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"CallbackOptions\",\"url\":\"modules/index.html#CallbackOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterCallback\",\"url\":\"modules/index.html#AfterCallback\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefetch\",\"url\":\"modules/index.html#AfterRefetch\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LoginOptions\",\"url\":\"modules/index.html#LoginOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LogoutOptions\",\"url\":\"modules/index.html#LogoutOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetLoginState\",\"url\":\"modules/index.html#GetLoginState\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"OnError\",\"url\":\"modules/index.html#OnError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Session\",\"url\":\"modules/session.html#Session\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"Claims\",\"url\":\"modules/session.html#Claims\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"GetSession\",\"url\":\"modules/session.html#GetSession\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"GetAccessToken\",\"url\":\"modules/session.html#GetAccessToken\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"AccessTokenRequest\",\"url\":\"modules/session.html#AccessTokenRequest\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"GetAccessTokenResult\",\"url\":\"modules/session.html#GetAccessTokenResult\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"TouchSession\",\"url\":\"modules/session.html#TouchSession\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"UpdateSession\",\"url\":\"modules/session.html#UpdateSession\",\"classes\":\"\",\"parent\":\"session\"}],\"index\":{\"version\":\"2.3.9\",\"fields\":[\"name\",\"comment\"],\"fieldVectors\":[[\"name/0\",[0,56.393]],[\"comment/0\",[]],[\"name/1\",[1,40.341,2,32.333]],[\"comment/1\",[]],[\"name/2\",[3,51.188]],[\"comment/2\",[]],[\"name/3\",[4,56.393]],[\"comment/3\",[]],[\"name/4\",[5,56.393]],[\"comment/4\",[]],[\"name/5\",[6,37.586]],[\"comment/5\",[]],[\"name/6\",[7,56.393]],[\"comment/6\",[]],[\"name/7\",[8,56.393]],[\"comment/7\",[]],[\"name/8\",[9,56.393]],[\"comment/8\",[]],[\"name/9\",[10,56.393]],[\"comment/9\",[]],[\"name/10\",[11,56.393]],[\"comment/10\",[]],[\"name/11\",[12,56.393]],[\"comment/11\",[]],[\"name/12\",[13,21.852]],[\"comment/12\",[]],[\"name/13\",[2,45.199]],[\"comment/13\",[]],[\"name/14\",[14,56.393]],[\"comment/14\",[]],[\"name/15\",[15,56.393]],[\"comment/15\",[]],[\"name/16\",[16,56.393]],[\"comment/16\",[]],[\"name/17\",[13,21.852]],[\"comment/17\",[]],[\"name/18\",[17,51.188]],[\"comment/18\",[]],[\"name/19\",[18,37.586]],[\"comment/19\",[]],[\"name/20\",[19,38.719]],[\"comment/20\",[]],[\"name/21\",[20,51.188]],[\"comment/21\",[]],[\"name/22\",[21,51.188]],[\"comment/22\",[]],[\"name/23\",[22,51.188]],[\"comment/23\",[]],[\"name/24\",[13,21.852]],[\"comment/24\",[]],[\"name/25\",[23,47.76]],[\"comment/25\",[]],[\"name/26\",[24,25.706,25,23.333,26,20.603,27,20.603]],[\"comment/26\",[]],[\"name/27\",[28,43.155]],[\"comment/27\",[]],[\"name/28\",[29,45.199]],[\"comment/28\",[]],[\"name/29\",[30,56.393]],[\"comment/29\",[]],[\"name/30\",[13,21.852]],[\"comment/30\",[]],[\"name/31\",[31,45.199]],[\"comment/31\",[]],[\"name/32\",[13,21.852]],[\"comment/32\",[]],[\"name/33\",[32,56.393]],[\"comment/33\",[]],[\"name/34\",[2,45.199]],[\"comment/34\",[]],[\"name/35\",[33,39.995]],[\"comment/35\",[]],[\"name/36\",[13,21.852]],[\"comment/36\",[]],[\"name/37\",[34,56.393]],[\"comment/37\",[]],[\"name/38\",[35,56.393]],[\"comment/38\",[]],[\"name/39\",[36,56.393]],[\"comment/39\",[]],[\"name/40\",[37,38.719]],[\"comment/40\",[]],[\"name/41\",[38,56.393]],[\"comment/41\",[]],[\"name/42\",[39,43.155]],[\"comment/42\",[]],[\"name/43\",[40,56.393]],[\"comment/43\",[]],[\"name/44\",[41,56.393]],[\"comment/44\",[]],[\"name/45\",[42,56.393]],[\"comment/45\",[]],[\"name/46\",[43,56.393]],[\"comment/46\",[]],[\"name/47\",[44,56.393]],[\"comment/47\",[]],[\"name/48\",[45,56.393]],[\"comment/48\",[]],[\"name/49\",[46,43.155]],[\"comment/49\",[]],[\"name/50\",[13,21.852]],[\"comment/50\",[]],[\"name/51\",[47,51.188]],[\"comment/51\",[]],[\"name/52\",[48,56.393]],[\"comment/52\",[]],[\"name/53\",[49,56.393]],[\"comment/53\",[]],[\"name/54\",[50,56.393]],[\"comment/54\",[]],[\"name/55\",[51,56.393]],[\"comment/55\",[]],[\"name/56\",[52,51.188]],[\"comment/56\",[]],[\"name/57\",[13,21.852]],[\"comment/57\",[]],[\"name/58\",[53,56.393]],[\"comment/58\",[]],[\"name/59\",[54,51.188]],[\"comment/59\",[]],[\"name/60\",[55,56.393]],[\"comment/60\",[]],[\"name/61\",[56,56.393]],[\"comment/61\",[]],[\"name/62\",[57,56.393]],[\"comment/62\",[]],[\"name/63\",[6,37.586]],[\"comment/63\",[]],[\"name/64\",[58,56.393]],[\"comment/64\",[]],[\"name/65\",[59,56.393]],[\"comment/65\",[]],[\"name/66\",[13,21.852]],[\"comment/66\",[]],[\"name/67\",[60,56.393]],[\"comment/67\",[]],[\"name/68\",[61,56.393]],[\"comment/68\",[]],[\"name/69\",[62,56.393]],[\"comment/69\",[]],[\"name/70\",[63,56.393]],[\"comment/70\",[]],[\"name/71\",[64,56.393]],[\"comment/71\",[]],[\"name/72\",[65,56.393]],[\"comment/72\",[]],[\"name/73\",[66,56.393]],[\"comment/73\",[]],[\"name/74\",[67,56.393]],[\"comment/74\",[]],[\"name/75\",[68,56.393]],[\"comment/75\",[]],[\"name/76\",[69,56.393]],[\"comment/76\",[]],[\"name/77\",[70,56.393]],[\"comment/77\",[]],[\"name/78\",[71,56.393]],[\"comment/78\",[]],[\"name/79\",[72,56.393]],[\"comment/79\",[]],[\"name/80\",[73,56.393]],[\"comment/80\",[]],[\"name/81\",[74,56.393]],[\"comment/81\",[]],[\"name/82\",[75,56.393]],[\"comment/82\",[]],[\"name/83\",[76,56.393]],[\"comment/83\",[]],[\"name/84\",[77,56.393]],[\"comment/84\",[]],[\"name/85\",[78,47.76]],[\"comment/85\",[]],[\"name/86\",[52,51.188]],[\"comment/86\",[]],[\"name/87\",[13,21.852]],[\"comment/87\",[]],[\"name/88\",[54,51.188]],[\"comment/88\",[]],[\"name/89\",[79,56.393]],[\"comment/89\",[]],[\"name/90\",[80,56.393]],[\"comment/90\",[]],[\"name/91\",[37,38.719]],[\"comment/91\",[]],[\"name/92\",[47,51.188]],[\"comment/92\",[]],[\"name/93\",[81,51.188]],[\"comment/93\",[]],[\"name/94\",[82,56.393]],[\"comment/94\",[]],[\"name/95\",[83,56.393]],[\"comment/95\",[]],[\"name/96\",[13,21.852]],[\"comment/96\",[]],[\"name/97\",[84,45.199]],[\"comment/97\",[]],[\"name/98\",[85,38.719]],[\"comment/98\",[]],[\"name/99\",[85,38.719]],[\"comment/99\",[]],[\"name/100\",[13,21.852]],[\"comment/100\",[]],[\"name/101\",[86,45.199]],[\"comment/101\",[]],[\"name/102\",[13,21.852]],[\"comment/102\",[]],[\"name/103\",[86,45.199]],[\"comment/103\",[]],[\"name/104\",[85,38.719]],[\"comment/104\",[]],[\"name/105\",[84,45.199]],[\"comment/105\",[]],[\"name/106\",[87,56.393]],[\"comment/106\",[]],[\"name/107\",[88,45.199]],[\"comment/107\",[]],[\"name/108\",[89,43.155]],[\"comment/108\",[]],[\"name/109\",[13,21.852]],[\"comment/109\",[]],[\"name/110\",[31,45.199]],[\"comment/110\",[]],[\"name/111\",[13,21.852]],[\"comment/111\",[]],[\"name/112\",[90,56.393]],[\"comment/112\",[]],[\"name/113\",[91,45.199]],[\"comment/113\",[]],[\"name/114\",[13,21.852]],[\"comment/114\",[]],[\"name/115\",[92,47.76]],[\"comment/115\",[]],[\"name/116\",[91,45.199]],[\"comment/116\",[]],[\"name/117\",[93,56.393]],[\"comment/117\",[]],[\"name/118\",[78,47.76]],[\"comment/118\",[]],[\"name/119\",[39,43.155]],[\"comment/119\",[]],[\"name/120\",[94,56.393]],[\"comment/120\",[]],[\"name/121\",[13,21.852]],[\"comment/121\",[]],[\"name/122\",[95,43.155]],[\"comment/122\",[]],[\"name/123\",[13,21.852]],[\"comment/123\",[]],[\"name/124\",[96,56.393]],[\"comment/124\",[]],[\"name/125\",[13,21.852]],[\"comment/125\",[]],[\"name/126\",[88,45.199]],[\"comment/126\",[]],[\"name/127\",[97,56.393]],[\"comment/127\",[]],[\"name/128\",[46,43.155]],[\"comment/128\",[]],[\"name/129\",[13,21.852]],[\"comment/129\",[]],[\"name/130\",[13,21.852]],[\"comment/130\",[]],[\"name/131\",[39,43.155]],[\"comment/131\",[]],[\"name/132\",[98,56.393]],[\"comment/132\",[]],[\"name/133\",[99,56.393]],[\"comment/133\",[]],[\"name/134\",[100,56.393]],[\"comment/134\",[]],[\"name/135\",[78,47.76]],[\"comment/135\",[]],[\"name/136\",[101,56.393]],[\"comment/136\",[]],[\"name/137\",[102,47.76]],[\"comment/137\",[]],[\"name/138\",[39,43.155]],[\"comment/138\",[]],[\"name/139\",[29,45.199]],[\"comment/139\",[]],[\"name/140\",[46,43.155]],[\"comment/140\",[]],[\"name/141\",[103,56.393]],[\"comment/141\",[]],[\"name/142\",[13,21.852]],[\"comment/142\",[]],[\"name/143\",[104,43.155]],[\"comment/143\",[]],[\"name/144\",[13,21.852]],[\"comment/144\",[]],[\"name/145\",[105,56.393]],[\"comment/145\",[]],[\"name/146\",[13,21.852]],[\"comment/146\",[]],[\"name/147\",[106,56.393]],[\"comment/147\",[]],[\"name/148\",[107,47.76]],[\"comment/148\",[]],[\"name/149\",[29,45.199]],[\"comment/149\",[]],[\"name/150\",[108,56.393]],[\"comment/150\",[]],[\"name/151\",[13,21.852]],[\"comment/151\",[]],[\"name/152\",[109,56.393]],[\"comment/152\",[]],[\"name/153\",[13,21.852]],[\"comment/153\",[]],[\"name/154\",[110,43.155]],[\"comment/154\",[]],[\"name/155\",[13,21.852]],[\"comment/155\",[]],[\"name/156\",[111,56.393]],[\"comment/156\",[]],[\"name/157\",[13,21.852]],[\"comment/157\",[]],[\"name/158\",[112,56.393]],[\"comment/158\",[]],[\"name/159\",[113,45.199]],[\"comment/159\",[]],[\"name/160\",[13,21.852]],[\"comment/160\",[]],[\"name/161\",[114,47.76]],[\"comment/161\",[]],[\"name/162\",[13,21.852]],[\"comment/162\",[]],[\"name/163\",[115,56.393]],[\"comment/163\",[]],[\"name/164\",[113,45.199]],[\"comment/164\",[]],[\"name/165\",[116,56.393]],[\"comment/165\",[]],[\"name/166\",[13,21.852]],[\"comment/166\",[]],[\"name/167\",[117,43.155]],[\"comment/167\",[]],[\"name/168\",[13,21.852]],[\"comment/168\",[]],[\"name/169\",[118,56.393]],[\"comment/169\",[]],[\"name/170\",[13,21.852]],[\"comment/170\",[]],[\"name/171\",[119,56.393]],[\"comment/171\",[]],[\"name/172\",[120,56.393]],[\"comment/172\",[]],[\"name/173\",[121,56.393]],[\"comment/173\",[]],[\"name/174\",[122,56.393]],[\"comment/174\",[]],[\"name/175\",[26,20.603,27,20.603,123,21.771,124,25.706]],[\"comment/175\",[]],[\"name/176\",[125,43.155]],[\"comment/176\",[]],[\"name/177\",[13,21.852]],[\"comment/177\",[]],[\"name/178\",[26,20.603,27,20.603,123,21.771,126,25.706]],[\"comment/178\",[]],[\"name/179\",[84,45.199]],[\"comment/179\",[]],[\"name/180\",[13,21.852]],[\"comment/180\",[]],[\"name/181\",[25,23.333,26,20.603,27,20.603,123,21.771]],[\"comment/181\",[]],[\"name/182\",[127,47.76]],[\"comment/182\",[]],[\"name/183\",[128,47.76]],[\"comment/183\",[]],[\"name/184\",[13,21.852]],[\"comment/184\",[]],[\"name/185\",[28,43.155]],[\"comment/185\",[]],[\"name/186\",[13,21.852]],[\"comment/186\",[]],[\"name/187\",[129,56.393]],[\"comment/187\",[]],[\"name/188\",[29,45.199]],[\"comment/188\",[]],[\"name/189\",[33,39.995]],[\"comment/189\",[]],[\"name/190\",[13,21.852]],[\"comment/190\",[]],[\"name/191\",[130,56.393]],[\"comment/191\",[]],[\"name/192\",[131,56.393]],[\"comment/192\",[]],[\"name/193\",[85,38.719]],[\"comment/193\",[]],[\"name/194\",[132,45.199]],[\"comment/194\",[]],[\"name/195\",[133,43.155]],[\"comment/195\",[]],[\"name/196\",[134,43.155]],[\"comment/196\",[]],[\"name/197\",[104,43.155]],[\"comment/197\",[]],[\"name/198\",[95,43.155]],[\"comment/198\",[]],[\"name/199\",[110,43.155]],[\"comment/199\",[]],[\"name/200\",[117,43.155]],[\"comment/200\",[]],[\"name/201\",[125,43.155]],[\"comment/201\",[]],[\"name/202\",[33,39.995]],[\"comment/202\",[]],[\"name/203\",[89,43.155]],[\"comment/203\",[]],[\"name/204\",[86,45.199]],[\"comment/204\",[]],[\"name/205\",[13,21.852]],[\"comment/205\",[]],[\"name/206\",[86,45.199]],[\"comment/206\",[]],[\"name/207\",[135,56.393]],[\"comment/207\",[]],[\"name/208\",[85,38.719]],[\"comment/208\",[]],[\"name/209\",[133,43.155]],[\"comment/209\",[]],[\"name/210\",[134,43.155]],[\"comment/210\",[]],[\"name/211\",[125,43.155]],[\"comment/211\",[]],[\"name/212\",[33,39.995]],[\"comment/212\",[]],[\"name/213\",[104,43.155]],[\"comment/213\",[]],[\"name/214\",[110,43.155]],[\"comment/214\",[]],[\"name/215\",[95,43.155]],[\"comment/215\",[]],[\"name/216\",[117,43.155]],[\"comment/216\",[]],[\"name/217\",[89,43.155]],[\"comment/217\",[]],[\"name/218\",[136,56.393]],[\"comment/218\",[]],[\"name/219\",[137,56.393]],[\"comment/219\",[]],[\"name/220\",[138,28.504,139,31.402,140,31.402]],[\"comment/220\",[]],[\"name/221\",[141,51.188]],[\"comment/221\",[]],[\"name/222\",[13,21.852]],[\"comment/222\",[]],[\"name/223\",[142,47.76]],[\"comment/223\",[]],[\"name/224\",[143,56.393]],[\"comment/224\",[]],[\"name/225\",[144,56.393]],[\"comment/225\",[]],[\"name/226\",[141,51.188]],[\"comment/226\",[]],[\"name/227\",[39,43.155]],[\"comment/227\",[]],[\"name/228\",[145,47.76]],[\"comment/228\",[]],[\"name/229\",[146,51.188]],[\"comment/229\",[]],[\"name/230\",[134,43.155]],[\"comment/230\",[]],[\"name/231\",[13,21.852]],[\"comment/231\",[]],[\"name/232\",[37,27.698,138,36.617]],[\"comment/232\",[]],[\"name/233\",[85,38.719]],[\"comment/233\",[]],[\"name/234\",[13,21.852]],[\"comment/234\",[]],[\"name/235\",[37,38.719]],[\"comment/235\",[]],[\"name/236\",[147,56.393]],[\"comment/236\",[]],[\"name/237\",[148,47.76]],[\"comment/237\",[]],[\"name/238\",[23,47.76]],[\"comment/238\",[]],[\"name/239\",[18,37.586]],[\"comment/239\",[]],[\"name/240\",[2,45.199]],[\"comment/240\",[]],[\"name/241\",[149,56.393]],[\"comment/241\",[]],[\"name/242\",[146,51.188]],[\"comment/242\",[]],[\"name/243\",[150,56.393]],[\"comment/243\",[]],[\"name/244\",[151,56.393]],[\"comment/244\",[]],[\"name/245\",[152,56.393]],[\"comment/245\",[]],[\"name/246\",[37,27.698,153,40.341]],[\"comment/246\",[]],[\"name/247\",[132,45.199]],[\"comment/247\",[]],[\"name/248\",[13,21.852]],[\"comment/248\",[]],[\"name/249\",[37,27.698,154,40.341]],[\"comment/249\",[]],[\"name/250\",[133,43.155]],[\"comment/250\",[]],[\"name/251\",[13,21.852]],[\"comment/251\",[]],[\"name/252\",[155,56.393]],[\"comment/252\",[]],[\"name/253\",[156,51.188]],[\"comment/253\",[]],[\"name/254\",[18,37.586]],[\"comment/254\",[]],[\"name/255\",[157,35.639]],[\"comment/255\",[]],[\"name/256\",[6,37.586]],[\"comment/256\",[]],[\"name/257\",[158,39.995]],[\"comment/257\",[]],[\"name/258\",[19,38.719]],[\"comment/258\",[]],[\"name/259\",[159,51.188]],[\"comment/259\",[]],[\"name/260\",[160,56.393]],[\"comment/260\",[]],[\"name/261\",[161,56.393]],[\"comment/261\",[]],[\"name/262\",[162,56.393]],[\"comment/262\",[]],[\"name/263\",[163,56.393]],[\"comment/263\",[]],[\"name/264\",[164,56.393]],[\"comment/264\",[]],[\"name/265\",[165,56.393]],[\"comment/265\",[]],[\"name/266\",[166,51.188]],[\"comment/266\",[]],[\"name/267\",[18,37.586]],[\"comment/267\",[]],[\"name/268\",[157,35.639]],[\"comment/268\",[]],[\"name/269\",[6,37.586]],[\"comment/269\",[]],[\"name/270\",[158,39.995]],[\"comment/270\",[]],[\"name/271\",[19,38.719]],[\"comment/271\",[]],[\"name/272\",[167,51.188]],[\"comment/272\",[]],[\"name/273\",[18,37.586]],[\"comment/273\",[]],[\"name/274\",[157,35.639]],[\"comment/274\",[]],[\"name/275\",[6,37.586]],[\"comment/275\",[]],[\"name/276\",[158,39.995]],[\"comment/276\",[]],[\"name/277\",[19,38.719]],[\"comment/277\",[]],[\"name/278\",[168,51.188]],[\"comment/278\",[]],[\"name/279\",[157,35.639]],[\"comment/279\",[]],[\"name/280\",[18,37.586]],[\"comment/280\",[]],[\"name/281\",[157,35.639]],[\"comment/281\",[]],[\"name/282\",[6,37.586]],[\"comment/282\",[]],[\"name/283\",[158,39.995]],[\"comment/283\",[]],[\"name/284\",[19,38.719]],[\"comment/284\",[]],[\"name/285\",[169,51.188]],[\"comment/285\",[]],[\"name/286\",[157,35.639]],[\"comment/286\",[]],[\"name/287\",[18,37.586]],[\"comment/287\",[]],[\"name/288\",[157,35.639]],[\"comment/288\",[]],[\"name/289\",[6,37.586]],[\"comment/289\",[]],[\"name/290\",[158,39.995]],[\"comment/290\",[]],[\"name/291\",[19,38.719]],[\"comment/291\",[]],[\"name/292\",[170,51.188]],[\"comment/292\",[]],[\"name/293\",[157,35.639]],[\"comment/293\",[]],[\"name/294\",[18,37.586]],[\"comment/294\",[]],[\"name/295\",[157,35.639]],[\"comment/295\",[]],[\"name/296\",[6,37.586]],[\"comment/296\",[]],[\"name/297\",[158,39.995]],[\"comment/297\",[]],[\"name/298\",[19,38.719]],[\"comment/298\",[]],[\"name/299\",[171,51.188]],[\"comment/299\",[]],[\"name/300\",[157,35.639]],[\"comment/300\",[]],[\"name/301\",[18,37.586]],[\"comment/301\",[]],[\"name/302\",[157,35.639]],[\"comment/302\",[]],[\"name/303\",[6,37.586]],[\"comment/303\",[]],[\"name/304\",[158,39.995]],[\"comment/304\",[]],[\"name/305\",[19,38.719]],[\"comment/305\",[]],[\"name/306\",[172,56.393]],[\"comment/306\",[]],[\"name/307\",[23,47.76]],[\"comment/307\",[]],[\"name/308\",[22,51.188]],[\"comment/308\",[]],[\"name/309\",[20,51.188]],[\"comment/309\",[]],[\"name/310\",[3,51.188]],[\"comment/310\",[]],[\"name/311\",[17,51.188]],[\"comment/311\",[]],[\"name/312\",[21,51.188]],[\"comment/312\",[]],[\"name/313\",[33,39.995]],[\"comment/313\",[]],[\"name/314\",[28,43.155]],[\"comment/314\",[]],[\"name/315\",[84,45.199]],[\"comment/315\",[]],[\"name/316\",[95,43.155]],[\"comment/316\",[]],[\"name/317\",[92,47.76]],[\"comment/317\",[]],[\"name/318\",[91,45.199]],[\"comment/318\",[]],[\"name/319\",[104,43.155]],[\"comment/319\",[]],[\"name/320\",[102,47.76]],[\"comment/320\",[]],[\"name/321\",[46,43.155]],[\"comment/321\",[]],[\"name/322\",[110,43.155]],[\"comment/322\",[]],[\"name/323\",[107,47.76]],[\"comment/323\",[]],[\"name/324\",[117,43.155]],[\"comment/324\",[]],[\"name/325\",[114,47.76]],[\"comment/325\",[]],[\"name/326\",[113,45.199]],[\"comment/326\",[]],[\"name/327\",[88,45.199]],[\"comment/327\",[]],[\"name/328\",[89,43.155]],[\"comment/328\",[]],[\"name/329\",[31,45.199]],[\"comment/329\",[]],[\"name/330\",[125,43.155]],[\"comment/330\",[]],[\"name/331\",[127,47.76]],[\"comment/331\",[]],[\"name/332\",[33,39.995]],[\"comment/332\",[]],[\"name/333\",[28,43.155]],[\"comment/333\",[]],[\"name/334\",[128,47.76]],[\"comment/334\",[]],[\"name/335\",[156,51.188]],[\"comment/335\",[]],[\"name/336\",[159,51.188]],[\"comment/336\",[]],[\"name/337\",[166,51.188]],[\"comment/337\",[]],[\"name/338\",[167,51.188]],[\"comment/338\",[]],[\"name/339\",[168,51.188]],[\"comment/339\",[]],[\"name/340\",[169,51.188]],[\"comment/340\",[]],[\"name/341\",[170,51.188]],[\"comment/341\",[]],[\"name/342\",[171,51.188]],[\"comment/342\",[]],[\"name/343\",[81,51.188]],[\"comment/343\",[]],[\"name/344\",[89,43.155]],[\"comment/344\",[]],[\"name/345\",[104,43.155]],[\"comment/345\",[]],[\"name/346\",[117,43.155]],[\"comment/346\",[]],[\"name/347\",[110,43.155]],[\"comment/347\",[]],[\"name/348\",[95,43.155]],[\"comment/348\",[]],[\"name/349\",[114,47.76]],[\"comment/349\",[]],[\"name/350\",[88,45.199]],[\"comment/350\",[]],[\"name/351\",[127,47.76]],[\"comment/351\",[]],[\"name/352\",[28,43.155]],[\"comment/352\",[]],[\"name/353\",[128,47.76]],[\"comment/353\",[]],[\"name/354\",[125,43.155]],[\"comment/354\",[]],[\"name/355\",[33,39.995]],[\"comment/355\",[]],[\"name/356\",[85,38.719]],[\"comment/356\",[]],[\"name/357\",[132,45.199]],[\"comment/357\",[]],[\"name/358\",[133,43.155]],[\"comment/358\",[]],[\"name/359\",[134,43.155]],[\"comment/359\",[]],[\"name/360\",[37,38.719]],[\"comment/360\",[]],[\"name/361\",[148,47.76]],[\"comment/361\",[]],[\"name/362\",[142,47.76]],[\"comment/362\",[]],[\"name/363\",[145,47.76]],[\"comment/363\",[]],[\"name/364\",[92,47.76]],[\"comment/364\",[]],[\"name/365\",[91,45.199]],[\"comment/365\",[]],[\"name/366\",[113,45.199]],[\"comment/366\",[]],[\"name/367\",[102,47.76]],[\"comment/367\",[]],[\"name/368\",[107,47.76]],[\"comment/368\",[]],[\"name/369\",[46,43.155]],[\"comment/369\",[]],[\"name/370\",[31,45.199]],[\"comment/370\",[]],[\"name/371\",[37,38.719]],[\"comment/371\",[]],[\"name/372\",[148,47.76]],[\"comment/372\",[]],[\"name/373\",[85,38.719]],[\"comment/373\",[]],[\"name/374\",[134,43.155]],[\"comment/374\",[]],[\"name/375\",[142,47.76]],[\"comment/375\",[]],[\"name/376\",[145,47.76]],[\"comment/376\",[]],[\"name/377\",[132,45.199]],[\"comment/377\",[]],[\"name/378\",[133,43.155]],[\"comment/378\",[]]],\"invertedIndex\":[[\"__type\",{\"_index\":13,\"name\":{\"12\":{},\"17\":{},\"24\":{},\"30\":{},\"32\":{},\"36\":{},\"50\":{},\"57\":{},\"66\":{},\"87\":{},\"96\":{},\"100\":{},\"102\":{},\"109\":{},\"111\":{},\"114\":{},\"121\":{},\"123\":{},\"125\":{},\"129\":{},\"130\":{},\"142\":{},\"144\":{},\"146\":{},\"151\":{},\"153\":{},\"155\":{},\"157\":{},\"160\":{},\"162\":{},\"166\":{},\"168\":{},\"170\":{},\"177\":{},\"180\":{},\"184\":{},\"186\":{},\"190\":{},\"205\":{},\"222\":{},\"231\":{},\"234\":{},\"248\":{},\"251\":{}},\"comment\":{}}],[\"_initauth\",{\"_index\":135,\"name\":{\"207\":{}},\"comment\":{}}],[\"absoluteduration\",{\"_index\":62,\"name\":{\"69\":{}},\"comment\":{}}],[\"access\",{\"_index\":139,\"name\":{\"220\":{}},\"comment\":{}}],[\"accesstoken\",{\"_index\":146,\"name\":{\"229\":{},\"242\":{}},\"comment\":{}}],[\"accesstokenerror\",{\"_index\":166,\"name\":{\"266\":{},\"337\":{}},\"comment\":{}}],[\"accesstokenerrorcode\",{\"_index\":159,\"name\":{\"259\":{},\"336\":{}},\"comment\":{}}],[\"accesstokenexpiresat\",{\"_index\":151,\"name\":{\"244\":{}},\"comment\":{}}],[\"accesstokenrequest\",{\"_index\":142,\"name\":{\"223\":{},\"362\":{},\"375\":{}},\"comment\":{}}],[\"accesstokenscope\",{\"_index\":150,\"name\":{\"243\":{}},\"comment\":{}}],[\"aftercallback\",{\"_index\":91,\"name\":{\"113\":{},\"116\":{},\"318\":{},\"365\":{}},\"comment\":{}}],[\"afterrefetch\",{\"_index\":113,\"name\":{\"159\":{},\"164\":{},\"326\":{},\"366\":{}},\"comment\":{}}],[\"afterrefresh\",{\"_index\":141,\"name\":{\"221\":{},\"226\":{}},\"comment\":{}}],[\"api\",{\"_index\":124,\"name\":{\"175\":{}},\"comment\":{}}],[\"auth\",{\"_index\":26,\"name\":{\"26\":{},\"175\":{},\"178\":{},\"181\":{}},\"comment\":{}}],[\"auth0edge\",{\"_index\":83,\"name\":{\"95\":{}},\"comment\":{}}],[\"auth0logout\",{\"_index\":38,\"name\":{\"41\":{}},\"comment\":{}}],[\"auth0server\",{\"_index\":131,\"name\":{\"192\":{}},\"comment\":{}}],[\"autherror\",{\"_index\":156,\"name\":{\"253\":{},\"335\":{}},\"comment\":{}}],[\"authorizationparameters\",{\"_index\":73,\"name\":{\"80\":{}},\"comment\":{}}],[\"authorizationparams\",{\"_index\":39,\"name\":{\"42\":{},\"119\":{},\"131\":{},\"138\":{},\"227\":{}},\"comment\":{}}],[\"autosave\",{\"_index\":63,\"name\":{\"70\":{}},\"comment\":{}}],[\"baseconfig\",{\"_index\":35,\"name\":{\"38\":{}},\"comment\":{}}],[\"baseurl\",{\"_index\":40,\"name\":{\"43\":{}},\"comment\":{}}],[\"callback\",{\"_index\":54,\"name\":{\"59\":{},\"88\":{}},\"comment\":{}}],[\"callbackhandler\",{\"_index\":96,\"name\":{\"124\":{}},\"comment\":{}}],[\"callbackhandlererror\",{\"_index\":168,\"name\":{\"278\":{},\"339\":{}},\"comment\":{}}],[\"callbackoptions\",{\"_index\":92,\"name\":{\"115\":{},\"317\":{},\"364\":{}},\"comment\":{}}],[\"callbackoptionsprovider\",{\"_index\":94,\"name\":{\"120\":{}},\"comment\":{}}],[\"cause\",{\"_index\":158,\"name\":{\"257\":{},\"270\":{},\"276\":{},\"283\":{},\"290\":{},\"297\":{},\"304\":{}},\"comment\":{}}],[\"checksession\",{\"_index\":16,\"name\":{\"16\":{}},\"comment\":{}}],[\"claims\",{\"_index\":148,\"name\":{\"237\":{},\"361\":{},\"372\":{}},\"comment\":{}}],[\"client\",{\"_index\":0,\"name\":{\"0\":{}},\"comment\":{}}],[\"client/use\",{\"_index\":1,\"name\":{\"1\":{}},\"comment\":{}}],[\"client/with\",{\"_index\":24,\"name\":{\"26\":{}},\"comment\":{}}],[\"clientassertionsigningalg\",{\"_index\":56,\"name\":{\"61\":{}},\"comment\":{}}],[\"clientassertionsigningkey\",{\"_index\":55,\"name\":{\"60\":{}},\"comment\":{}}],[\"clientid\",{\"_index\":41,\"name\":{\"44\":{}},\"comment\":{}}],[\"clientsecret\",{\"_index\":42,\"name\":{\"45\":{}},\"comment\":{}}],[\"clocktolerance\",{\"_index\":43,\"name\":{\"46\":{}},\"comment\":{}}],[\"code\",{\"_index\":157,\"name\":{\"255\":{},\"268\":{},\"274\":{},\"279\":{},\"281\":{},\"286\":{},\"288\":{},\"293\":{},\"295\":{},\"300\":{},\"302\":{}},\"comment\":{}}],[\"config\",{\"_index\":34,\"name\":{\"37\":{}},\"comment\":{}}],[\"configparameters\",{\"_index\":81,\"name\":{\"93\":{},\"343\":{}},\"comment\":{}}],[\"connection\",{\"_index\":98,\"name\":{\"132\":{}},\"comment\":{}}],[\"connection_scope\",{\"_index\":99,\"name\":{\"133\":{}},\"comment\":{}}],[\"constructor\",{\"_index\":18,\"name\":{\"19\":{},\"239\":{},\"254\":{},\"267\":{},\"273\":{},\"280\":{},\"287\":{},\"294\":{},\"301\":{}},\"comment\":{}}],[\"cookie\",{\"_index\":65,\"name\":{\"72\":{}},\"comment\":{}}],[\"cookieconfig\",{\"_index\":66,\"name\":{\"73\":{}},\"comment\":{}}],[\"default\",{\"_index\":23,\"name\":{\"25\":{},\"238\":{},\"307\":{}},\"comment\":{}}],[\"domain\",{\"_index\":67,\"name\":{\"74\":{}},\"comment\":{}}],[\"edge\",{\"_index\":82,\"name\":{\"94\":{}},\"comment\":{}}],[\"email\",{\"_index\":4,\"name\":{\"3\":{}},\"comment\":{}}],[\"email_verified\",{\"_index\":5,\"name\":{\"4\":{}},\"comment\":{}}],[\"enabletelemetry\",{\"_index\":45,\"name\":{\"48\":{}},\"comment\":{}}],[\"error\",{\"_index\":14,\"name\":{\"14\":{}},\"comment\":{}}],[\"expired_access_token\",{\"_index\":163,\"name\":{\"263\":{}},\"comment\":{}}],[\"failed_refresh_grant\",{\"_index\":165,\"name\":{\"265\":{}},\"comment\":{}}],[\"generatesessioncookie\",{\"_index\":122,\"name\":{\"174\":{}},\"comment\":{}}],[\"generatesessioncookieconfig\",{\"_index\":121,\"name\":{\"173\":{}},\"comment\":{}}],[\"genid\",{\"_index\":59,\"name\":{\"65\":{}},\"comment\":{}}],[\"getaccesstoken\",{\"_index\":134,\"name\":{\"196\":{},\"210\":{},\"230\":{},\"359\":{},\"374\":{}},\"comment\":{}}],[\"getaccesstokenresult\",{\"_index\":145,\"name\":{\"228\":{},\"363\":{},\"376\":{}},\"comment\":{}}],[\"getloginstate\",{\"_index\":46,\"name\":{\"49\":{},\"128\":{},\"140\":{},\"321\":{},\"369\":{}},\"comment\":{}}],[\"getserversideprops\",{\"_index\":129,\"name\":{\"187\":{}},\"comment\":{}}],[\"getserversidepropsresultwithsession\",{\"_index\":127,\"name\":{\"182\":{},\"331\":{},\"351\":{}},\"comment\":{}}],[\"getsession\",{\"_index\":85,\"name\":{\"98\":{},\"99\":{},\"104\":{},\"193\":{},\"208\":{},\"233\":{},\"356\":{},\"373\":{}},\"comment\":{}}],[\"handleauth\",{\"_index\":89,\"name\":{\"108\":{},\"203\":{},\"217\":{},\"328\":{},\"344\":{}},\"comment\":{}}],[\"handlecallback\",{\"_index\":95,\"name\":{\"122\":{},\"198\":{},\"215\":{},\"316\":{},\"348\":{}},\"comment\":{}}],[\"handlelogin\",{\"_index\":104,\"name\":{\"143\":{},\"197\":{},\"213\":{},\"319\":{},\"345\":{}},\"comment\":{}}],[\"handlelogout\",{\"_index\":110,\"name\":{\"154\":{},\"199\":{},\"214\":{},\"322\":{},\"347\":{}},\"comment\":{}}],[\"handleprofile\",{\"_index\":117,\"name\":{\"167\":{},\"200\":{},\"216\":{},\"324\":{},\"346\":{}},\"comment\":{}}],[\"handlererror\",{\"_index\":167,\"name\":{\"272\":{},\"338\":{}},\"comment\":{}}],[\"handlers\",{\"_index\":88,\"name\":{\"107\":{},\"126\":{},\"327\":{},\"350\":{}},\"comment\":{}}],[\"handlers/auth\",{\"_index\":87,\"name\":{\"106\":{}},\"comment\":{}}],[\"handlers/callback\",{\"_index\":90,\"name\":{\"112\":{}},\"comment\":{}}],[\"handlers/login\",{\"_index\":97,\"name\":{\"127\":{}},\"comment\":{}}],[\"handlers/logout\",{\"_index\":106,\"name\":{\"147\":{}},\"comment\":{}}],[\"handlers/profile\",{\"_index\":112,\"name\":{\"158\":{}},\"comment\":{}}],[\"helpers\",{\"_index\":119,\"name\":{\"171\":{}},\"comment\":{}}],[\"helpers/testing\",{\"_index\":120,\"name\":{\"172\":{}},\"comment\":{}}],[\"helpers/with\",{\"_index\":123,\"name\":{\"175\":{},\"178\":{},\"181\":{}},\"comment\":{}}],[\"httponly\",{\"_index\":70,\"name\":{\"77\":{}},\"comment\":{}}],[\"httptimeout\",{\"_index\":44,\"name\":{\"47\":{}},\"comment\":{}}],[\"identityclaimfilter\",{\"_index\":47,\"name\":{\"51\":{},\"92\":{}},\"comment\":{}}],[\"idplogout\",{\"_index\":48,\"name\":{\"52\":{}},\"comment\":{}}],[\"idtoken\",{\"_index\":149,\"name\":{\"241\":{}},\"comment\":{}}],[\"idtokensigningalg\",{\"_index\":49,\"name\":{\"53\":{}},\"comment\":{}}],[\"index\",{\"_index\":130,\"name\":{\"191\":{}},\"comment\":{}}],[\"initauth0\",{\"_index\":86,\"name\":{\"101\":{},\"103\":{},\"204\":{},\"206\":{}},\"comment\":{}}],[\"insufficient_scope\",{\"_index\":164,\"name\":{\"264\":{}},\"comment\":{}}],[\"invitation\",{\"_index\":100,\"name\":{\"134\":{}},\"comment\":{}}],[\"isloading\",{\"_index\":15,\"name\":{\"15\":{}},\"comment\":{}}],[\"issuerbaseurl\",{\"_index\":50,\"name\":{\"54\":{}},\"comment\":{}}],[\"legacysamesitecookie\",{\"_index\":51,\"name\":{\"55\":{}},\"comment\":{}}],[\"login\",{\"_index\":79,\"name\":{\"89\":{}},\"comment\":{}}],[\"loginhandler\",{\"_index\":105,\"name\":{\"145\":{}},\"comment\":{}}],[\"loginhandlererror\",{\"_index\":169,\"name\":{\"285\":{},\"340\":{}},\"comment\":{}}],[\"loginoptions\",{\"_index\":102,\"name\":{\"137\":{},\"320\":{},\"367\":{}},\"comment\":{}}],[\"loginoptionsprovider\",{\"_index\":103,\"name\":{\"141\":{}},\"comment\":{}}],[\"logouthandler\",{\"_index\":111,\"name\":{\"156\":{}},\"comment\":{}}],[\"logouthandlererror\",{\"_index\":170,\"name\":{\"292\":{},\"341\":{}},\"comment\":{}}],[\"logoutoptions\",{\"_index\":107,\"name\":{\"148\":{},\"323\":{},\"368\":{}},\"comment\":{}}],[\"logoutoptionsprovider\",{\"_index\":109,\"name\":{\"152\":{}},\"comment\":{}}],[\"logoutparams\",{\"_index\":108,\"name\":{\"150\":{}},\"comment\":{}}],[\"middleware\",{\"_index\":126,\"name\":{\"178\":{}},\"comment\":{}}],[\"missing_access_token\",{\"_index\":161,\"name\":{\"261\":{}},\"comment\":{}}],[\"missing_refresh_token\",{\"_index\":162,\"name\":{\"262\":{}},\"comment\":{}}],[\"missing_session\",{\"_index\":160,\"name\":{\"260\":{}},\"comment\":{}}],[\"name\",{\"_index\":6,\"name\":{\"5\":{},\"63\":{},\"256\":{},\"269\":{},\"275\":{},\"282\":{},\"289\":{},\"296\":{},\"303\":{}},\"comment\":{}}],[\"nextconfig\",{\"_index\":77,\"name\":{\"84\":{}},\"comment\":{}}],[\"nickname\",{\"_index\":7,\"name\":{\"6\":{}},\"comment\":{}}],[\"onerror\",{\"_index\":31,\"name\":{\"31\":{},\"110\":{},\"329\":{},\"370\":{}},\"comment\":{}}],[\"onredirecting\",{\"_index\":30,\"name\":{\"29\":{}},\"comment\":{}}],[\"org_id\",{\"_index\":11,\"name\":{\"10\":{}},\"comment\":{}}],[\"organization\",{\"_index\":78,\"name\":{\"85\":{},\"118\":{},\"135\":{}},\"comment\":{}}],[\"page\",{\"_index\":25,\"name\":{\"26\":{},\"181\":{}},\"comment\":{}}],[\"pageroute\",{\"_index\":128,\"name\":{\"183\":{},\"334\":{},\"353\":{}},\"comment\":{}}],[\"path\",{\"_index\":68,\"name\":{\"75\":{}},\"comment\":{}}],[\"picture\",{\"_index\":8,\"name\":{\"7\":{}},\"comment\":{}}],[\"postlogoutredirect\",{\"_index\":53,\"name\":{\"58\":{}},\"comment\":{}}],[\"profilehandler\",{\"_index\":118,\"name\":{\"169\":{}},\"comment\":{}}],[\"profilehandlererror\",{\"_index\":171,\"name\":{\"299\":{},\"342\":{}},\"comment\":{}}],[\"profileoptions\",{\"_index\":114,\"name\":{\"161\":{},\"325\":{},\"349\":{}},\"comment\":{}}],[\"profileoptionsprovider\",{\"_index\":116,\"name\":{\"165\":{}},\"comment\":{}}],[\"redirecturi\",{\"_index\":93,\"name\":{\"117\":{}},\"comment\":{}}],[\"refetch\",{\"_index\":115,\"name\":{\"163\":{}},\"comment\":{}}],[\"refresh\",{\"_index\":144,\"name\":{\"225\":{}},\"comment\":{}}],[\"refreshtoken\",{\"_index\":152,\"name\":{\"245\":{}},\"comment\":{}}],[\"requesterror\",{\"_index\":17,\"name\":{\"18\":{},\"311\":{}},\"comment\":{}}],[\"required\",{\"_index\":27,\"name\":{\"26\":{},\"175\":{},\"178\":{},\"181\":{}},\"comment\":{}}],[\"response_mode\",{\"_index\":75,\"name\":{\"82\":{}},\"comment\":{}}],[\"response_type\",{\"_index\":76,\"name\":{\"83\":{}},\"comment\":{}}],[\"returnto\",{\"_index\":29,\"name\":{\"28\":{},\"139\":{},\"149\":{},\"188\":{}},\"comment\":{}}],[\"rolling\",{\"_index\":60,\"name\":{\"67\":{}},\"comment\":{}}],[\"rollingduration\",{\"_index\":61,\"name\":{\"68\":{}},\"comment\":{}}],[\"routes\",{\"_index\":52,\"name\":{\"56\":{},\"86\":{}},\"comment\":{}}],[\"samesite\",{\"_index\":72,\"name\":{\"79\":{}},\"comment\":{}}],[\"scope\",{\"_index\":74,\"name\":{\"81\":{}},\"comment\":{}}],[\"scopes\",{\"_index\":143,\"name\":{\"224\":{}},\"comment\":{}}],[\"screen_hint\",{\"_index\":101,\"name\":{\"136\":{}},\"comment\":{}}],[\"secret\",{\"_index\":36,\"name\":{\"39\":{}},\"comment\":{}}],[\"secure\",{\"_index\":71,\"name\":{\"78\":{}},\"comment\":{}}],[\"session\",{\"_index\":37,\"name\":{\"40\":{},\"91\":{},\"232\":{},\"235\":{},\"246\":{},\"249\":{},\"360\":{},\"371\":{}},\"comment\":{}}],[\"session/get\",{\"_index\":138,\"name\":{\"220\":{},\"232\":{}},\"comment\":{}}],[\"session/session\",{\"_index\":147,\"name\":{\"236\":{}},\"comment\":{}}],[\"session/touch\",{\"_index\":153,\"name\":{\"246\":{}},\"comment\":{}}],[\"session/update\",{\"_index\":154,\"name\":{\"249\":{}},\"comment\":{}}],[\"sessionconfig\",{\"_index\":57,\"name\":{\"62\":{}},\"comment\":{}}],[\"sessionstore\",{\"_index\":136,\"name\":{\"218\":{}},\"comment\":{}}],[\"sessionstorepayload\",{\"_index\":137,\"name\":{\"219\":{}},\"comment\":{}}],[\"status\",{\"_index\":19,\"name\":{\"20\":{},\"258\":{},\"271\":{},\"277\":{},\"284\":{},\"291\":{},\"298\":{},\"305\":{}},\"comment\":{}}],[\"store\",{\"_index\":58,\"name\":{\"64\":{}},\"comment\":{}}],[\"storeidtoken\",{\"_index\":64,\"name\":{\"71\":{}},\"comment\":{}}],[\"sub\",{\"_index\":9,\"name\":{\"8\":{}},\"comment\":{}}],[\"token\",{\"_index\":140,\"name\":{\"220\":{}},\"comment\":{}}],[\"touchsession\",{\"_index\":132,\"name\":{\"194\":{},\"247\":{},\"357\":{},\"377\":{}},\"comment\":{}}],[\"transient\",{\"_index\":69,\"name\":{\"76\":{}},\"comment\":{}}],[\"unauthorized\",{\"_index\":80,\"name\":{\"90\":{}},\"comment\":{}}],[\"updated_at\",{\"_index\":10,\"name\":{\"9\":{}},\"comment\":{}}],[\"updatesession\",{\"_index\":133,\"name\":{\"195\":{},\"209\":{},\"250\":{},\"358\":{},\"378\":{}},\"comment\":{}}],[\"user\",{\"_index\":2,\"name\":{\"1\":{},\"13\":{},\"34\":{},\"240\":{}},\"comment\":{}}],[\"usercontext\",{\"_index\":12,\"name\":{\"11\":{}},\"comment\":{}}],[\"userprofile\",{\"_index\":3,\"name\":{\"2\":{},\"310\":{}},\"comment\":{}}],[\"userprops\",{\"_index\":32,\"name\":{\"33\":{}},\"comment\":{}}],[\"userprovider\",{\"_index\":22,\"name\":{\"23\":{},\"308\":{}},\"comment\":{}}],[\"userproviderprops\",{\"_index\":20,\"name\":{\"21\":{},\"309\":{}},\"comment\":{}}],[\"useuser\",{\"_index\":21,\"name\":{\"22\":{},\"312\":{}},\"comment\":{}}],[\"utils/errors\",{\"_index\":155,\"name\":{\"252\":{}},\"comment\":{}}],[\"version\",{\"_index\":172,\"name\":{\"306\":{}},\"comment\":{}}],[\"withapiauthrequired\",{\"_index\":125,\"name\":{\"176\":{},\"201\":{},\"211\":{},\"330\":{},\"354\":{}},\"comment\":{}}],[\"withmiddlewareauthrequired\",{\"_index\":84,\"name\":{\"97\":{},\"105\":{},\"179\":{},\"315\":{}},\"comment\":{}}],[\"withpageauthrequired\",{\"_index\":33,\"name\":{\"35\":{},\"189\":{},\"202\":{},\"212\":{},\"313\":{},\"332\":{},\"355\":{}},\"comment\":{}}],[\"withpageauthrequiredoptions\",{\"_index\":28,\"name\":{\"27\":{},\"185\":{},\"314\":{},\"333\":{},\"352\":{}},\"comment\":{}}]],\"pipeline\":[]}}"); \ No newline at end of file +window.searchData = JSON.parse("{\"rows\":[{\"kind\":2,\"name\":\"client\",\"url\":\"modules/client.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"client/use-user\",\"url\":\"modules/client_use_user.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"UserProfile\",\"url\":\"interfaces/client_use_user.UserProfile.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":1024,\"name\":\"email\",\"url\":\"interfaces/client_use_user.UserProfile.html#email\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"email_verified\",\"url\":\"interfaces/client_use_user.UserProfile.html#email_verified\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"interfaces/client_use_user.UserProfile.html#name\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"nickname\",\"url\":\"interfaces/client_use_user.UserProfile.html#nickname\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"picture\",\"url\":\"interfaces/client_use_user.UserProfile.html#picture\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"sub\",\"url\":\"interfaces/client_use_user.UserProfile.html#sub\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"updated_at\",\"url\":\"interfaces/client_use_user.UserProfile.html#updated_at\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":1024,\"name\":\"org_id\",\"url\":\"interfaces/client_use_user.UserProfile.html#org_id\",\"classes\":\"\",\"parent\":\"client/use-user.UserProfile\"},{\"kind\":4194304,\"name\":\"UserContext\",\"url\":\"types/client_use_user.UserContext.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_use_user.UserContext.html#__type\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext\"},{\"kind\":1024,\"name\":\"user\",\"url\":\"types/client_use_user.UserContext.html#__type.user\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":1024,\"name\":\"error\",\"url\":\"types/client_use_user.UserContext.html#__type.error\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":1024,\"name\":\"isLoading\",\"url\":\"types/client_use_user.UserContext.html#__type.isLoading\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":1024,\"name\":\"checkSession\",\"url\":\"types/client_use_user.UserContext.html#__type.checkSession\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_use_user.UserContext.html#__type.checkSession.__type-1\",\"classes\":\"\",\"parent\":\"client/use-user.UserContext.__type.checkSession\"},{\"kind\":128,\"name\":\"RequestError\",\"url\":\"classes/client_use_user.RequestError.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/client_use_user.RequestError.html#constructor\",\"classes\":\"\",\"parent\":\"client/use-user.RequestError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/client_use_user.RequestError.html#status\",\"classes\":\"\",\"parent\":\"client/use-user.RequestError\"},{\"kind\":4194304,\"name\":\"UserProviderProps\",\"url\":\"types/client_use_user.UserProviderProps.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":64,\"name\":\"useUser\",\"url\":\"functions/client_use_user.useUser.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":4194304,\"name\":\"UserProvider\",\"url\":\"types/client_use_user.UserProvider.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_use_user.UserProvider.html#__type\",\"classes\":\"\",\"parent\":\"client/use-user.UserProvider\"},{\"kind\":64,\"name\":\"default\",\"url\":\"functions/client_use_user.default.html\",\"classes\":\"\",\"parent\":\"client/use-user\"},{\"kind\":2,\"name\":\"client/with-page-auth-required\",\"url\":\"modules/client_with_page_auth_required.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#returnTo\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":1024,\"name\":\"onRedirecting\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onRedirecting\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onRedirecting.__type-2\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions.onRedirecting\"},{\"kind\":1024,\"name\":\"onError\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onError\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/client_with_page_auth_required.WithPageAuthRequiredOptions.html#onError.__type\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequiredOptions.onError\"},{\"kind\":256,\"name\":\"UserProps\",\"url\":\"interfaces/client_with_page_auth_required.UserProps.html\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required\"},{\"kind\":1024,\"name\":\"user\",\"url\":\"interfaces/client_with_page_auth_required.UserProps.html#user\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.UserProps\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequired\",\"url\":\"types/client_with_page_auth_required.WithPageAuthRequired.html\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/client_with_page_auth_required.WithPageAuthRequired.html#__type\",\"classes\":\"\",\"parent\":\"client/with-page-auth-required.WithPageAuthRequired\"},{\"kind\":2,\"name\":\"config\",\"url\":\"modules/config.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"BaseConfig\",\"url\":\"interfaces/config.BaseConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"secret\",\"url\":\"interfaces/config.BaseConfig.html#secret\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"session\",\"url\":\"interfaces/config.BaseConfig.html#session\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"auth0Logout\",\"url\":\"interfaces/config.BaseConfig.html#auth0Logout\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/config.BaseConfig.html#authorizationParams\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"baseURL\",\"url\":\"interfaces/config.BaseConfig.html#baseURL\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clientID\",\"url\":\"interfaces/config.BaseConfig.html#clientID\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clientSecret\",\"url\":\"interfaces/config.BaseConfig.html#clientSecret\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clockTolerance\",\"url\":\"interfaces/config.BaseConfig.html#clockTolerance\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"httpTimeout\",\"url\":\"interfaces/config.BaseConfig.html#httpTimeout\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"enableTelemetry\",\"url\":\"interfaces/config.BaseConfig.html#enableTelemetry\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"getLoginState\",\"url\":\"interfaces/config.BaseConfig.html#getLoginState\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.BaseConfig.html#getLoginState.__type\",\"classes\":\"\",\"parent\":\"config.BaseConfig.getLoginState\"},{\"kind\":1024,\"name\":\"identityClaimFilter\",\"url\":\"interfaces/config.BaseConfig.html#identityClaimFilter\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"idpLogout\",\"url\":\"interfaces/config.BaseConfig.html#idpLogout\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"idTokenSigningAlg\",\"url\":\"interfaces/config.BaseConfig.html#idTokenSigningAlg\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"issuerBaseURL\",\"url\":\"interfaces/config.BaseConfig.html#issuerBaseURL\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"legacySameSiteCookie\",\"url\":\"interfaces/config.BaseConfig.html#legacySameSiteCookie\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"routes\",\"url\":\"interfaces/config.BaseConfig.html#routes\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.BaseConfig.html#routes.__type-2\",\"classes\":\"\",\"parent\":\"config.BaseConfig.routes\"},{\"kind\":1024,\"name\":\"postLogoutRedirect\",\"url\":\"interfaces/config.BaseConfig.html#routes.__type-2.postLogoutRedirect\",\"classes\":\"\",\"parent\":\"config.BaseConfig.routes.__type\"},{\"kind\":1024,\"name\":\"callback\",\"url\":\"interfaces/config.BaseConfig.html#routes.__type-2.callback\",\"classes\":\"\",\"parent\":\"config.BaseConfig.routes.__type\"},{\"kind\":1024,\"name\":\"clientAssertionSigningKey\",\"url\":\"interfaces/config.BaseConfig.html#clientAssertionSigningKey\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":1024,\"name\":\"clientAssertionSigningAlg\",\"url\":\"interfaces/config.BaseConfig.html#clientAssertionSigningAlg\",\"classes\":\"\",\"parent\":\"config.BaseConfig\"},{\"kind\":256,\"name\":\"SessionConfig\",\"url\":\"interfaces/config.SessionConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"interfaces/config.SessionConfig.html#name\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"store\",\"url\":\"interfaces/config.SessionConfig.html#store\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"genId\",\"url\":\"interfaces/config.SessionConfig.html#genId\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.SessionConfig.html#genId.__type\",\"classes\":\"\",\"parent\":\"config.SessionConfig.genId\"},{\"kind\":1024,\"name\":\"rolling\",\"url\":\"interfaces/config.SessionConfig.html#rolling\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"rollingDuration\",\"url\":\"interfaces/config.SessionConfig.html#rollingDuration\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"absoluteDuration\",\"url\":\"interfaces/config.SessionConfig.html#absoluteDuration\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"autoSave\",\"url\":\"interfaces/config.SessionConfig.html#autoSave\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"storeIDToken\",\"url\":\"interfaces/config.SessionConfig.html#storeIDToken\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":1024,\"name\":\"cookie\",\"url\":\"interfaces/config.SessionConfig.html#cookie\",\"classes\":\"\",\"parent\":\"config.SessionConfig\"},{\"kind\":256,\"name\":\"CookieConfig\",\"url\":\"interfaces/config.CookieConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"domain\",\"url\":\"interfaces/config.CookieConfig.html#domain\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"path\",\"url\":\"interfaces/config.CookieConfig.html#path\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"transient\",\"url\":\"interfaces/config.CookieConfig.html#transient\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"httpOnly\",\"url\":\"interfaces/config.CookieConfig.html#httpOnly\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"secure\",\"url\":\"interfaces/config.CookieConfig.html#secure\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":1024,\"name\":\"sameSite\",\"url\":\"interfaces/config.CookieConfig.html#sameSite\",\"classes\":\"\",\"parent\":\"config.CookieConfig\"},{\"kind\":256,\"name\":\"AuthorizationParameters\",\"url\":\"interfaces/config.AuthorizationParameters.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"scope\",\"url\":\"interfaces/config.AuthorizationParameters.html#scope\",\"classes\":\"\",\"parent\":\"config.AuthorizationParameters\"},{\"kind\":1024,\"name\":\"response_mode\",\"url\":\"interfaces/config.AuthorizationParameters.html#response_mode\",\"classes\":\"\",\"parent\":\"config.AuthorizationParameters\"},{\"kind\":1024,\"name\":\"response_type\",\"url\":\"interfaces/config.AuthorizationParameters.html#response_type\",\"classes\":\"\",\"parent\":\"config.AuthorizationParameters\"},{\"kind\":256,\"name\":\"NextConfig\",\"url\":\"interfaces/config.NextConfig.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":1024,\"name\":\"organization\",\"url\":\"interfaces/config.NextConfig.html#organization\",\"classes\":\"\",\"parent\":\"config.NextConfig\"},{\"kind\":1024,\"name\":\"routes\",\"url\":\"interfaces/config.NextConfig.html#routes\",\"classes\":\"\",\"parent\":\"config.NextConfig\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/config.NextConfig.html#routes.__type\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes\"},{\"kind\":1024,\"name\":\"callback\",\"url\":\"interfaces/config.NextConfig.html#routes.__type.callback\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes.__type\"},{\"kind\":1024,\"name\":\"login\",\"url\":\"interfaces/config.NextConfig.html#routes.__type.login\",\"classes\":\"\",\"parent\":\"config.NextConfig.routes.__type\"},{\"kind\":1024,\"name\":\"session\",\"url\":\"interfaces/config.NextConfig.html#session\",\"classes\":\"\",\"parent\":\"config.NextConfig\"},{\"kind\":1024,\"name\":\"identityClaimFilter\",\"url\":\"interfaces/config.NextConfig.html#identityClaimFilter\",\"classes\":\"tsd-is-inherited\",\"parent\":\"config.NextConfig\"},{\"kind\":4194304,\"name\":\"ConfigParameters\",\"url\":\"types/config.ConfigParameters.html\",\"classes\":\"\",\"parent\":\"config\"},{\"kind\":2,\"name\":\"edge\",\"url\":\"modules/edge.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"Auth0Edge\",\"url\":\"types/edge.Auth0Edge.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/edge.Auth0Edge.html#__type\",\"classes\":\"\",\"parent\":\"edge.Auth0Edge\"},{\"kind\":1024,\"name\":\"withMiddlewareAuthRequired\",\"url\":\"types/edge.Auth0Edge.html#__type.withMiddlewareAuthRequired\",\"classes\":\"\",\"parent\":\"edge.Auth0Edge.__type\"},{\"kind\":1024,\"name\":\"getSession\",\"url\":\"types/edge.Auth0Edge.html#__type.getSession\",\"classes\":\"\",\"parent\":\"edge.Auth0Edge.__type\"},{\"kind\":4194304,\"name\":\"GetSession\",\"url\":\"types/edge.GetSession.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/edge.GetSession.html#__type\",\"classes\":\"\",\"parent\":\"edge.GetSession\"},{\"kind\":4194304,\"name\":\"InitAuth0\",\"url\":\"types/edge.InitAuth0.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/edge.InitAuth0.html#__type\",\"classes\":\"\",\"parent\":\"edge.InitAuth0\"},{\"kind\":64,\"name\":\"initAuth0\",\"url\":\"functions/edge.initAuth0-1.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":64,\"name\":\"getSession\",\"url\":\"functions/edge.getSession-1.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":64,\"name\":\"withMiddlewareAuthRequired\",\"url\":\"functions/edge.withMiddlewareAuthRequired-1.html\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":2,\"name\":\"handlers/auth\",\"url\":\"modules/handlers_auth.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"Handlers\",\"url\":\"types/handlers_auth.Handlers.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":4194304,\"name\":\"HandleAuth\",\"url\":\"types/handlers_auth.HandleAuth.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_auth.HandleAuth.html#__type\",\"classes\":\"\",\"parent\":\"handlers/auth.HandleAuth\"},{\"kind\":4194304,\"name\":\"PageRouterOnError\",\"url\":\"types/handlers_auth.PageRouterOnError.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_auth.PageRouterOnError.html#__type\",\"classes\":\"\",\"parent\":\"handlers/auth.PageRouterOnError\"},{\"kind\":4194304,\"name\":\"AppRouterOnError\",\"url\":\"types/handlers_auth.AppRouterOnError.html\",\"classes\":\"\",\"parent\":\"handlers/auth\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_auth.AppRouterOnError.html#__type\",\"classes\":\"\",\"parent\":\"handlers/auth.AppRouterOnError\"},{\"kind\":2,\"name\":\"handlers/callback\",\"url\":\"modules/handlers_callback.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AfterCallback\",\"url\":\"types/handlers_callback.AfterCallback.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":4194304,\"name\":\"AfterCallbackPageRoute\",\"url\":\"types/handlers_callback.AfterCallbackPageRoute.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_callback.AfterCallbackPageRoute.html#__type\",\"classes\":\"\",\"parent\":\"handlers/callback.AfterCallbackPageRoute\"},{\"kind\":4194304,\"name\":\"AfterCallbackAppRoute\",\"url\":\"types/handlers_callback.AfterCallbackAppRoute.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_callback.AfterCallbackAppRoute.html#__type\",\"classes\":\"\",\"parent\":\"handlers/callback.AfterCallbackAppRoute\"},{\"kind\":256,\"name\":\"CallbackOptions\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":1024,\"name\":\"afterCallback\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#afterCallback\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":1024,\"name\":\"redirectUri\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#redirectUri\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":1024,\"name\":\"organization\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#organization\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/handlers_callback.CallbackOptions.html#authorizationParams\",\"classes\":\"\",\"parent\":\"handlers/callback.CallbackOptions\"},{\"kind\":4194304,\"name\":\"CallbackOptionsProvider\",\"url\":\"types/handlers_callback.CallbackOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":4194304,\"name\":\"HandleCallback\",\"url\":\"types/handlers_callback.HandleCallback.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":4194304,\"name\":\"CallbackHandler\",\"url\":\"types/handlers_callback.CallbackHandler.html\",\"classes\":\"\",\"parent\":\"handlers/callback\"},{\"kind\":2,\"name\":\"handlers\",\"url\":\"modules/handlers.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"handlers/login\",\"url\":\"modules/handlers_login.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GetLoginState\",\"url\":\"types/handlers_login.GetLoginState.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":4194304,\"name\":\"GetLoginStatePageRoute\",\"url\":\"types/handlers_login.GetLoginStatePageRoute.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.GetLoginStatePageRoute.html#__type\",\"classes\":\"\",\"parent\":\"handlers/login.GetLoginStatePageRoute\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.GetLoginStatePageRoute.html#__type.__type-1.__type-2\",\"classes\":\"\",\"parent\":\"handlers/login.GetLoginStatePageRoute.__type.__type\"},{\"kind\":4194304,\"name\":\"GetLoginStateAppRoute\",\"url\":\"types/handlers_login.GetLoginStateAppRoute.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.GetLoginStateAppRoute.html#__type\",\"classes\":\"\",\"parent\":\"handlers/login.GetLoginStateAppRoute\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_login.GetLoginStateAppRoute.html#__type.__type-1.__type-2\",\"classes\":\"\",\"parent\":\"handlers/login.GetLoginStateAppRoute.__type.__type\"},{\"kind\":256,\"name\":\"AuthorizationParams\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":1024,\"name\":\"connection\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#connection\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"connection_scope\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#connection_scope\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"invitation\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#invitation\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"organization\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#organization\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":1024,\"name\":\"screen_hint\",\"url\":\"interfaces/handlers_login.AuthorizationParams.html#screen_hint\",\"classes\":\"\",\"parent\":\"handlers/login.AuthorizationParams\"},{\"kind\":256,\"name\":\"LoginOptions\",\"url\":\"interfaces/handlers_login.LoginOptions.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/handlers_login.LoginOptions.html#authorizationParams\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptions\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"interfaces/handlers_login.LoginOptions.html#returnTo\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptions\"},{\"kind\":1024,\"name\":\"getLoginState\",\"url\":\"interfaces/handlers_login.LoginOptions.html#getLoginState\",\"classes\":\"\",\"parent\":\"handlers/login.LoginOptions\"},{\"kind\":4194304,\"name\":\"LoginOptionsProvider\",\"url\":\"types/handlers_login.LoginOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":4194304,\"name\":\"HandleLogin\",\"url\":\"types/handlers_login.HandleLogin.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":4194304,\"name\":\"LoginHandler\",\"url\":\"types/handlers_login.LoginHandler.html\",\"classes\":\"\",\"parent\":\"handlers/login\"},{\"kind\":2,\"name\":\"handlers/logout\",\"url\":\"modules/handlers_logout.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"LogoutOptions\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html#returnTo\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptions\"},{\"kind\":1024,\"name\":\"logoutParams\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html#logoutParams\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptions\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"interfaces/handlers_logout.LogoutOptions.html#logoutParams.__type\",\"classes\":\"\",\"parent\":\"handlers/logout.LogoutOptions.logoutParams\"},{\"kind\":4194304,\"name\":\"LogoutOptionsProvider\",\"url\":\"types/handlers_logout.LogoutOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":4194304,\"name\":\"HandleLogout\",\"url\":\"types/handlers_logout.HandleLogout.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":4194304,\"name\":\"LogoutHandler\",\"url\":\"types/handlers_logout.LogoutHandler.html\",\"classes\":\"\",\"parent\":\"handlers/logout\"},{\"kind\":2,\"name\":\"handlers/profile\",\"url\":\"modules/handlers_profile.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AfterRefetch\",\"url\":\"types/handlers_profile.AfterRefetch.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":4194304,\"name\":\"AfterRefetchPageRoute\",\"url\":\"types/handlers_profile.AfterRefetchPageRoute.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.AfterRefetchPageRoute.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.AfterRefetchPageRoute\"},{\"kind\":4194304,\"name\":\"AfterRefetchAppRoute\",\"url\":\"types/handlers_profile.AfterRefetchAppRoute.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.AfterRefetchAppRoute.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.AfterRefetchAppRoute\"},{\"kind\":4194304,\"name\":\"ProfileOptions\",\"url\":\"types/handlers_profile.ProfileOptions.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_profile.ProfileOptions.html#__type\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptions\"},{\"kind\":1024,\"name\":\"refetch\",\"url\":\"types/handlers_profile.ProfileOptions.html#__type.refetch\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptions.__type\"},{\"kind\":1024,\"name\":\"afterRefetch\",\"url\":\"types/handlers_profile.ProfileOptions.html#__type.afterRefetch\",\"classes\":\"\",\"parent\":\"handlers/profile.ProfileOptions.__type\"},{\"kind\":4194304,\"name\":\"ProfileOptionsProvider\",\"url\":\"types/handlers_profile.ProfileOptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":4194304,\"name\":\"HandleProfile\",\"url\":\"types/handlers_profile.HandleProfile.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":4194304,\"name\":\"ProfileHandler\",\"url\":\"types/handlers_profile.ProfileHandler.html\",\"classes\":\"\",\"parent\":\"handlers/profile\"},{\"kind\":2,\"name\":\"handlers/router-helpers\",\"url\":\"modules/handlers_router_helpers.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AppRouteHandlerFnContext\",\"url\":\"types/handlers_router_helpers.AppRouteHandlerFnContext.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_router_helpers.AppRouteHandlerFnContext.html#__type\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.AppRouteHandlerFnContext\"},{\"kind\":1024,\"name\":\"params\",\"url\":\"types/handlers_router_helpers.AppRouteHandlerFnContext.html#__type.params\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.AppRouteHandlerFnContext.__type\"},{\"kind\":4194304,\"name\":\"AppRouteHandlerFn\",\"url\":\"types/handlers_router_helpers.AppRouteHandlerFn.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_router_helpers.AppRouteHandlerFn.html#__type\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.AppRouteHandlerFn\"},{\"kind\":4194304,\"name\":\"PageRouteHandlerFn\",\"url\":\"types/handlers_router_helpers.PageRouteHandlerFn.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_router_helpers.PageRouteHandlerFn.html#__type\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.PageRouteHandlerFn\"},{\"kind\":4194304,\"name\":\"OptionsProvider\",\"url\":\"types/handlers_router_helpers.OptionsProvider.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_router_helpers.OptionsProvider.html#__type\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.OptionsProvider\"},{\"kind\":4194304,\"name\":\"AuthHandler\",\"url\":\"types/handlers_router_helpers.AuthHandler.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":4194304,\"name\":\"Handler\",\"url\":\"types/handlers_router_helpers.Handler.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/handlers_router_helpers.Handler.html#__type\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.Handler\"},{\"kind\":64,\"name\":\"getHandler\",\"url\":\"functions/handlers_router_helpers.getHandler.html\",\"classes\":\"\",\"parent\":\"handlers/router-helpers\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"functions/handlers_router_helpers.getHandler.html#getHandler.__type\",\"classes\":\"\",\"parent\":\"handlers/router-helpers.getHandler.getHandler\"},{\"kind\":2,\"name\":\"helpers\",\"url\":\"modules/helpers.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"helpers/testing\",\"url\":\"modules/helpers_testing.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GenerateSessionCookieConfig\",\"url\":\"types/helpers_testing.GenerateSessionCookieConfig.html\",\"classes\":\"\",\"parent\":\"helpers/testing\"},{\"kind\":64,\"name\":\"generateSessionCookie\",\"url\":\"functions/helpers_testing.generateSessionCookie.html\",\"classes\":\"\",\"parent\":\"helpers/testing\"},{\"kind\":2,\"name\":\"helpers/with-api-auth-required\",\"url\":\"modules/helpers_with_api_auth_required.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AppRouteHandlerFnContext\",\"url\":\"types/helpers_with_api_auth_required.AppRouteHandlerFnContext.html\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_api_auth_required.AppRouteHandlerFnContext.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required.AppRouteHandlerFnContext\"},{\"kind\":1024,\"name\":\"params\",\"url\":\"types/helpers_with_api_auth_required.AppRouteHandlerFnContext.html#__type.params\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required.AppRouteHandlerFnContext.__type\"},{\"kind\":4194304,\"name\":\"AppRouteHandlerFn\",\"url\":\"types/helpers_with_api_auth_required.AppRouteHandlerFn.html\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_api_auth_required.AppRouteHandlerFn.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required.AppRouteHandlerFn\"},{\"kind\":4194304,\"name\":\"WithApiAuthRequiredAppRoute\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequiredAppRoute.html\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequiredAppRoute.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required.WithApiAuthRequiredAppRoute\"},{\"kind\":4194304,\"name\":\"WithApiAuthRequiredPageRoute\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequiredPageRoute.html\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequiredPageRoute.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required.WithApiAuthRequiredPageRoute\"},{\"kind\":4194304,\"name\":\"WithApiAuthRequired\",\"url\":\"types/helpers_with_api_auth_required.WithApiAuthRequired.html\",\"classes\":\"\",\"parent\":\"helpers/with-api-auth-required\"},{\"kind\":2,\"name\":\"helpers/with-middleware-auth-required\",\"url\":\"modules/helpers_with_middleware_auth_required.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"WithMiddlewareAuthRequired\",\"url\":\"types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html\",\"classes\":\"\",\"parent\":\"helpers/with-middleware-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-middleware-auth-required.WithMiddlewareAuthRequired\"},{\"kind\":2,\"name\":\"helpers/with-page-auth-required\",\"url\":\"modules/helpers_with_page_auth_required.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GetServerSidePropsResultWithSession\",\"url\":\"types/helpers_with_page_auth_required.GetServerSidePropsResultWithSession.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":4194304,\"name\":\"PageRoute\",\"url\":\"types/helpers_with_page_auth_required.PageRoute.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.PageRoute.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.PageRoute\"},{\"kind\":4194304,\"name\":\"AppRouterPageRouteOpts\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRouteOpts\"},{\"kind\":1024,\"name\":\"params\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html#__type.params\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRouteOpts.__type\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html#__type.params.__type-1\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRouteOpts.__type.params\"},{\"kind\":1024,\"name\":\"slug\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html#__type.params.__type-1.slug\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRouteOpts.__type.params.__type\"},{\"kind\":1024,\"name\":\"searchParams\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html#__type.searchParams\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRouteOpts.__type\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRouteOpts.html#__type.searchParams.__type-2\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRouteOpts.__type.searchParams\"},{\"kind\":4194304,\"name\":\"AppRouterPageRoute\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRoute.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.AppRouterPageRoute.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.AppRouterPageRoute\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequiredPageRouterOptions\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouterOptions.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouterOptions.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredPageRouterOptions\"},{\"kind\":1024,\"name\":\"getServerSideProps\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouterOptions.html#__type.getServerSideProps\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredPageRouterOptions.__type\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouterOptions.html#__type.returnTo\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredPageRouterOptions.__type\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequiredPageRouter\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouter.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredPageRouter.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredPageRouter\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequiredAppRouterOptions\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouterOptions.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouterOptions.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredAppRouterOptions\"},{\"kind\":1024,\"name\":\"returnTo\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouterOptions.html#__type.returnTo\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredAppRouterOptions.__type\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequiredAppRouter\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouter.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequiredAppRouter.html#__type\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required.WithPageAuthRequiredAppRouter\"},{\"kind\":4194304,\"name\":\"WithPageAuthRequired\",\"url\":\"types/helpers_with_page_auth_required.WithPageAuthRequired.html\",\"classes\":\"\",\"parent\":\"helpers/with-page-auth-required\"},{\"kind\":2,\"name\":\"http/auth0-next-api-request\",\"url\":\"modules/http_auth0_next_api_request.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/http_auth0_next_api_request.default.html\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-request\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/http_auth0_next_api_request.default.html#constructor\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-request.default\"},{\"kind\":2048,\"name\":\"getUrl\",\"url\":\"classes/http_auth0_next_api_request.default.html#getUrl\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-request.default\"},{\"kind\":2048,\"name\":\"getMethod\",\"url\":\"classes/http_auth0_next_api_request.default.html#getMethod\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-request.default\"},{\"kind\":2048,\"name\":\"getBody\",\"url\":\"classes/http_auth0_next_api_request.default.html#getBody\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-request.default\"},{\"kind\":2048,\"name\":\"getCookies\",\"url\":\"classes/http_auth0_next_api_request.default.html#getCookies\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-request.default\"},{\"kind\":2,\"name\":\"http/auth0-next-api-response\",\"url\":\"modules/http_auth0_next_api_response.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/http_auth0_next_api_response.default.html\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-response\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/http_auth0_next_api_response.default.html#constructor\",\"classes\":\"tsd-is-inherited\",\"parent\":\"http/auth0-next-api-response.default\"},{\"kind\":2048,\"name\":\"redirect\",\"url\":\"classes/http_auth0_next_api_response.default.html#redirect\",\"classes\":\"\",\"parent\":\"http/auth0-next-api-response.default\"},{\"kind\":2,\"name\":\"http/auth0-next-request-cookies\",\"url\":\"modules/http_auth0_next_request_cookies.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/http_auth0_next_request_cookies.default.html\",\"classes\":\"\",\"parent\":\"http/auth0-next-request-cookies\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/http_auth0_next_request_cookies.default.html#constructor\",\"classes\":\"\",\"parent\":\"http/auth0-next-request-cookies.default\"},{\"kind\":2048,\"name\":\"getCookies\",\"url\":\"classes/http_auth0_next_request_cookies.default.html#getCookies\",\"classes\":\"\",\"parent\":\"http/auth0-next-request-cookies.default\"},{\"kind\":2,\"name\":\"http/auth0-next-request\",\"url\":\"modules/http_auth0_next_request.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/http_auth0_next_request.default.html\",\"classes\":\"\",\"parent\":\"http/auth0-next-request\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/http_auth0_next_request.default.html#constructor\",\"classes\":\"\",\"parent\":\"http/auth0-next-request.default\"},{\"kind\":2048,\"name\":\"getUrl\",\"url\":\"classes/http_auth0_next_request.default.html#getUrl\",\"classes\":\"\",\"parent\":\"http/auth0-next-request.default\"},{\"kind\":2048,\"name\":\"getMethod\",\"url\":\"classes/http_auth0_next_request.default.html#getMethod\",\"classes\":\"\",\"parent\":\"http/auth0-next-request.default\"},{\"kind\":2048,\"name\":\"getBody\",\"url\":\"classes/http_auth0_next_request.default.html#getBody\",\"classes\":\"\",\"parent\":\"http/auth0-next-request.default\"},{\"kind\":2048,\"name\":\"getCookies\",\"url\":\"classes/http_auth0_next_request.default.html#getCookies\",\"classes\":\"\",\"parent\":\"http/auth0-next-request.default\"},{\"kind\":2,\"name\":\"http/auth0-next-response-cookies\",\"url\":\"modules/http_auth0_next_response_cookies.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/http_auth0_next_response_cookies.default.html\",\"classes\":\"\",\"parent\":\"http/auth0-next-response-cookies\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/http_auth0_next_response_cookies.default.html#constructor\",\"classes\":\"\",\"parent\":\"http/auth0-next-response-cookies.default\"},{\"kind\":2048,\"name\":\"setCookie\",\"url\":\"classes/http_auth0_next_response_cookies.default.html#setCookie\",\"classes\":\"\",\"parent\":\"http/auth0-next-response-cookies.default\"},{\"kind\":2048,\"name\":\"clearCookie\",\"url\":\"classes/http_auth0_next_response_cookies.default.html#clearCookie\",\"classes\":\"\",\"parent\":\"http/auth0-next-response-cookies.default\"},{\"kind\":2,\"name\":\"http/auth0-next-response\",\"url\":\"modules/http_auth0_next_response.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/http_auth0_next_response.default.html\",\"classes\":\"\",\"parent\":\"http/auth0-next-response\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/http_auth0_next_response.default.html#constructor\",\"classes\":\"\",\"parent\":\"http/auth0-next-response.default\"},{\"kind\":2048,\"name\":\"setCookie\",\"url\":\"classes/http_auth0_next_response.default.html#setCookie\",\"classes\":\"\",\"parent\":\"http/auth0-next-response.default\"},{\"kind\":2048,\"name\":\"clearCookie\",\"url\":\"classes/http_auth0_next_response.default.html#clearCookie\",\"classes\":\"\",\"parent\":\"http/auth0-next-response.default\"},{\"kind\":2048,\"name\":\"redirect\",\"url\":\"classes/http_auth0_next_response.default.html#redirect\",\"classes\":\"\",\"parent\":\"http/auth0-next-response.default\"},{\"kind\":2,\"name\":\"http\",\"url\":\"modules/http.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"index\",\"url\":\"modules/index.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"Auth0Server\",\"url\":\"interfaces/index.Auth0Server.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":1024,\"name\":\"getSession\",\"url\":\"interfaces/index.Auth0Server.html#getSession\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"touchSession\",\"url\":\"interfaces/index.Auth0Server.html#touchSession\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"updateSession\",\"url\":\"interfaces/index.Auth0Server.html#updateSession\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"getAccessToken\",\"url\":\"interfaces/index.Auth0Server.html#getAccessToken\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleLogin\",\"url\":\"interfaces/index.Auth0Server.html#handleLogin\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleCallback\",\"url\":\"interfaces/index.Auth0Server.html#handleCallback\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleLogout\",\"url\":\"interfaces/index.Auth0Server.html#handleLogout\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleProfile\",\"url\":\"interfaces/index.Auth0Server.html#handleProfile\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"withApiAuthRequired\",\"url\":\"interfaces/index.Auth0Server.html#withApiAuthRequired\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"withPageAuthRequired\",\"url\":\"interfaces/index.Auth0Server.html#withPageAuthRequired\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":1024,\"name\":\"handleAuth\",\"url\":\"interfaces/index.Auth0Server.html#handleAuth\",\"classes\":\"\",\"parent\":\"index.Auth0Server\"},{\"kind\":4194304,\"name\":\"InitAuth0\",\"url\":\"types/index.InitAuth0.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/index.InitAuth0.html#__type\",\"classes\":\"\",\"parent\":\"index.InitAuth0\"},{\"kind\":64,\"name\":\"initAuth0\",\"url\":\"functions/index.initAuth0-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"_initAuth\",\"url\":\"functions/index._initAuth.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"getSession\",\"url\":\"functions/index.getSession-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"updateSession\",\"url\":\"functions/index.updateSession-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"getAccessToken\",\"url\":\"functions/index.getAccessToken-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"withApiAuthRequired\",\"url\":\"functions/index.withApiAuthRequired-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"withPageAuthRequired\",\"url\":\"functions/index.withPageAuthRequired-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleLogin\",\"url\":\"functions/index.handleLogin-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleLogout\",\"url\":\"functions/index.handleLogout-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleCallback\",\"url\":\"functions/index.handleCallback-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleProfile\",\"url\":\"functions/index.handleProfile-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":64,\"name\":\"handleAuth\",\"url\":\"functions/index.handleAuth-1.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":4194304,\"name\":\"SessionStore\",\"url\":\"types/index.SessionStore.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":4194304,\"name\":\"SessionStorePayload\",\"url\":\"types/index.SessionStorePayload.html\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":2,\"name\":\"session/get-access-token\",\"url\":\"modules/session_get_access_token.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"AfterRefresh\",\"url\":\"types/session_get_access_token.AfterRefresh.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":4194304,\"name\":\"AfterRefreshPageRoute\",\"url\":\"types/session_get_access_token.AfterRefreshPageRoute.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_access_token.AfterRefreshPageRoute.html#__type\",\"classes\":\"\",\"parent\":\"session/get-access-token.AfterRefreshPageRoute\"},{\"kind\":4194304,\"name\":\"AfterRefreshAppRoute\",\"url\":\"types/session_get_access_token.AfterRefreshAppRoute.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_access_token.AfterRefreshAppRoute.html#__type\",\"classes\":\"\",\"parent\":\"session/get-access-token.AfterRefreshAppRoute\"},{\"kind\":256,\"name\":\"AccessTokenRequest\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":1024,\"name\":\"scopes\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#scopes\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":1024,\"name\":\"refresh\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#refresh\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":1024,\"name\":\"afterRefresh\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#afterRefresh\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":1024,\"name\":\"authorizationParams\",\"url\":\"interfaces/session_get_access_token.AccessTokenRequest.html#authorizationParams\",\"classes\":\"\",\"parent\":\"session/get-access-token.AccessTokenRequest\"},{\"kind\":256,\"name\":\"GetAccessTokenResult\",\"url\":\"interfaces/session_get_access_token.GetAccessTokenResult.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":1024,\"name\":\"accessToken\",\"url\":\"interfaces/session_get_access_token.GetAccessTokenResult.html#accessToken\",\"classes\":\"\",\"parent\":\"session/get-access-token.GetAccessTokenResult\"},{\"kind\":4194304,\"name\":\"GetAccessToken\",\"url\":\"types/session_get_access_token.GetAccessToken.html\",\"classes\":\"\",\"parent\":\"session/get-access-token\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_access_token.GetAccessToken.html#__type\",\"classes\":\"\",\"parent\":\"session/get-access-token.GetAccessToken\"},{\"kind\":2,\"name\":\"session/get-session\",\"url\":\"modules/session_get_session.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"GetSession\",\"url\":\"types/session_get_session.GetSession.html\",\"classes\":\"\",\"parent\":\"session/get-session\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_get_session.GetSession.html#__type\",\"classes\":\"\",\"parent\":\"session/get-session.GetSession\"},{\"kind\":2,\"name\":\"session\",\"url\":\"modules/session.html\",\"classes\":\"\"},{\"kind\":2,\"name\":\"session/session\",\"url\":\"modules/session_session.html\",\"classes\":\"\"},{\"kind\":256,\"name\":\"Claims\",\"url\":\"interfaces/session_session.Claims.html\",\"classes\":\"\",\"parent\":\"session/session\"},{\"kind\":128,\"name\":\"default\",\"url\":\"classes/session_session.default.html\",\"classes\":\"\",\"parent\":\"session/session\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/session_session.default.html#constructor\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"user\",\"url\":\"classes/session_session.default.html#user\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"idToken\",\"url\":\"classes/session_session.default.html#idToken\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"accessToken\",\"url\":\"classes/session_session.default.html#accessToken\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"accessTokenScope\",\"url\":\"classes/session_session.default.html#accessTokenScope\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"accessTokenExpiresAt\",\"url\":\"classes/session_session.default.html#accessTokenExpiresAt\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":1024,\"name\":\"refreshToken\",\"url\":\"classes/session_session.default.html#refreshToken\",\"classes\":\"\",\"parent\":\"session/session.default\"},{\"kind\":2,\"name\":\"session/touch-session\",\"url\":\"modules/session_touch_session.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"TouchSession\",\"url\":\"types/session_touch_session.TouchSession.html\",\"classes\":\"\",\"parent\":\"session/touch-session\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_touch_session.TouchSession.html#__type\",\"classes\":\"\",\"parent\":\"session/touch-session.TouchSession\"},{\"kind\":2,\"name\":\"session/update-session\",\"url\":\"modules/session_update_session.html\",\"classes\":\"\"},{\"kind\":4194304,\"name\":\"UpdateSession\",\"url\":\"types/session_update_session.UpdateSession.html\",\"classes\":\"\",\"parent\":\"session/update-session\"},{\"kind\":65536,\"name\":\"__type\",\"url\":\"types/session_update_session.UpdateSession.html#__type\",\"classes\":\"\",\"parent\":\"session/update-session.UpdateSession\"},{\"kind\":2,\"name\":\"utils/errors\",\"url\":\"modules/utils_errors.html\",\"classes\":\"\"},{\"kind\":128,\"name\":\"AuthError\",\"url\":\"classes/utils_errors.AuthError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.AuthError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.AuthError.html#code\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.AuthError.html#name\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.AuthError.html#cause\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.AuthError.html#status\",\"classes\":\"\",\"parent\":\"utils/errors.AuthError\"},{\"kind\":8,\"name\":\"AccessTokenErrorCode\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":16,\"name\":\"MISSING_SESSION\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#MISSING_SESSION\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"MISSING_ACCESS_TOKEN\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#MISSING_ACCESS_TOKEN\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"MISSING_REFRESH_TOKEN\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#MISSING_REFRESH_TOKEN\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"EXPIRED_ACCESS_TOKEN\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#EXPIRED_ACCESS_TOKEN\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"INSUFFICIENT_SCOPE\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#INSUFFICIENT_SCOPE\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":16,\"name\":\"FAILED_REFRESH_GRANT\",\"url\":\"enums/utils_errors.AccessTokenErrorCode.html#FAILED_REFRESH_GRANT\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenErrorCode\"},{\"kind\":128,\"name\":\"AccessTokenError\",\"url\":\"classes/utils_errors.AccessTokenError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.AccessTokenError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.AccessTokenError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.AccessTokenError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.AccessTokenError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.AccessTokenError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.AccessTokenError\"},{\"kind\":128,\"name\":\"HandlerError\",\"url\":\"classes/utils_errors.HandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.HandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.HandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.HandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.HandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.HandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.HandlerError\"},{\"kind\":128,\"name\":\"CallbackHandlerError\",\"url\":\"classes/utils_errors.CallbackHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.CallbackHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.CallbackHandlerError\"},{\"kind\":128,\"name\":\"LoginHandlerError\",\"url\":\"classes/utils_errors.LoginHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LoginHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.LoginHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LoginHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.LoginHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.LoginHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.LoginHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LoginHandlerError\"},{\"kind\":128,\"name\":\"LogoutHandlerError\",\"url\":\"classes/utils_errors.LogoutHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.LogoutHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.LogoutHandlerError\"},{\"kind\":128,\"name\":\"ProfileHandlerError\",\"url\":\"classes/utils_errors.ProfileHandlerError.html\",\"classes\":\"\",\"parent\":\"utils/errors\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#code-1\",\"classes\":\"\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#constructor\",\"classes\":\"\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"code\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#code\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"name\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#name\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"cause\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#cause\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":1024,\"name\":\"status\",\"url\":\"classes/utils_errors.ProfileHandlerError.html#status\",\"classes\":\"tsd-is-inherited\",\"parent\":\"utils/errors.ProfileHandlerError\"},{\"kind\":2,\"name\":\"version\",\"url\":\"modules/version.html\",\"classes\":\"\"},{\"kind\":32,\"name\":\"default\",\"url\":\"variables/version.default.html\",\"classes\":\"\",\"parent\":\"version\"},{\"kind\":8388608,\"name\":\"UserProvider\",\"url\":\"modules/client.html#UserProvider\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"UserProviderProps\",\"url\":\"modules/client.html#UserProviderProps\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"UserProfile\",\"url\":\"modules/client.html#UserProfile\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"RequestError\",\"url\":\"modules/client.html#RequestError\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"useUser\",\"url\":\"modules/client.html#useUser\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequired\",\"url\":\"modules/client.html#WithPageAuthRequired\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredOptions\",\"url\":\"modules/client.html#WithPageAuthRequiredOptions\",\"classes\":\"\",\"parent\":\"client\"},{\"kind\":8388608,\"name\":\"WithMiddlewareAuthRequired\",\"url\":\"modules/edge.html#WithMiddlewareAuthRequired\",\"classes\":\"\",\"parent\":\"edge\"},{\"kind\":8388608,\"name\":\"HandleCallback\",\"url\":\"modules/handlers.html#HandleCallback\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"CallbackOptions\",\"url\":\"modules/handlers.html#CallbackOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterCallback\",\"url\":\"modules/handlers.html#AfterCallback\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterCallbackPageRoute\",\"url\":\"modules/handlers.html#AfterCallbackPageRoute\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterCallbackAppRoute\",\"url\":\"modules/handlers.html#AfterCallbackAppRoute\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleLogin\",\"url\":\"modules/handlers.html#HandleLogin\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"LoginOptions\",\"url\":\"modules/handlers.html#LoginOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"GetLoginState\",\"url\":\"modules/handlers.html#GetLoginState\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"GetLoginStatePageRoute\",\"url\":\"modules/handlers.html#GetLoginStatePageRoute\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"GetLoginStateAppRoute\",\"url\":\"modules/handlers.html#GetLoginStateAppRoute\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleLogout\",\"url\":\"modules/handlers.html#HandleLogout\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"LogoutOptions\",\"url\":\"modules/handlers.html#LogoutOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleProfile\",\"url\":\"modules/handlers.html#HandleProfile\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"ProfileOptions\",\"url\":\"modules/handlers.html#ProfileOptions\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterRefetch\",\"url\":\"modules/handlers.html#AfterRefetch\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterRefetchPageRoute\",\"url\":\"modules/handlers.html#AfterRefetchPageRoute\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AfterRefetchAppRoute\",\"url\":\"modules/handlers.html#AfterRefetchAppRoute\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"Handlers\",\"url\":\"modules/handlers.html#Handlers\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"HandleAuth\",\"url\":\"modules/handlers.html#HandleAuth\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AppRouterOnError\",\"url\":\"modules/handlers.html#AppRouterOnError\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"PageRouterOnError\",\"url\":\"modules/handlers.html#PageRouterOnError\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AppRouteHandlerFn\",\"url\":\"modules/handlers.html#AppRouteHandlerFn\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"AppRouteHandlerFnContext\",\"url\":\"modules/handlers.html#AppRouteHandlerFnContext\",\"classes\":\"\",\"parent\":\"handlers\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequired\",\"url\":\"modules/helpers.html#WithApiAuthRequired\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"AppRouteHandlerFn\",\"url\":\"modules/helpers.html#AppRouteHandlerFn\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequiredAppRoute\",\"url\":\"modules/helpers.html#WithApiAuthRequiredAppRoute\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequiredPageRoute\",\"url\":\"modules/helpers.html#WithApiAuthRequiredPageRoute\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"GetServerSidePropsResultWithSession\",\"url\":\"modules/helpers.html#GetServerSidePropsResultWithSession\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequired\",\"url\":\"modules/helpers.html#WithPageAuthRequired\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredPageRouterOptions\",\"url\":\"modules/helpers.html#WithPageAuthRequiredPageRouterOptions\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredAppRouterOptions\",\"url\":\"modules/helpers.html#WithPageAuthRequiredAppRouterOptions\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"PageRoute\",\"url\":\"modules/helpers.html#PageRoute\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"AppRouterPageRouteOpts\",\"url\":\"modules/helpers.html#AppRouterPageRouteOpts\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"AppRouterPageRoute\",\"url\":\"modules/helpers.html#AppRouterPageRoute\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredPageRouter\",\"url\":\"modules/helpers.html#WithPageAuthRequiredPageRouter\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredAppRouter\",\"url\":\"modules/helpers.html#WithPageAuthRequiredAppRouter\",\"classes\":\"\",\"parent\":\"helpers\"},{\"kind\":8388608,\"name\":\"Auth0NextApiRequest\",\"url\":\"modules/http.html#Auth0NextApiRequest\",\"classes\":\"\",\"parent\":\"http\"},{\"kind\":8388608,\"name\":\"Auth0NextApiResponse\",\"url\":\"modules/http.html#Auth0NextApiResponse\",\"classes\":\"\",\"parent\":\"http\"},{\"kind\":8388608,\"name\":\"Auth0NextRequest\",\"url\":\"modules/http.html#Auth0NextRequest\",\"classes\":\"\",\"parent\":\"http\"},{\"kind\":8388608,\"name\":\"Auth0NextResponse\",\"url\":\"modules/http.html#Auth0NextResponse\",\"classes\":\"\",\"parent\":\"http\"},{\"kind\":8388608,\"name\":\"Auth0NextRequestCookies\",\"url\":\"modules/http.html#Auth0NextRequestCookies\",\"classes\":\"\",\"parent\":\"http\"},{\"kind\":8388608,\"name\":\"Auth0NextResponseCookies\",\"url\":\"modules/http.html#Auth0NextResponseCookies\",\"classes\":\"\",\"parent\":\"http\"},{\"kind\":8388608,\"name\":\"AuthError\",\"url\":\"modules/index.html#AuthError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AccessTokenErrorCode\",\"url\":\"modules/index.html#AccessTokenErrorCode\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AccessTokenError\",\"url\":\"modules/index.html#AccessTokenError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandlerError\",\"url\":\"modules/index.html#HandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"CallbackHandlerError\",\"url\":\"modules/index.html#CallbackHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LoginHandlerError\",\"url\":\"modules/index.html#LoginHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LogoutHandlerError\",\"url\":\"modules/index.html#LogoutHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"ProfileHandlerError\",\"url\":\"modules/index.html#ProfileHandlerError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Handlers\",\"url\":\"modules/index.html#Handlers\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LoginOptions\",\"url\":\"modules/index.html#LoginOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"LogoutOptions\",\"url\":\"modules/index.html#LogoutOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetLoginState\",\"url\":\"modules/index.html#GetLoginState\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetLoginStatePageRoute\",\"url\":\"modules/index.html#GetLoginStatePageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetLoginStateAppRoute\",\"url\":\"modules/index.html#GetLoginStateAppRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"ProfileOptions\",\"url\":\"modules/index.html#ProfileOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"CallbackOptions\",\"url\":\"modules/index.html#CallbackOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterCallback\",\"url\":\"modules/index.html#AfterCallback\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterCallbackPageRoute\",\"url\":\"modules/index.html#AfterCallbackPageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterCallbackAppRoute\",\"url\":\"modules/index.html#AfterCallbackAppRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefetch\",\"url\":\"modules/index.html#AfterRefetch\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefetchPageRoute\",\"url\":\"modules/index.html#AfterRefetchPageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefetchAppRoute\",\"url\":\"modules/index.html#AfterRefetchAppRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AppRouterOnError\",\"url\":\"modules/index.html#AppRouterOnError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"PageRouterOnError\",\"url\":\"modules/index.html#PageRouterOnError\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AppRouterPageRouteOpts\",\"url\":\"modules/index.html#AppRouterPageRouteOpts\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AppRouterPageRoute\",\"url\":\"modules/index.html#AppRouterPageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredPageRouter\",\"url\":\"modules/index.html#WithPageAuthRequiredPageRouter\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredAppRouter\",\"url\":\"modules/index.html#WithPageAuthRequiredAppRouter\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetServerSidePropsResultWithSession\",\"url\":\"modules/index.html#GetServerSidePropsResultWithSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredPageRouterOptions\",\"url\":\"modules/index.html#WithPageAuthRequiredPageRouterOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequiredAppRouterOptions\",\"url\":\"modules/index.html#WithPageAuthRequiredAppRouterOptions\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"PageRoute\",\"url\":\"modules/index.html#PageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AppRouteHandlerFn\",\"url\":\"modules/index.html#AppRouteHandlerFn\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequiredAppRoute\",\"url\":\"modules/index.html#WithApiAuthRequiredAppRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequiredPageRoute\",\"url\":\"modules/index.html#WithApiAuthRequiredPageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AccessTokenRequest\",\"url\":\"modules/index.html#AccessTokenRequest\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetAccessTokenResult\",\"url\":\"modules/index.html#GetAccessTokenResult\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Claims\",\"url\":\"modules/index.html#Claims\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefresh\",\"url\":\"modules/index.html#AfterRefresh\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefreshPageRoute\",\"url\":\"modules/index.html#AfterRefreshPageRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"AfterRefreshAppRoute\",\"url\":\"modules/index.html#AfterRefreshAppRoute\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"ConfigParameters\",\"url\":\"modules/index.html#ConfigParameters\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleAuth\",\"url\":\"modules/index.html#HandleAuth\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleLogin\",\"url\":\"modules/index.html#HandleLogin\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleProfile\",\"url\":\"modules/index.html#HandleProfile\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleLogout\",\"url\":\"modules/index.html#HandleLogout\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"HandleCallback\",\"url\":\"modules/index.html#HandleCallback\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithApiAuthRequired\",\"url\":\"modules/index.html#WithApiAuthRequired\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"WithPageAuthRequired\",\"url\":\"modules/index.html#WithPageAuthRequired\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetSession\",\"url\":\"modules/index.html#GetSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"TouchSession\",\"url\":\"modules/index.html#TouchSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"UpdateSession\",\"url\":\"modules/index.html#UpdateSession\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"GetAccessToken\",\"url\":\"modules/index.html#GetAccessToken\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Session\",\"url\":\"modules/index.html#Session\",\"classes\":\"\",\"parent\":\"index\"},{\"kind\":8388608,\"name\":\"Session\",\"url\":\"modules/session.html#Session\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"Claims\",\"url\":\"modules/session.html#Claims\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"GetSession\",\"url\":\"modules/session.html#GetSession\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"GetAccessToken\",\"url\":\"modules/session.html#GetAccessToken\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"AccessTokenRequest\",\"url\":\"modules/session.html#AccessTokenRequest\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"GetAccessTokenResult\",\"url\":\"modules/session.html#GetAccessTokenResult\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"AfterRefresh\",\"url\":\"modules/session.html#AfterRefresh\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"AfterRefreshPageRoute\",\"url\":\"modules/session.html#AfterRefreshPageRoute\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"AfterRefreshAppRoute\",\"url\":\"modules/session.html#AfterRefreshAppRoute\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"TouchSession\",\"url\":\"modules/session.html#TouchSession\",\"classes\":\"\",\"parent\":\"session\"},{\"kind\":8388608,\"name\":\"UpdateSession\",\"url\":\"modules/session.html#UpdateSession\",\"classes\":\"\",\"parent\":\"session\"}],\"index\":{\"version\":\"2.3.9\",\"fields\":[\"name\",\"comment\"],\"fieldVectors\":[[\"name/0\",[0,59.657]],[\"comment/0\",[]],[\"name/1\",[1,42.834,2,34.728]],[\"comment/1\",[]],[\"name/2\",[3,54.407]],[\"comment/2\",[]],[\"name/3\",[4,59.657]],[\"comment/3\",[]],[\"name/4\",[5,59.657]],[\"comment/4\",[]],[\"name/5\",[6,40.688]],[\"comment/5\",[]],[\"name/6\",[7,59.657]],[\"comment/6\",[]],[\"name/7\",[8,59.657]],[\"comment/7\",[]],[\"name/8\",[9,59.657]],[\"comment/8\",[]],[\"name/9\",[10,59.657]],[\"comment/9\",[]],[\"name/10\",[11,59.657]],[\"comment/10\",[]],[\"name/11\",[12,59.657]],[\"comment/11\",[]],[\"name/12\",[13,22.926]],[\"comment/12\",[]],[\"name/13\",[2,48.367]],[\"comment/13\",[]],[\"name/14\",[14,59.657]],[\"comment/14\",[]],[\"name/15\",[15,59.657]],[\"comment/15\",[]],[\"name/16\",[16,59.657]],[\"comment/16\",[]],[\"name/17\",[13,22.926]],[\"comment/17\",[]],[\"name/18\",[17,54.407]],[\"comment/18\",[]],[\"name/19\",[18,35.657]],[\"comment/19\",[]],[\"name/20\",[19,41.831]],[\"comment/20\",[]],[\"name/21\",[20,54.407]],[\"comment/21\",[]],[\"name/22\",[21,54.407]],[\"comment/22\",[]],[\"name/23\",[22,54.407]],[\"comment/23\",[]],[\"name/24\",[13,22.926]],[\"comment/24\",[]],[\"name/25\",[23,40.688]],[\"comment/25\",[]],[\"name/26\",[24,27.388,25,24.978,26,22.205,27,22.205]],[\"comment/26\",[]],[\"name/27\",[28,54.407]],[\"comment/27\",[]],[\"name/28\",[29,46.305]],[\"comment/28\",[]],[\"name/29\",[30,59.657]],[\"comment/29\",[]],[\"name/30\",[13,22.926]],[\"comment/30\",[]],[\"name/31\",[31,59.657]],[\"comment/31\",[]],[\"name/32\",[13,22.926]],[\"comment/32\",[]],[\"name/33\",[32,59.657]],[\"comment/33\",[]],[\"name/34\",[2,48.367]],[\"comment/34\",[]],[\"name/35\",[33,43.117]],[\"comment/35\",[]],[\"name/36\",[13,22.926]],[\"comment/36\",[]],[\"name/37\",[34,59.657]],[\"comment/37\",[]],[\"name/38\",[35,59.657]],[\"comment/38\",[]],[\"name/39\",[36,59.657]],[\"comment/39\",[]],[\"name/40\",[37,41.831]],[\"comment/40\",[]],[\"name/41\",[38,59.657]],[\"comment/41\",[]],[\"name/42\",[39,46.305]],[\"comment/42\",[]],[\"name/43\",[40,59.657]],[\"comment/43\",[]],[\"name/44\",[41,59.657]],[\"comment/44\",[]],[\"name/45\",[42,59.657]],[\"comment/45\",[]],[\"name/46\",[43,59.657]],[\"comment/46\",[]],[\"name/47\",[44,59.657]],[\"comment/47\",[]],[\"name/48\",[45,59.657]],[\"comment/48\",[]],[\"name/49\",[46,46.305]],[\"comment/49\",[]],[\"name/50\",[13,22.926]],[\"comment/50\",[]],[\"name/51\",[47,54.407]],[\"comment/51\",[]],[\"name/52\",[48,59.657]],[\"comment/52\",[]],[\"name/53\",[49,59.657]],[\"comment/53\",[]],[\"name/54\",[50,59.657]],[\"comment/54\",[]],[\"name/55\",[51,59.657]],[\"comment/55\",[]],[\"name/56\",[52,54.407]],[\"comment/56\",[]],[\"name/57\",[13,22.926]],[\"comment/57\",[]],[\"name/58\",[53,59.657]],[\"comment/58\",[]],[\"name/59\",[54,54.407]],[\"comment/59\",[]],[\"name/60\",[55,59.657]],[\"comment/60\",[]],[\"name/61\",[56,59.657]],[\"comment/61\",[]],[\"name/62\",[57,59.657]],[\"comment/62\",[]],[\"name/63\",[6,40.688]],[\"comment/63\",[]],[\"name/64\",[58,59.657]],[\"comment/64\",[]],[\"name/65\",[59,59.657]],[\"comment/65\",[]],[\"name/66\",[13,22.926]],[\"comment/66\",[]],[\"name/67\",[60,59.657]],[\"comment/67\",[]],[\"name/68\",[61,59.657]],[\"comment/68\",[]],[\"name/69\",[62,59.657]],[\"comment/69\",[]],[\"name/70\",[63,59.657]],[\"comment/70\",[]],[\"name/71\",[64,59.657]],[\"comment/71\",[]],[\"name/72\",[65,59.657]],[\"comment/72\",[]],[\"name/73\",[66,59.657]],[\"comment/73\",[]],[\"name/74\",[67,59.657]],[\"comment/74\",[]],[\"name/75\",[68,59.657]],[\"comment/75\",[]],[\"name/76\",[69,59.657]],[\"comment/76\",[]],[\"name/77\",[70,59.657]],[\"comment/77\",[]],[\"name/78\",[71,59.657]],[\"comment/78\",[]],[\"name/79\",[72,59.657]],[\"comment/79\",[]],[\"name/80\",[73,59.657]],[\"comment/80\",[]],[\"name/81\",[74,59.657]],[\"comment/81\",[]],[\"name/82\",[75,59.657]],[\"comment/82\",[]],[\"name/83\",[76,59.657]],[\"comment/83\",[]],[\"name/84\",[77,59.657]],[\"comment/84\",[]],[\"name/85\",[78,50.95]],[\"comment/85\",[]],[\"name/86\",[52,54.407]],[\"comment/86\",[]],[\"name/87\",[13,22.926]],[\"comment/87\",[]],[\"name/88\",[54,54.407]],[\"comment/88\",[]],[\"name/89\",[79,59.657]],[\"comment/89\",[]],[\"name/90\",[37,41.831]],[\"comment/90\",[]],[\"name/91\",[47,54.407]],[\"comment/91\",[]],[\"name/92\",[80,54.407]],[\"comment/92\",[]],[\"name/93\",[81,59.657]],[\"comment/93\",[]],[\"name/94\",[82,59.657]],[\"comment/94\",[]],[\"name/95\",[13,22.926]],[\"comment/95\",[]],[\"name/96\",[83,48.367]],[\"comment/96\",[]],[\"name/97\",[84,41.831]],[\"comment/97\",[]],[\"name/98\",[84,41.831]],[\"comment/98\",[]],[\"name/99\",[13,22.926]],[\"comment/99\",[]],[\"name/100\",[85,48.367]],[\"comment/100\",[]],[\"name/101\",[13,22.926]],[\"comment/101\",[]],[\"name/102\",[85,48.367]],[\"comment/102\",[]],[\"name/103\",[84,41.831]],[\"comment/103\",[]],[\"name/104\",[83,48.367]],[\"comment/104\",[]],[\"name/105\",[86,59.657]],[\"comment/105\",[]],[\"name/106\",[87,48.367]],[\"comment/106\",[]],[\"name/107\",[88,46.305]],[\"comment/107\",[]],[\"name/108\",[13,22.926]],[\"comment/108\",[]],[\"name/109\",[89,50.95]],[\"comment/109\",[]],[\"name/110\",[13,22.926]],[\"comment/110\",[]],[\"name/111\",[90,50.95]],[\"comment/111\",[]],[\"name/112\",[13,22.926]],[\"comment/112\",[]],[\"name/113\",[91,59.657]],[\"comment/113\",[]],[\"name/114\",[92,48.367]],[\"comment/114\",[]],[\"name/115\",[93,50.95]],[\"comment/115\",[]],[\"name/116\",[13,22.926]],[\"comment/116\",[]],[\"name/117\",[94,50.95]],[\"comment/117\",[]],[\"name/118\",[13,22.926]],[\"comment/118\",[]],[\"name/119\",[95,50.95]],[\"comment/119\",[]],[\"name/120\",[92,48.367]],[\"comment/120\",[]],[\"name/121\",[96,59.657]],[\"comment/121\",[]],[\"name/122\",[78,50.95]],[\"comment/122\",[]],[\"name/123\",[39,46.305]],[\"comment/123\",[]],[\"name/124\",[97,59.657]],[\"comment/124\",[]],[\"name/125\",[98,46.305]],[\"comment/125\",[]],[\"name/126\",[99,59.657]],[\"comment/126\",[]],[\"name/127\",[87,48.367]],[\"comment/127\",[]],[\"name/128\",[100,59.657]],[\"comment/128\",[]],[\"name/129\",[46,46.305]],[\"comment/129\",[]],[\"name/130\",[101,50.95]],[\"comment/130\",[]],[\"name/131\",[13,22.926]],[\"comment/131\",[]],[\"name/132\",[13,22.926]],[\"comment/132\",[]],[\"name/133\",[102,50.95]],[\"comment/133\",[]],[\"name/134\",[13,22.926]],[\"comment/134\",[]],[\"name/135\",[13,22.926]],[\"comment/135\",[]],[\"name/136\",[39,46.305]],[\"comment/136\",[]],[\"name/137\",[103,59.657]],[\"comment/137\",[]],[\"name/138\",[104,59.657]],[\"comment/138\",[]],[\"name/139\",[105,59.657]],[\"comment/139\",[]],[\"name/140\",[78,50.95]],[\"comment/140\",[]],[\"name/141\",[106,59.657]],[\"comment/141\",[]],[\"name/142\",[107,50.95]],[\"comment/142\",[]],[\"name/143\",[39,46.305]],[\"comment/143\",[]],[\"name/144\",[29,46.305]],[\"comment/144\",[]],[\"name/145\",[46,46.305]],[\"comment/145\",[]],[\"name/146\",[108,59.657]],[\"comment/146\",[]],[\"name/147\",[109,46.305]],[\"comment/147\",[]],[\"name/148\",[110,59.657]],[\"comment/148\",[]],[\"name/149\",[111,59.657]],[\"comment/149\",[]],[\"name/150\",[112,50.95]],[\"comment/150\",[]],[\"name/151\",[29,46.305]],[\"comment/151\",[]],[\"name/152\",[113,59.657]],[\"comment/152\",[]],[\"name/153\",[13,22.926]],[\"comment/153\",[]],[\"name/154\",[114,59.657]],[\"comment/154\",[]],[\"name/155\",[115,46.305]],[\"comment/155\",[]],[\"name/156\",[116,59.657]],[\"comment/156\",[]],[\"name/157\",[117,59.657]],[\"comment/157\",[]],[\"name/158\",[118,48.367]],[\"comment/158\",[]],[\"name/159\",[119,50.95]],[\"comment/159\",[]],[\"name/160\",[13,22.926]],[\"comment/160\",[]],[\"name/161\",[120,50.95]],[\"comment/161\",[]],[\"name/162\",[13,22.926]],[\"comment/162\",[]],[\"name/163\",[121,50.95]],[\"comment/163\",[]],[\"name/164\",[13,22.926]],[\"comment/164\",[]],[\"name/165\",[122,59.657]],[\"comment/165\",[]],[\"name/166\",[118,48.367]],[\"comment/166\",[]],[\"name/167\",[123,59.657]],[\"comment/167\",[]],[\"name/168\",[124,46.305]],[\"comment/168\",[]],[\"name/169\",[125,59.657]],[\"comment/169\",[]],[\"name/170\",[126,42.834,127,39.065]],[\"comment/170\",[]],[\"name/171\",[128,50.95]],[\"comment/171\",[]],[\"name/172\",[13,22.926]],[\"comment/172\",[]],[\"name/173\",[129,50.95]],[\"comment/173\",[]],[\"name/174\",[130,46.305]],[\"comment/174\",[]],[\"name/175\",[13,22.926]],[\"comment/175\",[]],[\"name/176\",[131,59.657]],[\"comment/176\",[]],[\"name/177\",[13,22.926]],[\"comment/177\",[]],[\"name/178\",[132,59.657]],[\"comment/178\",[]],[\"name/179\",[13,22.926]],[\"comment/179\",[]],[\"name/180\",[133,59.657]],[\"comment/180\",[]],[\"name/181\",[134,59.657]],[\"comment/181\",[]],[\"name/182\",[13,22.926]],[\"comment/182\",[]],[\"name/183\",[135,59.657]],[\"comment/183\",[]],[\"name/184\",[13,22.926]],[\"comment/184\",[]],[\"name/185\",[127,54.407]],[\"comment/185\",[]],[\"name/186\",[136,59.657]],[\"comment/186\",[]],[\"name/187\",[137,59.657]],[\"comment/187\",[]],[\"name/188\",[138,59.657]],[\"comment/188\",[]],[\"name/189\",[26,22.205,27,22.205,139,23.39,140,23.39]],[\"comment/189\",[]],[\"name/190\",[128,50.95]],[\"comment/190\",[]],[\"name/191\",[13,22.926]],[\"comment/191\",[]],[\"name/192\",[129,50.95]],[\"comment/192\",[]],[\"name/193\",[130,46.305]],[\"comment/193\",[]],[\"name/194\",[13,22.926]],[\"comment/194\",[]],[\"name/195\",[141,50.95]],[\"comment/195\",[]],[\"name/196\",[13,22.926]],[\"comment/196\",[]],[\"name/197\",[142,50.95]],[\"comment/197\",[]],[\"name/198\",[13,22.926]],[\"comment/198\",[]],[\"name/199\",[143,46.305]],[\"comment/199\",[]],[\"name/200\",[26,22.205,27,22.205,139,23.39,144,27.388]],[\"comment/200\",[]],[\"name/201\",[83,48.367]],[\"comment/201\",[]],[\"name/202\",[13,22.926]],[\"comment/202\",[]],[\"name/203\",[25,24.978,26,22.205,27,22.205,139,23.39]],[\"comment/203\",[]],[\"name/204\",[145,50.95]],[\"comment/204\",[]],[\"name/205\",[146,50.95]],[\"comment/205\",[]],[\"name/206\",[13,22.926]],[\"comment/206\",[]],[\"name/207\",[147,50.95]],[\"comment/207\",[]],[\"name/208\",[13,22.926]],[\"comment/208\",[]],[\"name/209\",[129,50.95]],[\"comment/209\",[]],[\"name/210\",[13,22.926]],[\"comment/210\",[]],[\"name/211\",[148,59.657]],[\"comment/211\",[]],[\"name/212\",[149,59.657]],[\"comment/212\",[]],[\"name/213\",[13,22.926]],[\"comment/213\",[]],[\"name/214\",[150,50.95]],[\"comment/214\",[]],[\"name/215\",[13,22.926]],[\"comment/215\",[]],[\"name/216\",[151,50.95]],[\"comment/216\",[]],[\"name/217\",[13,22.926]],[\"comment/217\",[]],[\"name/218\",[152,59.657]],[\"comment/218\",[]],[\"name/219\",[29,46.305]],[\"comment/219\",[]],[\"name/220\",[153,50.95]],[\"comment/220\",[]],[\"name/221\",[13,22.926]],[\"comment/221\",[]],[\"name/222\",[154,50.95]],[\"comment/222\",[]],[\"name/223\",[13,22.926]],[\"comment/223\",[]],[\"name/224\",[29,46.305]],[\"comment/224\",[]],[\"name/225\",[155,50.95]],[\"comment/225\",[]],[\"name/226\",[13,22.926]],[\"comment/226\",[]],[\"name/227\",[33,43.117]],[\"comment/227\",[]],[\"name/228\",[140,23.39,156,20.47,157,20.47,158,23.39]],[\"comment/228\",[]],[\"name/229\",[23,40.688]],[\"comment/229\",[]],[\"name/230\",[18,35.657]],[\"comment/230\",[]],[\"name/231\",[159,54.407]],[\"comment/231\",[]],[\"name/232\",[160,54.407]],[\"comment/232\",[]],[\"name/233\",[161,54.407]],[\"comment/233\",[]],[\"name/234\",[162,50.95]],[\"comment/234\",[]],[\"name/235\",[140,23.39,156,20.47,157,20.47,163,23.39]],[\"comment/235\",[]],[\"name/236\",[23,40.688]],[\"comment/236\",[]],[\"name/237\",[18,35.657]],[\"comment/237\",[]],[\"name/238\",[164,54.407]],[\"comment/238\",[]],[\"name/239\",[156,20.47,157,20.47,158,23.39,165,24.978]],[\"comment/239\",[]],[\"name/240\",[23,40.688]],[\"comment/240\",[]],[\"name/241\",[18,35.657]],[\"comment/241\",[]],[\"name/242\",[162,50.95]],[\"comment/242\",[]],[\"name/243\",[156,24.972,157,24.972,158,28.535]],[\"comment/243\",[]],[\"name/244\",[23,40.688]],[\"comment/244\",[]],[\"name/245\",[18,35.657]],[\"comment/245\",[]],[\"name/246\",[159,54.407]],[\"comment/246\",[]],[\"name/247\",[160,54.407]],[\"comment/247\",[]],[\"name/248\",[161,54.407]],[\"comment/248\",[]],[\"name/249\",[162,50.95]],[\"comment/249\",[]],[\"name/250\",[156,20.47,157,20.47,163,23.39,165,24.978]],[\"comment/250\",[]],[\"name/251\",[23,40.688]],[\"comment/251\",[]],[\"name/252\",[18,35.657]],[\"comment/252\",[]],[\"name/253\",[166,54.407]],[\"comment/253\",[]],[\"name/254\",[167,54.407]],[\"comment/254\",[]],[\"name/255\",[156,24.972,157,24.972,163,28.535]],[\"comment/255\",[]],[\"name/256\",[23,40.688]],[\"comment/256\",[]],[\"name/257\",[18,35.657]],[\"comment/257\",[]],[\"name/258\",[166,54.407]],[\"comment/258\",[]],[\"name/259\",[167,54.407]],[\"comment/259\",[]],[\"name/260\",[164,54.407]],[\"comment/260\",[]],[\"name/261\",[168,59.657]],[\"comment/261\",[]],[\"name/262\",[169,59.657]],[\"comment/262\",[]],[\"name/263\",[170,59.657]],[\"comment/263\",[]],[\"name/264\",[84,41.831]],[\"comment/264\",[]],[\"name/265\",[171,48.367]],[\"comment/265\",[]],[\"name/266\",[172,46.305]],[\"comment/266\",[]],[\"name/267\",[173,46.305]],[\"comment/267\",[]],[\"name/268\",[109,46.305]],[\"comment/268\",[]],[\"name/269\",[98,46.305]],[\"comment/269\",[]],[\"name/270\",[115,46.305]],[\"comment/270\",[]],[\"name/271\",[124,46.305]],[\"comment/271\",[]],[\"name/272\",[143,46.305]],[\"comment/272\",[]],[\"name/273\",[33,43.117]],[\"comment/273\",[]],[\"name/274\",[88,46.305]],[\"comment/274\",[]],[\"name/275\",[85,48.367]],[\"comment/275\",[]],[\"name/276\",[13,22.926]],[\"comment/276\",[]],[\"name/277\",[85,48.367]],[\"comment/277\",[]],[\"name/278\",[174,59.657]],[\"comment/278\",[]],[\"name/279\",[84,41.831]],[\"comment/279\",[]],[\"name/280\",[172,46.305]],[\"comment/280\",[]],[\"name/281\",[173,46.305]],[\"comment/281\",[]],[\"name/282\",[143,46.305]],[\"comment/282\",[]],[\"name/283\",[33,43.117]],[\"comment/283\",[]],[\"name/284\",[109,46.305]],[\"comment/284\",[]],[\"name/285\",[115,46.305]],[\"comment/285\",[]],[\"name/286\",[98,46.305]],[\"comment/286\",[]],[\"name/287\",[124,46.305]],[\"comment/287\",[]],[\"name/288\",[88,46.305]],[\"comment/288\",[]],[\"name/289\",[175,59.657]],[\"comment/289\",[]],[\"name/290\",[176,59.657]],[\"comment/290\",[]],[\"name/291\",[177,30.472,178,33.412,179,33.412]],[\"comment/291\",[]],[\"name/292\",[180,48.367]],[\"comment/292\",[]],[\"name/293\",[181,50.95]],[\"comment/293\",[]],[\"name/294\",[13,22.926]],[\"comment/294\",[]],[\"name/295\",[182,50.95]],[\"comment/295\",[]],[\"name/296\",[13,22.926]],[\"comment/296\",[]],[\"name/297\",[183,50.95]],[\"comment/297\",[]],[\"name/298\",[184,59.657]],[\"comment/298\",[]],[\"name/299\",[185,59.657]],[\"comment/299\",[]],[\"name/300\",[180,48.367]],[\"comment/300\",[]],[\"name/301\",[39,46.305]],[\"comment/301\",[]],[\"name/302\",[186,50.95]],[\"comment/302\",[]],[\"name/303\",[187,54.407]],[\"comment/303\",[]],[\"name/304\",[173,46.305]],[\"comment/304\",[]],[\"name/305\",[13,22.926]],[\"comment/305\",[]],[\"name/306\",[37,30.035,177,39.065]],[\"comment/306\",[]],[\"name/307\",[84,41.831]],[\"comment/307\",[]],[\"name/308\",[13,22.926]],[\"comment/308\",[]],[\"name/309\",[37,41.831]],[\"comment/309\",[]],[\"name/310\",[188,59.657]],[\"comment/310\",[]],[\"name/311\",[189,50.95]],[\"comment/311\",[]],[\"name/312\",[23,40.688]],[\"comment/312\",[]],[\"name/313\",[18,35.657]],[\"comment/313\",[]],[\"name/314\",[2,48.367]],[\"comment/314\",[]],[\"name/315\",[190,59.657]],[\"comment/315\",[]],[\"name/316\",[187,54.407]],[\"comment/316\",[]],[\"name/317\",[191,59.657]],[\"comment/317\",[]],[\"name/318\",[192,59.657]],[\"comment/318\",[]],[\"name/319\",[193,59.657]],[\"comment/319\",[]],[\"name/320\",[37,30.035,194,42.834]],[\"comment/320\",[]],[\"name/321\",[171,48.367]],[\"comment/321\",[]],[\"name/322\",[13,22.926]],[\"comment/322\",[]],[\"name/323\",[37,30.035,195,42.834]],[\"comment/323\",[]],[\"name/324\",[172,46.305]],[\"comment/324\",[]],[\"name/325\",[13,22.926]],[\"comment/325\",[]],[\"name/326\",[196,59.657]],[\"comment/326\",[]],[\"name/327\",[197,54.407]],[\"comment/327\",[]],[\"name/328\",[18,35.657]],[\"comment/328\",[]],[\"name/329\",[198,38.725]],[\"comment/329\",[]],[\"name/330\",[6,40.688]],[\"comment/330\",[]],[\"name/331\",[199,43.117]],[\"comment/331\",[]],[\"name/332\",[19,41.831]],[\"comment/332\",[]],[\"name/333\",[200,54.407]],[\"comment/333\",[]],[\"name/334\",[201,59.657]],[\"comment/334\",[]],[\"name/335\",[202,59.657]],[\"comment/335\",[]],[\"name/336\",[203,59.657]],[\"comment/336\",[]],[\"name/337\",[204,59.657]],[\"comment/337\",[]],[\"name/338\",[205,59.657]],[\"comment/338\",[]],[\"name/339\",[206,59.657]],[\"comment/339\",[]],[\"name/340\",[207,54.407]],[\"comment/340\",[]],[\"name/341\",[18,35.657]],[\"comment/341\",[]],[\"name/342\",[198,38.725]],[\"comment/342\",[]],[\"name/343\",[6,40.688]],[\"comment/343\",[]],[\"name/344\",[199,43.117]],[\"comment/344\",[]],[\"name/345\",[19,41.831]],[\"comment/345\",[]],[\"name/346\",[208,54.407]],[\"comment/346\",[]],[\"name/347\",[18,35.657]],[\"comment/347\",[]],[\"name/348\",[198,38.725]],[\"comment/348\",[]],[\"name/349\",[6,40.688]],[\"comment/349\",[]],[\"name/350\",[199,43.117]],[\"comment/350\",[]],[\"name/351\",[19,41.831]],[\"comment/351\",[]],[\"name/352\",[209,54.407]],[\"comment/352\",[]],[\"name/353\",[198,38.725]],[\"comment/353\",[]],[\"name/354\",[18,35.657]],[\"comment/354\",[]],[\"name/355\",[198,38.725]],[\"comment/355\",[]],[\"name/356\",[6,40.688]],[\"comment/356\",[]],[\"name/357\",[199,43.117]],[\"comment/357\",[]],[\"name/358\",[19,41.831]],[\"comment/358\",[]],[\"name/359\",[210,54.407]],[\"comment/359\",[]],[\"name/360\",[198,38.725]],[\"comment/360\",[]],[\"name/361\",[18,35.657]],[\"comment/361\",[]],[\"name/362\",[198,38.725]],[\"comment/362\",[]],[\"name/363\",[6,40.688]],[\"comment/363\",[]],[\"name/364\",[199,43.117]],[\"comment/364\",[]],[\"name/365\",[19,41.831]],[\"comment/365\",[]],[\"name/366\",[211,54.407]],[\"comment/366\",[]],[\"name/367\",[198,38.725]],[\"comment/367\",[]],[\"name/368\",[18,35.657]],[\"comment/368\",[]],[\"name/369\",[198,38.725]],[\"comment/369\",[]],[\"name/370\",[6,40.688]],[\"comment/370\",[]],[\"name/371\",[199,43.117]],[\"comment/371\",[]],[\"name/372\",[19,41.831]],[\"comment/372\",[]],[\"name/373\",[212,54.407]],[\"comment/373\",[]],[\"name/374\",[198,38.725]],[\"comment/374\",[]],[\"name/375\",[18,35.657]],[\"comment/375\",[]],[\"name/376\",[198,38.725]],[\"comment/376\",[]],[\"name/377\",[6,40.688]],[\"comment/377\",[]],[\"name/378\",[199,43.117]],[\"comment/378\",[]],[\"name/379\",[19,41.831]],[\"comment/379\",[]],[\"name/380\",[213,59.657]],[\"comment/380\",[]],[\"name/381\",[23,40.688]],[\"comment/381\",[]],[\"name/382\",[22,54.407]],[\"comment/382\",[]],[\"name/383\",[20,54.407]],[\"comment/383\",[]],[\"name/384\",[3,54.407]],[\"comment/384\",[]],[\"name/385\",[17,54.407]],[\"comment/385\",[]],[\"name/386\",[21,54.407]],[\"comment/386\",[]],[\"name/387\",[33,43.117]],[\"comment/387\",[]],[\"name/388\",[28,54.407]],[\"comment/388\",[]],[\"name/389\",[83,48.367]],[\"comment/389\",[]],[\"name/390\",[98,46.305]],[\"comment/390\",[]],[\"name/391\",[95,50.95]],[\"comment/391\",[]],[\"name/392\",[92,48.367]],[\"comment/392\",[]],[\"name/393\",[93,50.95]],[\"comment/393\",[]],[\"name/394\",[94,50.95]],[\"comment/394\",[]],[\"name/395\",[109,46.305]],[\"comment/395\",[]],[\"name/396\",[107,50.95]],[\"comment/396\",[]],[\"name/397\",[46,46.305]],[\"comment/397\",[]],[\"name/398\",[101,50.95]],[\"comment/398\",[]],[\"name/399\",[102,50.95]],[\"comment/399\",[]],[\"name/400\",[115,46.305]],[\"comment/400\",[]],[\"name/401\",[112,50.95]],[\"comment/401\",[]],[\"name/402\",[124,46.305]],[\"comment/402\",[]],[\"name/403\",[121,50.95]],[\"comment/403\",[]],[\"name/404\",[118,48.367]],[\"comment/404\",[]],[\"name/405\",[119,50.95]],[\"comment/405\",[]],[\"name/406\",[120,50.95]],[\"comment/406\",[]],[\"name/407\",[87,48.367]],[\"comment/407\",[]],[\"name/408\",[88,46.305]],[\"comment/408\",[]],[\"name/409\",[90,50.95]],[\"comment/409\",[]],[\"name/410\",[89,50.95]],[\"comment/410\",[]],[\"name/411\",[130,46.305]],[\"comment/411\",[]],[\"name/412\",[128,50.95]],[\"comment/412\",[]],[\"name/413\",[143,46.305]],[\"comment/413\",[]],[\"name/414\",[130,46.305]],[\"comment/414\",[]],[\"name/415\",[141,50.95]],[\"comment/415\",[]],[\"name/416\",[142,50.95]],[\"comment/416\",[]],[\"name/417\",[145,50.95]],[\"comment/417\",[]],[\"name/418\",[33,43.117]],[\"comment/418\",[]],[\"name/419\",[151,50.95]],[\"comment/419\",[]],[\"name/420\",[154,50.95]],[\"comment/420\",[]],[\"name/421\",[146,50.95]],[\"comment/421\",[]],[\"name/422\",[147,50.95]],[\"comment/422\",[]],[\"name/423\",[150,50.95]],[\"comment/423\",[]],[\"name/424\",[153,50.95]],[\"comment/424\",[]],[\"name/425\",[155,50.95]],[\"comment/425\",[]],[\"name/426\",[214,59.657]],[\"comment/426\",[]],[\"name/427\",[215,59.657]],[\"comment/427\",[]],[\"name/428\",[216,59.657]],[\"comment/428\",[]],[\"name/429\",[217,59.657]],[\"comment/429\",[]],[\"name/430\",[218,59.657]],[\"comment/430\",[]],[\"name/431\",[219,59.657]],[\"comment/431\",[]],[\"name/432\",[197,54.407]],[\"comment/432\",[]],[\"name/433\",[200,54.407]],[\"comment/433\",[]],[\"name/434\",[207,54.407]],[\"comment/434\",[]],[\"name/435\",[208,54.407]],[\"comment/435\",[]],[\"name/436\",[209,54.407]],[\"comment/436\",[]],[\"name/437\",[210,54.407]],[\"comment/437\",[]],[\"name/438\",[211,54.407]],[\"comment/438\",[]],[\"name/439\",[212,54.407]],[\"comment/439\",[]],[\"name/440\",[87,48.367]],[\"comment/440\",[]],[\"name/441\",[107,50.95]],[\"comment/441\",[]],[\"name/442\",[112,50.95]],[\"comment/442\",[]],[\"name/443\",[46,46.305]],[\"comment/443\",[]],[\"name/444\",[101,50.95]],[\"comment/444\",[]],[\"name/445\",[102,50.95]],[\"comment/445\",[]],[\"name/446\",[121,50.95]],[\"comment/446\",[]],[\"name/447\",[95,50.95]],[\"comment/447\",[]],[\"name/448\",[92,48.367]],[\"comment/448\",[]],[\"name/449\",[93,50.95]],[\"comment/449\",[]],[\"name/450\",[94,50.95]],[\"comment/450\",[]],[\"name/451\",[118,48.367]],[\"comment/451\",[]],[\"name/452\",[119,50.95]],[\"comment/452\",[]],[\"name/453\",[120,50.95]],[\"comment/453\",[]],[\"name/454\",[90,50.95]],[\"comment/454\",[]],[\"name/455\",[89,50.95]],[\"comment/455\",[]],[\"name/456\",[147,50.95]],[\"comment/456\",[]],[\"name/457\",[150,50.95]],[\"comment/457\",[]],[\"name/458\",[153,50.95]],[\"comment/458\",[]],[\"name/459\",[155,50.95]],[\"comment/459\",[]],[\"name/460\",[145,50.95]],[\"comment/460\",[]],[\"name/461\",[151,50.95]],[\"comment/461\",[]],[\"name/462\",[154,50.95]],[\"comment/462\",[]],[\"name/463\",[146,50.95]],[\"comment/463\",[]],[\"name/464\",[130,46.305]],[\"comment/464\",[]],[\"name/465\",[141,50.95]],[\"comment/465\",[]],[\"name/466\",[142,50.95]],[\"comment/466\",[]],[\"name/467\",[183,50.95]],[\"comment/467\",[]],[\"name/468\",[186,50.95]],[\"comment/468\",[]],[\"name/469\",[189,50.95]],[\"comment/469\",[]],[\"name/470\",[180,48.367]],[\"comment/470\",[]],[\"name/471\",[181,50.95]],[\"comment/471\",[]],[\"name/472\",[182,50.95]],[\"comment/472\",[]],[\"name/473\",[80,54.407]],[\"comment/473\",[]],[\"name/474\",[88,46.305]],[\"comment/474\",[]],[\"name/475\",[109,46.305]],[\"comment/475\",[]],[\"name/476\",[124,46.305]],[\"comment/476\",[]],[\"name/477\",[115,46.305]],[\"comment/477\",[]],[\"name/478\",[98,46.305]],[\"comment/478\",[]],[\"name/479\",[143,46.305]],[\"comment/479\",[]],[\"name/480\",[33,43.117]],[\"comment/480\",[]],[\"name/481\",[84,41.831]],[\"comment/481\",[]],[\"name/482\",[171,48.367]],[\"comment/482\",[]],[\"name/483\",[172,46.305]],[\"comment/483\",[]],[\"name/484\",[173,46.305]],[\"comment/484\",[]],[\"name/485\",[37,41.831]],[\"comment/485\",[]],[\"name/486\",[37,41.831]],[\"comment/486\",[]],[\"name/487\",[189,50.95]],[\"comment/487\",[]],[\"name/488\",[84,41.831]],[\"comment/488\",[]],[\"name/489\",[173,46.305]],[\"comment/489\",[]],[\"name/490\",[183,50.95]],[\"comment/490\",[]],[\"name/491\",[186,50.95]],[\"comment/491\",[]],[\"name/492\",[180,48.367]],[\"comment/492\",[]],[\"name/493\",[181,50.95]],[\"comment/493\",[]],[\"name/494\",[182,50.95]],[\"comment/494\",[]],[\"name/495\",[171,48.367]],[\"comment/495\",[]],[\"name/496\",[172,46.305]],[\"comment/496\",[]]],\"invertedIndex\":[[\"__type\",{\"_index\":13,\"name\":{\"12\":{},\"17\":{},\"24\":{},\"30\":{},\"32\":{},\"36\":{},\"50\":{},\"57\":{},\"66\":{},\"87\":{},\"95\":{},\"99\":{},\"101\":{},\"108\":{},\"110\":{},\"112\":{},\"116\":{},\"118\":{},\"131\":{},\"132\":{},\"134\":{},\"135\":{},\"153\":{},\"160\":{},\"162\":{},\"164\":{},\"172\":{},\"175\":{},\"177\":{},\"179\":{},\"182\":{},\"184\":{},\"191\":{},\"194\":{},\"196\":{},\"198\":{},\"202\":{},\"206\":{},\"208\":{},\"210\":{},\"213\":{},\"215\":{},\"217\":{},\"221\":{},\"223\":{},\"226\":{},\"276\":{},\"294\":{},\"296\":{},\"305\":{},\"308\":{},\"322\":{},\"325\":{}},\"comment\":{}}],[\"_initauth\",{\"_index\":174,\"name\":{\"278\":{}},\"comment\":{}}],[\"absoluteduration\",{\"_index\":62,\"name\":{\"69\":{}},\"comment\":{}}],[\"access\",{\"_index\":178,\"name\":{\"291\":{}},\"comment\":{}}],[\"accesstoken\",{\"_index\":187,\"name\":{\"303\":{},\"316\":{}},\"comment\":{}}],[\"accesstokenerror\",{\"_index\":207,\"name\":{\"340\":{},\"434\":{}},\"comment\":{}}],[\"accesstokenerrorcode\",{\"_index\":200,\"name\":{\"333\":{},\"433\":{}},\"comment\":{}}],[\"accesstokenexpiresat\",{\"_index\":192,\"name\":{\"318\":{}},\"comment\":{}}],[\"accesstokenrequest\",{\"_index\":183,\"name\":{\"297\":{},\"467\":{},\"490\":{}},\"comment\":{}}],[\"accesstokenscope\",{\"_index\":191,\"name\":{\"317\":{}},\"comment\":{}}],[\"aftercallback\",{\"_index\":92,\"name\":{\"114\":{},\"120\":{},\"392\":{},\"448\":{}},\"comment\":{}}],[\"aftercallbackapproute\",{\"_index\":94,\"name\":{\"117\":{},\"394\":{},\"450\":{}},\"comment\":{}}],[\"aftercallbackpageroute\",{\"_index\":93,\"name\":{\"115\":{},\"393\":{},\"449\":{}},\"comment\":{}}],[\"afterrefetch\",{\"_index\":118,\"name\":{\"158\":{},\"166\":{},\"404\":{},\"451\":{}},\"comment\":{}}],[\"afterrefetchapproute\",{\"_index\":120,\"name\":{\"161\":{},\"406\":{},\"453\":{}},\"comment\":{}}],[\"afterrefetchpageroute\",{\"_index\":119,\"name\":{\"159\":{},\"405\":{},\"452\":{}},\"comment\":{}}],[\"afterrefresh\",{\"_index\":180,\"name\":{\"292\":{},\"300\":{},\"470\":{},\"492\":{}},\"comment\":{}}],[\"afterrefreshapproute\",{\"_index\":182,\"name\":{\"295\":{},\"472\":{},\"494\":{}},\"comment\":{}}],[\"afterrefreshpageroute\",{\"_index\":181,\"name\":{\"293\":{},\"471\":{},\"493\":{}},\"comment\":{}}],[\"api\",{\"_index\":140,\"name\":{\"189\":{},\"228\":{},\"235\":{}},\"comment\":{}}],[\"approutehandlerfn\",{\"_index\":130,\"name\":{\"174\":{},\"193\":{},\"411\":{},\"414\":{},\"464\":{}},\"comment\":{}}],[\"approutehandlerfncontext\",{\"_index\":128,\"name\":{\"171\":{},\"190\":{},\"412\":{}},\"comment\":{}}],[\"approuteronerror\",{\"_index\":90,\"name\":{\"111\":{},\"409\":{},\"454\":{}},\"comment\":{}}],[\"approuterpageroute\",{\"_index\":150,\"name\":{\"214\":{},\"423\":{},\"457\":{}},\"comment\":{}}],[\"approuterpagerouteopts\",{\"_index\":147,\"name\":{\"207\":{},\"422\":{},\"456\":{}},\"comment\":{}}],[\"auth\",{\"_index\":26,\"name\":{\"26\":{},\"189\":{},\"200\":{},\"203\":{}},\"comment\":{}}],[\"auth0edge\",{\"_index\":82,\"name\":{\"94\":{}},\"comment\":{}}],[\"auth0logout\",{\"_index\":38,\"name\":{\"41\":{}},\"comment\":{}}],[\"auth0nextapirequest\",{\"_index\":214,\"name\":{\"426\":{}},\"comment\":{}}],[\"auth0nextapiresponse\",{\"_index\":215,\"name\":{\"427\":{}},\"comment\":{}}],[\"auth0nextrequest\",{\"_index\":216,\"name\":{\"428\":{}},\"comment\":{}}],[\"auth0nextrequestcookies\",{\"_index\":218,\"name\":{\"430\":{}},\"comment\":{}}],[\"auth0nextresponse\",{\"_index\":217,\"name\":{\"429\":{}},\"comment\":{}}],[\"auth0nextresponsecookies\",{\"_index\":219,\"name\":{\"431\":{}},\"comment\":{}}],[\"auth0server\",{\"_index\":170,\"name\":{\"263\":{}},\"comment\":{}}],[\"autherror\",{\"_index\":197,\"name\":{\"327\":{},\"432\":{}},\"comment\":{}}],[\"authhandler\",{\"_index\":133,\"name\":{\"180\":{}},\"comment\":{}}],[\"authorizationparameters\",{\"_index\":73,\"name\":{\"80\":{}},\"comment\":{}}],[\"authorizationparams\",{\"_index\":39,\"name\":{\"42\":{},\"123\":{},\"136\":{},\"143\":{},\"301\":{}},\"comment\":{}}],[\"autosave\",{\"_index\":63,\"name\":{\"70\":{}},\"comment\":{}}],[\"baseconfig\",{\"_index\":35,\"name\":{\"38\":{}},\"comment\":{}}],[\"baseurl\",{\"_index\":40,\"name\":{\"43\":{}},\"comment\":{}}],[\"callback\",{\"_index\":54,\"name\":{\"59\":{},\"88\":{}},\"comment\":{}}],[\"callbackhandler\",{\"_index\":99,\"name\":{\"126\":{}},\"comment\":{}}],[\"callbackhandlererror\",{\"_index\":209,\"name\":{\"352\":{},\"436\":{}},\"comment\":{}}],[\"callbackoptions\",{\"_index\":95,\"name\":{\"119\":{},\"391\":{},\"447\":{}},\"comment\":{}}],[\"callbackoptionsprovider\",{\"_index\":97,\"name\":{\"124\":{}},\"comment\":{}}],[\"cause\",{\"_index\":199,\"name\":{\"331\":{},\"344\":{},\"350\":{},\"357\":{},\"364\":{},\"371\":{},\"378\":{}},\"comment\":{}}],[\"checksession\",{\"_index\":16,\"name\":{\"16\":{}},\"comment\":{}}],[\"claims\",{\"_index\":189,\"name\":{\"311\":{},\"469\":{},\"487\":{}},\"comment\":{}}],[\"clearcookie\",{\"_index\":167,\"name\":{\"254\":{},\"259\":{}},\"comment\":{}}],[\"client\",{\"_index\":0,\"name\":{\"0\":{}},\"comment\":{}}],[\"client/use\",{\"_index\":1,\"name\":{\"1\":{}},\"comment\":{}}],[\"client/with\",{\"_index\":24,\"name\":{\"26\":{}},\"comment\":{}}],[\"clientassertionsigningalg\",{\"_index\":56,\"name\":{\"61\":{}},\"comment\":{}}],[\"clientassertionsigningkey\",{\"_index\":55,\"name\":{\"60\":{}},\"comment\":{}}],[\"clientid\",{\"_index\":41,\"name\":{\"44\":{}},\"comment\":{}}],[\"clientsecret\",{\"_index\":42,\"name\":{\"45\":{}},\"comment\":{}}],[\"clocktolerance\",{\"_index\":43,\"name\":{\"46\":{}},\"comment\":{}}],[\"code\",{\"_index\":198,\"name\":{\"329\":{},\"342\":{},\"348\":{},\"353\":{},\"355\":{},\"360\":{},\"362\":{},\"367\":{},\"369\":{},\"374\":{},\"376\":{}},\"comment\":{}}],[\"config\",{\"_index\":34,\"name\":{\"37\":{}},\"comment\":{}}],[\"configparameters\",{\"_index\":80,\"name\":{\"92\":{},\"473\":{}},\"comment\":{}}],[\"connection\",{\"_index\":103,\"name\":{\"137\":{}},\"comment\":{}}],[\"connection_scope\",{\"_index\":104,\"name\":{\"138\":{}},\"comment\":{}}],[\"constructor\",{\"_index\":18,\"name\":{\"19\":{},\"230\":{},\"237\":{},\"241\":{},\"245\":{},\"252\":{},\"257\":{},\"313\":{},\"328\":{},\"341\":{},\"347\":{},\"354\":{},\"361\":{},\"368\":{},\"375\":{}},\"comment\":{}}],[\"cookie\",{\"_index\":65,\"name\":{\"72\":{}},\"comment\":{}}],[\"cookieconfig\",{\"_index\":66,\"name\":{\"73\":{}},\"comment\":{}}],[\"cookies\",{\"_index\":165,\"name\":{\"239\":{},\"250\":{}},\"comment\":{}}],[\"default\",{\"_index\":23,\"name\":{\"25\":{},\"229\":{},\"236\":{},\"240\":{},\"244\":{},\"251\":{},\"256\":{},\"312\":{},\"381\":{}},\"comment\":{}}],[\"domain\",{\"_index\":67,\"name\":{\"74\":{}},\"comment\":{}}],[\"edge\",{\"_index\":81,\"name\":{\"93\":{}},\"comment\":{}}],[\"email\",{\"_index\":4,\"name\":{\"3\":{}},\"comment\":{}}],[\"email_verified\",{\"_index\":5,\"name\":{\"4\":{}},\"comment\":{}}],[\"enabletelemetry\",{\"_index\":45,\"name\":{\"48\":{}},\"comment\":{}}],[\"error\",{\"_index\":14,\"name\":{\"14\":{}},\"comment\":{}}],[\"expired_access_token\",{\"_index\":204,\"name\":{\"337\":{}},\"comment\":{}}],[\"failed_refresh_grant\",{\"_index\":206,\"name\":{\"339\":{}},\"comment\":{}}],[\"generatesessioncookie\",{\"_index\":138,\"name\":{\"188\":{}},\"comment\":{}}],[\"generatesessioncookieconfig\",{\"_index\":137,\"name\":{\"187\":{}},\"comment\":{}}],[\"genid\",{\"_index\":59,\"name\":{\"65\":{}},\"comment\":{}}],[\"getaccesstoken\",{\"_index\":173,\"name\":{\"267\":{},\"281\":{},\"304\":{},\"484\":{},\"489\":{}},\"comment\":{}}],[\"getaccesstokenresult\",{\"_index\":186,\"name\":{\"302\":{},\"468\":{},\"491\":{}},\"comment\":{}}],[\"getbody\",{\"_index\":161,\"name\":{\"233\":{},\"248\":{}},\"comment\":{}}],[\"getcookies\",{\"_index\":162,\"name\":{\"234\":{},\"242\":{},\"249\":{}},\"comment\":{}}],[\"gethandler\",{\"_index\":135,\"name\":{\"183\":{}},\"comment\":{}}],[\"getloginstate\",{\"_index\":46,\"name\":{\"49\":{},\"129\":{},\"145\":{},\"397\":{},\"443\":{}},\"comment\":{}}],[\"getloginstateapproute\",{\"_index\":102,\"name\":{\"133\":{},\"399\":{},\"445\":{}},\"comment\":{}}],[\"getloginstatepageroute\",{\"_index\":101,\"name\":{\"130\":{},\"398\":{},\"444\":{}},\"comment\":{}}],[\"getmethod\",{\"_index\":160,\"name\":{\"232\":{},\"247\":{}},\"comment\":{}}],[\"getserversideprops\",{\"_index\":152,\"name\":{\"218\":{}},\"comment\":{}}],[\"getserversidepropsresultwithsession\",{\"_index\":145,\"name\":{\"204\":{},\"417\":{},\"460\":{}},\"comment\":{}}],[\"getsession\",{\"_index\":84,\"name\":{\"97\":{},\"98\":{},\"103\":{},\"264\":{},\"279\":{},\"307\":{},\"481\":{},\"488\":{}},\"comment\":{}}],[\"geturl\",{\"_index\":159,\"name\":{\"231\":{},\"246\":{}},\"comment\":{}}],[\"handleauth\",{\"_index\":88,\"name\":{\"107\":{},\"274\":{},\"288\":{},\"408\":{},\"474\":{}},\"comment\":{}}],[\"handlecallback\",{\"_index\":98,\"name\":{\"125\":{},\"269\":{},\"286\":{},\"390\":{},\"478\":{}},\"comment\":{}}],[\"handlelogin\",{\"_index\":109,\"name\":{\"147\":{},\"268\":{},\"284\":{},\"395\":{},\"475\":{}},\"comment\":{}}],[\"handlelogout\",{\"_index\":115,\"name\":{\"155\":{},\"270\":{},\"285\":{},\"400\":{},\"477\":{}},\"comment\":{}}],[\"handleprofile\",{\"_index\":124,\"name\":{\"168\":{},\"271\":{},\"287\":{},\"402\":{},\"476\":{}},\"comment\":{}}],[\"handler\",{\"_index\":134,\"name\":{\"181\":{}},\"comment\":{}}],[\"handlererror\",{\"_index\":208,\"name\":{\"346\":{},\"435\":{}},\"comment\":{}}],[\"handlers\",{\"_index\":87,\"name\":{\"106\":{},\"127\":{},\"407\":{},\"440\":{}},\"comment\":{}}],[\"handlers/auth\",{\"_index\":86,\"name\":{\"105\":{}},\"comment\":{}}],[\"handlers/callback\",{\"_index\":91,\"name\":{\"113\":{}},\"comment\":{}}],[\"handlers/login\",{\"_index\":100,\"name\":{\"128\":{}},\"comment\":{}}],[\"handlers/logout\",{\"_index\":111,\"name\":{\"149\":{}},\"comment\":{}}],[\"handlers/profile\",{\"_index\":117,\"name\":{\"157\":{}},\"comment\":{}}],[\"handlers/router\",{\"_index\":126,\"name\":{\"170\":{}},\"comment\":{}}],[\"helpers\",{\"_index\":127,\"name\":{\"170\":{},\"185\":{}},\"comment\":{}}],[\"helpers/testing\",{\"_index\":136,\"name\":{\"186\":{}},\"comment\":{}}],[\"helpers/with\",{\"_index\":139,\"name\":{\"189\":{},\"200\":{},\"203\":{}},\"comment\":{}}],[\"http\",{\"_index\":168,\"name\":{\"261\":{}},\"comment\":{}}],[\"http/auth0\",{\"_index\":156,\"name\":{\"228\":{},\"235\":{},\"239\":{},\"243\":{},\"250\":{},\"255\":{}},\"comment\":{}}],[\"httponly\",{\"_index\":70,\"name\":{\"77\":{}},\"comment\":{}}],[\"httptimeout\",{\"_index\":44,\"name\":{\"47\":{}},\"comment\":{}}],[\"identityclaimfilter\",{\"_index\":47,\"name\":{\"51\":{},\"91\":{}},\"comment\":{}}],[\"idplogout\",{\"_index\":48,\"name\":{\"52\":{}},\"comment\":{}}],[\"idtoken\",{\"_index\":190,\"name\":{\"315\":{}},\"comment\":{}}],[\"idtokensigningalg\",{\"_index\":49,\"name\":{\"53\":{}},\"comment\":{}}],[\"index\",{\"_index\":169,\"name\":{\"262\":{}},\"comment\":{}}],[\"initauth0\",{\"_index\":85,\"name\":{\"100\":{},\"102\":{},\"275\":{},\"277\":{}},\"comment\":{}}],[\"insufficient_scope\",{\"_index\":205,\"name\":{\"338\":{}},\"comment\":{}}],[\"invitation\",{\"_index\":105,\"name\":{\"139\":{}},\"comment\":{}}],[\"isloading\",{\"_index\":15,\"name\":{\"15\":{}},\"comment\":{}}],[\"issuerbaseurl\",{\"_index\":50,\"name\":{\"54\":{}},\"comment\":{}}],[\"legacysamesitecookie\",{\"_index\":51,\"name\":{\"55\":{}},\"comment\":{}}],[\"login\",{\"_index\":79,\"name\":{\"89\":{}},\"comment\":{}}],[\"loginhandler\",{\"_index\":110,\"name\":{\"148\":{}},\"comment\":{}}],[\"loginhandlererror\",{\"_index\":210,\"name\":{\"359\":{},\"437\":{}},\"comment\":{}}],[\"loginoptions\",{\"_index\":107,\"name\":{\"142\":{},\"396\":{},\"441\":{}},\"comment\":{}}],[\"loginoptionsprovider\",{\"_index\":108,\"name\":{\"146\":{}},\"comment\":{}}],[\"logouthandler\",{\"_index\":116,\"name\":{\"156\":{}},\"comment\":{}}],[\"logouthandlererror\",{\"_index\":211,\"name\":{\"366\":{},\"438\":{}},\"comment\":{}}],[\"logoutoptions\",{\"_index\":112,\"name\":{\"150\":{},\"401\":{},\"442\":{}},\"comment\":{}}],[\"logoutoptionsprovider\",{\"_index\":114,\"name\":{\"154\":{}},\"comment\":{}}],[\"logoutparams\",{\"_index\":113,\"name\":{\"152\":{}},\"comment\":{}}],[\"middleware\",{\"_index\":144,\"name\":{\"200\":{}},\"comment\":{}}],[\"missing_access_token\",{\"_index\":202,\"name\":{\"335\":{}},\"comment\":{}}],[\"missing_refresh_token\",{\"_index\":203,\"name\":{\"336\":{}},\"comment\":{}}],[\"missing_session\",{\"_index\":201,\"name\":{\"334\":{}},\"comment\":{}}],[\"name\",{\"_index\":6,\"name\":{\"5\":{},\"63\":{},\"330\":{},\"343\":{},\"349\":{},\"356\":{},\"363\":{},\"370\":{},\"377\":{}},\"comment\":{}}],[\"next\",{\"_index\":157,\"name\":{\"228\":{},\"235\":{},\"239\":{},\"243\":{},\"250\":{},\"255\":{}},\"comment\":{}}],[\"nextconfig\",{\"_index\":77,\"name\":{\"84\":{}},\"comment\":{}}],[\"nickname\",{\"_index\":7,\"name\":{\"6\":{}},\"comment\":{}}],[\"onerror\",{\"_index\":31,\"name\":{\"31\":{}},\"comment\":{}}],[\"onredirecting\",{\"_index\":30,\"name\":{\"29\":{}},\"comment\":{}}],[\"optionsprovider\",{\"_index\":132,\"name\":{\"178\":{}},\"comment\":{}}],[\"org_id\",{\"_index\":11,\"name\":{\"10\":{}},\"comment\":{}}],[\"organization\",{\"_index\":78,\"name\":{\"85\":{},\"122\":{},\"140\":{}},\"comment\":{}}],[\"page\",{\"_index\":25,\"name\":{\"26\":{},\"203\":{}},\"comment\":{}}],[\"pageroute\",{\"_index\":146,\"name\":{\"205\":{},\"421\":{},\"463\":{}},\"comment\":{}}],[\"pageroutehandlerfn\",{\"_index\":131,\"name\":{\"176\":{}},\"comment\":{}}],[\"pagerouteronerror\",{\"_index\":89,\"name\":{\"109\":{},\"410\":{},\"455\":{}},\"comment\":{}}],[\"params\",{\"_index\":129,\"name\":{\"173\":{},\"192\":{},\"209\":{}},\"comment\":{}}],[\"path\",{\"_index\":68,\"name\":{\"75\":{}},\"comment\":{}}],[\"picture\",{\"_index\":8,\"name\":{\"7\":{}},\"comment\":{}}],[\"postlogoutredirect\",{\"_index\":53,\"name\":{\"58\":{}},\"comment\":{}}],[\"profilehandler\",{\"_index\":125,\"name\":{\"169\":{}},\"comment\":{}}],[\"profilehandlererror\",{\"_index\":212,\"name\":{\"373\":{},\"439\":{}},\"comment\":{}}],[\"profileoptions\",{\"_index\":121,\"name\":{\"163\":{},\"403\":{},\"446\":{}},\"comment\":{}}],[\"profileoptionsprovider\",{\"_index\":123,\"name\":{\"167\":{}},\"comment\":{}}],[\"redirect\",{\"_index\":164,\"name\":{\"238\":{},\"260\":{}},\"comment\":{}}],[\"redirecturi\",{\"_index\":96,\"name\":{\"121\":{}},\"comment\":{}}],[\"refetch\",{\"_index\":122,\"name\":{\"165\":{}},\"comment\":{}}],[\"refresh\",{\"_index\":185,\"name\":{\"299\":{}},\"comment\":{}}],[\"refreshtoken\",{\"_index\":193,\"name\":{\"319\":{}},\"comment\":{}}],[\"request\",{\"_index\":158,\"name\":{\"228\":{},\"239\":{},\"243\":{}},\"comment\":{}}],[\"requesterror\",{\"_index\":17,\"name\":{\"18\":{},\"385\":{}},\"comment\":{}}],[\"required\",{\"_index\":27,\"name\":{\"26\":{},\"189\":{},\"200\":{},\"203\":{}},\"comment\":{}}],[\"response\",{\"_index\":163,\"name\":{\"235\":{},\"250\":{},\"255\":{}},\"comment\":{}}],[\"response_mode\",{\"_index\":75,\"name\":{\"82\":{}},\"comment\":{}}],[\"response_type\",{\"_index\":76,\"name\":{\"83\":{}},\"comment\":{}}],[\"returnto\",{\"_index\":29,\"name\":{\"28\":{},\"144\":{},\"151\":{},\"219\":{},\"224\":{}},\"comment\":{}}],[\"rolling\",{\"_index\":60,\"name\":{\"67\":{}},\"comment\":{}}],[\"rollingduration\",{\"_index\":61,\"name\":{\"68\":{}},\"comment\":{}}],[\"routes\",{\"_index\":52,\"name\":{\"56\":{},\"86\":{}},\"comment\":{}}],[\"samesite\",{\"_index\":72,\"name\":{\"79\":{}},\"comment\":{}}],[\"scope\",{\"_index\":74,\"name\":{\"81\":{}},\"comment\":{}}],[\"scopes\",{\"_index\":184,\"name\":{\"298\":{}},\"comment\":{}}],[\"screen_hint\",{\"_index\":106,\"name\":{\"141\":{}},\"comment\":{}}],[\"searchparams\",{\"_index\":149,\"name\":{\"212\":{}},\"comment\":{}}],[\"secret\",{\"_index\":36,\"name\":{\"39\":{}},\"comment\":{}}],[\"secure\",{\"_index\":71,\"name\":{\"78\":{}},\"comment\":{}}],[\"session\",{\"_index\":37,\"name\":{\"40\":{},\"90\":{},\"306\":{},\"309\":{},\"320\":{},\"323\":{},\"485\":{},\"486\":{}},\"comment\":{}}],[\"session/get\",{\"_index\":177,\"name\":{\"291\":{},\"306\":{}},\"comment\":{}}],[\"session/session\",{\"_index\":188,\"name\":{\"310\":{}},\"comment\":{}}],[\"session/touch\",{\"_index\":194,\"name\":{\"320\":{}},\"comment\":{}}],[\"session/update\",{\"_index\":195,\"name\":{\"323\":{}},\"comment\":{}}],[\"sessionconfig\",{\"_index\":57,\"name\":{\"62\":{}},\"comment\":{}}],[\"sessionstore\",{\"_index\":175,\"name\":{\"289\":{}},\"comment\":{}}],[\"sessionstorepayload\",{\"_index\":176,\"name\":{\"290\":{}},\"comment\":{}}],[\"setcookie\",{\"_index\":166,\"name\":{\"253\":{},\"258\":{}},\"comment\":{}}],[\"slug\",{\"_index\":148,\"name\":{\"211\":{}},\"comment\":{}}],[\"status\",{\"_index\":19,\"name\":{\"20\":{},\"332\":{},\"345\":{},\"351\":{},\"358\":{},\"365\":{},\"372\":{},\"379\":{}},\"comment\":{}}],[\"store\",{\"_index\":58,\"name\":{\"64\":{}},\"comment\":{}}],[\"storeidtoken\",{\"_index\":64,\"name\":{\"71\":{}},\"comment\":{}}],[\"sub\",{\"_index\":9,\"name\":{\"8\":{}},\"comment\":{}}],[\"token\",{\"_index\":179,\"name\":{\"291\":{}},\"comment\":{}}],[\"touchsession\",{\"_index\":171,\"name\":{\"265\":{},\"321\":{},\"482\":{},\"495\":{}},\"comment\":{}}],[\"transient\",{\"_index\":69,\"name\":{\"76\":{}},\"comment\":{}}],[\"updated_at\",{\"_index\":10,\"name\":{\"9\":{}},\"comment\":{}}],[\"updatesession\",{\"_index\":172,\"name\":{\"266\":{},\"280\":{},\"324\":{},\"483\":{},\"496\":{}},\"comment\":{}}],[\"user\",{\"_index\":2,\"name\":{\"1\":{},\"13\":{},\"34\":{},\"314\":{}},\"comment\":{}}],[\"usercontext\",{\"_index\":12,\"name\":{\"11\":{}},\"comment\":{}}],[\"userprofile\",{\"_index\":3,\"name\":{\"2\":{},\"384\":{}},\"comment\":{}}],[\"userprops\",{\"_index\":32,\"name\":{\"33\":{}},\"comment\":{}}],[\"userprovider\",{\"_index\":22,\"name\":{\"23\":{},\"382\":{}},\"comment\":{}}],[\"userproviderprops\",{\"_index\":20,\"name\":{\"21\":{},\"383\":{}},\"comment\":{}}],[\"useuser\",{\"_index\":21,\"name\":{\"22\":{},\"386\":{}},\"comment\":{}}],[\"utils/errors\",{\"_index\":196,\"name\":{\"326\":{}},\"comment\":{}}],[\"version\",{\"_index\":213,\"name\":{\"380\":{}},\"comment\":{}}],[\"withapiauthrequired\",{\"_index\":143,\"name\":{\"199\":{},\"272\":{},\"282\":{},\"413\":{},\"479\":{}},\"comment\":{}}],[\"withapiauthrequiredapproute\",{\"_index\":141,\"name\":{\"195\":{},\"415\":{},\"465\":{}},\"comment\":{}}],[\"withapiauthrequiredpageroute\",{\"_index\":142,\"name\":{\"197\":{},\"416\":{},\"466\":{}},\"comment\":{}}],[\"withmiddlewareauthrequired\",{\"_index\":83,\"name\":{\"96\":{},\"104\":{},\"201\":{},\"389\":{}},\"comment\":{}}],[\"withpageauthrequired\",{\"_index\":33,\"name\":{\"35\":{},\"227\":{},\"273\":{},\"283\":{},\"387\":{},\"418\":{},\"480\":{}},\"comment\":{}}],[\"withpageauthrequiredapprouter\",{\"_index\":155,\"name\":{\"225\":{},\"425\":{},\"459\":{}},\"comment\":{}}],[\"withpageauthrequiredapprouteroptions\",{\"_index\":154,\"name\":{\"222\":{},\"420\":{},\"462\":{}},\"comment\":{}}],[\"withpageauthrequiredoptions\",{\"_index\":28,\"name\":{\"27\":{},\"388\":{}},\"comment\":{}}],[\"withpageauthrequiredpagerouter\",{\"_index\":153,\"name\":{\"220\":{},\"424\":{},\"458\":{}},\"comment\":{}}],[\"withpageauthrequiredpagerouteroptions\",{\"_index\":151,\"name\":{\"216\":{},\"419\":{},\"461\":{}},\"comment\":{}}]],\"pipeline\":[]}}"); \ No newline at end of file diff --git a/docs/classes/client_use_user.RequestError.html b/docs/classes/client_use_user.RequestError.html index ac97bc0c9..52308aad6 100644 --- a/docs/classes/client_use_user.RequestError.html +++ b/docs/classes/client_use_user.RequestError.html @@ -31,7 +31,7 @@

    Hierarchy

    • RequestError
  • +
  • Defined in client/use-user.tsx:46
  • @@ -60,14 +60,14 @@
    status: Returns RequestError
    +
  • Defined in client/use-user.tsx:49
  • Properties

    status: number
    +
  • Defined in client/use-user.tsx:47
  • +
  • Defined in session/session.ts:23
  • +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -116,7 +116,7 @@
    +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -119,12 +119,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_CALLBACK_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:172
  • +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -123,7 +123,7 @@
    +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_LOGIN_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:192
  • +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_LOGOUT_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:212
  • +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_PROFILE_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:232
  • +
  • Defined in utils/errors.ts:79
  • @@ -42,32 +42,32 @@

    Enumeration Members

    EXPIRED_ACCESS_TOKEN: "ERR_EXPIRED_ACCESS_TOKEN"
    +
  • Defined in utils/errors.ts:83
  • FAILED_REFRESH_GRANT: "ERR_FAILED_REFRESH_GRANT"
    +
  • Defined in utils/errors.ts:85
  • INSUFFICIENT_SCOPE: "ERR_INSUFFICIENT_SCOPE"
    +
  • Defined in utils/errors.ts:84
  • MISSING_ACCESS_TOKEN: "ERR_MISSING_ACCESS_TOKEN"
    +
  • Defined in utils/errors.ts:81
  • MISSING_REFRESH_TOKEN: "ERR_MISSING_REFRESH_TOKEN"
    +
  • Defined in utils/errors.ts:82
  • MISSING_SESSION: "ERR_MISSING_SESSION"
    +
  • Defined in utils/errors.ts:80
  • +
  • Defined in client/use-user.tsx:190
  • Returns Promise<undefined | null | default>

    +
  • Defined in edge.ts:15
  • +
    +
    +
    + +
    +
      + +
    • +
      +

      Type Parameters

      +
        +
      • +

        Opts extends Record<string, any>

      +
      +

      Parameters

      +
      +

      Returns ((reqOrOptions, resOrCtx, options?) => void | Promise<void> | Response | Promise<Response> | ((req, resOrCtxInner) => void | Promise<void> | Response | Promise<Response>))

      +
        +
      • +
          +
        • (reqOrOptions, resOrCtx, options?): void | Promise<void> | Response | Promise<Response> | ((req, resOrCtxInner) => void | Promise<void> | Response | Promise<Response>)
        • +
        • +
          +

          Parameters

          +
            +
          • +
            reqOrOptions: NextApiRequest | NextRequest | Opts
          • +
          • +
            resOrCtx: NextApiResponse<any> | AppRouteHandlerFnContext
          • +
          • +
            Optional options: Opts
          +

          Returns void | Promise<void> | Response | Promise<Response> | ((req, resOrCtxInner) => void | Promise<void> | Response | Promise<Response>)

    +
    +
    \ No newline at end of file diff --git a/docs/functions/helpers_testing.generateSessionCookie.html b/docs/functions/helpers_testing.generateSessionCookie.html index f508e1533..62cc928c4 100644 --- a/docs/functions/helpers_testing.generateSessionCookie.html +++ b/docs/functions/helpers_testing.generateSessionCookie.html @@ -29,7 +29,7 @@
    session: config: GenerateSessionCookieConfig

    Returns Promise<string>

    +
  • Defined in helpers/testing.ts:23
  • +
  • Defined in index.ts:143
  • Parameters

    • -
      req: IncomingMessage | NextApiRequest
    • -
    • -
      res: ServerResponse<IncomingMessage> | NextApiResponse<any>
    • -
    • -
      Optional accessTokenRequest: AccessTokenRequest
    +
    Rest ...args: [IncomingMessage, ServerResponse<IncomingMessage>, AccessTokenRequest?] | [NextApiRequest, NextApiResponse<any>, AccessTokenRequest?] | [NextRequest, NextResponse<unknown>, AccessTokenRequest?] | [AccessTokenRequest?]

    Returns Promise<GetAccessTokenResult>

    +
  • Defined in session/get-access-token.ts:110
  • Parameters

    • -
      req: IncomingMessage | NextApiRequest
    • -
    • -
      res: ServerResponse<IncomingMessage> | NextApiResponse<any>
    +
    Rest ...args: [] | [IncomingMessage, ServerResponse<IncomingMessage>] | [NextApiRequest, NextApiResponse<any>] | [NextRequest, NextResponse<unknown>]

    Returns Promise<undefined | null | default>

    +
  • Defined in session/get-session.ts:11
  • +
  • Defined in handlers/auth.ts:114
  • -

    Returns Promise<void>

    +
  • Parameters

    -

    Returns CallbackHandler

  • +
  • +
    resOrOpts: NextApiResponse<any> | AppRouteHandlerFnContext
  • +
  • +
    Optional options: CallbackOptions
  • +

    Returns unknown

    - +
  • Defined in handlers/router-helpers.ts:52
  • +
  • Parameters

    -

    Returns CallbackHandler

  • +

    Returns Handler<CallbackOptions>

    + +
  • +
    +

    Parameters

    +
    +

    Returns Handler<CallbackOptions>

  • +
  • Defined in handlers/router-helpers.ts:46
  • -

    Returns Promise<void>

    +
  • Parameters

    -

    Returns LoginHandler

  • +
  • +
    resOrOpts: NextApiResponse<any> | AppRouteHandlerFnContext
  • +
  • +
    Optional options: LoginOptions
  • +

    Returns unknown

    - +
  • Defined in handlers/router-helpers.ts:52
  • +
  • Parameters

    -

    Returns LoginHandler

  • +

    Returns Handler<LoginOptions>

    + +
  • +
    +

    Parameters

    +
    +

    Returns Handler<LoginOptions>

  • +
  • Defined in handlers/router-helpers.ts:46
  • -

    Returns Promise<void>

    +
  • Parameters

    -

    Returns LogoutHandler

  • +
  • +
    resOrOpts: NextApiResponse<any> | AppRouteHandlerFnContext
  • +
  • +
    Optional options: LogoutOptions
  • +

    Returns unknown

    - +
  • Defined in handlers/router-helpers.ts:52
  • +
  • Parameters

    -

    Returns LogoutHandler

  • +

    Returns Handler<LogoutOptions>

    + +
  • +
    +

    Parameters

    +
    +

    Returns Handler<LogoutOptions>

  • +
  • Defined in handlers/router-helpers.ts:46
  • -

    Returns Promise<void>

    +
  • Parameters

    -

    Returns ProfileHandler

  • +
  • +
    resOrOpts: NextApiResponse<any> | AppRouteHandlerFnContext
  • +
  • +
    Optional options: ProfileOptions
  • +

    Returns unknown

    - +
  • Defined in handlers/router-helpers.ts:52
  • +
  • Parameters

    -

    Returns ProfileHandler

  • +

    Returns Handler<ProfileOptions>

    + +
  • +
    +

    Parameters

    +
    +

    Returns Handler<ProfileOptions>

  • +
  • Defined in handlers/router-helpers.ts:46
  • +
    Rest ...args: [IncomingMessage, ServerResponse<IncomingMessage>, default] | [NextApiRequest, NextApiResponse<any>, default] | [NextRequest, NextResponse<unknown>, default] | [default]

    Returns Promise<void>

    +
  • Defined in session/update-session.ts:27
  • +
  • Defined in helpers/with-api-auth-required.ts:74
  • Returns PageRoute<P, Q>

    +
  • Defined in helpers/with-page-auth-required.ts:112
  • + +
  • +

    Wrap your Server Component with this method to make sure the user is authenticated before +visiting the page.

    +
    // app/protected-page/page.js
    import { withPageAuthRequired } from '@auth0/nextjs-auth0';

    export default function withPageAuthRequired(ProtectedPage() {
    return <div>Protected content</div>;
    }, { returnTo: '/protected-page' }); +
    +

    If the user visits /protected-page without a valid session, it will redirect the user to the +login page.

    +

    Note: Server Components are not aware of the req or the url of the page. So if you want the user to return to the +page after login, you must specify the returnTo option.

    +

    You can specify a function to returnTo that accepts the params (An object containing the dynamic +route parameters) and searchParams (An object containing the search parameters of the current URL) +argument from the page, to preserve dynamic routes and search params.

    +
    // app/protected-page/[slug]/page.js
    import { withPageAuthRequired } from '@auth0/nextjs-auth0';

    export default function withPageAuthRequired(ProtectedPage() {
    return <div>Protected content</div>;
    }, {
    returnTo({ params }) {
    return `/protected-page/${params.slug}`
    }
    }); +
    +
    +
    +

    Parameters

    +
    +

    Returns AppRouterPageRoute

    +
  • +
  • Defined in client/use-user.tsx:1
  • Index

    @@ -79,11 +79,19 @@

    Theme

    +
  • Defined in config.ts:1
  • Index

    @@ -71,11 +71,19 @@

    Theme

    +
    +
    +
    + +

    Type alias AppRouterOnError

    +
    AppRouterOnError: ((req, error) => Promise<Response | void> | Response | void)
    +
    +

    Type declaration

    +
      +
    • +
        +
      • (req, error): Promise<Response | void> | Response | void
      • +
      • +
        +

        Parameters

        +
        +

        Returns Promise<Response | void> | Response | void

    +
    +
    \ No newline at end of file diff --git a/docs/types/handlers_auth.HandleAuth.html b/docs/types/handlers_auth.HandleAuth.html index f078cf649..fb994a411 100644 --- a/docs/types/handlers_auth.HandleAuth.html +++ b/docs/types/handlers_auth.HandleAuth.html @@ -16,26 +16,31 @@
  • handlers/auth
  • HandleAuth
  • Type alias HandleAuth

    -
    HandleAuth: ((userHandlers?) => NextApiHandler)
    +
    HandleAuth: ((userHandlers?) => NextApiHandler | AppRouteHandlerFn | any)

    Type declaration

      • -
      • (userHandlers?): NextApiHandler
      • +
      • (userHandlers?): NextApiHandler | AppRouteHandlerFn | any
      • The main way to use the server SDK.

        +

        Page Router

        Simply set the environment variables per ConfigParameters then create the file pages/api/auth/[auth0].js. For example:

        // pages/api/auth/[auth0].js
        import { handleAuth } from '@auth0/nextjs-auth0';

        export default handleAuth();
        +

        App Router

        +

        Simply set the environment variables per ConfigParameters then create the file +app/api/auth/[auth0]/route.js. For example:

        +
        // app/api/auth/[auth0]/route.js
        import { handleAuth } from '@auth0/nextjs-auth0';

        export const GET = handleAuth(); +

        This will create 5 handlers for the following urls:

        • /api/auth/login: log the user in to your app by redirecting them to your identity provider.
        • /api/auth/callback: The page that your identity provider will redirect the user back to on login.
        • /api/auth/logout: log the user out of your app.
        • /api/auth/me: View the user profile JSON (used by the UseUser hook).
        • -
        • /api/auth/unauthorized: Returns a 401 for use by WithMiddlewareAuthRequired when protecting API routes.
        @@ -43,10 +48,10 @@

        Parameters

        -

        Returns NextApiHandler

        +

        Returns NextApiHandler | AppRouteHandlerFn | any

    +
  • Defined in handlers/auth.ts:114
  • +
  • Defined in handlers/auth.ts:137
  • -
    CallbackHandler: ((req, res, options?) => Promise<void>)
    -
    -

    Type declaration

    -
      -
    • -
        -
      • (req, res, options?): Promise<void>
      • -
      • +
        CallbackHandler: Handler<CallbackOptions>

        The handler for the /api/auth/callback API route.

        -
        -

        Parameters

        -
          -
        • -
          req: NextApiRequest
        • -
        • -
          res: NextApiResponse
        • -
        • -
          Optional options: CallbackOptions
        -

        Returns Promise<void>

        Throws

        HandlerError

        -
    +
  • Defined in handlers/callback.ts:277
  • -
    HandleCallback: {
        (req, res, options?): Promise<void>;
        (provider): CallbackHandler;
        (options): CallbackHandler;
    }
    -
    -

    Type declaration

    -
      -
    • -
        -
      • (req, res, options?): Promise<void>
      • -
      • +
        HandleCallback: AuthHandler<CallbackOptions>

        Use this to customize the default callback handler without overriding it. You can still override the handler if needed.

        -
        -

        Parameters

        -
          -
        • -
          req: NextApiRequest
        • -
        • -
          res: NextApiResponse<any>
        • -
        • -
          Optional options: CallbackOptions
        -

        Returns Promise<void>

        -
        -

        Example

        Pass an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: handleCallback({ redirectUri: 'https://example.com' })
        }); -
        - -

        Example

        Pass a function that receives the request and returns an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: handleCallback((req) => {
        return { redirectUri: 'https://example.com' };
        })
        }); -
        -

        This is useful for generating options that depend on values from the request.

        - -

        Example

        Override the callback handler

        -
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: async (req, res) => {
        try {
        await handleCallback(req, res, {
        redirectUri: 'https://example.com'
        });
        } catch (error) {
        console.error(error);
        }
        }
        }); -
        -
      • -
      • (provider): CallbackHandler
      • -
      • -

        Use this to customize the default callback handler without overriding it. -You can still override the handler if needed.

        -
        -
        -

        Parameters

        -
        -

        Returns CallbackHandler

        -
        -

        Example

        Pass an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: handleCallback({ redirectUri: 'https://example.com' })
        }); -
        - -

        Example

        Pass a function that receives the request and returns an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: handleCallback((req) => {
        return { redirectUri: 'https://example.com' };
        })
        }); -
        -

        This is useful for generating options that depend on values from the request.

        - -

        Example

        Override the callback handler

        -
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: async (req, res) => {
        try {
        await handleCallback(req, res, {
        redirectUri: 'https://example.com'
        });
        } catch (error) {
        console.error(error);
        }
        }
        }); -
        -
      • -
      • (options): CallbackHandler
      • -
      • -

        Use this to customize the default callback handler without overriding it. -You can still override the handler if needed.

        -
        -
        -

        Parameters

        -
        -

        Returns CallbackHandler

        Example

        Pass an options object

        // pages/api/auth/[auth0].js
        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: handleCallback({ redirectUri: 'https://example.com' })
        }); @@ -100,9 +33,9 @@

        Example

        Pass a function that receives the request and returns an opti

        Example

        Override the callback handler

        import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

        export default handleAuth({
        callback: async (req, res) => {
        try {
        await handleCallback(req, res, {
        redirectUri: 'https://example.com'
        });
        } catch (error) {
        console.error(error);
        }
        }
        });
        -
    +
  • Defined in handlers/callback.ts:268
  • -
    HandleLogin: {
        (req, res, options?): Promise<void>;
        (provider): LoginHandler;
        (options): LoginHandler;
    }
    -
    -

    Type declaration

    -
      -
    • -
        -
      • (req, res, options?): Promise<void>
      • -
      • +

        Use this to customize the default login handler without overriding it. You can still override the handler if needed.

        -
        -

        Parameters

        -
          -
        • -
          req: NextApiRequest
        • -
        • -
          res: NextApiResponse<any>
        • -
        • -
          Optional options: LoginOptions
        -

        Returns Promise<void>

        -
        -

        Example

        Pass an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: handleLogin({
        authorizationParams: { connection: 'github' }
        })
        }); -
        - -

        Example

        Pass a function that receives the request and returns an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: handleLogin((req) => {
        return {
        authorizationParams: { connection: 'github' }
        };
        })
        }); -
        -

        This is useful for generating options that depend on values from the request.

        - -

        Example

        Override the login handler

        -
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: async (req, res) => {
        try {
        await handleLogin(req, res, {
        authorizationParams: { connection: 'github' }
        });
        } catch (error) {
        console.error(error);
        }
        }
        }); -
        -
      • -
      • (provider): LoginHandler
      • -
      • -

        Use this to customize the default login handler without overriding it. -You can still override the handler if needed.

        -
        -
        -

        Parameters

        -
        -

        Returns LoginHandler

        -
        -

        Example

        Pass an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: handleLogin({
        authorizationParams: { connection: 'github' }
        })
        }); -
        - -

        Example

        Pass a function that receives the request and returns an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: handleLogin((req) => {
        return {
        authorizationParams: { connection: 'github' }
        };
        })
        }); -
        -

        This is useful for generating options that depend on values from the request.

        - -

        Example

        Override the login handler

        -
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: async (req, res) => {
        try {
        await handleLogin(req, res, {
        authorizationParams: { connection: 'github' }
        });
        } catch (error) {
        console.error(error);
        }
        }
        }); -
        -
      • -
      • (options): LoginHandler
      • -
      • -

        Use this to customize the default login handler without overriding it. -You can still override the handler if needed.

        -
        -
        -

        Parameters

        -
        -

        Returns LoginHandler

        Example

        Pass an options object

        // pages/api/auth/[auth0].js
        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: handleLogin({
        authorizationParams: { connection: 'github' }
        })
        }); @@ -100,9 +33,9 @@

        Example

        Pass a function that receives the request and returns an opti

        Example

        Override the login handler

        import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';

        export default handleAuth({
        login: async (req, res) => {
        try {
        await handleLogin(req, res, {
        authorizationParams: { connection: 'github' }
        });
        } catch (error) {
        console.error(error);
        }
        }
        });
        -
    +
  • Defined in handlers/login.ts:248
  • -
    LoginOptionsProvider: ((req) => LoginOptions)
    -
    -

    Type declaration

    -
      -
    • -
        -
      • (req): LoginOptions
      • -
      • +
        LoginOptionsProvider: OptionsProvider<LoginOptions>

        Options provider for the default login handler. Use this to generate options that depend on values from the request.

        -
        -

        Parameters

        -
          -
        • -
          req: NextApiRequest
        -

        Returns LoginOptions

        -
    +
  • Defined in handlers/login.ts:192
  • -
    LogoutHandler: ((req, res, options?) => Promise<void>)
    -
    -

    Type declaration

    -
      -
    • -
        -
      • (req, res, options?): Promise<void>
      • -
      • +
        LogoutHandler: Handler<LogoutOptions>

        The handler for the /api/auth/logout API route.

        -
        -

        Parameters

        -
          -
        • -
          req: NextApiRequest
        • -
        • -
          res: NextApiResponse
        • -
        • -
          Optional options: LogoutOptions
        -

        Returns Promise<void>

        Throws

        HandlerError

        -
    +
  • Defined in handlers/logout.ts:97
  • -
    AfterRefetch: ((req, res, session) => Promise<default> | default)
    -
    -

    Type declaration

    -
    +
  • Defined in handlers/profile.ts:14
  • +
    + +
    \ No newline at end of file diff --git a/docs/types/handlers_profile.AfterRefetchPageRoute.html b/docs/types/handlers_profile.AfterRefetchPageRoute.html new file mode 100644 index 000000000..d03b57e07 --- /dev/null +++ b/docs/types/handlers_profile.AfterRefetchPageRoute.html @@ -0,0 +1,101 @@ +AfterRefetchPageRoute | @auth0/nextjs-auth0
    +
    + +
    + +
    \ No newline at end of file diff --git a/docs/types/handlers_profile.HandleProfile.html b/docs/types/handlers_profile.HandleProfile.html index fddc18196..db8b962a5 100644 --- a/docs/types/handlers_profile.HandleProfile.html +++ b/docs/types/handlers_profile.HandleProfile.html @@ -16,77 +16,10 @@
  • handlers/profile
  • HandleProfile
  • Type alias HandleProfile

    -
    HandleProfile: {
        (req, res, options?): Promise<void>;
        (provider): ProfileHandler;
        (options): ProfileHandler;
    }
    -
    -

    Type declaration

    -
      -
    • -
        -
      • (req, res, options?): Promise<void>
      • -
      • +
        HandleProfile: AuthHandler<ProfileOptions>

        Use this to customize the default profile handler without overriding it. You can still override the handler if needed.

        -
        -

        Parameters

        -
          -
        • -
          req: NextApiRequest
        • -
        • -
          res: NextApiResponse<any>
        • -
        • -
          Optional options: ProfileOptions
        -

        Returns Promise<void>

        -
        -

        Example

        Pass an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: handleProfile({ refetch: true })
        }); -
        - -

        Example

        Pass a function that receives the request and returns an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: handleProfile((req) => {
        return { refetch: true };
        })
        }); -
        -

        This is useful for generating options that depend on values from the request.

        - -

        Example

        Override the profile handler

        -
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: async (req, res) => {
        try {
        await handleProfile(req, res, { refetch: true });
        } catch (error) {
        console.error(error);
        }
        }
        }); -
        -
      • -
      • (provider): ProfileHandler
      • -
      • -

        Use this to customize the default profile handler without overriding it. -You can still override the handler if needed.

        -
        -
        -

        Parameters

        -
        -

        Returns ProfileHandler

        -
        -

        Example

        Pass an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: handleProfile({ refetch: true })
        }); -
        - -

        Example

        Pass a function that receives the request and returns an options object

        -
        // pages/api/auth/[auth0].js
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: handleProfile((req) => {
        return { refetch: true };
        })
        }); -
        -

        This is useful for generating options that depend on values from the request.

        - -

        Example

        Override the profile handler

        -
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: async (req, res) => {
        try {
        await handleProfile(req, res, { refetch: true });
        } catch (error) {
        console.error(error);
        }
        }
        }); -
        -
      • -
      • (options): ProfileHandler
      • -
      • -

        Use this to customize the default profile handler without overriding it. -You can still override the handler if needed.

        -
        -
        -

        Parameters

        -
        -

        Returns ProfileHandler

        Example

        Pass an options object

        // pages/api/auth/[auth0].js
        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: handleProfile({ refetch: true })
        }); @@ -100,9 +33,9 @@

        Example

        Pass a function that receives the request and returns an opti

        Example

        Override the profile handler

        import { handleAuth, handleProfile } from '@auth0/nextjs-auth0';

        export default handleAuth({
        profile: async (req, res) => {
        try {
        await handleProfile(req, res, { refetch: true });
        } catch (error) {
        console.error(error);
        }
        }
        });
        -
    +
  • Defined in handlers/profile.ts:41
  • +
  • Defined in helpers/testing.ts:7
  • +
  • Defined in helpers/with-api-auth-required.ts:82
  • +
    +
    +
    + +
    WithApiAuthRequiredAppRoute: ((apiRoute) => AppRouteHandlerFn)
    +
    +

    Type declaration

    +
      +
    • +
        +
      • (apiRoute): AppRouteHandlerFn
      • +
      • +

        Wrap an app router API route to check that the user has a valid session. If they're not logged in the +handler will return a 401 Unauthorized.

        +
        // app/protected-api/route.js
        import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';

        export default withApiAuthRequired(function Protected(req) {
        const session = getSession();
        ...
        }); +
        +

        If you visit /protected-api without a valid session cookie, you will get a 401 response.

        +
        +
        +

        Parameters

        +
        +

        Returns AppRouteHandlerFn

        +
    +
    +
    \ No newline at end of file diff --git a/docs/types/helpers_with_api_auth_required.WithApiAuthRequiredPageRoute.html b/docs/types/helpers_with_api_auth_required.WithApiAuthRequiredPageRoute.html new file mode 100644 index 000000000..8fd4ae60d --- /dev/null +++ b/docs/types/helpers_with_api_auth_required.WithApiAuthRequiredPageRoute.html @@ -0,0 +1,99 @@ +WithApiAuthRequiredPageRoute | @auth0/nextjs-auth0
    +
    + +
    +
    +
    + +
    WithApiAuthRequiredPageRoute: ((apiRoute) => NextApiHandler)
    +
    +

    Type declaration

    +
      +
    • +
        +
      • (apiRoute): NextApiHandler
      • +
      • +

        Wrap a page router API route to check that the user has a valid session. If they're not logged in the +handler will return a 401 Unauthorized.

        +
        // pages/api/protected-route.js
        import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';

        export default withApiAuthRequired(function ProtectedRoute(req, res) {
        const session = getSession(req, res);
        ...
        }); +
        +

        If you visit /api/protected-route without a valid session cookie, you will get a 401 response.

        +
        +
        +

        Parameters

        +
          +
        • +
          apiRoute: NextApiHandler
        +

        Returns NextApiHandler

        +
    +
    +
    \ No newline at end of file diff --git a/docs/types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html b/docs/types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html index c477b4db8..a6aaa59b0 100644 --- a/docs/types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html +++ b/docs/types/helpers_with_middleware_auth_required.WithMiddlewareAuthRequired.html @@ -44,7 +44,7 @@
    Optional Returns NextMiddleware
    +
  • Defined in helpers/with-middleware-auth-required.ts:46
  • +
  • Defined in helpers/with-page-auth-required.ts:25
  • -
    PageRoute<P, Q>: ((cts) => Promise<GetServerSidePropsResultWithSession<P>>)
    +
    PageRoute<P, Q>: ((ctx) => Promise<GetServerSidePropsResultWithSession<P>>)

    Type Parameters

    +
    ctx: GetServerSidePropsContext<Q>

    Returns Promise<GetServerSidePropsResultWithSession<P>>

    +
  • Defined in helpers/with-page-auth-required.ts:32
  • -
    WithPageAuthRequired: (<P, Q>(opts?) => PageRoute<P, Q>)
    -
    -

    Type declaration

    -
      -
    • -
        -
      • <P, Q>(opts?): PageRoute<P, Q>
      • -
      • -

        Wrap your getServerSideProps with this method to make sure the user is authenticated before -visiting the page.

        -
        // pages/protected-page.js
        import { withPageAuthRequired } from '@auth0/nextjs-auth0';

        export default function ProtectedPage() {
        return <div>Protected content</div>;
        }

        export const getServerSideProps = withPageAuthRequired(); -
        -

        If the user visits /protected-page without a valid session, it will redirect the user to the -login page. Then they will be returned to /protected-page after login.

        + +

        Protects Page router pages WithPageAuthRequiredPageRouter or +App router pages WithPageAuthRequiredAppRouter

        -
        -

        Type Parameters

        -
          -
        • -

          P extends {
              [key: string]: any;
          } = {
              [key: string]: any;
          }

        • -
        • -

          Q extends ParsedUrlQuery = ParsedUrlQuery

        -
        -

        Parameters

        -
        -

        Returns PageRoute<P, Q>

        -
    +
  • Defined in helpers/with-page-auth-required.ts:178
  • +
  • Defined in helpers/with-page-auth-required.ts:84
  • +
  • Defined in index.ts:120
  • +
  • Defined in index.ts:282
  • +
  • Defined in index.ts:283
  • +
  • Defined in session/get-access-token.ts:16
  • -
    GetAccessToken: ((req, res, accessTokenRequest?) => Promise<GetAccessTokenResult>)
    +
    GetAccessToken: ((...args) => Promise<GetAccessTokenResult>)

    Type declaration

      • -
      • (req, res, accessTokenRequest?): Promise<GetAccessTokenResult>
      • +
      • (...args): Promise<GetAccessTokenResult>
      • Get an access token to access an external API.

        @@ -30,17 +30,13 @@

        Type declaration

        Parameters

        • -
          req: IncomingMessage | NextApiRequest
        • -
        • -
          res: ServerResponse | NextApiResponse
        • -
        • -
          Optional accessTokenRequest: AccessTokenRequest
    +
    Rest ...args: [IncomingMessage, ServerResponse, AccessTokenRequest?] | [NextApiRequest, NextApiResponse, AccessTokenRequest?] | [NextRequest, NextResponse, AccessTokenRequest?] | [AccessTokenRequest?]

    Returns Promise<GetAccessTokenResult>

    +
  • Defined in session/get-access-token.ts:110
  • -
    GetSession: ((req, res) => Promise<default | null | undefined>)
    +
    GetSession: ((...args) => Promise<default | null | undefined>)

    Type declaration

      • -
      • (req, res): Promise<default | null | undefined>
      • +
      • (...args): Promise<default | null | undefined>
      • Get the user's session from the request.

        @@ -30,13 +30,11 @@

        Type declaration

        Parameters

        • -
          req: IncomingMessage | NextApiRequest
        • -
        • -
          res: ServerResponse | NextApiResponse
    +
    Rest ...args: [IncomingMessage, ServerResponse] | [NextApiRequest, NextApiResponse] | [NextRequest, NextResponse] | []

    Returns Promise<default | null | undefined>

    +
  • Defined in session/get-session.ts:11
  • +
  • Defined in session/update-session.ts:27
  • +
  • Defined in session/session.ts:23
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -107,7 +107,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -116,7 +116,7 @@
    +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -110,7 +110,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -119,12 +119,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_CALLBACK_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:172
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -114,7 +114,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -123,7 +123,7 @@
    +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_LOGIN_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:192
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_LOGOUT_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:212
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_PROFILE_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:232
  • +
  • Defined in utils/errors.ts:79
  • @@ -42,32 +42,32 @@

    Enumeration Members

    EXPIRED_ACCESS_TOKEN: "ERR_EXPIRED_ACCESS_TOKEN"
    +
  • Defined in utils/errors.ts:83
  • FAILED_REFRESH_GRANT: "ERR_FAILED_REFRESH_GRANT"
    +
  • Defined in utils/errors.ts:85
  • INSUFFICIENT_SCOPE: "ERR_INSUFFICIENT_SCOPE"
    +
  • Defined in utils/errors.ts:84
  • MISSING_ACCESS_TOKEN: "ERR_MISSING_ACCESS_TOKEN"
    +
  • Defined in utils/errors.ts:81
  • MISSING_REFRESH_TOKEN: "ERR_MISSING_REFRESH_TOKEN"
    +
  • Defined in utils/errors.ts:82
  • MISSING_SESSION: "ERR_MISSING_SESSION"
    +
  • Defined in utils/errors.ts:80
  • +
  • Defined in client/use-user.tsx:190
  • +
  • Defined in client/use-user.tsx:134
  • +
  • Defined in edge.ts:15
  • +
  • Defined in edge.ts:17
  • +
  • Defined in handlers/router-helpers.ts:60
  • +
  • Defined in helpers/testing.ts:23
  • +
  • Defined in index.ts:143
  • +
  • Defined in session/get-access-token.ts:110
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: CallbackOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<CallbackOptions>
  • Returns Handler<CallbackOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: CallbackOptions
  • Returns Handler<CallbackOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: LoginOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<LoginOptions>
  • Returns Handler<LoginOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: LoginOptions
  • Returns Handler<LoginOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: LogoutOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<LogoutOptions>
  • Returns Handler<LogoutOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: LogoutOptions
  • Returns Handler<LogoutOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: ProfileOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<ProfileOptions>
  • Returns Handler<ProfileOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: ProfileOptions
  • Returns Handler<ProfileOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • +
  • Defined in client/with-page-auth-required.tsx:42
  • returnTo?: string
    @@ -89,7 +89,7 @@
    +
  • Defined in client/with-page-auth-required.tsx:32
  • +
  • Defined in config.ts:335
  • +
  • Defined in config.ts:71
  • clientAssertionSigningAlg?: string
    @@ -101,7 +101,7 @@
    +
  • Defined in config.ts:193
  • clientAssertionSigningKey?: string
    @@ -111,7 +111,7 @@
    +
  • Defined in config.ts:184
  • clientID: string
    @@ -120,7 +120,7 @@
    +
  • Defined in config.ts:77
  • clientSecret?: string
    @@ -130,7 +130,7 @@
    +
  • Defined in config.ts:84
  • clockTolerance: number
    @@ -138,7 +138,7 @@
    +
  • Defined in config.ts:91
  • enableTelemetry: boolean
    @@ -148,7 +148,7 @@
    +
  • Defined in config.ts:105
  • getLoginState: ((options) => Record<string, any>)
    @@ -173,7 +173,7 @@
    options: Returns Record<string, any>
    +
  • Defined in config.ts:124
  • httpTimeout: number
    @@ -183,7 +183,7 @@
    +
  • Defined in config.ts:98
  • idTokenSigningAlg: string
    @@ -192,7 +192,7 @@
    +
  • Defined in config.ts:143
  • identityClaimFilter: string[]
    @@ -202,7 +202,7 @@
    +
  • Defined in config.ts:131
  • idpLogout: boolean
    @@ -211,7 +211,7 @@
    +
  • Defined in config.ts:137
  • issuerBaseURL: string
    @@ -221,7 +221,7 @@
    +
  • Defined in config.ts:150
  • legacySameSiteCookie: boolean
    @@ -231,7 +231,7 @@
    +
  • Defined in config.ts:157
  • routes: {
        callback: string;
        postLogoutRedirect: string;
    }
    @@ -257,7 +257,7 @@
    postLogoutRedirect
    +
  • Defined in config.ts:162
  • secret: string | string[]
    @@ -269,7 +269,7 @@
    +
  • Defined in config.ts:18
  • session: SessionConfig
    @@ -277,7 +277,7 @@
    +
  • Defined in config.ts:23
  • +
  • Defined in config.ts:354
  • session: Pick<SessionConfig, "storeIDToken">
    +
  • Defined in config.ts:358
  • +
  • Defined in config.ts:222
  • name: string
    @@ -115,7 +115,7 @@
    +
  • Defined in config.ts:208
  • rolling: boolean
    @@ -128,7 +128,7 @@
    +
  • Defined in config.ts:235
  • rollingDuration: number | false
    @@ -140,7 +140,7 @@
    +
  • Defined in config.ts:244
  • store?: SessionStore<default>
    @@ -149,7 +149,7 @@
    +
  • Defined in config.ts:214
  • storeIDToken: boolean
    @@ -159,7 +159,7 @@
    +
  • Defined in config.ts:268
  • +
  • Defined in handlers/logout.ts:22
  • +
  • Defined in index.ts:85
  • handleLogin: HandleLogin
    @@ -88,7 +88,7 @@
    +
  • Defined in index.ts:80
  • handleLogout: HandleLogout
    @@ -96,7 +96,7 @@
    +
  • Defined in index.ts:90
  • handleProfile: HandleProfile
    @@ -104,7 +104,7 @@
    +
  • Defined in index.ts:95
  • touchSession: TouchSession
    @@ -112,7 +112,7 @@
    +
  • Defined in index.ts:65
  • updateSession: UpdateSession
    @@ -120,7 +120,7 @@
    +
  • Defined in index.ts:70
  • withApiAuthRequired: WithApiAuthRequired
    @@ -128,7 +128,7 @@
    +
  • Defined in index.ts:100
  • withPageAuthRequired: WithPageAuthRequired
    @@ -136,7 +136,7 @@
    +
  • Defined in index.ts:105
  • +
  • Defined in session/get-access-token.ts:45
  • +
  • Defined in client/use-user.tsx:1
  • Index

    diff --git a/docs/modules/client_with_page_auth_required.html b/docs/modules/client_with_page_auth_required.html index e2bcbde64..981ac79c5 100644 --- a/docs/modules/client_with_page_auth_required.html +++ b/docs/modules/client_with_page_auth_required.html @@ -16,7 +16,7 @@
  • client/with-page-auth-required
  • Module client/with-page-auth-required

    +
  • Defined in client/with-page-auth-required.tsx:1
  • Index

    diff --git a/docs/modules/config.html b/docs/modules/config.html index 919af8323..faab62684 100644 --- a/docs/modules/config.html +++ b/docs/modules/config.html @@ -16,7 +16,7 @@
  • config
  • Module config

    +
  • Defined in config.ts:1
  • Index

    diff --git a/docs/modules/edge.html b/docs/modules/edge.html index bc17ecc82..01d106de4 100644 --- a/docs/modules/edge.html +++ b/docs/modules/edge.html @@ -16,7 +16,7 @@
  • edge
  • Module edge

    +
  • Defined in edge.ts:1
  • diff --git a/docs/modules/handlers.html b/docs/modules/handlers.html index 7d6fbbbe3..965a325a1 100644 --- a/docs/modules/handlers.html +++ b/docs/modules/handlers.html @@ -16,7 +16,7 @@
  • handlers
  • Module handlers

    +
  • Defined in handlers/index.ts:1
  • diff --git a/docs/modules/handlers_auth.html b/docs/modules/handlers_auth.html index 9592fd180..605ef612c 100644 --- a/docs/modules/handlers_auth.html +++ b/docs/modules/handlers_auth.html @@ -16,7 +16,7 @@
  • handlers/auth
  • Module handlers/auth

    +
  • Defined in handlers/auth.ts:1
  • Index

    diff --git a/docs/modules/handlers_callback.html b/docs/modules/handlers_callback.html index d17c61fbe..8815be157 100644 --- a/docs/modules/handlers_callback.html +++ b/docs/modules/handlers_callback.html @@ -16,7 +16,7 @@
  • handlers/callback
  • Module handlers/callback

    +
  • Defined in handlers/callback.ts:1
  • Index

    diff --git a/docs/modules/handlers_login.html b/docs/modules/handlers_login.html index 2f987151d..9d409ea70 100644 --- a/docs/modules/handlers_login.html +++ b/docs/modules/handlers_login.html @@ -16,7 +16,7 @@
  • handlers/login
  • Module handlers/login

    +
  • Defined in handlers/login.ts:1
  • Index

    diff --git a/docs/modules/handlers_logout.html b/docs/modules/handlers_logout.html index f91724e57..d42a24b63 100644 --- a/docs/modules/handlers_logout.html +++ b/docs/modules/handlers_logout.html @@ -16,7 +16,7 @@
  • handlers/logout
  • Module handlers/logout

    +
  • Defined in handlers/logout.ts:1
  • Index

    diff --git a/docs/modules/handlers_profile.html b/docs/modules/handlers_profile.html index 39e223655..0b45748a4 100644 --- a/docs/modules/handlers_profile.html +++ b/docs/modules/handlers_profile.html @@ -16,7 +16,7 @@
  • handlers/profile
  • Module handlers/profile

    +
  • Defined in handlers/profile.ts:1
  • Index

    diff --git a/docs/modules/handlers_router_helpers.html b/docs/modules/handlers_router_helpers.html index 486d406b7..bcefe9bf8 100644 --- a/docs/modules/handlers_router_helpers.html +++ b/docs/modules/handlers_router_helpers.html @@ -16,7 +16,7 @@
  • handlers/router-helpers
  • Module handlers/router-helpers

    +
  • Defined in handlers/router-helpers.ts:1
  • Index

    diff --git a/docs/modules/helpers.html b/docs/modules/helpers.html index 246aa914e..c6b36ad39 100644 --- a/docs/modules/helpers.html +++ b/docs/modules/helpers.html @@ -16,7 +16,7 @@
  • helpers
  • Module helpers

    +
  • Defined in helpers/index.ts:1
  • diff --git a/docs/modules/helpers_testing.html b/docs/modules/helpers_testing.html index dabfb5b82..e5b89f3ab 100644 --- a/docs/modules/helpers_testing.html +++ b/docs/modules/helpers_testing.html @@ -16,7 +16,7 @@
  • helpers/testing
  • Module helpers/testing

    +
  • Defined in helpers/testing.ts:1
  • Index

    diff --git a/docs/modules/helpers_with_api_auth_required.html b/docs/modules/helpers_with_api_auth_required.html index 692604bf5..b557d560c 100644 --- a/docs/modules/helpers_with_api_auth_required.html +++ b/docs/modules/helpers_with_api_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-api-auth-required
  • Module helpers/with-api-auth-required

    +
  • Defined in helpers/with-api-auth-required.ts:1
  • Index

    diff --git a/docs/modules/helpers_with_middleware_auth_required.html b/docs/modules/helpers_with_middleware_auth_required.html index fe4bdb6fa..377c9df33 100644 --- a/docs/modules/helpers_with_middleware_auth_required.html +++ b/docs/modules/helpers_with_middleware_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-middleware-auth-required
  • Module helpers/with-middleware-auth-required

    +
  • Defined in helpers/with-middleware-auth-required.ts:1
  • Index

    diff --git a/docs/modules/helpers_with_page_auth_required.html b/docs/modules/helpers_with_page_auth_required.html index eec787546..5f5c444b4 100644 --- a/docs/modules/helpers_with_page_auth_required.html +++ b/docs/modules/helpers_with_page_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-page-auth-required
  • Module helpers/with-page-auth-required

    +
  • Defined in helpers/with-page-auth-required.ts:1
  • Index

    diff --git a/docs/modules/http.html b/docs/modules/http.html index bd4c0e58b..e9da8493b 100644 --- a/docs/modules/http.html +++ b/docs/modules/http.html @@ -16,7 +16,7 @@
  • http
  • Module http

    +
  • Defined in http/index.ts:1
  • diff --git a/docs/modules/http_auth0_next_api_request.html b/docs/modules/http_auth0_next_api_request.html index ca0e31d01..b7c5c0cb7 100644 --- a/docs/modules/http_auth0_next_api_request.html +++ b/docs/modules/http_auth0_next_api_request.html @@ -16,7 +16,7 @@
  • http/auth0-next-api-request
  • Module http/auth0-next-api-request

    +
  • Defined in http/auth0-next-api-request.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_api_response.html b/docs/modules/http_auth0_next_api_response.html index 2c8b8a19f..e19b902ca 100644 --- a/docs/modules/http_auth0_next_api_response.html +++ b/docs/modules/http_auth0_next_api_response.html @@ -16,7 +16,7 @@
  • http/auth0-next-api-response
  • Module http/auth0-next-api-response

    +
  • Defined in http/auth0-next-api-response.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_request.html b/docs/modules/http_auth0_next_request.html index 453a7ca32..21432e5ed 100644 --- a/docs/modules/http_auth0_next_request.html +++ b/docs/modules/http_auth0_next_request.html @@ -16,7 +16,7 @@
  • http/auth0-next-request
  • Module http/auth0-next-request

    +
  • Defined in http/auth0-next-request.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_request_cookies.html b/docs/modules/http_auth0_next_request_cookies.html index 07cf3e8b6..4e990c61e 100644 --- a/docs/modules/http_auth0_next_request_cookies.html +++ b/docs/modules/http_auth0_next_request_cookies.html @@ -16,7 +16,7 @@
  • http/auth0-next-request-cookies
  • Module http/auth0-next-request-cookies

    +
  • Defined in http/auth0-next-request-cookies.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_response.html b/docs/modules/http_auth0_next_response.html index 96170ec27..1905cef35 100644 --- a/docs/modules/http_auth0_next_response.html +++ b/docs/modules/http_auth0_next_response.html @@ -16,7 +16,7 @@
  • http/auth0-next-response
  • Module http/auth0-next-response

    +
  • Defined in http/auth0-next-response.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_response_cookies.html b/docs/modules/http_auth0_next_response_cookies.html index 8634c6ec3..08cc49259 100644 --- a/docs/modules/http_auth0_next_response_cookies.html +++ b/docs/modules/http_auth0_next_response_cookies.html @@ -16,7 +16,7 @@
  • http/auth0-next-response-cookies
  • Module http/auth0-next-response-cookies

    +
  • Defined in http/auth0-next-response-cookies.ts:1
  • Index

    diff --git a/docs/modules/index.html b/docs/modules/index.html index 51fca95fd..51cd5b1bf 100644 --- a/docs/modules/index.html +++ b/docs/modules/index.html @@ -16,7 +16,7 @@
  • index
  • Module index

    +
  • Defined in index.ts:1
  • diff --git a/docs/modules/session.html b/docs/modules/session.html index 98c2ef3e4..74666c4ea 100644 --- a/docs/modules/session.html +++ b/docs/modules/session.html @@ -16,7 +16,7 @@
  • session
  • Module session

    +
  • Defined in session/index.ts:1
  • diff --git a/docs/modules/session_get_access_token.html b/docs/modules/session_get_access_token.html index 97ac0c400..3b734d498 100644 --- a/docs/modules/session_get_access_token.html +++ b/docs/modules/session_get_access_token.html @@ -16,7 +16,7 @@
  • session/get-access-token
  • Module session/get-access-token

    +
  • Defined in session/get-access-token.ts:1
  • Index

    diff --git a/docs/modules/session_get_session.html b/docs/modules/session_get_session.html index 1ee67a3bf..1e2cb335b 100644 --- a/docs/modules/session_get_session.html +++ b/docs/modules/session_get_session.html @@ -16,7 +16,7 @@
  • session/get-session
  • Module session/get-session

    +
  • Defined in session/get-session.ts:1
  • Index

    diff --git a/docs/modules/session_session.html b/docs/modules/session_session.html index 12a4166ff..cf96593fe 100644 --- a/docs/modules/session_session.html +++ b/docs/modules/session_session.html @@ -16,7 +16,7 @@
  • session/session
  • Module session/session

    +
  • Defined in session/session.ts:1
  • Index

    diff --git a/docs/modules/session_touch_session.html b/docs/modules/session_touch_session.html index 134cdfdbe..6912127e1 100644 --- a/docs/modules/session_touch_session.html +++ b/docs/modules/session_touch_session.html @@ -16,7 +16,7 @@
  • session/touch-session
  • Module session/touch-session

    +
  • Defined in session/touch-session.ts:1
  • Index

    diff --git a/docs/modules/session_update_session.html b/docs/modules/session_update_session.html index 1f61297bc..90715ceb7 100644 --- a/docs/modules/session_update_session.html +++ b/docs/modules/session_update_session.html @@ -16,7 +16,7 @@
  • session/update-session
  • Module session/update-session

    +
  • Defined in session/update-session.ts:1
  • Index

    diff --git a/docs/modules/utils_errors.html b/docs/modules/utils_errors.html index eca918271..2e270b51e 100644 --- a/docs/modules/utils_errors.html +++ b/docs/modules/utils_errors.html @@ -16,7 +16,7 @@
  • utils/errors
  • Module utils/errors

    +
  • Defined in utils/errors.ts:1
  • Index

    diff --git a/docs/modules/version.html b/docs/modules/version.html index 0cb2c8cba..d47c2013e 100644 --- a/docs/modules/version.html +++ b/docs/modules/version.html @@ -16,7 +16,7 @@
  • version
  • Module version

    +
  • Defined in version.ts:1
  • Index

    diff --git a/docs/types/client_use_user.UserContext.html b/docs/types/client_use_user.UserContext.html index 14532f06b..770710bec 100644 --- a/docs/types/client_use_user.UserContext.html +++ b/docs/types/client_use_user.UserContext.html @@ -38,8 +38,8 @@
    isLoadingOptional user?: UserProfile
    +
  • Defined in client/use-user.tsx:28
  • +
  • Defined in client/use-user.tsx:116
  • +
  • Defined in client/use-user.tsx:164
  • +
  • Defined in client/use-user.tsx:104
  • +
  • Defined in client/with-page-auth-required.tsx:69
  • +
  • Defined in config.ts:449
  • +
  • Defined in edge.ts:13
  • +
  • Defined in edge.ts:15
  • +
  • Defined in edge.ts:17
  • +
  • Defined in handlers/auth.ts:143
  • +
  • Defined in handlers/auth.ts:115
  • +
  • Defined in handlers/auth.ts:64
  • +
  • Defined in handlers/auth.ts:138
  • +
  • Defined in handlers/callback.ts:21
  • +
  • Defined in handlers/callback.ts:175
  • +
  • Defined in handlers/callback.ts:103
  • +
  • Defined in handlers/callback.ts:277
  • +
  • Defined in handlers/callback.ts:216
  • +
  • Defined in handlers/callback.ts:268
  • +
  • Defined in handlers/login.ts:18
  • +
  • Defined in handlers/login.ts:58
  • +
  • Defined in handlers/login.ts:38
  • +
  • Defined in handlers/login.ts:248
  • +
  • Defined in handlers/login.ts:257
  • +
  • Defined in handlers/login.ts:192
  • +
  • Defined in handlers/logout.ts:88
  • +
  • Defined in handlers/logout.ts:97
  • +
  • Defined in handlers/logout.ts:36
  • +
  • Defined in handlers/profile.ts:14
  • +
  • Defined in handlers/profile.ts:32
  • +
  • Defined in handlers/profile.ts:21
  • +
  • Defined in handlers/profile.ts:112
  • +
  • Defined in handlers/profile.ts:121
  • +
  • Defined in handlers/profile.ts:41
  • +
  • Defined in handlers/profile.ts:62
  • +
  • Defined in handlers/router-helpers.ts:12
  • +
  • Defined in handlers/router-helpers.ts:5
  • +
  • Defined in handlers/router-helpers.ts:45
  • +
  • Defined in handlers/router-helpers.ts:50
  • +
  • Defined in handlers/router-helpers.ts:43
  • +
  • Defined in handlers/router-helpers.ts:29
  • +
  • Defined in helpers/testing.ts:7
  • +
  • Defined in helpers/with-api-auth-required.ts:25
  • +
  • Defined in helpers/with-api-auth-required.ts:14
  • +
  • Defined in helpers/with-api-auth-required.ts:83
  • +
  • Defined in helpers/with-api-auth-required.ts:55
  • +
  • Defined in helpers/with-api-auth-required.ts:75
  • +
  • Defined in helpers/with-middleware-auth-required.ts:46
  • +
  • Defined in helpers/with-page-auth-required.ts:51
  • +
  • Defined in helpers/with-page-auth-required.ts:41
  • +
  • Defined in helpers/with-page-auth-required.ts:25
  • +
  • Defined in helpers/with-page-auth-required.ts:32
  • +
  • Defined in helpers/with-page-auth-required.ts:178
  • +
  • Defined in helpers/with-page-auth-required.ts:167
  • +
  • Defined in helpers/with-page-auth-required.ts:125
  • +
  • Defined in helpers/with-page-auth-required.ts:112
  • +
  • Defined in helpers/with-page-auth-required.ts:84
  • +
  • Defined in index.ts:120
  • +
  • Defined in index.ts:282
  • +
  • Defined in index.ts:283
  • +
  • Defined in session/get-access-token.ts:16
  • +
  • Defined in session/get-access-token.ts:34
  • +
  • Defined in session/get-access-token.ts:23
  • +
  • Defined in session/get-access-token.ts:110
  • +
  • Defined in session/get-session.ts:11
  • +
  • Defined in session/touch-session.ts:23
  • +
  • Defined in session/update-session.ts:27
  • +
  • Defined in version.ts:1
  • +
  • Defined in client/use-user.tsx:46
  • @@ -60,14 +60,14 @@
    status: Returns RequestError
    +
  • Defined in client/use-user.tsx:49
  • Properties

    status: number
    +
  • Defined in client/use-user.tsx:47
  • +
  • Defined in http/auth0-next-api-request.ts:4
  • @@ -55,7 +55,7 @@
    req: Returns default
    +
  • Defined in http/auth0-next-api-request.ts:5
  • Methods

    @@ -66,7 +66,7 @@
    +
  • Defined in http/auth0-next-api-request.ts:16
  • +
  • Defined in http/auth0-next-api-request.ts:19
  • +
  • Defined in http/auth0-next-api-request.ts:13
  • +
  • Defined in http/auth0-next-api-request.ts:10
  • +
  • Defined in http/auth0-next-api-response.ts:4
  • @@ -52,7 +52,7 @@
    res: Returns default
    +
  • Defined in auth0-session/http/node-response.ts:7
  • Methods

    @@ -70,7 +70,7 @@
    status: Returns void
    +
  • Defined in http/auth0-next-api-response.ts:5
  • +
  • Defined in http/auth0-next-request.ts:4
  • @@ -55,7 +55,7 @@
    req: Returns default
    +
  • Defined in http/auth0-next-request.ts:5
  • Methods

    @@ -66,7 +66,7 @@
    +
  • Defined in http/auth0-next-request.ts:16
  • +
  • Defined in http/auth0-next-request.ts:19
  • +
  • Defined in http/auth0-next-request.ts:13
  • +
  • Defined in http/auth0-next-request.ts:10
  • +
  • Defined in http/auth0-next-request-cookies.ts:4
  • @@ -47,7 +47,7 @@
    +
  • Defined in http/auth0-next-request-cookies.ts:5
  • Methods

    @@ -58,7 +58,7 @@
    +
  • Defined in http/auth0-next-request-cookies.ts:9
  • +
  • Defined in http/auth0-next-response.ts:5
  • @@ -54,7 +54,7 @@
    res: Returns default
    +
  • Defined in http/auth0-next-response.ts:6
  • Methods

    @@ -72,7 +72,7 @@
    Optional Returns void
    +
  • Defined in http/auth0-next-response.ts:15
  • +
  • Defined in http/auth0-next-response.ts:19
  • +
  • Defined in http/auth0-next-response.ts:11
  • +
  • Defined in http/auth0-next-response-cookies.ts:7
  • @@ -48,7 +48,7 @@
    +
  • Defined in http/auth0-next-response-cookies.ts:8
  • Methods

    @@ -66,7 +66,7 @@
    Optional Returns void
    +
  • Defined in http/auth0-next-response-cookies.ts:30
  • +
  • Defined in http/auth0-next-response-cookies.ts:12
  • +
  • Defined in session/session.ts:19
  • @@ -61,7 +61,7 @@

    Parameters

    user: Claims

    Returns default

    +
  • Defined in session/session.ts:55
  • Properties

    @@ -71,7 +71,7 @@
    +
  • Defined in session/session.ts:33
  • accessTokenExpiresAt?: number
    @@ -79,7 +79,7 @@
    +
  • Defined in session/session.ts:43
  • accessTokenScope?: string
    @@ -87,7 +87,7 @@
    +
  • Defined in session/session.ts:38
  • idToken?: string
    @@ -95,7 +95,7 @@
    +
  • Defined in session/session.ts:28
  • refreshToken?: string
    @@ -105,7 +105,7 @@
    +
  • Defined in session/session.ts:51
  • user: Claims
    @@ -113,7 +113,7 @@
    +
  • Defined in session/session.ts:23
  • +
  • Defined in utils/errors.ts:99
  • @@ -71,7 +71,7 @@
    Optional Returns AccessTokenError
    +
  • Defined in utils/errors.ts:100
  • Properties

    @@ -86,7 +86,7 @@
    +
  • Defined in utils/errors.ts:57
  • code: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -107,7 +107,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -116,7 +116,7 @@
    +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -110,7 +110,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -119,12 +119,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_CALLBACK_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:172
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -114,7 +114,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -123,7 +123,7 @@
    +
  • Defined in utils/errors.ts:62
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_LOGIN_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:192
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_LOGOUT_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:212
  • +
  • Defined in utils/errors.ts:41
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:46
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:62
  • code: string = 'ERR_PROFILE_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:232
  • +
  • Defined in utils/errors.ts:79
  • @@ -42,32 +42,32 @@

    Enumeration Members

    EXPIRED_ACCESS_TOKEN: "ERR_EXPIRED_ACCESS_TOKEN"
    +
  • Defined in utils/errors.ts:83
  • FAILED_REFRESH_GRANT: "ERR_FAILED_REFRESH_GRANT"
    +
  • Defined in utils/errors.ts:85
  • INSUFFICIENT_SCOPE: "ERR_INSUFFICIENT_SCOPE"
    +
  • Defined in utils/errors.ts:84
  • MISSING_ACCESS_TOKEN: "ERR_MISSING_ACCESS_TOKEN"
    +
  • Defined in utils/errors.ts:81
  • MISSING_REFRESH_TOKEN: "ERR_MISSING_REFRESH_TOKEN"
    +
  • Defined in utils/errors.ts:82
  • MISSING_SESSION: "ERR_MISSING_SESSION"
    +
  • Defined in utils/errors.ts:80
  • +
  • Defined in client/use-user.tsx:190
  • +
  • Defined in client/use-user.tsx:134
  • +
  • Defined in edge.ts:15
  • +
  • Defined in edge.ts:17
  • +
  • Defined in handlers/router-helpers.ts:60
  • +
  • Defined in helpers/testing.ts:23
  • +
  • Defined in index.ts:143
  • +
  • Defined in session/get-access-token.ts:110
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: CallbackOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<CallbackOptions>
  • Returns Handler<CallbackOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: CallbackOptions
  • Returns Handler<CallbackOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: LoginOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<LoginOptions>
  • Returns Handler<LoginOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: LoginOptions
  • Returns Handler<LoginOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: LogoutOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<LogoutOptions>
  • Returns Handler<LogoutOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: LogoutOptions
  • Returns Handler<LogoutOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: ProfileOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<ProfileOptions>
  • Returns Handler<ProfileOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: ProfileOptions
  • Returns Handler<ProfileOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • +
  • Defined in client/with-page-auth-required.tsx:42
  • returnTo?: string
    @@ -89,7 +89,7 @@
    +
  • Defined in client/with-page-auth-required.tsx:32
  • +
  • Defined in config.ts:335
  • +
  • Defined in config.ts:71
  • clientAssertionSigningAlg?: string
    @@ -101,7 +101,7 @@
    +
  • Defined in config.ts:193
  • clientAssertionSigningKey?: string
    @@ -111,7 +111,7 @@
    +
  • Defined in config.ts:184
  • clientID: string
    @@ -120,7 +120,7 @@
    +
  • Defined in config.ts:77
  • clientSecret?: string
    @@ -130,7 +130,7 @@
    +
  • Defined in config.ts:84
  • clockTolerance: number
    @@ -138,7 +138,7 @@
    +
  • Defined in config.ts:91
  • enableTelemetry: boolean
    @@ -148,7 +148,7 @@
    +
  • Defined in config.ts:105
  • getLoginState: ((options) => Record<string, any>)
    @@ -173,7 +173,7 @@
    options: Returns Record<string, any>
    +
  • Defined in config.ts:124
  • httpTimeout: number
    @@ -183,7 +183,7 @@
    +
  • Defined in config.ts:98
  • idTokenSigningAlg: string
    @@ -192,7 +192,7 @@
    +
  • Defined in config.ts:143
  • identityClaimFilter: string[]
    @@ -202,7 +202,7 @@
    +
  • Defined in config.ts:131
  • idpLogout: boolean
    @@ -211,7 +211,7 @@
    +
  • Defined in config.ts:137
  • issuerBaseURL: string
    @@ -221,7 +221,7 @@
    +
  • Defined in config.ts:150
  • legacySameSiteCookie: boolean
    @@ -231,7 +231,7 @@
    +
  • Defined in config.ts:157
  • routes: {
        callback: string;
        postLogoutRedirect: string;
    }
    @@ -257,7 +257,7 @@
    postLogoutRedirect
    +
  • Defined in config.ts:162
  • secret: string | string[]
    @@ -269,7 +269,7 @@
    +
  • Defined in config.ts:18
  • session: SessionConfig
    @@ -277,7 +277,7 @@
    +
  • Defined in config.ts:23
  • +
  • Defined in config.ts:354
  • session: Pick<SessionConfig, "storeIDToken">
    +
  • Defined in config.ts:358
  • +
  • Defined in config.ts:222
  • name: string
    @@ -115,7 +115,7 @@
    +
  • Defined in config.ts:208
  • rolling: boolean
    @@ -128,7 +128,7 @@
    +
  • Defined in config.ts:235
  • rollingDuration: number | false
    @@ -140,7 +140,7 @@
    +
  • Defined in config.ts:244
  • store?: SessionStore<default>
    @@ -149,7 +149,7 @@
    +
  • Defined in config.ts:214
  • storeIDToken: boolean
    @@ -159,7 +159,7 @@
    +
  • Defined in config.ts:268
  • +
  • Defined in handlers/logout.ts:22
  • +
  • Defined in index.ts:85
  • handleLogin: HandleLogin
    @@ -88,7 +88,7 @@
    +
  • Defined in index.ts:80
  • handleLogout: HandleLogout
    @@ -96,7 +96,7 @@
    +
  • Defined in index.ts:90
  • handleProfile: HandleProfile
    @@ -104,7 +104,7 @@
    +
  • Defined in index.ts:95
  • touchSession: TouchSession
    @@ -112,7 +112,7 @@
    +
  • Defined in index.ts:65
  • updateSession: UpdateSession
    @@ -120,7 +120,7 @@
    +
  • Defined in index.ts:70
  • withApiAuthRequired: WithApiAuthRequired
    @@ -128,7 +128,7 @@
    +
  • Defined in index.ts:100
  • withPageAuthRequired: WithPageAuthRequired
    @@ -136,7 +136,7 @@
    +
  • Defined in index.ts:105
  • +
  • Defined in session/get-access-token.ts:45
  • +
  • Defined in client/use-user.tsx:1
  • Index

    diff --git a/docs/modules/client_with_page_auth_required.html b/docs/modules/client_with_page_auth_required.html index 981ac79c5..96e9a3395 100644 --- a/docs/modules/client_with_page_auth_required.html +++ b/docs/modules/client_with_page_auth_required.html @@ -16,7 +16,7 @@
  • client/with-page-auth-required
  • Module client/with-page-auth-required

    +
  • Defined in client/with-page-auth-required.tsx:1
  • Index

    diff --git a/docs/modules/config.html b/docs/modules/config.html index faab62684..c262a7318 100644 --- a/docs/modules/config.html +++ b/docs/modules/config.html @@ -16,7 +16,7 @@
  • config
  • Module config

    +
  • Defined in config.ts:1
  • Index

    diff --git a/docs/modules/edge.html b/docs/modules/edge.html index 01d106de4..6bf92cc18 100644 --- a/docs/modules/edge.html +++ b/docs/modules/edge.html @@ -16,7 +16,7 @@
  • edge
  • Module edge

    +
  • Defined in edge.ts:1
  • diff --git a/docs/modules/handlers.html b/docs/modules/handlers.html index 965a325a1..3ff03957c 100644 --- a/docs/modules/handlers.html +++ b/docs/modules/handlers.html @@ -16,7 +16,7 @@
  • handlers
  • Module handlers

    +
  • Defined in handlers/index.ts:1
  • diff --git a/docs/modules/handlers_auth.html b/docs/modules/handlers_auth.html index 605ef612c..3d5cc4bc1 100644 --- a/docs/modules/handlers_auth.html +++ b/docs/modules/handlers_auth.html @@ -16,7 +16,7 @@
  • handlers/auth
  • Module handlers/auth

    +
  • Defined in handlers/auth.ts:1
  • Index

    diff --git a/docs/modules/handlers_callback.html b/docs/modules/handlers_callback.html index 8815be157..1d3a2e63b 100644 --- a/docs/modules/handlers_callback.html +++ b/docs/modules/handlers_callback.html @@ -16,7 +16,7 @@
  • handlers/callback
  • Module handlers/callback

    +
  • Defined in handlers/callback.ts:1
  • Index

    diff --git a/docs/modules/handlers_login.html b/docs/modules/handlers_login.html index 9d409ea70..51976bcdd 100644 --- a/docs/modules/handlers_login.html +++ b/docs/modules/handlers_login.html @@ -16,7 +16,7 @@
  • handlers/login
  • Module handlers/login

    +
  • Defined in handlers/login.ts:1
  • Index

    diff --git a/docs/modules/handlers_logout.html b/docs/modules/handlers_logout.html index d42a24b63..d11d07cdf 100644 --- a/docs/modules/handlers_logout.html +++ b/docs/modules/handlers_logout.html @@ -16,7 +16,7 @@
  • handlers/logout
  • Module handlers/logout

    +
  • Defined in handlers/logout.ts:1
  • Index

    diff --git a/docs/modules/handlers_profile.html b/docs/modules/handlers_profile.html index 0b45748a4..0d7656b84 100644 --- a/docs/modules/handlers_profile.html +++ b/docs/modules/handlers_profile.html @@ -16,7 +16,7 @@
  • handlers/profile
  • Module handlers/profile

    +
  • Defined in handlers/profile.ts:1
  • Index

    diff --git a/docs/modules/handlers_router_helpers.html b/docs/modules/handlers_router_helpers.html index bcefe9bf8..746be0e98 100644 --- a/docs/modules/handlers_router_helpers.html +++ b/docs/modules/handlers_router_helpers.html @@ -16,7 +16,7 @@
  • handlers/router-helpers
  • Module handlers/router-helpers

    +
  • Defined in handlers/router-helpers.ts:1
  • Index

    diff --git a/docs/modules/helpers.html b/docs/modules/helpers.html index c6b36ad39..90aa97d77 100644 --- a/docs/modules/helpers.html +++ b/docs/modules/helpers.html @@ -16,7 +16,7 @@
  • helpers
  • Module helpers

    +
  • Defined in helpers/index.ts:1
  • diff --git a/docs/modules/helpers_testing.html b/docs/modules/helpers_testing.html index e5b89f3ab..2978e09ce 100644 --- a/docs/modules/helpers_testing.html +++ b/docs/modules/helpers_testing.html @@ -16,7 +16,7 @@
  • helpers/testing
  • Module helpers/testing

    +
  • Defined in helpers/testing.ts:1
  • Index

    diff --git a/docs/modules/helpers_with_api_auth_required.html b/docs/modules/helpers_with_api_auth_required.html index b557d560c..be7864198 100644 --- a/docs/modules/helpers_with_api_auth_required.html +++ b/docs/modules/helpers_with_api_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-api-auth-required
  • Module helpers/with-api-auth-required

    +
  • Defined in helpers/with-api-auth-required.ts:1
  • Index

    diff --git a/docs/modules/helpers_with_middleware_auth_required.html b/docs/modules/helpers_with_middleware_auth_required.html index 377c9df33..c96c7c01b 100644 --- a/docs/modules/helpers_with_middleware_auth_required.html +++ b/docs/modules/helpers_with_middleware_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-middleware-auth-required
  • Module helpers/with-middleware-auth-required

    +
  • Defined in helpers/with-middleware-auth-required.ts:1
  • Index

    diff --git a/docs/modules/helpers_with_page_auth_required.html b/docs/modules/helpers_with_page_auth_required.html index 5f5c444b4..4e2de1be2 100644 --- a/docs/modules/helpers_with_page_auth_required.html +++ b/docs/modules/helpers_with_page_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-page-auth-required
  • Module helpers/with-page-auth-required

    +
  • Defined in helpers/with-page-auth-required.ts:1
  • Index

    diff --git a/docs/modules/http.html b/docs/modules/http.html index e9da8493b..6e0ade3bb 100644 --- a/docs/modules/http.html +++ b/docs/modules/http.html @@ -16,7 +16,7 @@
  • http
  • Module http

    +
  • Defined in http/index.ts:1
  • diff --git a/docs/modules/http_auth0_next_api_request.html b/docs/modules/http_auth0_next_api_request.html index b7c5c0cb7..efc0905c0 100644 --- a/docs/modules/http_auth0_next_api_request.html +++ b/docs/modules/http_auth0_next_api_request.html @@ -16,7 +16,7 @@
  • http/auth0-next-api-request
  • Module http/auth0-next-api-request

    +
  • Defined in http/auth0-next-api-request.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_api_response.html b/docs/modules/http_auth0_next_api_response.html index e19b902ca..17d926c9e 100644 --- a/docs/modules/http_auth0_next_api_response.html +++ b/docs/modules/http_auth0_next_api_response.html @@ -16,7 +16,7 @@
  • http/auth0-next-api-response
  • Module http/auth0-next-api-response

    +
  • Defined in http/auth0-next-api-response.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_request.html b/docs/modules/http_auth0_next_request.html index 21432e5ed..96d156d52 100644 --- a/docs/modules/http_auth0_next_request.html +++ b/docs/modules/http_auth0_next_request.html @@ -16,7 +16,7 @@
  • http/auth0-next-request
  • Module http/auth0-next-request

    +
  • Defined in http/auth0-next-request.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_request_cookies.html b/docs/modules/http_auth0_next_request_cookies.html index 4e990c61e..c2733c7a5 100644 --- a/docs/modules/http_auth0_next_request_cookies.html +++ b/docs/modules/http_auth0_next_request_cookies.html @@ -16,7 +16,7 @@
  • http/auth0-next-request-cookies
  • Module http/auth0-next-request-cookies

    +
  • Defined in http/auth0-next-request-cookies.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_response.html b/docs/modules/http_auth0_next_response.html index 1905cef35..fec9a732d 100644 --- a/docs/modules/http_auth0_next_response.html +++ b/docs/modules/http_auth0_next_response.html @@ -16,7 +16,7 @@
  • http/auth0-next-response
  • Module http/auth0-next-response

    +
  • Defined in http/auth0-next-response.ts:1
  • Index

    diff --git a/docs/modules/http_auth0_next_response_cookies.html b/docs/modules/http_auth0_next_response_cookies.html index 08cc49259..70a254b23 100644 --- a/docs/modules/http_auth0_next_response_cookies.html +++ b/docs/modules/http_auth0_next_response_cookies.html @@ -16,7 +16,7 @@
  • http/auth0-next-response-cookies
  • Module http/auth0-next-response-cookies

    +
  • Defined in http/auth0-next-response-cookies.ts:1
  • Index

    diff --git a/docs/modules/index.html b/docs/modules/index.html index 51cd5b1bf..20f4d09b4 100644 --- a/docs/modules/index.html +++ b/docs/modules/index.html @@ -16,7 +16,7 @@
  • index
  • Module index

    +
  • Defined in index.ts:1
  • diff --git a/docs/modules/session.html b/docs/modules/session.html index 74666c4ea..7bd3578c1 100644 --- a/docs/modules/session.html +++ b/docs/modules/session.html @@ -16,7 +16,7 @@
  • session
  • Module session

    +
  • Defined in session/index.ts:1
  • diff --git a/docs/modules/session_get_access_token.html b/docs/modules/session_get_access_token.html index 3b734d498..c787507d3 100644 --- a/docs/modules/session_get_access_token.html +++ b/docs/modules/session_get_access_token.html @@ -16,7 +16,7 @@
  • session/get-access-token
  • Module session/get-access-token

    +
  • Defined in session/get-access-token.ts:1
  • Index

    diff --git a/docs/modules/session_get_session.html b/docs/modules/session_get_session.html index 1e2cb335b..bb63444be 100644 --- a/docs/modules/session_get_session.html +++ b/docs/modules/session_get_session.html @@ -16,7 +16,7 @@
  • session/get-session
  • Module session/get-session

    +
  • Defined in session/get-session.ts:1
  • Index

    diff --git a/docs/modules/session_session.html b/docs/modules/session_session.html index cf96593fe..86501bc49 100644 --- a/docs/modules/session_session.html +++ b/docs/modules/session_session.html @@ -16,7 +16,7 @@
  • session/session
  • Module session/session

    +
  • Defined in session/session.ts:1
  • Index

    diff --git a/docs/modules/session_touch_session.html b/docs/modules/session_touch_session.html index 6912127e1..4e64dc5df 100644 --- a/docs/modules/session_touch_session.html +++ b/docs/modules/session_touch_session.html @@ -16,7 +16,7 @@
  • session/touch-session
  • Module session/touch-session

    +
  • Defined in session/touch-session.ts:1
  • Index

    diff --git a/docs/modules/session_update_session.html b/docs/modules/session_update_session.html index 90715ceb7..c2fa92ca8 100644 --- a/docs/modules/session_update_session.html +++ b/docs/modules/session_update_session.html @@ -16,7 +16,7 @@
  • session/update-session
  • Module session/update-session

    +
  • Defined in session/update-session.ts:1
  • Index

    diff --git a/docs/modules/utils_errors.html b/docs/modules/utils_errors.html index 2e270b51e..71cc5e0e4 100644 --- a/docs/modules/utils_errors.html +++ b/docs/modules/utils_errors.html @@ -16,7 +16,7 @@
  • utils/errors
  • Module utils/errors

    +
  • Defined in utils/errors.ts:1
  • Index

    diff --git a/docs/modules/version.html b/docs/modules/version.html index d47c2013e..a15f6d163 100644 --- a/docs/modules/version.html +++ b/docs/modules/version.html @@ -16,7 +16,7 @@
  • version
  • Module version

    +
  • Defined in version.ts:1
  • Index

    diff --git a/docs/types/client_use_user.UserContext.html b/docs/types/client_use_user.UserContext.html index 770710bec..f1e53fa22 100644 --- a/docs/types/client_use_user.UserContext.html +++ b/docs/types/client_use_user.UserContext.html @@ -38,8 +38,8 @@
    isLoadingOptional user?: UserProfile
    +
  • Defined in client/use-user.tsx:28
  • +
  • Defined in client/use-user.tsx:116
  • +
  • Defined in client/use-user.tsx:164
  • +
  • Defined in client/use-user.tsx:104
  • +
  • Defined in client/with-page-auth-required.tsx:69
  • +
  • Defined in config.ts:449
  • +
  • Defined in edge.ts:13
  • +
  • Defined in edge.ts:15
  • +
  • Defined in edge.ts:17
  • +
  • Defined in handlers/auth.ts:143
  • +
  • Defined in handlers/auth.ts:115
  • +
  • Defined in handlers/auth.ts:64
  • +
  • Defined in handlers/auth.ts:138
  • +
  • Defined in handlers/callback.ts:21
  • +
  • Defined in handlers/callback.ts:175
  • +
  • Defined in handlers/callback.ts:103
  • +
  • Defined in handlers/callback.ts:277
  • +
  • Defined in handlers/callback.ts:216
  • +
  • Defined in handlers/callback.ts:268
  • +
  • Defined in handlers/login.ts:18
  • +
  • Defined in handlers/login.ts:58
  • +
  • Defined in handlers/login.ts:38
  • +
  • Defined in handlers/login.ts:248
  • +
  • Defined in handlers/login.ts:257
  • +
  • Defined in handlers/login.ts:192
  • +
  • Defined in handlers/logout.ts:88
  • +
  • Defined in handlers/logout.ts:97
  • +
  • Defined in handlers/logout.ts:36
  • +
  • Defined in handlers/profile.ts:14
  • +
  • Defined in handlers/profile.ts:32
  • +
  • Defined in handlers/profile.ts:21
  • +
  • Defined in handlers/profile.ts:112
  • +
  • Defined in handlers/profile.ts:121
  • +
  • Defined in handlers/profile.ts:41
  • +
  • Defined in handlers/profile.ts:62
  • +
  • Defined in handlers/router-helpers.ts:12
  • +
  • Defined in handlers/router-helpers.ts:5
  • +
  • Defined in handlers/router-helpers.ts:45
  • +
  • Defined in handlers/router-helpers.ts:50
  • +
  • Defined in handlers/router-helpers.ts:43
  • +
  • Defined in handlers/router-helpers.ts:29
  • +
  • Defined in helpers/testing.ts:7
  • +
  • Defined in helpers/with-api-auth-required.ts:25
  • +
  • Defined in helpers/with-api-auth-required.ts:14
  • +
  • Defined in helpers/with-api-auth-required.ts:83
  • +
  • Defined in helpers/with-api-auth-required.ts:55
  • +
  • Defined in helpers/with-api-auth-required.ts:75
  • +
  • Defined in helpers/with-middleware-auth-required.ts:46
  • +
  • Defined in helpers/with-page-auth-required.ts:51
  • +
  • Defined in helpers/with-page-auth-required.ts:41
  • +
  • Defined in helpers/with-page-auth-required.ts:25
  • +
  • Defined in helpers/with-page-auth-required.ts:32
  • +
  • Defined in helpers/with-page-auth-required.ts:178
  • +
  • Defined in helpers/with-page-auth-required.ts:167
  • +
  • Defined in helpers/with-page-auth-required.ts:125
  • +
  • Defined in helpers/with-page-auth-required.ts:112
  • +
  • Defined in helpers/with-page-auth-required.ts:84
  • +
  • Defined in index.ts:120
  • +
  • Defined in index.ts:282
  • +
  • Defined in index.ts:283
  • +
  • Defined in session/get-access-token.ts:16
  • +
  • Defined in session/get-access-token.ts:34
  • +
  • Defined in session/get-access-token.ts:23
  • +
  • Defined in session/get-access-token.ts:110
  • +
  • Defined in session/get-session.ts:11
  • +
  • Defined in session/touch-session.ts:23
  • +
  • Defined in session/update-session.ts:27
  • +
  • Defined in version.ts:1
  • +
  • Defined in client/use-user.tsx:46
  • @@ -60,14 +60,14 @@
    status: Returns RequestError
    +
  • Defined in client/use-user.tsx:49
  • Properties

    status: number
    +
  • Defined in client/use-user.tsx:47
  • \ No newline at end of file diff --git a/docs/classes/http_auth0_next_api_request.default.html b/docs/classes/http_auth0_next_api_request.default.html index 0339791f3..f55704749 100644 --- a/docs/classes/http_auth0_next_api_request.default.html +++ b/docs/classes/http_auth0_next_api_request.default.html @@ -23,7 +23,7 @@

    Hierarchy

    • default
    +
  • Defined in http/auth0-next-api-request.ts:4
  • @@ -55,7 +55,7 @@
    req: Returns default
    +
  • Defined in http/auth0-next-api-request.ts:5
  • Methods

    @@ -66,7 +66,7 @@
    +
  • Defined in http/auth0-next-api-request.ts:16
  • +
  • Defined in http/auth0-next-api-request.ts:19
  • +
  • Defined in http/auth0-next-api-request.ts:13
  • +
  • Defined in http/auth0-next-api-request.ts:10
  • \ No newline at end of file diff --git a/docs/classes/http_auth0_next_api_response.default.html b/docs/classes/http_auth0_next_api_response.default.html index b6bbebbdc..48b79a22a 100644 --- a/docs/classes/http_auth0_next_api_response.default.html +++ b/docs/classes/http_auth0_next_api_response.default.html @@ -23,7 +23,7 @@

    Hierarchy

    • default
    +
  • Defined in http/auth0-next-api-response.ts:4
  • @@ -52,7 +52,7 @@
    res: Returns default
    +
  • Defined in auth0-session/http/node-response.ts:7
  • Methods

    @@ -70,7 +70,7 @@
    status: Returns void
    +
  • Defined in http/auth0-next-api-response.ts:5
  • \ No newline at end of file diff --git a/docs/classes/http_auth0_next_request.default.html b/docs/classes/http_auth0_next_request.default.html index ded9d34f6..b844e966d 100644 --- a/docs/classes/http_auth0_next_request.default.html +++ b/docs/classes/http_auth0_next_request.default.html @@ -23,7 +23,7 @@

    Hierarchy

    • default
    +
  • Defined in http/auth0-next-request.ts:4
  • @@ -55,7 +55,7 @@
    req: Returns default
    +
  • Defined in http/auth0-next-request.ts:5
  • Methods

    @@ -66,7 +66,7 @@
    +
  • Defined in http/auth0-next-request.ts:16
  • +
  • Defined in http/auth0-next-request.ts:19
  • +
  • Defined in http/auth0-next-request.ts:13
  • +
  • Defined in http/auth0-next-request.ts:10
  • \ No newline at end of file diff --git a/docs/classes/http_auth0_next_request_cookies.default.html b/docs/classes/http_auth0_next_request_cookies.default.html index 88ee62190..e48cd3da4 100644 --- a/docs/classes/http_auth0_next_request_cookies.default.html +++ b/docs/classes/http_auth0_next_request_cookies.default.html @@ -23,7 +23,7 @@

    Hierarchy

    • default
    +
  • Defined in http/auth0-next-request-cookies.ts:4
  • @@ -47,7 +47,7 @@
    +
  • Defined in http/auth0-next-request-cookies.ts:5
  • Methods

    @@ -58,7 +58,7 @@
    +
  • Defined in http/auth0-next-request-cookies.ts:9
  • \ No newline at end of file diff --git a/docs/classes/http_auth0_next_response.default.html b/docs/classes/http_auth0_next_response.default.html index 73e9294f3..dedff59cf 100644 --- a/docs/classes/http_auth0_next_response.default.html +++ b/docs/classes/http_auth0_next_response.default.html @@ -23,7 +23,7 @@

    Hierarchy

    • default
    +
  • Defined in http/auth0-next-response.ts:5
  • @@ -54,7 +54,7 @@
    res: Returns default
    +
  • Defined in http/auth0-next-response.ts:6
  • Methods

    @@ -72,7 +72,7 @@
    Optional Returns void
    +
  • Defined in http/auth0-next-response.ts:15
  • +
  • Defined in http/auth0-next-response.ts:19
  • +
  • Defined in http/auth0-next-response.ts:11
  • http/auth0-next-response-cookies
  • index
  • +
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • +
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/classes/http_auth0_next_response_cookies.default.html b/docs/classes/http_auth0_next_response_cookies.default.html index 2ba3b2643..a6e16f4f1 100644 --- a/docs/classes/http_auth0_next_response_cookies.default.html +++ b/docs/classes/http_auth0_next_response_cookies.default.html @@ -23,7 +23,7 @@

    Hierarchy

    • default
    +
  • Defined in http/auth0-next-response-cookies.ts:7
  • @@ -48,7 +48,7 @@
    +
  • Defined in http/auth0-next-response-cookies.ts:8
  • Methods

    @@ -66,7 +66,7 @@
    Optional Returns void
    +
  • Defined in http/auth0-next-response-cookies.ts:30
  • +
  • Defined in http/auth0-next-response-cookies.ts:12
  • index
  • +
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • +
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/classes/session_session.default.html b/docs/classes/session_session.default.html index d9f52f72e..96a09c6cf 100644 --- a/docs/classes/session_session.default.html +++ b/docs/classes/session_session.default.html @@ -28,7 +28,7 @@

    Hierarchy

    Indexable

    [key: string]: any
    +
  • Defined in session/session.ts:20
  • @@ -61,7 +61,7 @@

    Parameters

    user: Claims

    Returns default

    +
  • Defined in session/session.ts:56
  • Properties

    @@ -71,7 +71,7 @@
    +
  • Defined in session/session.ts:34
  • accessTokenExpiresAt?: number
    @@ -79,7 +79,7 @@
    +
  • Defined in session/session.ts:44
  • accessTokenScope?: string
    @@ -87,7 +87,7 @@
    +
  • Defined in session/session.ts:39
  • idToken?: string
    @@ -95,7 +95,7 @@
    +
  • Defined in session/session.ts:29
  • refreshToken?: string
    @@ -105,7 +105,7 @@
    +
  • Defined in session/session.ts:52
  • user: Claims
    @@ -113,7 +113,7 @@
    +
  • Defined in session/session.ts:24
  • \ No newline at end of file diff --git a/docs/classes/utils_errors.AccessTokenError.html b/docs/classes/utils_errors.AccessTokenError.html index 7b457a080..6c4ebc19e 100644 --- a/docs/classes/utils_errors.AccessTokenError.html +++ b/docs/classes/utils_errors.AccessTokenError.html @@ -35,7 +35,7 @@

    Hierarchy

    • AccessTokenError
    +
  • Defined in utils/errors.ts:97
  • @@ -71,7 +71,7 @@
    Optional Returns AccessTokenError
    +
  • Defined in utils/errors.ts:98
  • Properties

    @@ -86,7 +86,7 @@
    +
  • Defined in utils/errors.ts:55
  • code: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:39
  • name: string
    @@ -107,7 +107,7 @@
    +
  • Defined in utils/errors.ts:44
  • status?: number
    @@ -116,7 +116,7 @@
    +
  • Defined in utils/errors.ts:60
  • +
  • Defined in utils/errors.ts:60
  • +
  • Defined in utils/errors.ts:39
  • name: string
    @@ -110,7 +110,7 @@
    +
  • Defined in utils/errors.ts:44
  • status?: number
    @@ -119,12 +119,12 @@
    +
  • Defined in utils/errors.ts:60
  • code: string = 'ERR_CALLBACK_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:178
  • +
  • Defined in utils/errors.ts:39
  • name: string
    @@ -114,7 +114,7 @@
    +
  • Defined in utils/errors.ts:44
  • status?: number
    @@ -123,7 +123,7 @@
    +
  • Defined in utils/errors.ts:60
  • +
  • Defined in utils/errors.ts:39
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:44
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:60
  • code: string = 'ERR_LOGIN_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:198
  • +
  • Defined in utils/errors.ts:39
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:44
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:60
  • code: string = 'ERR_LOGOUT_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:218
  • +
  • Defined in utils/errors.ts:39
  • name: string
    @@ -98,7 +98,7 @@
    +
  • Defined in utils/errors.ts:44
  • status?: number
    @@ -107,12 +107,12 @@
    +
  • Defined in utils/errors.ts:60
  • code: string = 'ERR_PROFILE_HANDLER_FAILURE'
    +
  • Defined in utils/errors.ts:238
  • +
  • Defined in utils/errors.ts:78
  • +
  • Defined in client/use-user.tsx:190
  • \ No newline at end of file diff --git a/docs/functions/edge.getAccessToken-1.html b/docs/functions/edge.getAccessToken-1.html new file mode 100644 index 000000000..7078ef42e --- /dev/null +++ b/docs/functions/edge.getAccessToken-1.html @@ -0,0 +1,161 @@ +getAccessToken | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function getAccessToken

    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.getSession-1.html b/docs/functions/edge.getSession-1.html index f0a90a459..58b030bbf 100644 --- a/docs/functions/edge.getSession-1.html +++ b/docs/functions/edge.getSession-1.html @@ -18,18 +18,19 @@

    Function getSession

      - +
    • +

      Get the user's session from the request.

      +

      Parameters

      • -
        req: NextRequest
      • -
      • -
        res: NextResponse<unknown>
      -

      Returns Promise<undefined | null | default>

    +

    Returns Promise<undefined | null | default>

    +
    +
  • Defined in session/get-session.ts:11
  • \ No newline at end of file diff --git a/docs/functions/edge.handleAuth-1.html b/docs/functions/edge.handleAuth-1.html new file mode 100644 index 000000000..aed02bd35 --- /dev/null +++ b/docs/functions/edge.handleAuth-1.html @@ -0,0 +1,176 @@ +handleAuth | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function handleAuth

    +
    +
      + +
    • +

      The main way to use the server SDK.

      +

      Page Router

      +

      Simply set the environment variables per ConfigParameters then create the file +pages/api/auth/[auth0].js. For example:

      +
      // pages/api/auth/[auth0].js
      import { handleAuth } from '@auth0/nextjs-auth0';

      export default handleAuth(); +
      +

      App Router

      +

      Simply set the environment variables per ConfigParameters then create the file +app/api/auth/[auth0]/route.js. For example:

      +
      // app/api/auth/[auth0]/route.js
      import { handleAuth } from '@auth0/nextjs-auth0';

      export const GET = handleAuth(); +
      +

      This will create 5 handlers for the following urls:

      +
        +
      • /api/auth/login: log the user in to your app by redirecting them to your identity provider.
      • +
      • /api/auth/callback: The page that your identity provider will redirect the user back to on login.
      • +
      • /api/auth/logout: log the user out of your app.
      • +
      • /api/auth/me: View the user profile JSON (used by the UseUser hook).
      • +
      +
      +
      +

      Parameters

      +
      +

      Returns any

      +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.handleCallback-1.html b/docs/functions/edge.handleCallback-1.html new file mode 100644 index 000000000..6dd1e3772 --- /dev/null +++ b/docs/functions/edge.handleCallback-1.html @@ -0,0 +1,208 @@ +handleCallback | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function handleCallback

    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.handleLogin-1.html b/docs/functions/edge.handleLogin-1.html new file mode 100644 index 000000000..2786c7f7e --- /dev/null +++ b/docs/functions/edge.handleLogin-1.html @@ -0,0 +1,208 @@ +handleLogin | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function handleLogin

    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.handleLogout-1.html b/docs/functions/edge.handleLogout-1.html new file mode 100644 index 000000000..39a4e33fc --- /dev/null +++ b/docs/functions/edge.handleLogout-1.html @@ -0,0 +1,208 @@ +handleLogout | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function handleLogout

    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.handleProfile-1.html b/docs/functions/edge.handleProfile-1.html new file mode 100644 index 000000000..cbe7b408b --- /dev/null +++ b/docs/functions/edge.handleProfile-1.html @@ -0,0 +1,208 @@ +handleProfile | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function handleProfile

    +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.initAuth0-1.html b/docs/functions/edge.initAuth0-1.html index f7bda1c35..d8c0f4185 100644 --- a/docs/functions/edge.initAuth0-1.html +++ b/docs/functions/edge.initAuth0-1.html @@ -18,16 +18,20 @@

    Function initAuth0

    +
  • Defined in edge.ts:41
  • \ No newline at end of file diff --git a/docs/functions/edge.updateSession-1.html b/docs/functions/edge.updateSession-1.html new file mode 100644 index 000000000..e46ceaa92 --- /dev/null +++ b/docs/functions/edge.updateSession-1.html @@ -0,0 +1,163 @@ +updateSession | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function updateSession

    +
    +
      + +
    • +

      Update the session object. The provided session object will replace the existing session.

      +

      Note you can't use this method to login or logout - you should use the login and logout handlers for this. +If no session is provided, it doesn't contain a user or the user is not authenticated; this is a no-op.

      +
      // pages/api/update-user.js
      import { getSession, updateSession } from '@auth0/nextjs-auth0';

      export default async function updateSession(req, res) {
      if (req.method === 'PUT') {
      const session = await getSession(req, res);
      updateSession(req, res, { ...session, user: { ...session.user, foo: req.query.foo } });
      res.json({ success: true });
      }
      }; +
      +
      +
      +

      Parameters

      +
        +
      • +
        Rest ...args: [IncomingMessage, ServerResponse<IncomingMessage>, default] | [NextApiRequest, NextApiResponse<any>, default] | [NextRequest, NextResponse<unknown>, default] | [default]
      +

      Returns Promise<void>

      +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.withApiAuthRequired-1.html b/docs/functions/edge.withApiAuthRequired-1.html new file mode 100644 index 000000000..5b72d4610 --- /dev/null +++ b/docs/functions/edge.withApiAuthRequired-1.html @@ -0,0 +1,180 @@ +withApiAuthRequired | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function withApiAuthRequired

    +
    +
      + +
    • +

      Wrap an app router API route to check that the user has a valid session. If they're not logged in the +handler will return a 401 Unauthorized.

      +
      // app/protected-api/route.js
      import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';

      export default withApiAuthRequired(function Protected(req) {
      const session = getSession();
      ...
      }); +
      +

      If you visit /protected-api without a valid session cookie, you will get a 401 response.

      +
      +
      +

      Parameters

      +
      +

      Returns AppRouteHandlerFn

      +
    • + +
    • +

      Wrap a page router API route to check that the user has a valid session. If they're not logged in the +handler will return a 401 Unauthorized.

      +
      // pages/api/protected-route.js
      import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';

      export default withApiAuthRequired(function ProtectedRoute(req, res) {
      const session = getSession(req, res);
      ...
      }); +
      +

      If you visit /api/protected-route without a valid session cookie, you will get a 401 response.

      +
      +
      +

      Parameters

      +
        +
      • +
        apiRoute: NextApiHandler<any>
      +

      Returns NextApiHandler<any>

      +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/edge.withMiddlewareAuthRequired-1.html b/docs/functions/edge.withMiddlewareAuthRequired.html similarity index 55% rename from docs/functions/edge.withMiddlewareAuthRequired-1.html rename to docs/functions/edge.withMiddlewareAuthRequired.html index 48565260c..597a88d1b 100644 --- a/docs/functions/edge.withMiddlewareAuthRequired-1.html +++ b/docs/functions/edge.withMiddlewareAuthRequired.html @@ -14,7 +14,7 @@ +
  • withMiddlewareAuthRequired
  • Function withMiddlewareAuthRequired

    +
  • Defined in helpers/with-middleware-auth-required.ts:46
  • \ No newline at end of file diff --git a/docs/functions/edge.withPageAuthRequired-1.html b/docs/functions/edge.withPageAuthRequired-1.html new file mode 100644 index 000000000..6ab03a90d --- /dev/null +++ b/docs/functions/edge.withPageAuthRequired-1.html @@ -0,0 +1,198 @@ +withPageAuthRequired | @auth0/nextjs-auth0
    +
    + +
    +
    +
    +
    + +

    Function withPageAuthRequired

    +
    +
      + +
    • +

      Wrap your getServerSideProps with this method to make sure the user is authenticated before +visiting the page.

      +
      // pages/protected-page.js
      import { withPageAuthRequired } from '@auth0/nextjs-auth0';

      export default function ProtectedPage() {
      return <div>Protected content</div>;
      }

      export const getServerSideProps = withPageAuthRequired(); +
      +

      If the user visits /protected-page without a valid session, it will redirect the user to the +login page. Then they will be returned to /protected-page after login.

      +
      +
      +

      Type Parameters

      +
        +
      • +

        P extends {
            [key: string]: any;
        } = {
            [key: string]: any;
        }

      • +
      • +

        Q extends ParsedUrlQuery<Q> = ParsedUrlQuery

      +
      +

      Parameters

      +
      +

      Returns PageRoute<P, Q>

      +
    • + +
    • +

      Wrap your Server Component with this method to make sure the user is authenticated before +visiting the page.

      +
      // app/protected-page/page.js
      import { withPageAuthRequired } from '@auth0/nextjs-auth0';

      export default function withPageAuthRequired(ProtectedPage() {
      return <div>Protected content</div>;
      }, { returnTo: '/protected-page' }); +
      +

      If the user visits /protected-page without a valid session, it will redirect the user to the +login page.

      +

      Note: Server Components are not aware of the req or the url of the page. So if you want the user to return to the +page after login, you must specify the returnTo option.

      +

      You can specify a function to returnTo that accepts the params (An object containing the dynamic +route parameters) and searchParams (An object containing the search parameters of the current URL) +argument from the page, to preserve dynamic routes and search params.

      +
      // app/protected-page/[slug]/page.js
      import { withPageAuthRequired } from '@auth0/nextjs-auth0';

      export default function withPageAuthRequired(ProtectedPage() {
      return <div>Protected content</div>;
      }, {
      returnTo({ params }) {
      return `/protected-page/${params.slug}`
      }
      }); +
      +
      +
      +

      Parameters

      +
      +

      Returns AppRouterPageRoute

      +
    +
    + +
    +
    \ No newline at end of file diff --git a/docs/functions/handlers_router_helpers.getHandler.html b/docs/functions/handlers_router_helpers.getHandler.html index ed70df578..2b046feff 100644 --- a/docs/functions/handlers_router_helpers.getHandler.html +++ b/docs/functions/handlers_router_helpers.getHandler.html @@ -49,7 +49,7 @@
    resOrCtx: Optional options: Opts

    Returns void | Promise<void> | Response | Promise<Response> | ((req, resOrCtxInner) => void | Promise<void> | Response | Promise<Response>)

    +
  • Defined in handlers/router-helpers.ts:60
  • \ No newline at end of file diff --git a/docs/functions/index._initAuth.html b/docs/functions/index._initAuth.html deleted file mode 100644 index ff19a37d9..000000000 --- a/docs/functions/index._initAuth.html +++ /dev/null @@ -1,153 +0,0 @@ -_initAuth | @auth0/nextjs-auth0
    -
    - -
    -
    -
    -
    - -

    Function _initAuth

    -
    -
    -
    - -
    -
    \ No newline at end of file diff --git a/docs/functions/index.getAccessToken-1.html b/docs/functions/index.getAccessToken-1.html index 613d4fb84..3ac19196b 100644 --- a/docs/functions/index.getAccessToken-1.html +++ b/docs/functions/index.getAccessToken-1.html @@ -32,7 +32,7 @@

    Returns Promise<

    Throws

    AccessTokenError

    +
  • Defined in session/get-access-token.ts:109
  • \ No newline at end of file diff --git a/docs/functions/index.handleAuth-1.html b/docs/functions/index.handleAuth-1.html index 476c7db48..6148aadb8 100644 --- a/docs/functions/index.handleAuth-1.html +++ b/docs/functions/index.handleAuth-1.html @@ -47,7 +47,7 @@
    Optional Returns any
    +
  • Defined in handlers/auth.ts:115
  • \ No newline at end of file diff --git a/docs/functions/index.handleLogin-1.html b/docs/functions/index.handleLogin-1.html index 37d7e5616..9e46b03a6 100644 --- a/docs/functions/index.handleLogin-1.html +++ b/docs/functions/index.handleLogin-1.html @@ -31,7 +31,7 @@
    ctx: Optional options: LoginOptions

    Returns Response | Promise<Response>

    +
  • Defined in handlers/router-helpers.ts:51
  • @@ -45,7 +45,7 @@
    res: Optional options: LoginOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: LoginOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<LoginOptions>
  • Returns Handler<LoginOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: LoginOptions
  • Returns Handler<LoginOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • \ No newline at end of file diff --git a/docs/functions/index.handleProfile-1.html b/docs/functions/index.handleProfile-1.html index fd77f321b..bff2ca982 100644 --- a/docs/functions/index.handleProfile-1.html +++ b/docs/functions/index.handleProfile-1.html @@ -31,7 +31,7 @@
    ctx: Optional options: ProfileOptions

    Returns Response | Promise<Response>

    +
  • Defined in handlers/router-helpers.ts:51
  • @@ -45,7 +45,7 @@
    res: Optional options: ProfileOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:52
  • @@ -59,7 +59,7 @@
    resOrOpts: Optional options: ProfileOptions
  • Returns unknown

    +
  • Defined in handlers/router-helpers.ts:53
  • @@ -69,7 +69,7 @@

    Parameters

    Optional provider: OptionsProvider<ProfileOptions>
  • Returns Handler<ProfileOptions>

    +
  • Defined in handlers/router-helpers.ts:46
  • @@ -79,7 +79,7 @@

    Parameters

    Optional options: ProfileOptions
  • Returns Handler<ProfileOptions>

    +
  • Defined in handlers/router-helpers.ts:47
  • \ No newline at end of file diff --git a/docs/functions/index.updateSession-1.html b/docs/functions/index.updateSession-1.html index c9e71dc4c..569eb441a 100644 --- a/docs/functions/index.updateSession-1.html +++ b/docs/functions/index.updateSession-1.html @@ -34,7 +34,7 @@
    Rest Returns Promise<void>
    +
  • Defined in session/update-session.ts:27
  • \ No newline at end of file diff --git a/docs/functions/index.withPageAuthRequired-1.html b/docs/functions/index.withPageAuthRequired-1.html index 8ac2871be..f9adaef1b 100644 --- a/docs/functions/index.withPageAuthRequired-1.html +++ b/docs/functions/index.withPageAuthRequired-1.html @@ -42,7 +42,7 @@
    Optional Returns PageRoute<P, Q>
    +
  • Defined in helpers/with-page-auth-required.ts:112
  • Wrap your Server Component with this method to make sure the user is authenticated before @@ -69,7 +69,7 @@

    Optional Returns AppRouterPageRoute
  • +
  • Defined in helpers/with-page-auth-required.ts:167
  • +

    Returns Auth0Server & {
        sessionCache: default;
    }

    +
  • Defined in init.ts:33
  • \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index a15992814..7c6d3a657 100644 --- a/docs/index.html +++ b/docs/index.html @@ -42,12 +42,14 @@

    Modules

    http/auth0-next-response http/auth0-next-response-cookies index +init session session/get-access-token session/get-session session/session session/touch-session session/update-session +shared utils/errors version @@ -92,12 +94,14 @@

    Theme

    +
  • WithMiddlewareAuthRequired
  • handlers
  • handlers/auth
  • handlers/callback
  • handlers/login
  • handlers/logout
  • handlers/profile
  • -
  • handlers/router-helpers
  • helpers
  • helpers/testing
  • helpers/with-api-auth-required
  • helpers/with-middleware-auth-required
  • helpers/with-page-auth-required
  • -
  • http
  • -
  • http/auth0-next-api-request
  • -
  • http/auth0-next-api-response
  • -
  • http/auth0-next-request
  • -
  • http/auth0-next-request-cookies
  • -
  • http/auth0-next-response
  • -
  • http/auth0-next-response-cookies
  • index
  • -
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • -
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/modules/handlers.html b/docs/modules/handlers.html index 60122ca3b..96ef86920 100644 --- a/docs/modules/handlers.html +++ b/docs/modules/handlers.html @@ -16,7 +16,7 @@
  • handlers
  • Module handlers

    +
  • Defined in handlers/index.ts:1
  • @@ -25,18 +25,9 @@

    References

    References

    Re-exports AfterCallback
    -
    -Re-exports AfterCallbackAppRoute
    -
    -Re-exports AfterCallbackPageRoute
    Re-exports AfterRefetch
    -
    -Re-exports AfterRefetchAppRoute
    -
    -Re-exports AfterRefetchPageRoute
    -
    -Re-exports AppRouteHandlerFn
    -
    -Re-exports AppRouteHandlerFnContext
    -
    -Re-exports AppRouterOnError
    Re-exports CallbackOptions
    Re-exports GetLoginState
    -
    -Re-exports GetLoginStateAppRoute
    -
    -Re-exports GetLoginStatePageRoute
    Re-exports HandleAuth
    @@ -92,8 +65,8 @@ Re-exports LoginOptions
    Re-exports LogoutOptions
    -
    -Re-exports PageRouterOnError
    +
    +Re-exports OnError
    Re-exports ProfileOptions
  • handlers/auth
  • handlers/callback
  • handlers/login
  • handlers/logout
  • handlers/profile
  • -
  • handlers/router-helpers
  • helpers
  • helpers/testing
  • helpers/with-api-auth-required
  • helpers/with-middleware-auth-required
  • helpers/with-page-auth-required
  • -
  • http
  • -
  • http/auth0-next-api-request
  • -
  • http/auth0-next-api-response
  • -
  • http/auth0-next-request
  • -
  • http/auth0-next-request-cookies
  • -
  • http/auth0-next-response
  • -
  • http/auth0-next-response-cookies
  • index
  • -
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • -
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/modules/handlers_auth.html b/docs/modules/handlers_auth.html index 7dafb3a8b..7e8b35dc4 100644 --- a/docs/modules/handlers_auth.html +++ b/docs/modules/handlers_auth.html @@ -16,19 +16,15 @@
  • handlers/auth
  • Module handlers/auth

    +
  • Defined in handlers/auth.ts:1
  • Index

    -

    Type Aliases - Other

    -
    -

    Type Aliases - Server

    -
  • handlers/login
  • handlers/logout
  • handlers/profile
  • -
  • handlers/router-helpers
  • helpers
  • helpers/testing
  • helpers/with-api-auth-required
  • helpers/with-middleware-auth-required
  • helpers/with-page-auth-required
  • -
  • http
  • -
  • http/auth0-next-api-request
  • -
  • http/auth0-next-api-response
  • -
  • http/auth0-next-request
  • -
  • http/auth0-next-request-cookies
  • -
  • http/auth0-next-response
  • -
  • http/auth0-next-response-cookies
  • index
  • -
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • -
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/modules/handlers_login.html b/docs/modules/handlers_login.html index f1a03b339..600935bca 100644 --- a/docs/modules/handlers_login.html +++ b/docs/modules/handlers_login.html @@ -16,7 +16,7 @@
  • handlers/login
  • Module handlers/login

    +
  • Defined in handlers/login.ts:1
  • Index

    @@ -26,13 +26,8 @@

    Interfaces - Server

    LoginOptions
    -

    Type Aliases - Other

    -
    -

    Type Aliases - Server

    -
    GetLoginStateAppRoute -GetLoginStatePageRoute +
    GetLoginState HandleLogin LoginHandler LoginOptionsProvider @@ -68,35 +63,23 @@

    Theme

  • handlers/profile
  • -
  • handlers/router-helpers
  • helpers
  • helpers/testing
  • helpers/with-api-auth-required
  • helpers/with-middleware-auth-required
  • helpers/with-page-auth-required
  • -
  • http
  • -
  • http/auth0-next-api-request
  • -
  • http/auth0-next-api-response
  • -
  • http/auth0-next-request
  • -
  • http/auth0-next-request-cookies
  • -
  • http/auth0-next-response
  • -
  • http/auth0-next-response-cookies
  • index
  • -
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • -
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/modules/handlers_profile.html b/docs/modules/handlers_profile.html index 381867c3c..05d0056bf 100644 --- a/docs/modules/handlers_profile.html +++ b/docs/modules/handlers_profile.html @@ -16,16 +16,17 @@
  • handlers/profile
  • Module handlers/profile

    +
  • Defined in handlers/profile.ts:1
  • Index

    -

    Type Aliases - Server

    +

    Type Aliases - Other

    +
    +

    Type Aliases - Server

    +
    HandleProfile ProfileHandler ProfileOptions ProfileOptionsProvider @@ -61,34 +62,22 @@

    Theme

    -
    -
    -
      -
    • Preparing search index...
    • -
    • The search index is not available
    @auth0/nextjs-auth0 -
    - -
    \ No newline at end of file diff --git a/docs/modules/helpers.html b/docs/modules/helpers.html index a6416db59..b82ff8821 100644 --- a/docs/modules/helpers.html +++ b/docs/modules/helpers.html @@ -16,7 +16,7 @@
  • helpers
  • Module helpers

    +
  • Defined in helpers/index.ts:1
  • @@ -24,48 +24,24 @@

    References

    -
    -Re-exports AppRouteHandlerFn
    -
    -Re-exports AppRouterPageRoute
    -
    -Re-exports AppRouterPageRouteOpts
    -Re-exports GetServerSidePropsResultWithSession
    +Re-exports GetServerSidePropsResultWithSession
    Re-exports PageRoute
    Re-exports WithApiAuthRequired
    -
    -Re-exports WithApiAuthRequiredAppRoute
    -
    -Re-exports WithApiAuthRequiredPageRoute
    Re-exports WithPageAuthRequired
    -
    -Re-exports WithPageAuthRequiredAppRouter
    -
    -Re-exports WithPageAuthRequiredAppRouterOptions
    -
    -Re-exports WithPageAuthRequiredPageRouter
    -
    -Re-exports WithPageAuthRequiredPageRouterOptions
    +
    +Re-exports WithPageAuthRequiredOptions
    \ No newline at end of file diff --git a/docs/modules/helpers_with_middleware_auth_required.html b/docs/modules/helpers_with_middleware_auth_required.html index ab19f254c..2dd789e1e 100644 --- a/docs/modules/helpers_with_middleware_auth_required.html +++ b/docs/modules/helpers_with_middleware_auth_required.html @@ -16,7 +16,7 @@
  • helpers/with-middleware-auth-required
  • Module helpers/with-middleware-auth-required

    +
  • Defined in helpers/with-middleware-auth-required.ts:1
  • Index

    @@ -51,7 +51,6 @@

    Theme

    -
  • init
  • session
  • session/get-access-token
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • -
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/modules/init.html b/docs/modules/init.html deleted file mode 100644 index 5b1cd9cd0..000000000 --- a/docs/modules/init.html +++ /dev/null @@ -1,87 +0,0 @@ -init | @auth0/nextjs-auth0
    -
    - -
    - -
    \ No newline at end of file diff --git a/docs/modules/session.html b/docs/modules/session.html index 3ce677a12..42cacab1f 100644 --- a/docs/modules/session.html +++ b/docs/modules/session.html @@ -16,7 +16,7 @@
  • session
  • Module session

    +
  • Defined in session/index.ts:1
  • @@ -25,9 +25,6 @@

    References

    \ No newline at end of file diff --git a/docs/modules/session_get_access_token.html b/docs/modules/session_get_access_token.html index 3ae9fd21a..4b3644716 100644 --- a/docs/modules/session_get_access_token.html +++ b/docs/modules/session_get_access_token.html @@ -16,7 +16,7 @@
  • session/get-access-token
  • Module session/get-access-token

    +
  • Defined in session/get-access-token.ts:1
  • Index

    @@ -26,11 +26,12 @@

    Interfaces - Server

    GetAccessTokenResult
    -

    Type Aliases - Server

    +

    Type Aliases - Other

    +
    +

    Type Aliases - Server

    +
  • session/get-session
  • session/session
  • session/touch-session
  • session/update-session
  • -
  • shared
  • utils/errors
  • version
  • \ No newline at end of file diff --git a/docs/modules/session_get_session.html b/docs/modules/session_get_session.html index fc0bbeba5..1e3bf018d 100644 --- a/docs/modules/session_get_session.html +++ b/docs/modules/session_get_session.html @@ -16,7 +16,7 @@
  • session/get-session
  • Module session/get-session

    +
  • Defined in session/get-session.ts:1
  • Index

    @@ -51,21 +51,12 @@

    Theme