From 4517b98fb28c065b3f38e6c7588a012ba15c1837 Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Wed, 23 Sep 2020 09:19:02 +0200 Subject: [PATCH 01/13] Use NextAuth session instead of oidc-client --- package.json | 2 ++ src/authentication/api/index.ts | 6 +++- src/pages/api/auth/[...nextauth].ts | 33 ++++++++++++++++++++++ src/pages/api/auth/callback/Onlineweb4.tsx | 0 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/pages/api/auth/[...nextauth].ts create mode 100644 src/pages/api/auth/callback/Onlineweb4.tsx diff --git a/package.json b/package.json index b0160728..e903c4c0 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@sentry/node": "^5.6.2", "@types/classnames": "^2.2.7", "@types/markdown-to-jsx": "^6.11.0", + "@types/next-auth": "^3.1.5", "@types/react-google-recaptcha": "^1.1.1", "@types/react-redux": "^7.1.0", "@types/react-select": "^3.0.5", @@ -91,6 +92,7 @@ "isomorphic-fetch": "^2.2.1", "markdown-to-jsx": "^6.11.1", "next": "^9.5.3", + "next-auth": "^3.1.0", "next-redux-wrapper": "^5.0.0", "postcss-focus-visible": "^4.0.0", "react-day-picker": "^7.3.0", diff --git a/src/authentication/api/index.ts b/src/authentication/api/index.ts index de274c7f..cd5491c2 100644 --- a/src/authentication/api/index.ts +++ b/src/authentication/api/index.ts @@ -2,7 +2,7 @@ import { __CLIENT__ } from 'common/constants/environment'; import { UserManager } from 'oidc-client'; import settings from './settings'; import { IAuthUser } from 'authentication/models/User'; - +import { signIn, signOut, useSession, session } from 'next-auth/client'; /** * @summary Basic wrapper for OIDC login. * Redirects the user to the authentication page defined in settings. @@ -11,6 +11,7 @@ import { IAuthUser } from 'authentication/models/User'; export const USER_MANAGER = __CLIENT__ ? new UserManager(settings) : null; export const logIn = async () => { + return await signIn() if (USER_MANAGER) { const user = await USER_MANAGER.signinRedirect({ data: window.location.pathname }); return user; @@ -22,6 +23,8 @@ export const logIn = async () => { * @summary Returns user if logged in */ export const getUser = async (): Promise => { + const [session, loading] = useSession(); + return session.user as unknown as IAuthUser; if (USER_MANAGER) { const user = (await USER_MANAGER.getUser()) as IAuthUser | null; return user || undefined; @@ -34,6 +37,7 @@ export const getUser = async (): Promise => { */ export const logOut = async () => { + return await signOut(); if (USER_MANAGER) { await USER_MANAGER.signoutRedirect(); } diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts new file mode 100644 index 00000000..816f311d --- /dev/null +++ b/src/pages/api/auth/[...nextauth].ts @@ -0,0 +1,33 @@ +import NextAuth from 'next-auth' +import { NextApiRequest, NextApiResponse } from 'next'; +import { Profile } from 'oidc-client'; + +const options = { + providers: [ + { + id: 'Onlineweb4', + name: 'Onlineweb4', + type: 'oauth', + version: '2.0', + scope: 'openid profile onlineweb4', + params: { response_type: 'code'}, + accessTokenUrl: 'https://online.ntnu.no/openid/token', + requestTokenUrl: '"https://online.ntnu.no/openid/authorize', + authorizationUrl: 'https://online.ntnu.no/openid/authorize', + profileUrl: 'https://online.ntnu.no/openid/userinfo', + profile: (profile: Profile) => { + return { + id: profile.id, + name: profile.name, + email: profile.email, + image: profile.picture + } + }, + clientId: process.env.OW4_SSO_CLIENT_ID, + clientSecret: process.env.OW4_SSO_CLIENT_SECRET, + idToken: true, + } + ] +} + +export default (req: NextApiRequest, res: NextApiResponse) => NextAuth(req, res, options); \ No newline at end of file diff --git a/src/pages/api/auth/callback/Onlineweb4.tsx b/src/pages/api/auth/callback/Onlineweb4.tsx new file mode 100644 index 00000000..e69de29b From c68fc9f6108c4936a261aaeae5c672c59cb25b12 Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Fri, 25 Sep 2020 15:02:15 +0200 Subject: [PATCH 02/13] remove unneeded callback --- src/pages/api/auth/[...nextauth].ts | 18 ++++++++++-------- src/pages/api/auth/callback/Onlineweb4.tsx | 0 2 files changed, 10 insertions(+), 8 deletions(-) delete mode 100644 src/pages/api/auth/callback/Onlineweb4.tsx diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 816f311d..c9263307 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -9,23 +9,25 @@ const options = { name: 'Onlineweb4', type: 'oauth', version: '2.0', - scope: 'openid profile onlineweb4', - params: { response_type: 'code'}, + scope: 'openid onlineweb4', + params: { + grant_type: 'authorization_code', + }, accessTokenUrl: 'https://online.ntnu.no/openid/token', requestTokenUrl: '"https://online.ntnu.no/openid/authorize', - authorizationUrl: 'https://online.ntnu.no/openid/authorize', + authorizationUrl: 'https://online.ntnu.no/openid/authorize?response_type=code', profileUrl: 'https://online.ntnu.no/openid/userinfo', profile: (profile: Profile) => { + console.log("profile", profile); + // https://next-auth.js.org/configuration/options#jwt-helpers return { - id: profile.id, - name: profile.name, - email: profile.email, - image: profile.picture + id: profile.sub, } }, clientId: process.env.OW4_SSO_CLIENT_ID, clientSecret: process.env.OW4_SSO_CLIENT_SECRET, - idToken: true, + debug: true, + idToken: false, } ] } diff --git a/src/pages/api/auth/callback/Onlineweb4.tsx b/src/pages/api/auth/callback/Onlineweb4.tsx deleted file mode 100644 index e69de29b..00000000 From 2932133ee62af50997e3834088a6cceca41913e3 Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Sun, 27 Sep 2020 15:54:21 +0200 Subject: [PATCH 03/13] Properly configure nextauth --- src/pages/api/auth/[...nextauth].ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index c9263307..d163a848 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -5,11 +5,11 @@ import { Profile } from 'oidc-client'; const options = { providers: [ { - id: 'Onlineweb4', + id: 'onlineweb4', name: 'Onlineweb4', type: 'oauth', version: '2.0', - scope: 'openid onlineweb4', + scope: 'openid profile onlineweb4', params: { grant_type: 'authorization_code', }, @@ -20,8 +20,14 @@ const options = { profile: (profile: Profile) => { console.log("profile", profile); // https://next-auth.js.org/configuration/options#jwt-helpers + /* + Dette funker jo råbra, ikke sjekket om man faktisk får useren i frontend, men den logges her korrekt. + Problemet akkurat nå er at man får "Missing or invalid provider account" med oauth_callback_handler_error. + */ return { + ...profile, id: profile.sub, + image: profile.picture, } }, clientId: process.env.OW4_SSO_CLIENT_ID, From 22f100703f97e54d4febf07f7d1158edbd2d31f7 Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Sun, 27 Sep 2020 20:57:49 +0200 Subject: [PATCH 04/13] Use NextAuth for API Calls --- src/authentication/api/index.ts | 6 ++-- src/pages/_app.tsx | 23 ++++++++------ src/pages/api/auth/[...nextauth].ts | 49 ++++++++++++++++++++++++----- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/authentication/api/index.ts b/src/authentication/api/index.ts index cd5491c2..f9421629 100644 --- a/src/authentication/api/index.ts +++ b/src/authentication/api/index.ts @@ -2,7 +2,7 @@ import { __CLIENT__ } from 'common/constants/environment'; import { UserManager } from 'oidc-client'; import settings from './settings'; import { IAuthUser } from 'authentication/models/User'; -import { signIn, signOut, useSession, session } from 'next-auth/client'; +import { signIn, signOut, getSession } from 'next-auth/client'; /** * @summary Basic wrapper for OIDC login. * Redirects the user to the authentication page defined in settings. @@ -23,8 +23,8 @@ export const logIn = async () => { * @summary Returns user if logged in */ export const getUser = async (): Promise => { - const [session, loading] = useSession(); - return session.user as unknown as IAuthUser; + const session = await getSession(); + return session?.user as unknown as IAuthUser; if (USER_MANAGER) { const user = (await USER_MANAGER.getUser()) as IAuthUser | null; return user || undefined; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index cfc10e97..24257a2a 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,7 +5,7 @@ import { Settings as LuxonSettings } from 'luxon'; import withRedux, { ReduxWrapperAppProps } from 'next-redux-wrapper'; import DefaultApp, { AppProps } from 'next/app'; import React from 'react'; -import { Provider } from 'react-redux'; +import { Provider as ReduxProvider } from 'react-redux'; import 'react-day-picker/lib/style.css'; @@ -16,6 +16,7 @@ import ContextWrapper from 'core/providers/ContextWrapper'; import { initStore, State } from 'core/redux/Store'; import UserProfileProvider from 'profile/providers/UserProfile'; import { registerServiceWorker } from 'serviceworker/browser'; +import { Provider as SessionProvider } from 'next-auth/client'; import { GlobalStyle } from '@dotkomonline/design-system'; @@ -37,15 +38,17 @@ const CustomApp = (appProps: Props): JSX.Element => { return ( <> - - - - - - - - - + + + + + + + + + + + ); }; diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index d163a848..f04bef38 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -1,15 +1,55 @@ import NextAuth from 'next-auth' import { NextApiRequest, NextApiResponse } from 'next'; +import { Session } from 'next-auth/client'; import { Profile } from 'oidc-client'; +interface Token { + name?: string, + email?: string, + picture?: string, // url to image + accessToken?: string, + iat: number, + exp: number, +} + +interface APISession extends Session { + user: Session["user"] & { + access_token?: string; + } +} + +interface Account { + provider: string | null, + type: string | null, + id: number | null, + refreshToken: string | null, + accessToken: string | null, + accessTokenExpires: null +} + + const options = { + callbacks: { + session: async (session: APISession, token: Token): Promise => { + if (token.accessToken) { + session.user.access_token = token.accessToken; + } + return Promise.resolve(session) + }, + jwt: async (token: Token, _: Token, account: Account) => { + if (account && account.accessToken) { + token.accessToken = account.accessToken; + } + return Promise.resolve(token) + } + }, providers: [ { id: 'onlineweb4', name: 'Onlineweb4', type: 'oauth', version: '2.0', - scope: 'openid profile onlineweb4', + scope: 'openid profile email onlineweb4', params: { grant_type: 'authorization_code', }, @@ -18,16 +58,11 @@ const options = { authorizationUrl: 'https://online.ntnu.no/openid/authorize?response_type=code', profileUrl: 'https://online.ntnu.no/openid/userinfo', profile: (profile: Profile) => { - console.log("profile", profile); - // https://next-auth.js.org/configuration/options#jwt-helpers - /* - Dette funker jo råbra, ikke sjekket om man faktisk får useren i frontend, men den logges her korrekt. - Problemet akkurat nå er at man får "Missing or invalid provider account" med oauth_callback_handler_error. - */ return { ...profile, id: profile.sub, image: profile.picture, + email: profile.email, } }, clientId: process.env.OW4_SSO_CLIENT_ID, From 60b42b37ca1404c864e4137d8ec430ba1eac42b8 Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Sun, 27 Sep 2020 21:20:45 +0200 Subject: [PATCH 05/13] Remove old usermanager from api calls --- src/authentication/api/index.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/authentication/api/index.ts b/src/authentication/api/index.ts index f9421629..8a0a6085 100644 --- a/src/authentication/api/index.ts +++ b/src/authentication/api/index.ts @@ -11,12 +11,7 @@ import { signIn, signOut, getSession } from 'next-auth/client'; export const USER_MANAGER = __CLIENT__ ? new UserManager(settings) : null; export const logIn = async () => { - return await signIn() - if (USER_MANAGER) { - const user = await USER_MANAGER.signinRedirect({ data: window.location.pathname }); - return user; - } - return null; + return await signIn('onlineweb4'); }; /** @@ -25,11 +20,6 @@ export const logIn = async () => { export const getUser = async (): Promise => { const session = await getSession(); return session?.user as unknown as IAuthUser; - if (USER_MANAGER) { - const user = (await USER_MANAGER.getUser()) as IAuthUser | null; - return user || undefined; - } - return undefined; }; /** @@ -38,7 +28,4 @@ export const getUser = async (): Promise => { export const logOut = async () => { return await signOut(); - if (USER_MANAGER) { - await USER_MANAGER.signoutRedirect(); - } }; From 35b7b48a8157431277e09196477c00efff88cec0 Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Sun, 27 Sep 2020 21:41:59 +0200 Subject: [PATCH 06/13] Purge OIDC-client --- package.json | 1 - src/authentication/api/settings.ts | 18 ---- .../components/AuthenticationCallback.tsx | 28 ------- src/authentication/components/SilentRenew.tsx | 34 -------- .../providers/AuthenticationProvider.tsx | 82 ------------------- .../selectors/authentication.ts | 17 ---- src/authentication/slices/authentication.ts | 72 ---------------- 7 files changed, 252 deletions(-) delete mode 100644 src/authentication/api/settings.ts delete mode 100644 src/authentication/components/AuthenticationCallback.tsx delete mode 100644 src/authentication/components/SilentRenew.tsx delete mode 100644 src/authentication/providers/AuthenticationProvider.tsx delete mode 100644 src/authentication/selectors/authentication.ts delete mode 100644 src/authentication/slices/authentication.ts diff --git a/package.json b/package.json index e903c4c0..cc28dac5 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "less": "^3.9.0", "luxon": "^1.11.1", "multirange": "^2.0.0", - "oidc-client": "^1.6.1", "postcss-loader": "^3.0.0", "prettier": "^1.16.4", "query-string": "^6.2.0", diff --git a/src/authentication/api/settings.ts b/src/authentication/api/settings.ts deleted file mode 100644 index c47eea2b..00000000 --- a/src/authentication/api/settings.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DOMAIN } from 'common/constants/endpoints'; -import { UserManagerSettings } from 'oidc-client'; - -const settings: UserManagerSettings = { - authority: DOMAIN + '/openid', - client_id: process.env.OW4_SSO_CLIENT_ID || '', - redirect_uri: process.env.OW4_SSO_CALLBACK || '', - post_logout_redirect_uri: DOMAIN + '/', - response_type: 'id_token token', - scope: 'openid profile onlineweb4', - automaticSilentRenew: true, - filterProtocolClaims: true, - loadUserInfo: true, - silent_redirect_uri: process.env.OW4_SSO_CALLBACK || '', - revokeAccessTokenOnSignout: true, -}; - -export default settings; diff --git a/src/authentication/components/AuthenticationCallback.tsx b/src/authentication/components/AuthenticationCallback.tsx deleted file mode 100644 index 810d18d1..00000000 --- a/src/authentication/components/AuthenticationCallback.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useRouter } from 'next/router'; -import React, { FC, useEffect } from 'react'; - -import { getFrontPageUrl } from 'core/appUrls'; -import { USER_MANAGER } from 'authentication/api'; - -const AuthenticationCallbackComponent: FC = ({ children }) => { - const router = useRouter(); - const catchCallback = async () => { - if (USER_MANAGER) { - const frontPageUrl = getFrontPageUrl().href; - try { - const user = await USER_MANAGER.signinCallback(); - router.push(user.state || frontPageUrl); - } catch (err) { - router.push(frontPageUrl); - } - } - }; - - useEffect(() => { - catchCallback(); - }, []); - - return <>{children}; -}; - -export const AuthenticationCallback = AuthenticationCallbackComponent; diff --git a/src/authentication/components/SilentRenew.tsx b/src/authentication/components/SilentRenew.tsx deleted file mode 100644 index 6d4addb6..00000000 --- a/src/authentication/components/SilentRenew.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useEffect, FC, useLayoutEffect } from 'react'; - -import { USER_MANAGER } from 'authentication/api'; -import { __CLIENT__ } from 'common/constants/environment'; -import { useDispatch } from 'core/redux/hooks'; -import { authenticationActions } from 'authentication/slices/authentication'; -import { IAuthUser } from 'authentication/models/User'; - -const useIsomorphicLayoutEffect = __CLIENT__ ? useLayoutEffect : useEffect; - -type JSON = string & { __JSON__: T }; -declare const JSON: { - parse: (str: JSON) => T; - stringify: (obj: T) => JSON; -}; - -export const SilentRenewComponent: FC = () => { - const dispatch = useDispatch(); - const loadCurrentUser = async () => { - if (USER_MANAGER) { - const user = await USER_MANAGER.getUser(); - if (user) dispatch(authenticationActions.userSignIn(JSON.stringify(user as IAuthUser))); - else USER_MANAGER.signinSilent(); - } - }; - - useIsomorphicLayoutEffect(() => { - loadCurrentUser(); - }, [loadCurrentUser]); - - return <>; -}; - -export const SilentRenew = SilentRenewComponent; diff --git a/src/authentication/providers/AuthenticationProvider.tsx b/src/authentication/providers/AuthenticationProvider.tsx deleted file mode 100644 index e38d5aa0..00000000 --- a/src/authentication/providers/AuthenticationProvider.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { FC, useCallback, useEffect } from 'react'; - -import { useDispatch } from 'core/redux/hooks'; -import { authenticationActions } from 'authentication/slices/authentication'; -import { USER_MANAGER } from 'authentication/api'; -import { User } from 'oidc-client'; -import { IAuthUser } from 'authentication/models/User'; - -type JSON = string & { __JSON__: T }; -declare const JSON: { - parse: (str: JSON) => T; - stringify: (obj: T) => JSON; -}; - -// This component registers listeners for all authentication/user-related events which adds the events to the Redux store. -const AuthenticationProviderComponent: FC = ({ children }) => { - const dispatch = useDispatch(); - - // event callback when the user has been loaded (on silent renew or redirect) - const onUserLoaded = useCallback( - (user: User) => { - dispatch(authenticationActions.userSignIn(JSON.stringify(user as IAuthUser))); - }, - [dispatch] - ); - - // event callback when silent renew errored - const onSilentRenewError = useCallback(() => { - dispatch(authenticationActions.userSilentRenewError()); - }, [dispatch]); - - // event callback when the access token expired - const onAccessTokenExpired = useCallback(() => { - dispatch(authenticationActions.userExpired()); - }, [dispatch]); - - // event callback when the user is logged out - const onUserUnloaded = useCallback(() => { - dispatch(authenticationActions.userSessionTerminated()); - }, [dispatch]); - - // event callback when the user is expiring - const onAccessTokenExpiring = useCallback(() => { - dispatch(authenticationActions.userExpired()); - }, [dispatch]); - - // event callback when the user is signed out - const onUserSignedOut = useCallback(() => { - dispatch(authenticationActions.userSignOut()); - }, [dispatch]); - - const addEventListeners = useCallback(() => { - if (USER_MANAGER) { - USER_MANAGER.events.addUserLoaded(onUserLoaded); - USER_MANAGER.events.addSilentRenewError(onSilentRenewError); - USER_MANAGER.events.addAccessTokenExpired(onAccessTokenExpired); - USER_MANAGER.events.addAccessTokenExpiring(onAccessTokenExpiring); - USER_MANAGER.events.addUserUnloaded(onUserUnloaded); - USER_MANAGER.events.addUserSignedOut(onUserSignedOut); - } - }, [onUserLoaded, onSilentRenewError, onAccessTokenExpired, onAccessTokenExpiring, onUserUnloaded, onUserSignedOut]); - - const removeEventListeners = useCallback(() => { - if (USER_MANAGER) { - USER_MANAGER.events.removeUserLoaded(onUserLoaded); - USER_MANAGER.events.removeSilentRenewError(onSilentRenewError); - USER_MANAGER.events.removeAccessTokenExpired(onAccessTokenExpired); - USER_MANAGER.events.removeAccessTokenExpiring(onAccessTokenExpiring); - USER_MANAGER.events.removeUserUnloaded(onUserUnloaded); - USER_MANAGER.events.removeUserSignedOut(onUserSignedOut); - } - }, [onUserLoaded, onSilentRenewError, onAccessTokenExpired, onAccessTokenExpiring, onUserUnloaded, onUserSignedOut]); - - useEffect(() => { - addEventListeners(); - return () => removeEventListeners(); - }, [addEventListeners]); - - return <>{children}; -}; - -export const AuthenticationProvider = React.memo(AuthenticationProviderComponent); diff --git a/src/authentication/selectors/authentication.ts b/src/authentication/selectors/authentication.ts deleted file mode 100644 index 7779df47..00000000 --- a/src/authentication/selectors/authentication.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { State } from 'core/redux/Store'; - -export const selectIsLoggedIn = () => (state: State): boolean => { - return state.authentication.loggedIn; -}; - -export const selectFullName = () => (state: State): string => { - return state.authentication.user?.name || 'Anonym bruker'; -}; - -export const selectUserImage = () => (state: State): string => { - return state.authentication.user?.picture || 'Ingen bilde'; -}; - -export const selectUserName = () => (state: State): string => { - return state.authentication.user?.preferred_username || 'anonymous'; -}; diff --git a/src/authentication/slices/authentication.ts b/src/authentication/slices/authentication.ts deleted file mode 100644 index b9e5d20d..00000000 --- a/src/authentication/slices/authentication.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { createSlice, SerializedError, PayloadAction } from '@reduxjs/toolkit'; - -import { IAuthUser, IAuthProfile } from 'authentication/models/User'; - -// https://github.com/microsoft/TypeScript/issues/27930 -type JSON = string & { __JSON__: T }; -declare const JSON: { - parse: (str: JSON) => T; - stringify: (obj: T) => JSON; -}; - -interface IState { - loading: 'idle' | 'pending'; - error: null | SerializedError; - user: IAuthProfile | null; - loggedIn: boolean; - token: string | null; -} - -const INITIAL_STATE: IState = { - loading: 'idle', - error: null, - token: null, - user: null, - loggedIn: false, -}; - -const authenticationSlice = createSlice({ - name: 'authentication', - initialState: INITIAL_STATE, - reducers: { - userExpired(state) { - state.user = null; - state.token = null; - state.loggedIn = false; - state.loading = 'idle'; - }, - userSilentRenewError(state) { - state.user = null; - state.token = null; - state.loggedIn = false; - state.loading = 'idle'; - }, - userSessionTerminated(state) { - state.user = null; - state.token = null; - state.loggedIn = false; - state.loading = 'idle'; - }, - userSignOut(state) { - state.user = null; - state.token = null; - state.loggedIn = false; - state.loading = 'idle'; - }, - userSignIn(state, action: PayloadAction>) { - // https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants - const user = JSON.parse(action.payload); - state.user = user.profile; - state.loggedIn = true; - state.token = user.access_token; - state.loading = 'idle'; - }, - userLoading(state) { - state.loading = 'pending'; - }, - }, -}); - -export const authenticationActions = authenticationSlice.actions; - -export const authenticationReducer = authenticationSlice.reducer; From e09cd729c151914d4ed9a8d0c4ef326a431f1d7b Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Sun, 27 Sep 2020 21:42:19 +0200 Subject: [PATCH 07/13] lint --- src/authentication/api/index.ts | 2 +- .../providers/RequiresLogin.tsx | 9 +- src/pages/api/auth/[...nextauth].ts | 123 +++++++++--------- 3 files changed, 65 insertions(+), 69 deletions(-) diff --git a/src/authentication/api/index.ts b/src/authentication/api/index.ts index 8a0a6085..7f2dd7fc 100644 --- a/src/authentication/api/index.ts +++ b/src/authentication/api/index.ts @@ -19,7 +19,7 @@ export const logIn = async () => { */ export const getUser = async (): Promise => { const session = await getSession(); - return session?.user as unknown as IAuthUser; + return (session?.user as unknown) as IAuthUser; }; /** diff --git a/src/authentication/providers/RequiresLogin.tsx b/src/authentication/providers/RequiresLogin.tsx index 7463f019..f004ab64 100644 --- a/src/authentication/providers/RequiresLogin.tsx +++ b/src/authentication/providers/RequiresLogin.tsx @@ -1,12 +1,13 @@ import React from 'react'; import LoginPage from 'pages/login'; -import { useSelector } from 'react-redux'; -import { selectIsLoggedIn } from 'authentication/selectors/authentication'; +import { useSession } from 'next-auth/client'; +import Spinner from 'common/components/Spinner'; const RequiresLogin: React.FC = (props) => { - const isLoggedIn = useSelector(selectIsLoggedIn()); + const [session, loading] = useSession(); - if (!isLoggedIn) return ; + if (loading) return ; + if (!session) return ; return <>{props.children}; }; diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index f04bef38..68afca91 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -1,76 +1,71 @@ -import NextAuth from 'next-auth' +import NextAuth from 'next-auth'; import { NextApiRequest, NextApiResponse } from 'next'; -import { Session } from 'next-auth/client'; +import { Session } from 'next-auth'; import { Profile } from 'oidc-client'; interface Token { - name?: string, - email?: string, - picture?: string, // url to image - accessToken?: string, - iat: number, - exp: number, -} - -interface APISession extends Session { - user: Session["user"] & { - access_token?: string; - } + name?: string; + email?: string; + picture?: string; // url to image + accessToken?: string; + iat: number; + exp: number; } interface Account { - provider: string | null, - type: string | null, - id: number | null, - refreshToken: string | null, - accessToken: string | null, - accessTokenExpires: null + provider: string | null; + type: string | null; + id: number | null; + refreshToken: string | null; + accessToken: string | null; + accessTokenExpires: null; } - const options = { - callbacks: { - session: async (session: APISession, token: Token): Promise => { - if (token.accessToken) { - session.user.access_token = token.accessToken; - } - return Promise.resolve(session) - }, - jwt: async (token: Token, _: Token, account: Account) => { - if (account && account.accessToken) { - token.accessToken = account.accessToken; - } - return Promise.resolve(token) - } + callbacks: { + session: async (session: Session, token: Token) => { + if (token.accessToken) { + // NextAuth's types does not like adding a key. + // and the session-type used in options does not match the session type which is actually used in "next-auth/client"; + ((session as any).user as any).access_token = token.accessToken; + } + return Promise.resolve(session); }, - providers: [ - { - id: 'onlineweb4', - name: 'Onlineweb4', - type: 'oauth', - version: '2.0', - scope: 'openid profile email onlineweb4', - params: { - grant_type: 'authorization_code', - }, - accessTokenUrl: 'https://online.ntnu.no/openid/token', - requestTokenUrl: '"https://online.ntnu.no/openid/authorize', - authorizationUrl: 'https://online.ntnu.no/openid/authorize?response_type=code', - profileUrl: 'https://online.ntnu.no/openid/userinfo', - profile: (profile: Profile) => { - return { - ...profile, - id: profile.sub, - image: profile.picture, - email: profile.email, - } - }, - clientId: process.env.OW4_SSO_CLIENT_ID, - clientSecret: process.env.OW4_SSO_CLIENT_SECRET, - debug: true, - idToken: false, - } - ] -} + jwt: async (token: Token, _: Token, account: Account) => { + if (account && account.accessToken) { + token.accessToken = account.accessToken; + } + return Promise.resolve(token); + }, + }, + providers: [ + { + id: 'onlineweb4', + name: 'Onlineweb4', + type: 'oauth', + version: '2.0', + scope: 'openid profile email onlineweb4', + params: { + grant_type: 'authorization_code', + }, + accessTokenUrl: 'https://online.ntnu.no/openid/token', + requestTokenUrl: '"https://online.ntnu.no/openid/authorize', + authorizationUrl: 'https://online.ntnu.no/openid/authorize?response_type=code', + profileUrl: 'https://online.ntnu.no/openid/userinfo', + profile: (profile: Profile) => { + return { + ...profile, + id: profile.sub, + image: profile.picture, + email: profile.email, + }; + }, + clientId: process.env.OW4_SSO_CLIENT_ID, + clientSecret: process.env.OW4_SSO_CLIENT_SECRET, + debug: true, + idToken: false, + }, + ], +}; -export default (req: NextApiRequest, res: NextApiResponse) => NextAuth(req, res, options); \ No newline at end of file +export default (req: NextApiRequest, res: NextApiResponse) => NextAuth(req, res, options); From 21bf983d423b54c2a134360e3cbd29ca603073ee Mon Sep 17 00:00:00 2001 From: FluidSense <22146437+FluidSense@users.noreply.github.com> Date: Sun, 27 Sep 2020 22:42:50 +0200 Subject: [PATCH 08/13] Remove unused imports --- src/authentication/api/index.ts | 5 ----- src/core/index.tsx | 4 ---- src/core/redux/Store.tsx | 2 -- 3 files changed, 11 deletions(-) diff --git a/src/authentication/api/index.ts b/src/authentication/api/index.ts index 7f2dd7fc..524715f7 100644 --- a/src/authentication/api/index.ts +++ b/src/authentication/api/index.ts @@ -1,6 +1,3 @@ -import { __CLIENT__ } from 'common/constants/environment'; -import { UserManager } from 'oidc-client'; -import settings from './settings'; import { IAuthUser } from 'authentication/models/User'; import { signIn, signOut, getSession } from 'next-auth/client'; /** @@ -8,8 +5,6 @@ import { signIn, signOut, getSession } from 'next-auth/client'; * Redirects the user to the authentication page defined in settings. */ -export const USER_MANAGER = __CLIENT__ ? new UserManager(settings) : null; - export const logIn = async () => { return await signIn('onlineweb4'); }; diff --git a/src/core/index.tsx b/src/core/index.tsx index 87769696..8791bf0e 100644 --- a/src/core/index.tsx +++ b/src/core/index.tsx @@ -5,8 +5,6 @@ import Footer from './components/Footer/index'; import Header from './components/Header/index'; import './less/core.less'; import { ToastMessages } from './utils/toast/ToastMessages'; -import { AuthenticationProvider } from 'authentication/providers/AuthenticationProvider'; -import { SilentRenew } from 'authentication/components/SilentRenew'; const Core: FC = ({ children }) => ( <> @@ -17,8 +15,6 @@ const Core: FC = ({ children }) => (
{children}