From 8a386a867ca92f313b74f785477a48cd7c9a1679 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 25 May 2022 17:03:25 +0200 Subject: [PATCH 1/4] fix: Fix auth errors if auth is not configured. --- packages/api/src/context/errors.js | 16 +------ packages/api/src/index.js | 12 +---- .../auth/callbacks/createCallbackPlugins.js | 0 .../auth/callbacks/createCallbacks.js | 0 .../auth/callbacks/createJWTCallback.js | 0 .../auth/callbacks/createRedirectCallback.js | 0 .../auth/callbacks/createSessionCallback.js | 0 .../auth/callbacks/createSignInCallback.js | 0 .../src/{ => routes}/auth/createProviders.js | 0 .../auth/events/createCreateUserEvent.js | 0 .../auth/events/createEventPlugins.js | 0 .../{ => routes}/auth/events/createEvents.js | 0 .../auth/events/createLinkAccountEvent.js | 0 .../auth/events/createSessionEvent.js | 0 .../auth/events/createSignInEvent.js | 0 .../auth/events/createSignOutEvent.js | 0 .../auth/events/createUpdateUserEvent.js | 0 .../{ => routes}/auth/getNextAuthConfig.js | 0 .../build/src/build/buildAuth/buildAuth.js | 4 ++ packages/client/src/Client.js | 2 - packages/client/src/initLowdefyContext.js | 4 +- packages/server-dev/lib/App.js | 3 +- packages/server-dev/lib/Page.js | 11 +---- packages/server-dev/lib/auth/Auth.js | 35 ++++++++++++++ .../server-dev/lib/auth/AuthConfigured.js | 47 +++++++++++++++++++ .../server-dev/lib/auth/AuthNotConfigured.js | 32 +++++++++++++ .../server-dev/lib/auth/getServerSession.js | 27 +++++++++++ packages/server-dev/pages/_app.js | 7 ++- .../server-dev/pages/api/page/[pageId].js | 4 +- .../pages/api/request/[pageId]/[requestId].js | 5 +- packages/server-dev/pages/api/root.js | 5 +- packages/server/lib/Page.js | 15 +----- packages/server/lib/auth/Auth.js | 35 ++++++++++++++ packages/server/lib/auth/AuthConfigured.js | 47 +++++++++++++++++++ packages/server/lib/auth/AuthNotConfigured.js | 32 +++++++++++++ packages/server/lib/auth/getServerSession.js | 27 +++++++++++ packages/server/pages/[pageId].js | 4 +- packages/server/pages/_app.js | 13 ++--- .../server/pages/api/auth/[...nextauth].js | 14 +++++- .../pages/api/request/[pageId]/[requestId].js | 6 ++- packages/server/pages/index.js | 5 +- 41 files changed, 337 insertions(+), 75 deletions(-) rename packages/api/src/{ => routes}/auth/callbacks/createCallbackPlugins.js (100%) rename packages/api/src/{ => routes}/auth/callbacks/createCallbacks.js (100%) rename packages/api/src/{ => routes}/auth/callbacks/createJWTCallback.js (100%) rename packages/api/src/{ => routes}/auth/callbacks/createRedirectCallback.js (100%) rename packages/api/src/{ => routes}/auth/callbacks/createSessionCallback.js (100%) rename packages/api/src/{ => routes}/auth/callbacks/createSignInCallback.js (100%) rename packages/api/src/{ => routes}/auth/createProviders.js (100%) rename packages/api/src/{ => routes}/auth/events/createCreateUserEvent.js (100%) rename packages/api/src/{ => routes}/auth/events/createEventPlugins.js (100%) rename packages/api/src/{ => routes}/auth/events/createEvents.js (100%) rename packages/api/src/{ => routes}/auth/events/createLinkAccountEvent.js (100%) rename packages/api/src/{ => routes}/auth/events/createSessionEvent.js (100%) rename packages/api/src/{ => routes}/auth/events/createSignInEvent.js (100%) rename packages/api/src/{ => routes}/auth/events/createSignOutEvent.js (100%) rename packages/api/src/{ => routes}/auth/events/createUpdateUserEvent.js (100%) rename packages/api/src/{ => routes}/auth/getNextAuthConfig.js (100%) create mode 100644 packages/server-dev/lib/auth/Auth.js create mode 100644 packages/server-dev/lib/auth/AuthConfigured.js create mode 100644 packages/server-dev/lib/auth/AuthNotConfigured.js create mode 100644 packages/server-dev/lib/auth/getServerSession.js create mode 100644 packages/server/lib/auth/Auth.js create mode 100644 packages/server/lib/auth/AuthConfigured.js create mode 100644 packages/server/lib/auth/AuthNotConfigured.js create mode 100644 packages/server/lib/auth/getServerSession.js diff --git a/packages/api/src/context/errors.js b/packages/api/src/context/errors.js index a40e3539f1..e56750d6c7 100644 --- a/packages/api/src/context/errors.js +++ b/packages/api/src/context/errors.js @@ -16,13 +16,6 @@ /* eslint-disable max-classes-per-file */ -class AuthenticationError extends Error { - constructor(message) { - super(message); - this.name = 'AuthenticationError'; - } -} - class ConfigurationError extends Error { constructor(message) { super(message); @@ -44,11 +37,4 @@ class ServerError extends Error { } } -class TokenExpiredError extends Error { - constructor(message) { - super(message); - this.name = 'TokenExpiredError'; - } -} - -export { AuthenticationError, ConfigurationError, RequestError, ServerError, TokenExpiredError }; +export { ConfigurationError, RequestError, ServerError }; diff --git a/packages/api/src/index.js b/packages/api/src/index.js index c644e6bd1d..ba0d31cbb3 100644 --- a/packages/api/src/index.js +++ b/packages/api/src/index.js @@ -17,20 +17,13 @@ import callRequest from './routes/request/callRequest.js'; import createApiContext from './context/createApiContext.js'; import getHomeAndMenus from './routes/rootConfig/getHomeAndMenus.js'; -import getNextAuthConfig from './auth/getNextAuthConfig.js'; +import getNextAuthConfig from './routes/auth/getNextAuthConfig.js'; import getPageConfig from './routes/page/getPageConfig.js'; import getRootConfig from './routes/rootConfig/getRootConfig.js'; -import { - AuthenticationError, - ConfigurationError, - RequestError, - ServerError, - TokenExpiredError, -} from './context/errors.js'; +import { ConfigurationError, RequestError, ServerError } from './context/errors.js'; export { - AuthenticationError, callRequest, ConfigurationError, createApiContext, @@ -40,5 +33,4 @@ export { getRootConfig, RequestError, ServerError, - TokenExpiredError, }; diff --git a/packages/api/src/auth/callbacks/createCallbackPlugins.js b/packages/api/src/routes/auth/callbacks/createCallbackPlugins.js similarity index 100% rename from packages/api/src/auth/callbacks/createCallbackPlugins.js rename to packages/api/src/routes/auth/callbacks/createCallbackPlugins.js diff --git a/packages/api/src/auth/callbacks/createCallbacks.js b/packages/api/src/routes/auth/callbacks/createCallbacks.js similarity index 100% rename from packages/api/src/auth/callbacks/createCallbacks.js rename to packages/api/src/routes/auth/callbacks/createCallbacks.js diff --git a/packages/api/src/auth/callbacks/createJWTCallback.js b/packages/api/src/routes/auth/callbacks/createJWTCallback.js similarity index 100% rename from packages/api/src/auth/callbacks/createJWTCallback.js rename to packages/api/src/routes/auth/callbacks/createJWTCallback.js diff --git a/packages/api/src/auth/callbacks/createRedirectCallback.js b/packages/api/src/routes/auth/callbacks/createRedirectCallback.js similarity index 100% rename from packages/api/src/auth/callbacks/createRedirectCallback.js rename to packages/api/src/routes/auth/callbacks/createRedirectCallback.js diff --git a/packages/api/src/auth/callbacks/createSessionCallback.js b/packages/api/src/routes/auth/callbacks/createSessionCallback.js similarity index 100% rename from packages/api/src/auth/callbacks/createSessionCallback.js rename to packages/api/src/routes/auth/callbacks/createSessionCallback.js diff --git a/packages/api/src/auth/callbacks/createSignInCallback.js b/packages/api/src/routes/auth/callbacks/createSignInCallback.js similarity index 100% rename from packages/api/src/auth/callbacks/createSignInCallback.js rename to packages/api/src/routes/auth/callbacks/createSignInCallback.js diff --git a/packages/api/src/auth/createProviders.js b/packages/api/src/routes/auth/createProviders.js similarity index 100% rename from packages/api/src/auth/createProviders.js rename to packages/api/src/routes/auth/createProviders.js diff --git a/packages/api/src/auth/events/createCreateUserEvent.js b/packages/api/src/routes/auth/events/createCreateUserEvent.js similarity index 100% rename from packages/api/src/auth/events/createCreateUserEvent.js rename to packages/api/src/routes/auth/events/createCreateUserEvent.js diff --git a/packages/api/src/auth/events/createEventPlugins.js b/packages/api/src/routes/auth/events/createEventPlugins.js similarity index 100% rename from packages/api/src/auth/events/createEventPlugins.js rename to packages/api/src/routes/auth/events/createEventPlugins.js diff --git a/packages/api/src/auth/events/createEvents.js b/packages/api/src/routes/auth/events/createEvents.js similarity index 100% rename from packages/api/src/auth/events/createEvents.js rename to packages/api/src/routes/auth/events/createEvents.js diff --git a/packages/api/src/auth/events/createLinkAccountEvent.js b/packages/api/src/routes/auth/events/createLinkAccountEvent.js similarity index 100% rename from packages/api/src/auth/events/createLinkAccountEvent.js rename to packages/api/src/routes/auth/events/createLinkAccountEvent.js diff --git a/packages/api/src/auth/events/createSessionEvent.js b/packages/api/src/routes/auth/events/createSessionEvent.js similarity index 100% rename from packages/api/src/auth/events/createSessionEvent.js rename to packages/api/src/routes/auth/events/createSessionEvent.js diff --git a/packages/api/src/auth/events/createSignInEvent.js b/packages/api/src/routes/auth/events/createSignInEvent.js similarity index 100% rename from packages/api/src/auth/events/createSignInEvent.js rename to packages/api/src/routes/auth/events/createSignInEvent.js diff --git a/packages/api/src/auth/events/createSignOutEvent.js b/packages/api/src/routes/auth/events/createSignOutEvent.js similarity index 100% rename from packages/api/src/auth/events/createSignOutEvent.js rename to packages/api/src/routes/auth/events/createSignOutEvent.js diff --git a/packages/api/src/auth/events/createUpdateUserEvent.js b/packages/api/src/routes/auth/events/createUpdateUserEvent.js similarity index 100% rename from packages/api/src/auth/events/createUpdateUserEvent.js rename to packages/api/src/routes/auth/events/createUpdateUserEvent.js diff --git a/packages/api/src/auth/getNextAuthConfig.js b/packages/api/src/routes/auth/getNextAuthConfig.js similarity index 100% rename from packages/api/src/auth/getNextAuthConfig.js rename to packages/api/src/routes/auth/getNextAuthConfig.js diff --git a/packages/build/src/build/buildAuth/buildAuth.js b/packages/build/src/build/buildAuth/buildAuth.js index 856a57cdbe..ce7ab739b8 100644 --- a/packages/build/src/build/buildAuth/buildAuth.js +++ b/packages/build/src/build/buildAuth/buildAuth.js @@ -16,12 +16,16 @@ limitations under the License. */ +import { type } from '@lowdefy/helpers'; import buildAuthPlugins from './buildAuthPlugins.js'; import buildPageAuth from './buildPageAuth.js'; import validateAuthConfig from './validateAuthConfig.js'; function buildAuth({ components, context }) { + const configured = !type.isNone(components.auth); + validateAuthConfig({ components }); + components.auth.configured = configured; buildPageAuth({ components }); buildAuthPlugins({ components, context }); diff --git a/packages/client/src/Client.js b/packages/client/src/Client.js index 114f3b6925..c06294c91f 100644 --- a/packages/client/src/Client.js +++ b/packages/client/src/Client.js @@ -30,7 +30,6 @@ const Client = ({ config, resetContext = { reset: false, setReset: () => undefined }, router, - session, stage, types, window, @@ -40,7 +39,6 @@ const Client = ({ Components, config, router, - session, stage, types, window, diff --git a/packages/client/src/initLowdefyContext.js b/packages/client/src/initLowdefyContext.js index 80291781ff..dce3460adc 100644 --- a/packages/client/src/initLowdefyContext.js +++ b/packages/client/src/initLowdefyContext.js @@ -44,7 +44,7 @@ const lowdefy = { lowdefyGlobal: {}, }; -function initLowdefyContext({ auth, Components, config, router, session, stage, types, window }) { +function initLowdefyContext({ auth, Components, config, router, stage, types, window }) { if (stage === 'dev') { window.lowdefy = lowdefy; } @@ -54,7 +54,7 @@ function initLowdefyContext({ auth, Components, config, router, session, stage, lowdefy.menus = config.rootConfig.menus; lowdefy.pageId = config.pageConfig.pageId; lowdefy.urlQuery = urlQuery.parse(window.location.search.slice(1)); - lowdefy.user = session?.user ?? null; + lowdefy.user = auth?.session?.user ?? null; lowdefy._internal.window = window; lowdefy._internal.document = window.document; diff --git a/packages/server-dev/lib/App.js b/packages/server-dev/lib/App.js index e75340ee3c..9eb544ac9a 100644 --- a/packages/server-dev/lib/App.js +++ b/packages/server-dev/lib/App.js @@ -31,7 +31,7 @@ import blocks from '../build/plugins/blocks.js'; import icons from '../build/plugins/icons.js'; import operators from '../build/plugins/operators/client.js'; -const App = () => { +const App = ({ auth }) => { const router = useRouter(); const { data: rootConfig } = useRootConfig(router.basePath); @@ -44,6 +44,7 @@ const App = () => { {(resetContext) => ( { - const { data: session, status } = useSession(); +const Page = ({ auth, Components, config, pageId, resetContext, router, types }) => { const { data: pageConfig } = usePageConfig(pageId, router.basePath); - if (status === 'loading') { - return ''; - } if (!pageConfig) { router.replace(`/404`); return ''; @@ -38,7 +32,7 @@ const Page = ({ Components, config, pageId, resetContext, router, types }) => { } return ( { }} resetContext={resetContext} router={router} - session={session} stage="dev" types={types} window={window} diff --git a/packages/server-dev/lib/auth/Auth.js b/packages/server-dev/lib/auth/Auth.js new file mode 100644 index 0000000000..8281576b1e --- /dev/null +++ b/packages/server-dev/lib/auth/Auth.js @@ -0,0 +1,35 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* eslint-disable react/jsx-props-no-spreading */ + +import React from 'react'; +import AuthConfigured from './AuthConfigured.js'; +import AuthNotConfigured from './AuthNotConfigured.js'; + +import authConfig from '../../build/auth.json'; + +function Auth({ children, session }) { + if (authConfig.configured === true) { + return ( + + {(auth) => children(auth)} + + ); + } + return {(auth) => children(auth)}; +} + +export default Auth; diff --git a/packages/server-dev/lib/auth/AuthConfigured.js b/packages/server-dev/lib/auth/AuthConfigured.js new file mode 100644 index 0000000000..21c2803978 --- /dev/null +++ b/packages/server-dev/lib/auth/AuthConfigured.js @@ -0,0 +1,47 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* eslint-disable react/jsx-props-no-spreading */ + +import React from 'react'; +import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; + +function Session({ children }) { + const { data: session, status } = useSession(); + // If session is passed to SessionProvider from getServerSideProps + // we won't have a loading state here. + // But 404 uses getStaticProps so we have this for 404. + if (status === 'loading') { + return ''; + } + return children(session); +} + +function AuthConfigured({ authConfig, children, serverSession }) { + const auth = { signIn, signOut, authConfig }; + + return ( + + + {(session) => { + auth.session = session; + return children(auth); + }} + + + ); +} + +export default AuthConfigured; diff --git a/packages/server-dev/lib/auth/AuthNotConfigured.js b/packages/server-dev/lib/auth/AuthNotConfigured.js new file mode 100644 index 0000000000..97fee61318 --- /dev/null +++ b/packages/server-dev/lib/auth/AuthNotConfigured.js @@ -0,0 +1,32 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* eslint-disable react/jsx-props-no-spreading */ + +function authNotConfigured() { + throw new Error('Auth not configured.'); +} + +function AuthNotConfigured({ authConfig, children }) { + const auth = { + authConfig, + signIn: authNotConfigured, + signOut: authNotConfigured, + }; + + return children(auth); +} + +export default AuthNotConfigured; diff --git a/packages/server-dev/lib/auth/getServerSession.js b/packages/server-dev/lib/auth/getServerSession.js new file mode 100644 index 0000000000..7bc75dae2a --- /dev/null +++ b/packages/server-dev/lib/auth/getServerSession.js @@ -0,0 +1,27 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { getSession } from 'next-auth/react'; +import authJson from '../../build/auth.json'; + +async function getServerSession(context) { + if (authJson.configured === true) { + return await getSession(context); + } + return undefined; +} + +export default getServerSession; diff --git a/packages/server-dev/pages/_app.js b/packages/server-dev/pages/_app.js index 7875fd515a..571153aaf3 100644 --- a/packages/server-dev/pages/_app.js +++ b/packages/server-dev/pages/_app.js @@ -16,7 +16,8 @@ import React, { Suspense } from 'react'; import dynamic from 'next/dynamic'; -import { SessionProvider } from 'next-auth/react'; + +import Auth from '../lib/auth/Auth.js'; // Must be in _app due to next specifications. import '../build/plugins/styles.less'; @@ -24,9 +25,7 @@ import '../build/plugins/styles.less'; function App({ Component }) { return ( - - - + {(auth) => } ); } diff --git a/packages/server-dev/pages/api/page/[pageId].js b/packages/server-dev/pages/api/page/[pageId].js index 3426226c54..92c31bf7c4 100644 --- a/packages/server-dev/pages/api/page/[pageId].js +++ b/packages/server-dev/pages/api/page/[pageId].js @@ -15,10 +15,10 @@ */ import { createApiContext, getPageConfig } from '@lowdefy/api'; -import { getSession } from 'next-auth/react'; +import getServerSession from '../../../lib/auth/getServerSession.js'; export default async function handler(req, res) { - const session = await getSession({ req }); + const session = await getServerSession({ req }); const apiContext = await createApiContext({ buildDirectory: './build', logger: console, diff --git a/packages/server-dev/pages/api/request/[pageId]/[requestId].js b/packages/server-dev/pages/api/request/[pageId]/[requestId].js index 648177e0f2..8e121733a1 100644 --- a/packages/server-dev/pages/api/request/[pageId]/[requestId].js +++ b/packages/server-dev/pages/api/request/[pageId]/[requestId].js @@ -16,8 +16,9 @@ import { callRequest, createApiContext } from '@lowdefy/api'; import { getSecretsFromEnv } from '@lowdefy/node-utils'; -import { getSession } from 'next-auth/react'; + import connections from '../../../../build/plugins/connections.js'; +import getServerSession from '../../../../lib/auth/getServerSession.js'; import operators from '../../../../build/plugins/operators/server.js'; export default async function handler(req, res) { @@ -25,7 +26,7 @@ export default async function handler(req, res) { if (req.method !== 'POST') { throw new Error('Only POST requests are supported.'); } - const session = await getSession({ req }); + const session = await getServerSession({ req }); const apiContext = await createApiContext({ buildDirectory: './build', connections, diff --git a/packages/server-dev/pages/api/root.js b/packages/server-dev/pages/api/root.js index 8d6db2cd7d..b2bcd4738e 100644 --- a/packages/server-dev/pages/api/root.js +++ b/packages/server-dev/pages/api/root.js @@ -15,10 +15,11 @@ */ import { createApiContext, getRootConfig } from '@lowdefy/api'; -import { getSession } from 'next-auth/react'; + +import getServerSession from '../../lib/auth/getServerSession.js'; export default async function handler(req, res) { - const session = await getSession({ req }); + const session = await getServerSession({ req }); const apiContext = await createApiContext({ buildDirectory: './build', logger: console, diff --git a/packages/server/lib/Page.js b/packages/server/lib/Page.js index fd62fc5fe7..773e6c5651 100644 --- a/packages/server/lib/Page.js +++ b/packages/server/lib/Page.js @@ -20,34 +20,23 @@ import { useRouter } from 'next/router'; import Client from '@lowdefy/client'; import Head from 'next/head'; import Link from 'next/link'; -import { signIn, signOut, useSession } from 'next-auth/react'; import actions from '../build/plugins/actions.js'; import blocks from '../build/plugins/blocks.js'; import icons from '../build/plugins/icons.js'; import operators from '../build/plugins/operators/client.js'; -const Page = ({ pageConfig, rootConfig }) => { +const Page = ({ auth, pageConfig, rootConfig }) => { const router = useRouter(); - const { data: session, status } = useSession(); - - // If session is passed to SessionProvider from getServerSideProps - // we won't have a loading state here. - // But 404 uses getStaticProps so we have this for 404. - if (status === 'loading') { - return ''; - } - return ( + {(auth) => children(auth)} + + ); + } + return {(auth) => children(auth)}; +} + +export default Auth; diff --git a/packages/server/lib/auth/AuthConfigured.js b/packages/server/lib/auth/AuthConfigured.js new file mode 100644 index 0000000000..21c2803978 --- /dev/null +++ b/packages/server/lib/auth/AuthConfigured.js @@ -0,0 +1,47 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* eslint-disable react/jsx-props-no-spreading */ + +import React from 'react'; +import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; + +function Session({ children }) { + const { data: session, status } = useSession(); + // If session is passed to SessionProvider from getServerSideProps + // we won't have a loading state here. + // But 404 uses getStaticProps so we have this for 404. + if (status === 'loading') { + return ''; + } + return children(session); +} + +function AuthConfigured({ authConfig, children, serverSession }) { + const auth = { signIn, signOut, authConfig }; + + return ( + + + {(session) => { + auth.session = session; + return children(auth); + }} + + + ); +} + +export default AuthConfigured; diff --git a/packages/server/lib/auth/AuthNotConfigured.js b/packages/server/lib/auth/AuthNotConfigured.js new file mode 100644 index 0000000000..97fee61318 --- /dev/null +++ b/packages/server/lib/auth/AuthNotConfigured.js @@ -0,0 +1,32 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* eslint-disable react/jsx-props-no-spreading */ + +function authNotConfigured() { + throw new Error('Auth not configured.'); +} + +function AuthNotConfigured({ authConfig, children }) { + const auth = { + authConfig, + signIn: authNotConfigured, + signOut: authNotConfigured, + }; + + return children(auth); +} + +export default AuthNotConfigured; diff --git a/packages/server/lib/auth/getServerSession.js b/packages/server/lib/auth/getServerSession.js new file mode 100644 index 0000000000..7bc75dae2a --- /dev/null +++ b/packages/server/lib/auth/getServerSession.js @@ -0,0 +1,27 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { getSession } from 'next-auth/react'; +import authJson from '../../build/auth.json'; + +async function getServerSession(context) { + if (authJson.configured === true) { + return await getSession(context); + } + return undefined; +} + +export default getServerSession; diff --git a/packages/server/pages/[pageId].js b/packages/server/pages/[pageId].js index 76a3b80774..e525c63442 100644 --- a/packages/server/pages/[pageId].js +++ b/packages/server/pages/[pageId].js @@ -15,13 +15,13 @@ */ import { createApiContext, getPageConfig, getRootConfig } from '@lowdefy/api'; -import { getSession } from 'next-auth/react'; +import getServerSession from '../lib/auth/getServerSession.js'; import Page from '../lib/Page.js'; export async function getServerSideProps(context) { const { pageId } = context.params; - const session = await getSession(context); + const session = await getServerSession(context); const apiContext = await createApiContext({ buildDirectory: './build', logger: console, diff --git a/packages/server/pages/_app.js b/packages/server/pages/_app.js index 8e426bb99d..30e011b65f 100644 --- a/packages/server/pages/_app.js +++ b/packages/server/pages/_app.js @@ -13,20 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; import dynamic from 'next/dynamic'; -import { SessionProvider } from 'next-auth/react'; + +import Auth from '../lib/auth/Auth.js'; // Must be in _app due to next specifications. import '../build/plugins/styles.less'; -// TODO: SessionProvider requires basebath -function App({ Component, pageProps: { session, ...pageProps } }) { +function App({ Component, pageProps: { session, rootConfig, pageConfig } }) { return ( - - - + + {(auth) => } + ); } diff --git a/packages/server/pages/api/auth/[...nextauth].js b/packages/server/pages/api/auth/[...nextauth].js index bf419a670f..03cf941374 100644 --- a/packages/server/pages/api/auth/[...nextauth].js +++ b/packages/server/pages/api/auth/[...nextauth].js @@ -22,4 +22,16 @@ import callbacks from '../../../build/plugins/auth/callbacks.js'; import events from '../../../build/plugins/auth/events.js'; import providers from '../../../build/plugins/auth/providers.js'; -export default NextAuth(getNextAuthConfig({ authJson, plugins: { callbacks, events, providers } })); +export default async function auth(req, res) { + if (authJson.configured === true) { + return await NextAuth( + req, + res, + getNextAuthConfig({ authJson, plugins: { callbacks, events, providers } }) + ); + } + + return res.status(404).json({ + message: 'Auth not configured', + }); +} diff --git a/packages/server/pages/api/request/[pageId]/[requestId].js b/packages/server/pages/api/request/[pageId]/[requestId].js index dedfd8c827..4557b98570 100644 --- a/packages/server/pages/api/request/[pageId]/[requestId].js +++ b/packages/server/pages/api/request/[pageId]/[requestId].js @@ -16,7 +16,9 @@ import { callRequest, createApiContext } from '@lowdefy/api'; import { getSecretsFromEnv } from '@lowdefy/node-utils'; -import { getSession } from 'next-auth/react'; + +import getServerSession from '../../../../lib/auth/getServerSession.js'; + import connections from '../../../../build/plugins/connections.js'; import operators from '../../../../build/plugins/operators/server.js'; @@ -25,7 +27,7 @@ export default async function handler(req, res) { if (req.method !== 'POST') { throw new Error('Only POST requests are supported.'); } - const session = await getSession({ req }); + const session = await getServerSession({ req }); const apiContext = await createApiContext({ buildDirectory: './build', connections, diff --git a/packages/server/pages/index.js b/packages/server/pages/index.js index 508bb196f5..b0b0edc7de 100644 --- a/packages/server/pages/index.js +++ b/packages/server/pages/index.js @@ -15,12 +15,13 @@ */ import { createApiContext, getPageConfig, getRootConfig } from '@lowdefy/api'; -import { getSession } from 'next-auth/react'; +import getServerSession from '../lib/auth/getServerSession.js'; import Page from '../lib/Page.js'; export async function getServerSideProps(context) { - const session = await getSession(context); + const session = await getServerSession(context); + const apiContext = await createApiContext({ buildDirectory: './build', logger: console, From 8bc34a1b0533e6231bfdc2655ba48e1df701a772 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 25 May 2022 17:19:00 +0200 Subject: [PATCH 2/4] feat: Set login providerId if only one provider is configured. --- packages/client/src/auth/createAuthMethods.js | 8 +++++--- packages/client/src/initLowdefyContext.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/client/src/auth/createAuthMethods.js b/packages/client/src/auth/createAuthMethods.js index 143a9016f6..fb10777348 100644 --- a/packages/client/src/auth/createAuthMethods.js +++ b/packages/client/src/auth/createAuthMethods.js @@ -34,12 +34,14 @@ function getCallbackUrl({ lowdefy, callbackUrl = {} }) { return undefined; } -function createAuthMethods(lowdefy, auth) { +function createAuthMethods({ lowdefy, auth }) { // login and logout are Lowdefy function that handle action params // signIn and signOut are the next-auth methods function login({ providerId, callbackUrl, authUrl = {} } = {}) { - // TODO: if only one provider exists, pass provider here - // to link directly to provider + if (type.isNone(providerId) && auth.authConfig.providers.length === 1) { + providerId = auth.authConfig.providers[0].id; + } + auth.signIn( providerId, { callbackUrl: getCallbackUrl({ lowdefy, callbackUrl }) }, diff --git a/packages/client/src/initLowdefyContext.js b/packages/client/src/initLowdefyContext.js index dce3460adc..288bb35b30 100644 --- a/packages/client/src/initLowdefyContext.js +++ b/packages/client/src/initLowdefyContext.js @@ -69,7 +69,7 @@ function initLowdefyContext({ auth, Components, config, router, stage, types, wi lowdefy._internal.operators = types.operators; // TODO: discuss not using object arguments - lowdefy._internal.auth = createAuthMethods(lowdefy, auth); + lowdefy._internal.auth = createAuthMethods({ lowdefy, auth }); return lowdefy; } From 7f099e1d55cab7ba79214870f1bc23235b8fd09a Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 25 May 2022 17:58:38 +0200 Subject: [PATCH 3/4] feat: Add openid connect standard claims to user object. --- .../auth/callbacks/createJWTCallback.js | 50 ++++++++++++++++++- .../auth/callbacks/createSessionCallback.js | 47 ++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/packages/api/src/routes/auth/callbacks/createJWTCallback.js b/packages/api/src/routes/auth/callbacks/createJWTCallback.js index 18caea10f3..922f937181 100644 --- a/packages/api/src/routes/auth/callbacks/createJWTCallback.js +++ b/packages/api/src/routes/auth/callbacks/createJWTCallback.js @@ -23,9 +23,55 @@ function createJWTCallback({ authConfig, plugins }) { type: 'jwt', }); - if (jwtCallbackPlugins.length === 0) return undefined; - async function jwtCallback({ token, user, account, profile, isNewUser }) { + if (profile) { + const { + sub, + name, + given_name, + family_name, + middle_name, + nickname, + preferred_username, + profile: profile_claim, + picture, + website, + email, + email_verified, + gender, + birthdate, + zoneinfo, + locale, + phone_number, + phone_number_verified, + address, + updated_at, + } = profile; + token = { + sub, + name, + given_name, + family_name, + middle_name, + nickname, + preferred_username, + profile: profile_claim, + picture, + website, + email, + email_verified, + gender, + birthdate, + zoneinfo, + locale, + phone_number, + phone_number_verified, + address, + updated_at, + ...token, + }; + } + for (const plugin of jwtCallbackPlugins) { token = await plugin.fn({ properties: plugin.properties ?? {}, diff --git a/packages/api/src/routes/auth/callbacks/createSessionCallback.js b/packages/api/src/routes/auth/callbacks/createSessionCallback.js index 60d69cbf7f..3edbbdf6d7 100644 --- a/packages/api/src/routes/auth/callbacks/createSessionCallback.js +++ b/packages/api/src/routes/auth/callbacks/createSessionCallback.js @@ -24,8 +24,53 @@ function createSessionCallback({ authConfig, plugins }) { }); async function sessionCallback({ session, token, user }) { + // console.log({ session, token, user }); if (token) { - session.user.sub = token.sub; + const { + sub, + name, + given_name, + family_name, + middle_name, + nickname, + preferred_username, + profile, + picture, + website, + email, + email_verified, + gender, + birthdate, + zoneinfo, + locale, + phone_number, + phone_number_verified, + address, + updated_at, + } = token; + session.user = { + sub, + name, + given_name, + family_name, + middle_name, + nickname, + preferred_username, + profile, + picture, + website, + email, + email_verified, + gender, + birthdate, + zoneinfo, + locale, + phone_number, + phone_number_verified, + address, + updated_at, + ...session.user, + }; } for (const plugin of sessionCallbackPlugins) { From 0882416d6fa9a9637ec8870180a022a2bb6dbd21 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 25 May 2022 18:03:58 +0200 Subject: [PATCH 4/4] fix(build): Fix buildAuth tests. --- packages/build/src/build/buildAuth/buildAuth.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/build/src/build/buildAuth/buildAuth.test.js b/packages/build/src/build/buildAuth/buildAuth.test.js index 6859477f1d..dbfcc8ab74 100644 --- a/packages/build/src/build/buildAuth/buildAuth.test.js +++ b/packages/build/src/build/buildAuth/buildAuth.test.js @@ -31,6 +31,7 @@ test('buildAuth default', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: false, events: [], pages: { roles: {}, @@ -53,6 +54,7 @@ test('buildAuth no pages', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: false, events: [], pages: { roles: {}, @@ -82,6 +84,7 @@ test('buildAuth all protected, some public', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: true, events: [], pages: { public: ['a', 'b'], @@ -117,6 +120,7 @@ test('buildAuth all public, some protected', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: true, events: [], pages: { protected: ['a', 'b'], @@ -152,6 +156,7 @@ test('buildAuth all public', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: true, events: [], pages: { public: true, @@ -188,6 +193,7 @@ test('buildAuth all protected', async () => { auth: { callbacks: [], events: [], + configured: true, pages: { protected: true, roles: {}, @@ -224,6 +230,7 @@ test('buildAuth roles', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: true, events: [], pages: { roles: { @@ -276,6 +283,7 @@ test('buildAuth roles and protected pages array', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: true, events: [], pages: { roles: { @@ -307,6 +315,7 @@ test('buildAuth roles and protected true', async () => { expect(res).toEqual({ auth: { callbacks: [], + configured: true, events: [], pages: { roles: {