From 1109a7d3a74f35730e2526bf9e5b73e29b702df6 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 18 Apr 2022 15:12:27 -0400 Subject: [PATCH 01/27] feat: :sparkles: add out-of-the-box support for NextAuth.js If the 'next-auth' package is detected, automatically set the NEXTAUTH_URL to the netlify site URL --- src/helpers/utils.ts | 10 ++++++++++ src/index.ts | 16 ++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 17cdc49f8f..cf509952e7 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -174,3 +174,13 @@ export const findModuleFromBase = ({ paths, candidates }): string | null => { } return null } + +export const isNextAuthInstalled = (): boolean => { + try { + require('next-auth'); + } catch { + // Ignore the MODULE_NOT_FOUND error + return false; + } + return true; +} diff --git a/src/index.ts b/src/index.ts index fede662fb3..ed204c783d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ import { getNextConfig, configureHandlerFunctions } from './helpers/config' import { moveStaticPages, movePublicFiles, patchNextFiles, unpatchNextFiles } from './helpers/files' import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions' import { generateRedirects, generateStaticRedirects } from './helpers/redirects' -import { shouldSkip } from './helpers/utils' +import { shouldSkip, isNextAuthInstalled } from './helpers/utils' import { verifyNetlifyBuildVersion, checkNextSiteHasBuilt, @@ -104,7 +104,7 @@ const plugin: NetlifyPlugin = { async onPostBuild({ netlifyConfig: { - build: { publish }, + build: { publish, environment }, redirects, }, utils: { @@ -117,14 +117,18 @@ const plugin: NetlifyPlugin = { }) { await saveCache({ cache, publish }) + if (isNextAuthInstalled()) { + console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${process.env.URL}`) + environment.NEXTAUTH_URL = process.env.URL; + } + if (shouldSkip()) { status.show({ title: 'Essential Next.js plugin did not run', - summary: `Next cache was stored, but all other functions were skipped because ${ - process.env.NETLIFY_NEXT_PLUGIN_SKIP + summary: `Next cache was stored, but all other functions were skipped because ${process.env.NETLIFY_NEXT_PLUGIN_SKIP ? `NETLIFY_NEXT_PLUGIN_SKIP is set` : `NEXT_PLUGIN_FORCE_RUN is set to ${process.env.NEXT_PLUGIN_FORCE_RUN}` - }`, + }`, }) return } @@ -135,6 +139,6 @@ const plugin: NetlifyPlugin = { warnForProblematicUserRewrites({ basePath, redirects }) warnForRootRedirects({ appDir }) await unpatchNextFiles(basePath) - }, + } } module.exports = plugin From 853a7b38b3123fdca8b0c3ae7d8603339df94142 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 18 Apr 2022 16:15:17 -0400 Subject: [PATCH 02/27] test: :white_check_mark: add test coverage for the setting of NEXTAUTH_URL env variable --- test/index.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/index.js b/test/index.js index 39639a907b..66abb3bd04 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,10 @@ +jest.mock('../src/helpers/utils', () => { + return { + ...(jest.requireActual('../src/helpers/utils')), + isNextAuthInstalled: jest.fn() + } +}); + const { writeJSON, unlink, existsSync, readFileSync, copy, ensureDir, readJson } = require('fs-extra') const path = require('path') const process = require('process') @@ -429,6 +436,66 @@ describe('onBuild()', () => { }) describe('onPostBuild', () => { + const { isNextAuthInstalled } = require('../src/helpers/utils') + + beforeEach(() => { + isNextAuthInstalled.mockImplementation(() => { + return true; + }) + }) + + test('sets NEXTAUTH_URL when next-auth package is detected', async () => { + const netlifyConfig = { ...defaultArgs.netlifyConfig } + const mockSiteUrl = "https://my-netlify-site.app"; + + // Value represents the main address to the site and is either + // a Netlify subdomain or custom domain set by the user. + // See https://docs.netlify.com/configure-builds/environment-variables/#deploy-urls-and-metadata + process.env.URL = mockSiteUrl; + + await moveNextDist() + + await plugin.onPostBuild({ + ...defaultArgs, + netlifyConfig, + utils: { + ...defaultArgs.utils, + functions: { + list: jest.fn().mockResolvedValue([]) + } + } + }) + + expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBe(mockSiteUrl); + + delete process.env.URL; + }) + + test('skips setting NEXTAUTH_URL when next-auth package is not found', async () => { + isNextAuthInstalled.mockImplementation(() => { + return false; + }) + + const netlifyConfig = { ...defaultArgs.netlifyConfig } + + await moveNextDist() + + await plugin.onPostBuild({ + ...defaultArgs, + netlifyConfig, + utils: { + ...defaultArgs.utils, + functions: { + list: jest.fn().mockResolvedValue([]) + } + } + }) + + expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBeUndefined; + + delete process.env.URL; + }) + test('saves cache with right paths', async () => { await moveNextDist() From b4713a3ab51aeaa43353200c0169b015e45217b4 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 18 Apr 2022 16:20:53 -0400 Subject: [PATCH 03/27] style: disable lint rules for line determining if package is installed --- src/helpers/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index cf509952e7..14f9c8c9d3 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -177,6 +177,7 @@ export const findModuleFromBase = ({ paths, candidates }): string | null => { export const isNextAuthInstalled = (): boolean => { try { + // eslint-disable-next-line import/no-unassigned-import, import/no-unresolved, node/no-missing-require require('next-auth'); } catch { // Ignore the MODULE_NOT_FOUND error From 84cebe9fc790f5a9e80b8bc545d135295dd338b6 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 18 Apr 2022 16:21:39 -0400 Subject: [PATCH 04/27] style: lint --- src/helpers/utils.ts | 6 +++--- src/index.ts | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 14f9c8c9d3..acfa5d5c43 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -178,10 +178,10 @@ export const findModuleFromBase = ({ paths, candidates }): string | null => { export const isNextAuthInstalled = (): boolean => { try { // eslint-disable-next-line import/no-unassigned-import, import/no-unresolved, node/no-missing-require - require('next-auth'); + require('next-auth') } catch { // Ignore the MODULE_NOT_FOUND error - return false; + return false } - return true; + return true } diff --git a/src/index.ts b/src/index.ts index ed204c783d..a306fca815 100644 --- a/src/index.ts +++ b/src/index.ts @@ -119,16 +119,17 @@ const plugin: NetlifyPlugin = { if (isNextAuthInstalled()) { console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${process.env.URL}`) - environment.NEXTAUTH_URL = process.env.URL; + environment.NEXTAUTH_URL = process.env.URL } if (shouldSkip()) { status.show({ title: 'Essential Next.js plugin did not run', - summary: `Next cache was stored, but all other functions were skipped because ${process.env.NETLIFY_NEXT_PLUGIN_SKIP + summary: `Next cache was stored, but all other functions were skipped because ${ + process.env.NETLIFY_NEXT_PLUGIN_SKIP ? `NETLIFY_NEXT_PLUGIN_SKIP is set` : `NEXT_PLUGIN_FORCE_RUN is set to ${process.env.NEXT_PLUGIN_FORCE_RUN}` - }`, + }`, }) return } @@ -139,6 +140,6 @@ const plugin: NetlifyPlugin = { warnForProblematicUserRewrites({ basePath, redirects }) warnForRootRedirects({ appDir }) await unpatchNextFiles(basePath) - } + }, } module.exports = plugin From 01f6b3e2f8e42a336c44e7e5f9ba5b05fa383859 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 18 Apr 2022 16:27:43 -0400 Subject: [PATCH 05/27] chore: remove unnecessary line in test --- test/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/index.js b/test/index.js index 66abb3bd04..2e160d542e 100644 --- a/test/index.js +++ b/test/index.js @@ -492,8 +492,6 @@ describe('onPostBuild', () => { }) expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBeUndefined; - - delete process.env.URL; }) test('saves cache with right paths', async () => { From fde4fe0a416a102159986ab1d1ee03de460073ee Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 18 Apr 2022 16:30:53 -0400 Subject: [PATCH 06/27] test: need to invoke the 'toBeUndefined' method --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 2e160d542e..ddd444f458 100644 --- a/test/index.js +++ b/test/index.js @@ -491,7 +491,7 @@ describe('onPostBuild', () => { } }) - expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBeUndefined; + expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBeUndefined(); }) test('saves cache with right paths', async () => { From eceb7949bf789628f82ff0d65c838be06cef36ab Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Tue, 19 Apr 2022 15:31:28 -0400 Subject: [PATCH 07/27] refactor: move logic for setting NEXTAUTH_URL to onBuild step rather than onPostBuild --- plugin/src/helpers/config.ts | 21 ++++++- plugin/src/index.ts | 18 +++--- test/index.js | 105 +++++++++++++++-------------------- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/plugin/src/helpers/config.ts b/plugin/src/helpers/config.ts index 3142e33715..1c37284b5d 100644 --- a/plugin/src/helpers/config.ts +++ b/plugin/src/helpers/config.ts @@ -1,4 +1,4 @@ -import { readJSON } from 'fs-extra' +import { readJSON, writeJSON } from 'fs-extra' import type { NextConfigComplete } from 'next/dist/server/config-shared' import { join, dirname, relative } from 'pathe' import slash from 'slash' @@ -36,6 +36,25 @@ export const getNextConfig = async function getNextConfig({ } } +/** + * Returns all of the NextJS configuration stored within 'required-server-files.json' + * To update the configuration within this file, use the 'updateRequiredServerFiles' method. + */ + export const getRequiredServerFiles = async (publish: string): Promise => { + const configFile = join(publish, 'required-server-files.json') + return await readJSON(configFile) +} + +/** + * Writes a modified configuration object to 'required-server-files.json'. + * To get the full configuration, use the 'getRequiredServerFiles' method. + */ +export const updateRequiredServerFiles = async (publish: string, modifiedConfig: RequiredServerFiles) => { + const configFile = join(publish, 'required-server-files.json') + await writeJSON(configFile, modifiedConfig) +} + + const resolveModuleRoot = (moduleName) => { try { return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] }))) diff --git a/plugin/src/index.ts b/plugin/src/index.ts index ca80bad4a7..e5b66078d3 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -7,7 +7,7 @@ import { outdent } from 'outdent' import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } from './constants' import { restoreCache, saveCache } from './helpers/cache' -import { getNextConfig, configureHandlerFunctions } from './helpers/config' +import { getNextConfig, getRequiredServerFiles, updateRequiredServerFiles, configureHandlerFunctions } from './helpers/config' import { updateConfig, writeMiddleware } from './helpers/edge' import { moveStaticPages, movePublicFiles, patchNextFiles, unpatchNextFiles } from './helpers/files' import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions' @@ -70,6 +70,15 @@ const plugin: NetlifyPlugin = { failBuild, }) + if (isNextAuthInstalled()) { + console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${process.env.URL}`) + + const config = await getRequiredServerFiles(publish); + config.config.env.NEXTAUTH_URL = process.env.URL + + await updateRequiredServerFiles(publish, config); + } + const buildId = readFileSync(join(publish, 'BUILD_ID'), 'utf8').trim() configureHandlerFunctions({ netlifyConfig, ignore, publish: relative(process.cwd(), publish) }) @@ -110,7 +119,7 @@ const plugin: NetlifyPlugin = { async onPostBuild({ netlifyConfig: { - build: { publish, environment }, + build: { publish }, redirects, }, utils: { @@ -123,11 +132,6 @@ const plugin: NetlifyPlugin = { }) { await saveCache({ cache, publish }) - if (isNextAuthInstalled()) { - console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${process.env.URL}`) - environment.NEXTAUTH_URL = process.env.URL - } - if (shouldSkip()) { status.show({ title: 'Essential Next.js plugin did not run', diff --git a/test/index.js b/test/index.js index 047b491c4f..19aeb8ec65 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,6 @@ -jest.mock('../src/helpers/utils', () => { +jest.mock('../plugin/src/helpers/utils', () => { return { - ...(jest.requireActual('../src/helpers/utils')), + ...(jest.requireActual('../plugin/src/helpers/utils')), isNextAuthInstalled: jest.fn() } }); @@ -25,6 +25,7 @@ const { patchNextFiles, unpatchNextFiles, } = require('../plugin/src/helpers/files') +const { getRequiredServerFiles } = require('../plugin/src/helpers/config'); const { dirname } = require('path') const { getProblematicUserRewrites } = require('../plugin/src/helpers/verification') @@ -212,6 +213,48 @@ describe('preBuild()', () => { }) describe('onBuild()', () => { + const { isNextAuthInstalled } = require('../plugin/src/helpers/utils') + + beforeEach(() => { + isNextAuthInstalled.mockImplementation(() => { + return true; + }) + }) + + test('sets NEXTAUTH_URL when next-auth package is detected', async () => { + const mockSiteUrl = "https://my-netlify-site.app"; + + // Value represents the main address to the site and is either + // a Netlify subdomain or custom domain set by the user. + // See https://docs.netlify.com/configure-builds/environment-variables/#deploy-urls-and-metadata + process.env.URL = mockSiteUrl; + + await moveNextDist() + + await plugin.onBuild(defaultArgs) + + expect(onBuildHasRun(netlifyConfig)).toBe(true) + const config = await getRequiredServerFiles(netlifyConfig.build.publish); + + expect(config.config.env.NEXTAUTH_URL).toEqual(mockSiteUrl) + + delete process.env.URL; + }) + + test('skips setting NEXTAUTH_URL when next-auth package is not found', async () => { + isNextAuthInstalled.mockImplementation(() => { + return false; + }) + + await moveNextDist() + await plugin.onBuild(defaultArgs) + + expect(onBuildHasRun(netlifyConfig)).toBe(true) + const config = await getRequiredServerFiles(netlifyConfig.build.publish); + + expect(config.config.env.NEXTAUTH_URL).toBeUndefined(); + }) + test('runs onBuild', async () => { await moveNextDist() @@ -436,64 +479,6 @@ describe('onBuild()', () => { }) describe('onPostBuild', () => { - const { isNextAuthInstalled } = require('../src/helpers/utils') - - beforeEach(() => { - isNextAuthInstalled.mockImplementation(() => { - return true; - }) - }) - - test('sets NEXTAUTH_URL when next-auth package is detected', async () => { - const netlifyConfig = { ...defaultArgs.netlifyConfig } - const mockSiteUrl = "https://my-netlify-site.app"; - - // Value represents the main address to the site and is either - // a Netlify subdomain or custom domain set by the user. - // See https://docs.netlify.com/configure-builds/environment-variables/#deploy-urls-and-metadata - process.env.URL = mockSiteUrl; - - await moveNextDist() - - await plugin.onPostBuild({ - ...defaultArgs, - netlifyConfig, - utils: { - ...defaultArgs.utils, - functions: { - list: jest.fn().mockResolvedValue([]) - } - } - }) - - expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBe(mockSiteUrl); - - delete process.env.URL; - }) - - test('skips setting NEXTAUTH_URL when next-auth package is not found', async () => { - isNextAuthInstalled.mockImplementation(() => { - return false; - }) - - const netlifyConfig = { ...defaultArgs.netlifyConfig } - - await moveNextDist() - - await plugin.onPostBuild({ - ...defaultArgs, - netlifyConfig, - utils: { - ...defaultArgs.utils, - functions: { - list: jest.fn().mockResolvedValue([]) - } - } - }) - - expect(netlifyConfig.build.environment.NEXTAUTH_URL).toBeUndefined(); - }) - test('saves cache with right paths', async () => { await moveNextDist() From b3bd617cd466b9e225f894305d8b9af6ab6598a0 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Tue, 19 Apr 2022 15:33:05 -0400 Subject: [PATCH 08/27] style: lint --- plugin/src/helpers/config.ts | 3 +-- plugin/src/index.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plugin/src/helpers/config.ts b/plugin/src/helpers/config.ts index 1c37284b5d..0ef80cdc8d 100644 --- a/plugin/src/helpers/config.ts +++ b/plugin/src/helpers/config.ts @@ -40,7 +40,7 @@ export const getNextConfig = async function getNextConfig({ * Returns all of the NextJS configuration stored within 'required-server-files.json' * To update the configuration within this file, use the 'updateRequiredServerFiles' method. */ - export const getRequiredServerFiles = async (publish: string): Promise => { +export const getRequiredServerFiles = async (publish: string): Promise => { const configFile = join(publish, 'required-server-files.json') return await readJSON(configFile) } @@ -54,7 +54,6 @@ export const updateRequiredServerFiles = async (publish: string, modifiedConfig: await writeJSON(configFile, modifiedConfig) } - const resolveModuleRoot = (moduleName) => { try { return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] }))) diff --git a/plugin/src/index.ts b/plugin/src/index.ts index e5b66078d3..87b1d4bd4a 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -7,7 +7,12 @@ import { outdent } from 'outdent' import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } from './constants' import { restoreCache, saveCache } from './helpers/cache' -import { getNextConfig, getRequiredServerFiles, updateRequiredServerFiles, configureHandlerFunctions } from './helpers/config' +import { + getNextConfig, + getRequiredServerFiles, + updateRequiredServerFiles, + configureHandlerFunctions, +} from './helpers/config' import { updateConfig, writeMiddleware } from './helpers/edge' import { moveStaticPages, movePublicFiles, patchNextFiles, unpatchNextFiles } from './helpers/files' import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions' @@ -72,11 +77,11 @@ const plugin: NetlifyPlugin = { if (isNextAuthInstalled()) { console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${process.env.URL}`) - - const config = await getRequiredServerFiles(publish); + + const config = await getRequiredServerFiles(publish) config.config.env.NEXTAUTH_URL = process.env.URL - - await updateRequiredServerFiles(publish, config); + + await updateRequiredServerFiles(publish, config) } const buildId = readFileSync(join(publish, 'BUILD_ID'), 'utf8').trim() From 741c189019cef3a3d27925b29eddab3c4d613063 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Wed, 20 Apr 2022 10:55:42 -0400 Subject: [PATCH 09/27] test: add NextAuth.js demo project for use in testing plugin functionality --- demos/nextAuth-demo/.env.local.example | 4 + demos/nextAuth-demo/.gitignore | 1 + demos/nextAuth-demo/LICENSE | 15 +++ demos/nextAuth-demo/README.md | 31 +++++ .../components/access-denied.tsx | 20 ++++ .../components/footer.module.css | 14 +++ demos/nextAuth-demo/components/footer.tsx | 30 +++++ .../components/header.module.css | 92 +++++++++++++++ demos/nextAuth-demo/components/header.tsx | 108 ++++++++++++++++++ demos/nextAuth-demo/components/layout.tsx | 17 +++ demos/nextAuth-demo/netlify.toml | 18 +++ demos/nextAuth-demo/next-auth.d.ts | 10 ++ demos/nextAuth-demo/next-env.d.ts | 5 + demos/nextAuth-demo/package.json | 40 +++++++ demos/nextAuth-demo/pages/_app.tsx | 13 +++ .../nextAuth-demo/pages/admin/_middleware.ts | 8 ++ demos/nextAuth-demo/pages/admin/index.tsx | 17 +++ demos/nextAuth-demo/pages/api-example.tsx | 19 +++ .../pages/api/auth/[...nextauth].ts | 21 ++++ demos/nextAuth-demo/pages/api/examples/jwt.ts | 10 ++ .../pages/api/examples/protected.ts | 18 +++ .../pages/api/examples/session.ts | 8 ++ demos/nextAuth-demo/pages/client.tsx | 27 +++++ demos/nextAuth-demo/pages/index.tsx | 13 +++ demos/nextAuth-demo/pages/me/_middleware.ts | 2 + demos/nextAuth-demo/pages/me/index.tsx | 12 ++ demos/nextAuth-demo/pages/policy.tsx | 32 ++++++ demos/nextAuth-demo/pages/protected.tsx | 44 +++++++ demos/nextAuth-demo/pages/server.tsx | 44 +++++++ demos/nextAuth-demo/pages/styles.css | 32 ++++++ demos/nextAuth-demo/process.d.ts | 16 +++ demos/nextAuth-demo/tsconfig.json | 24 ++++ 32 files changed, 765 insertions(+) create mode 100644 demos/nextAuth-demo/.env.local.example create mode 100644 demos/nextAuth-demo/.gitignore create mode 100644 demos/nextAuth-demo/LICENSE create mode 100644 demos/nextAuth-demo/README.md create mode 100644 demos/nextAuth-demo/components/access-denied.tsx create mode 100644 demos/nextAuth-demo/components/footer.module.css create mode 100644 demos/nextAuth-demo/components/footer.tsx create mode 100644 demos/nextAuth-demo/components/header.module.css create mode 100644 demos/nextAuth-demo/components/header.tsx create mode 100644 demos/nextAuth-demo/components/layout.tsx create mode 100644 demos/nextAuth-demo/netlify.toml create mode 100644 demos/nextAuth-demo/next-auth.d.ts create mode 100644 demos/nextAuth-demo/next-env.d.ts create mode 100644 demos/nextAuth-demo/package.json create mode 100644 demos/nextAuth-demo/pages/_app.tsx create mode 100644 demos/nextAuth-demo/pages/admin/_middleware.ts create mode 100644 demos/nextAuth-demo/pages/admin/index.tsx create mode 100644 demos/nextAuth-demo/pages/api-example.tsx create mode 100644 demos/nextAuth-demo/pages/api/auth/[...nextauth].ts create mode 100644 demos/nextAuth-demo/pages/api/examples/jwt.ts create mode 100644 demos/nextAuth-demo/pages/api/examples/protected.ts create mode 100644 demos/nextAuth-demo/pages/api/examples/session.ts create mode 100644 demos/nextAuth-demo/pages/client.tsx create mode 100644 demos/nextAuth-demo/pages/index.tsx create mode 100644 demos/nextAuth-demo/pages/me/_middleware.ts create mode 100644 demos/nextAuth-demo/pages/me/index.tsx create mode 100644 demos/nextAuth-demo/pages/policy.tsx create mode 100644 demos/nextAuth-demo/pages/protected.tsx create mode 100644 demos/nextAuth-demo/pages/server.tsx create mode 100644 demos/nextAuth-demo/pages/styles.css create mode 100644 demos/nextAuth-demo/process.d.ts create mode 100644 demos/nextAuth-demo/tsconfig.json diff --git a/demos/nextAuth-demo/.env.local.example b/demos/nextAuth-demo/.env.local.example new file mode 100644 index 0000000000..83cb4bc2d5 --- /dev/null +++ b/demos/nextAuth-demo/.env.local.example @@ -0,0 +1,4 @@ +NEXTAUTH_URL=http://localhost:3000 + +NETLIFY_CLIENT_ID= +NETLIFY_CLIENT_SECRET= diff --git a/demos/nextAuth-demo/.gitignore b/demos/nextAuth-demo/.gitignore new file mode 100644 index 0000000000..11ee758152 --- /dev/null +++ b/demos/nextAuth-demo/.gitignore @@ -0,0 +1 @@ +.env.local diff --git a/demos/nextAuth-demo/LICENSE b/demos/nextAuth-demo/LICENSE new file mode 100644 index 0000000000..eee418eafa --- /dev/null +++ b/demos/nextAuth-demo/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2018-2021, Iain Collins + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/demos/nextAuth-demo/README.md b/demos/nextAuth-demo/README.md new file mode 100644 index 0000000000..242d39f08e --- /dev/null +++ b/demos/nextAuth-demo/README.md @@ -0,0 +1,31 @@ +## NextAuth.js Example + +This example project came from the [next-auth-example](https://github.com/nextauthjs/next-auth-example) project provided by the NextAuth.js and was modified to use Netlify as the authentication provider as an example. + +For more details on how to get set up and configured with various providers, visit the link above. + +### Running locally + +``` +npm install +npm run dev +``` + +### Configuring your local environment + +Copy the .env.local.example file in this directory to .env.local (which will be ignored by Git): + +``` +cp .env.local.example .env.local +``` + +Add details for the [Netlify Provider](https://next-auth.js.org/providers/netlify). + +To add a Netlify OAuth application: +* Visit `https://app.netlify.com/user/applications` +* Click 'New OAuth App' +* Enter an application name +* Enter a redirect URI (for the purposes of this demo application you would use `http://localhost:3000/api/auth/callback/netlify`) +* Save the application, and copy the value for 'Client ID' as the `NETLIFY_CLIENT_ID` and the 'Client Secret' as the `NETLIFY_CLIENT_SECRET` into your `.env.local` file within the project + +For configuring additional authentication providers, see the original documentation [here](https://github.com/nextauthjs/next-auth-example#3-configure-authentication-providers) diff --git a/demos/nextAuth-demo/components/access-denied.tsx b/demos/nextAuth-demo/components/access-denied.tsx new file mode 100644 index 0000000000..4c9c0d7947 --- /dev/null +++ b/demos/nextAuth-demo/components/access-denied.tsx @@ -0,0 +1,20 @@ +import { signIn } from "next-auth/react" + +export default function AccessDenied() { + return ( + <> +

Access Denied

+

+ { + e.preventDefault() + signIn() + }} + > + You must be signed in to view this page + +

+ + ) +} diff --git a/demos/nextAuth-demo/components/footer.module.css b/demos/nextAuth-demo/components/footer.module.css new file mode 100644 index 0000000000..020bec6415 --- /dev/null +++ b/demos/nextAuth-demo/components/footer.module.css @@ -0,0 +1,14 @@ +.footer { + margin-top: 2rem; +} + +.navItems { + margin-bottom: 1rem; + padding: 0; + list-style: none; +} + +.navItem { + display: inline-block; + margin-right: 1rem; +} diff --git a/demos/nextAuth-demo/components/footer.tsx b/demos/nextAuth-demo/components/footer.tsx new file mode 100644 index 0000000000..e01b64f198 --- /dev/null +++ b/demos/nextAuth-demo/components/footer.tsx @@ -0,0 +1,30 @@ +import Link from "next/link" +import styles from "./footer.module.css" +import packageJSON from "../package.json" + +export default function Footer() { + return ( + + ) +} diff --git a/demos/nextAuth-demo/components/header.module.css b/demos/nextAuth-demo/components/header.module.css new file mode 100644 index 0000000000..773478c6af --- /dev/null +++ b/demos/nextAuth-demo/components/header.module.css @@ -0,0 +1,92 @@ +/* Set min-height to avoid page reflow while session loading */ +.signedInStatus { + display: block; + min-height: 4rem; + width: 100%; +} + +.loading, +.loaded { + position: relative; + top: 0; + opacity: 1; + overflow: hidden; + border-radius: 0 0 0.6rem 0.6rem; + padding: 0.6rem 1rem; + margin: 0; + background-color: rgba(0, 0, 0, 0.05); + transition: all 0.2s ease-in; +} + +.loading { + top: -2rem; + opacity: 0; +} + +.signedInText, +.notSignedInText { + position: absolute; + padding-top: 0.8rem; + left: 1rem; + right: 6.5rem; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: inherit; + z-index: 1; + line-height: 1.3rem; +} + +.signedInText { + padding-top: 0rem; + left: 4.6rem; +} + +.avatar { + border-radius: 2rem; + float: left; + height: 2.8rem; + width: 2.8rem; + background-color: white; + background-size: cover; + background-repeat: no-repeat; +} + +.button, +.buttonPrimary { + float: right; + margin-right: -0.4rem; + font-weight: 500; + border-radius: 0.3rem; + cursor: pointer; + font-size: 1rem; + line-height: 1.4rem; + padding: 0.7rem 0.8rem; + position: relative; + z-index: 10; + background-color: transparent; + color: #555; +} + +.buttonPrimary { + background-color: #346df1; + border-color: #346df1; + color: #fff; + text-decoration: none; + padding: 0.7rem 1.4rem; +} + +.buttonPrimary:hover { + box-shadow: inset 0 0 5rem rgba(0, 0, 0, 0.2); +} + +.navItems { + margin-bottom: 2rem; + padding: 0; + list-style: none; +} + +.navItem { + display: inline-block; + margin-right: 1rem; +} diff --git a/demos/nextAuth-demo/components/header.tsx b/demos/nextAuth-demo/components/header.tsx new file mode 100644 index 0000000000..8fed732d72 --- /dev/null +++ b/demos/nextAuth-demo/components/header.tsx @@ -0,0 +1,108 @@ +import Link from "next/link" +import { signIn, signOut, useSession } from "next-auth/react" +import styles from "./header.module.css" + +// The approach used in this component shows how to build a sign in and sign out +// component that works on pages which support both client and server side +// rendering, and avoids any flash incorrect content on initial page load. +export default function Header() { + const { data: session, status } = useSession() + const loading = status === "loading" + + return ( +
+ +
+

+ {!session && ( + <> + + You are not signed in + + { + e.preventDefault() + signIn() + }} + > + Sign in + + + )} + {session?.user && ( + <> + {session.user.image && ( + + )} + + Signed in as +
+ {session.user.email ?? session.user.name} +
+ { + e.preventDefault() + signOut() + }} + > + Sign out + + + )} +

+
+ +
+ ) +} diff --git a/demos/nextAuth-demo/components/layout.tsx b/demos/nextAuth-demo/components/layout.tsx new file mode 100644 index 0000000000..8495690ad5 --- /dev/null +++ b/demos/nextAuth-demo/components/layout.tsx @@ -0,0 +1,17 @@ +import Header from "./header" +import Footer from "./footer" +import type { ReactChildren } from "react" + +interface Props { + children: React.ReactNode +} + +export default function Layout({ children }: Props) { + return ( + <> +
+
{children}
+