From 4d44ab595f87c102c52efa26a733b057fff20f42 Mon Sep 17 00:00:00 2001 From: siddheshraze <81591724+siddheshraze@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:21:35 -0500 Subject: [PATCH] lift to updated versions completed. authv5 implemented --- .../app/api/auth/[[...nextauth]]/route.ts | 100 +--------- frontend/app/api/customsignin/route.ts | 39 ++++ frontend/auth.ts | 66 +++++++ frontend/components/client/loginfailure.tsx | 2 +- frontend/components/loginlogout.tsx | 18 +- frontend/components/sidebar.tsx | 4 +- frontend/middleware.ts | 36 ++-- frontend/next-env.d.ts | 1 + frontend/package-lock.json | 183 +++++++----------- frontend/package.json | 4 +- 10 files changed, 218 insertions(+), 235 deletions(-) create mode 100644 frontend/app/api/customsignin/route.ts create mode 100644 frontend/auth.ts diff --git a/frontend/app/api/auth/[[...nextauth]]/route.ts b/frontend/app/api/auth/[[...nextauth]]/route.ts index 516acf3f..0a98352f 100644 --- a/frontend/app/api/auth/[[...nextauth]]/route.ts +++ b/frontend/app/api/auth/[[...nextauth]]/route.ts @@ -1,99 +1,3 @@ -import NextAuth, { AzureADProfile } from 'next-auth'; -import AzureADProvider from 'next-auth/providers/azure-ad'; -import { UserAuthRoles } from '@/config/macros'; -import { SitesRDS, SitesResult } from '@/config/sqlrdsdefinitions/zones'; -import ConnectionManager from '@/config/connectionmanager'; -import MapperFactory from '@/config/datamapper'; +import { handlers } from '@/auth'; -const handler = NextAuth({ - secret: process.env.NEXTAUTH_SECRET!, - providers: [ - AzureADProvider({ - clientId: process.env.AZURE_AD_CLIENT_ID!, - clientSecret: process.env.AZURE_AD_CLIENT_SECRET!, - tenantId: process.env.AZURE_AD_TENANT_ID!, - authorization: { params: { scope: 'openid profile email user.Read' } } - }) - ], - session: { - strategy: 'jwt', - maxAge: 24 * 60 * 60 // 24 hours (you can adjust this value as needed) - }, - callbacks: { - async signIn({ user, profile, email: signInEmail }) { - const azureProfile = profile as AzureADProfile; - const userEmail = user.email || signInEmail || azureProfile.preferred_username; - if (typeof userEmail !== 'string') { - console.error('User email is not a string:', userEmail); - return false; // Email is not a valid string, abort sign-in - } - if (userEmail) { - const connectionManager = ConnectionManager.getInstance(); - let emailVerified, userStatus, userID; - try { - const query = `SELECT UserID, UserStatus FROM catalog.users WHERE Email = '${userEmail}' LIMIT 1`; - const results = await connectionManager.executeQuery(query); - - // emailVerified is true if there is at least one result - emailVerified = results.length > 0; - if (!emailVerified) { - console.error('User email not found.'); - return false; - } - userStatus = results[0].UserStatus; - userID = results[0].UserID; - } catch (e: any) { - console.error('Error fetching user status:', e); - throw new Error('Failed to fetch user status.'); - } - user.userStatus = userStatus as UserAuthRoles; - user.email = userEmail; - const allSites = MapperFactory.getMapper('sites').mapData(await connectionManager.executeQuery(`SELECT * FROM catalog.sites`)); - const allowedSites = MapperFactory.getMapper('sites').mapData( - await connectionManager.executeQuery( - `SELECT s.* FROM catalog.sites AS s JOIN catalog.usersiterelations AS usr ON s.SiteID = usr.SiteID WHERE usr.UserID = ?`, - [userID] - ) - ); - if (!allowedSites || !allSites) { - console.error('User does not have any allowed sites.'); - return false; - } - - user.sites = allowedSites; - user.allsites = allSites; - } - return true; - }, - - async jwt({ token, user }) { - // If this is the first time the JWT is issued, persist custom properties - if (user) { - token.userStatus = user.userStatus; - token.sites = user.sites; - token.allsites = user.allsites; - } - return token; - }, - - async session({ session, token }) { - if (typeof token.userStatus === 'string') { - session.user.userStatus = token.userStatus as UserAuthRoles; - } else { - session.user.userStatus = 'field crew' as UserAuthRoles; // default no admin permissions - } - if (token && token.allsites && Array.isArray(token.allsites)) { - session.user.allsites = token.allsites as SitesRDS[]; - } - if (token && token.sites && Array.isArray(token.sites)) { - session.user.sites = token.sites as SitesRDS[]; - } - return session; - } - }, - pages: { - error: '/loginfailed' - } -}); - -export { handler as GET, handler as POST }; +export const { GET, POST } = handlers; diff --git a/frontend/app/api/customsignin/route.ts b/frontend/app/api/customsignin/route.ts new file mode 100644 index 00000000..dbe2c695 --- /dev/null +++ b/frontend/app/api/customsignin/route.ts @@ -0,0 +1,39 @@ +import { NextResponse } from 'next/server'; +import ConnectionManager from '@/config/connectionmanager'; +import MapperFactory from '@/config/datamapper'; +import { SitesRDS, SitesResult } from '@/config/sqlrdsdefinitions/zones'; + +export async function POST(req: Request) { + const { email } = await req.json(); + const connectionManager = ConnectionManager.getInstance(); + + try { + const query = `SELECT UserID, UserStatus FROM catalog.users WHERE Email = ? LIMIT 1`; + const results = await connectionManager.executeQuery(query, [email]); + + if (results.length === 0) { + return NextResponse.json({ error: 'User not found' }, { status: 401 }); + } + + const userID = results[0].UserID; + const userStatus = results[0].UserStatus; + + const allSites = MapperFactory.getMapper('sites').mapData(await connectionManager.executeQuery(`SELECT * FROM catalog.sites`)); + + const allowedSites = MapperFactory.getMapper('sites').mapData( + await connectionManager.executeQuery( + `SELECT s.* FROM catalog.sites AS s JOIN catalog.usersiterelations AS usr ON s.SiteID = usr.SiteID WHERE usr.UserID = ?`, + [userID] + ) + ); + + return NextResponse.json({ + userStatus, + allSites, + allowedSites + }); + } catch (error) { + console.error('Database error:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} diff --git a/frontend/auth.ts b/frontend/auth.ts new file mode 100644 index 00000000..43fd42bf --- /dev/null +++ b/frontend/auth.ts @@ -0,0 +1,66 @@ +// auth.ts +import NextAuth from 'next-auth'; +import { UserAuthRoles } from '@/config/macros'; +import { SitesRDS } from '@/config/sqlrdsdefinitions/zones'; +import MicrosoftEntraID from '@auth/core/providers/microsoft-entra-id'; + +export const { auth, handlers, signIn, signOut } = NextAuth({ + secret: process.env.AUTH_SECRET!, + providers: [ + MicrosoftEntraID({ + clientId: process.env.AZURE_AD_CLIENT_ID, + clientSecret: process.env.AZURE_AD_CLIENT_SECRET, + issuer: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}/v2.0` + }) + ], + session: { + strategy: 'jwt', + maxAge: 24 * 60 * 60 // 24 hours + }, + callbacks: { + async signIn({ user, profile, email: signInEmail }) { + console.log('url: ', process.env.AUTH_URL); + const userEmail = user.email || signInEmail || profile?.preferred_username; + if (!userEmail) { + return false; // No email, reject sign-in + } + try { + const response = await fetch(`${process.env.NEXTAUTH_URL ?? 'http://localhost:3000'}/api/customsignin`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: userEmail }) + }); + + if (!response.ok) { + return false; + } + + const data = await response.json(); + user.userStatus = data.userStatus; + user.sites = data.allowedSites; + user.allsites = data.allSites; + } catch (error) { + console.error('Error fetching user data:', error); + return false; + } + + return true; + }, + + async jwt({ token, user }) { + if (user) { + token.userStatus = user.userStatus; + token.sites = user.sites; + token.allsites = user.allsites; + } + return token; + }, + + async session({ session, token }) { + session.user.userStatus = token.userStatus as UserAuthRoles; + session.user.sites = token.sites as SitesRDS[]; + session.user.allsites = token.allsites as SitesRDS[]; + return session; + } + } +}); diff --git a/frontend/components/client/loginfailure.tsx b/frontend/components/client/loginfailure.tsx index a209ec60..83dbfe3f 100644 --- a/frontend/components/client/loginfailure.tsx +++ b/frontend/components/client/loginfailure.tsx @@ -13,7 +13,7 @@ const LoginFailed = () => { const handleTryAgain = () => { sessionStorage.clear(); localStorage.clear(); - signOut({ callbackUrl: '/login' }).catch(console.error); + signOut({ redirectTo: '/login' }).catch(console.error); }; return ( diff --git a/frontend/components/loginlogout.tsx b/frontend/components/loginlogout.tsx index 7379696d..6e81fe4e 100644 --- a/frontend/components/loginlogout.tsx +++ b/frontend/components/loginlogout.tsx @@ -1,4 +1,5 @@ -import { signIn, signOut, useSession } from 'next-auth/react'; +// loginlogout.tsx +'use client'; import React from 'react'; import Avatar from '@mui/joy/Avatar'; import Box from '@mui/joy/Box'; @@ -8,14 +9,15 @@ import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded'; import LoginRoundedIcon from '@mui/icons-material/LoginRounded'; import CircularProgress from '@mui/joy/CircularProgress'; import { Skeleton } from '@mui/joy'; +import { signIn, signOut, useSession } from 'next-auth/react'; export const LoginLogout = () => { const { data: session, status } = useSession(); const handleRetryLogin = () => { - signIn('azure-ad', { callbackUrl: '/dashboard' }, { prompt: 'login' }).catch((error: any) => { + signIn('microsoft-entra-id', { redirectTo: '/dashboard' }).catch((error: any) => { console.error('Login error:', error); - signOut({ callbackUrl: `/loginfailed?reason=${error.message}` }) + signOut({ redirectTo: `/loginfailed?reason=${error.message}` }) .then(() => localStorage.clear()) .then(() => sessionStorage.clear()); }); @@ -31,7 +33,13 @@ export const LoginLogout = () => { Login to access your information - + handleRetryLogin()} + aria-label={'Login' + ' button'} + > @@ -57,7 +65,7 @@ export const LoginLogout = () => { {session?.user?.email ? session?.user?.email : ''} - void signOut({ callbackUrl: '/login' })} aria-label={'Logout button'}> + void signOut({ redirectTo: '/login' })} aria-label={'Logout button'}> {status == 'loading' ? : } diff --git a/frontend/components/sidebar.tsx b/frontend/components/sidebar.tsx index c63bed31..6ea25451 100644 --- a/frontend/components/sidebar.tsx +++ b/frontend/components/sidebar.tsx @@ -950,8 +950,8 @@ export default function Sidebar(props: SidebarProps) { { plotSelectionRequired: plot === undefined, censusSelectionRequired: census === undefined, - pathname, - isParentDataIncomplete + pathname: pathname ?? '', + isParentDataIncomplete: isParentDataIncomplete }, item, toggle, diff --git a/frontend/middleware.ts b/frontend/middleware.ts index 6af284ff..43798dec 100644 --- a/frontend/middleware.ts +++ b/frontend/middleware.ts @@ -6,35 +6,35 @@ * Allows the request to continue if no redirect conditions are met. */ -import { getToken } from 'next-auth/jwt'; -import { NextRequest, NextResponse } from 'next/server'; +import { NextResponse, NextRequest } from 'next/server'; +import { auth } from '@/auth'; -export async function middleware(request: NextRequest) { - const session = await getToken({ - req: request, - secret: process.env.NEXTAUTH_SECRET - }); +export default auth(async function middleware(request: NextRequest) { + const session = await auth(); // Fetch session once const url = request.nextUrl.clone(); - if (url.pathname.startsWith('/dashboard') || url.pathname.startsWith('/measurementshub') || url.pathname.startsWith('/fixeddatainput')) { - if (!session) { - // If user is not authenticated and tries to access protected routes, redirect to login + + const isAuthenticated = !!session; + const isProtectedRoute = ['/dashboard', '/measurementshub', '/fixeddatainput'].some(route => url.pathname.startsWith(route)); + + if (isProtectedRoute && !isAuthenticated) { + // Redirect unauthenticated users trying to access protected routes + if (url.pathname !== '/login') { url.pathname = '/login'; return NextResponse.redirect(url); } } else if (url.pathname === '/') { - // Redirect from home to dashboard if authenticated, or login if not - if (!session) { - url.pathname = '/login'; - } else { + // Redirect from home to dashboard if authenticated, otherwise to login + if (isAuthenticated) { url.pathname = '/dashboard'; + } else { + url.pathname = '/login'; } return NextResponse.redirect(url); } - // Allow request to continue if no conditions are met - return NextResponse.next(); -} + return NextResponse.next(); // Allow request to continue if no conditions are met +}); export const config = { - matcher: ['/', '/dashboard', '/measurementshub/:path*', '/fixeddatainput/:path*'] + matcher: ['/', '/dashboard/:path*', '/measurementshub/:path*', '/fixeddatainput/:path*'] }; diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 1b3be084..3cd7048e 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7eb0013d..0f95e26b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -32,7 +32,6 @@ "@testing-library/react": "^16.2.0", "@types/codemirror": "^5.60.15", "@types/fast-levenshtein": "^0.0.4", - "@types/node": "^22.13.0", "@types/p-queue": "^3.2.1", "@types/papaparse": "^5.3.15", "@types/react": "^19.0.8", @@ -54,7 +53,7 @@ "moment-duration-format": "^2.3.2", "mysql2": "^3.12.0", "next": "^15.1.6", - "next-auth": "^4.24.11", + "next-auth": "^5.0.0-beta.25", "next-router-mock": "^0.9.13", "node": "^23.7.0", "npm-force-resolutions": "^0.0.10", @@ -84,6 +83,7 @@ "@testing-library/cypress": "^10.0.3", "@types/cypress": "^0.1.6", "@types/jest": "^29.5.14", + "@types/node": "^22.13.1", "cypress": "^14.0.1", "eslint": "^9.19.0", "eslint-config-next": "^15.1.6", @@ -150,6 +150,37 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/@auth/core": { + "version": "0.37.2", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.37.2.tgz", + "integrity": "sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "@types/cookie": "0.6.0", + "cookie": "0.7.1", + "jose": "^5.9.3", + "oauth4webapi": "^3.0.0", + "preact": "10.11.3", + "preact-render-to-string": "5.2.3" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/@axe-core/react": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@axe-core/react/-/react-4.10.1.tgz", @@ -4510,6 +4541,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/cypress": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@types/cypress/-/cypress-0.1.6.tgz", @@ -4753,9 +4790,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", - "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -7315,9 +7352,9 @@ "peer": true }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -9380,16 +9417,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -12213,9 +12240,9 @@ } }, "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", + "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -14867,30 +14894,25 @@ } }, "node_modules/next-auth": { - "version": "4.24.11", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", - "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "version": "5.0.0-beta.25", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.25.tgz", + "integrity": "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==", "license": "ISC", "dependencies": { - "@babel/runtime": "^7.20.13", - "@panva/hkdf": "^1.0.2", - "cookie": "^0.7.0", - "jose": "^4.15.5", - "oauth": "^0.9.15", - "openid-client": "^5.4.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" + "@auth/core": "0.37.2" }, "peerDependencies": { - "@auth/core": "0.34.2", - "next": "^12.2.5 || ^13 || ^14 || ^15", + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0-0", "nodemailer": "^6.6.5", - "react": "^17.0.2 || ^18 || ^19", - "react-dom": "^17.0.2 || ^18 || ^19" + "react": "^18.2.0 || ^19.0.0-0" }, "peerDependenciesMeta": { - "@auth/core": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { "optional": true }, "nodemailer": { @@ -14898,15 +14920,6 @@ } } }, - "node_modules/next-auth/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/next-router-mock": { "version": "0.9.13", "resolved": "https://registry.npmjs.org/next-router-mock/-/next-router-mock-0.9.13.tgz", @@ -15050,11 +15063,14 @@ "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", "license": "MIT" }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==", - "license": "MIT" + "node_modules/oauth4webapi": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.1.4.tgz", + "integrity": "sha512-eVfN3nZNbok2s/ROifO0UAc5G8nRoLSbrcKJ09OqmucgnhXEfdIQOR4gq1eJH1rN3gV7rNw62bDEgftsgFtBEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } }, "node_modules/object-assign": { "version": "4.1.1", @@ -15065,15 +15081,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -15236,15 +15243,6 @@ "node": ">= 18" } }, - "node_modules/oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -15317,39 +15315,6 @@ "opener": "bin/opener-bin.js" } }, - "node_modules/openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", - "license": "MIT", - "dependencies": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/openid-client/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/openid-client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -15984,9 +15949,9 @@ "license": "MIT" }, "node_modules/preact": { - "version": "10.25.4", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz", - "integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==", + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", "license": "MIT", "funding": { "type": "opencollective", @@ -15994,9 +15959,9 @@ } }, "node_modules/preact-render-to-string": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", - "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", + "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", "license": "MIT", "dependencies": { "pretty-format": "^3.8.0" diff --git a/frontend/package.json b/frontend/package.json index 69c3645a..b486fd54 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -50,7 +50,6 @@ "@testing-library/react": "^16.2.0", "@types/codemirror": "^5.60.15", "@types/fast-levenshtein": "^0.0.4", - "@types/node": "^22.13.0", "@types/p-queue": "^3.2.1", "@types/papaparse": "^5.3.15", "@types/react": "^19.0.8", @@ -72,7 +71,7 @@ "moment-duration-format": "^2.3.2", "mysql2": "^3.12.0", "next": "^15.1.6", - "next-auth": "^4.24.11", + "next-auth": "^5.0.0-beta.25", "next-router-mock": "^0.9.13", "node": "^23.7.0", "npm-force-resolutions": "^0.0.10", @@ -106,6 +105,7 @@ "@testing-library/cypress": "^10.0.3", "@types/cypress": "^0.1.6", "@types/jest": "^29.5.14", + "@types/node": "^22.13.1", "cypress": "^14.0.1", "eslint": "^9.19.0", "eslint-config-next": "^15.1.6",