From 5274ad230324b61699c4bc89188a3f712d5d8179 Mon Sep 17 00:00:00 2001 From: Maxime Roucher Date: Thu, 6 Jun 2024 17:45:26 +0200 Subject: [PATCH 1/5] wip: required node js --- package.json | 1 + src/app/login/page.tsx | 43 +++++++++++++++++++-- src/components/login/MyECLButton.tsx | 42 +++++++++------------ yarn.lock | 56 ++++++++++++++++++++++++++-- 4 files changed, 112 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 1bcd9328..c9407873 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "next": "14.2.3", "next-plausible": "^3.12.0", "next-themes": "^0.3.0", + "openid-client": "^5.6.5", "react": "^18", "react-circular-progressbar": "^2.1.0", "react-currency-input-field": "^3.8.0", diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index c239306b..65dac9e1 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -13,21 +13,58 @@ import { } from "@/src/components/ui/card"; import MyECLButton from "../../components/login/MyECLButton"; import Link from "next/link"; +import { Issuer } from "openid-client"; +import { generators } from "openid-client"; +import { useRouter } from "next/navigation"; +import { useCodeVerifierStore } from "@/src/stores/codeVerifier"; +import { LoadingButton } from "@/src/components/custom/LoadingButton"; -const Login = () => { +function Login() { + const router = useRouter(); + const { setCodeVerifier } = useCodeVerifierStore(); + + async function openSSO() { + console.log("dscover", process.env.NEXT_PUBLIC_BACKEND_URL ?? ""); + const hyperionIssuer = await Issuer.discover( + "https://hyperion-3.dev.eclair.ec-lyon.fr", + ); + console.log('ezgrshgj') + + const client = new hyperionIssuer.Client({ + client_id: process.env.NEXT_PUBLIC_CLIENT_ID ?? "", + redirect_uris: [process.env.NEXT_PUBLIC_FRONTEND_URL ?? ""], + response_types: ["code"], + }); + + const codeVerifier = generators.codeVerifier(); + setCodeVerifier(codeVerifier); + const codeChallenge = generators.codeChallenge(codeVerifier); + + const url = client.authorizationUrl({ + scope: "API", + code_challenge: codeChallenge, + code_challenge_method: "SHA256", + }); + router.push(url); + } return (
Se connecter - Si vous possédez déjà un compte MyECL, vous pouvez vous connecter avec. + Si vous possédez déjà un compte MyECL, vous pouvez vous connecter + avec.
- +
diff --git a/src/components/login/MyECLButton.tsx b/src/components/login/MyECLButton.tsx index 1a90c38f..cd8ffd0c 100644 --- a/src/components/login/MyECLButton.tsx +++ b/src/components/login/MyECLButton.tsx @@ -4,31 +4,28 @@ import { useAuth } from "../../hooks/useAuth"; import { useRouter, useSearchParams } from "next/navigation"; import { useCodeVerifierStore } from "@/src/stores/codeVerifier"; import { useEffect, useState } from "react"; -import { LoadingButton } from "../custom/LoadingButton"; -const Login = () => { +async function Login() { const { token, isTokenExpired, login, isLoading, getTokenFromRequest } = useAuth(); - const router = useRouter(); const searchParams = useSearchParams(); const code = searchParams.get("code"); - const { codeVerifier } = useCodeVerifierStore(); const [isLoggingIn, setIsLoggingIn] = useState(false); - useEffect(() => { - if ( - code && - typeof window !== "undefined" && - !isLoading && - codeVerifier !== undefined && - !isLoggingIn - ) { - setIsLoggingIn(true); - login(code, () => { - router.replace("/"); - }); - } - }, [code, isLoading, codeVerifier, login, router, isLoggingIn]); + // useEffect(() => { + // if ( + // code && + // typeof window !== "undefined" && + // !isLoading && + // codeVerifier !== undefined && + // !isLoggingIn + // ) { + // setIsLoggingIn(true); + // login(code, () => { + // router.replace("/"); + // }); + // } + // }, [code, isLoading, codeVerifier, login, router, isLoggingIn]); if (token !== null && !isTokenExpired()) { router.replace("/"); @@ -39,13 +36,10 @@ const Login = () => { getTokenFromRequest(); } + + return ( - ); -}; +} export default Login; diff --git a/yarn.lock b/yarn.lock index 484029fa..1eef2084 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3310,6 +3310,11 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== +jose@^4.15.5: + version "4.15.5" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.5.tgz#6475d0f467ecd3c630a1b5dadd2735a7288df706" + integrity sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg== + "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" @@ -3763,6 +3768,11 @@ object-assign@^4.0.1, object-assign@^4.1.1: resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +object-hash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + object-hash@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" @@ -3834,6 +3844,11 @@ object.values@^1.1.6, object.values@^1.1.7: define-properties "^1.2.0" es-abstract "^1.22.1" +oidc-token-hash@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz#9a229f0a1ce9d4fc89bcaee5478c97a889e7b7b6" + integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw== + once@^1.3.0, once@^1.3.1: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" @@ -3855,6 +3870,16 @@ openapi3-ts@^2.0.1: dependencies: yaml "^1.10.2" +openid-client@^5.6.5: + version "5.6.5" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.6.5.tgz#c149ad07b9c399476dc347097e297bbe288b8b00" + integrity sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w== + dependencies: + jose "^4.15.5" + lru-cache "^6.0.0" + object-hash "^2.2.0" + oidc-token-hash "^5.0.3" + optimism@^0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz" @@ -4647,7 +4672,16 @@ streamsearch@^1.1.0: resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4714,7 +4748,14 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5181,7 +5222,7 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -5199,6 +5240,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" From 1135d3a450febca25e50307683f91f8b3824ae2b Mon Sep 17 00:00:00 2001 From: Maxime Roucher Date: Thu, 6 Jun 2024 21:42:03 +0200 Subject: [PATCH 2/5] feat: migrate to node openid client --- next.config.mjs | 2 +- package.json | 2 +- src/api/hyperionContext.ts | 9 +- src/api/hyperionFetcher.ts | 8 + src/app/login/page.tsx | 38 +-- src/app/page.tsx | 8 +- src/app/recover/page.tsx | 4 +- src/components/custom/PdfViewer.tsx | 32 +-- .../home/RegisteringCompleteDialog.tsx | 2 +- .../home/userSheet/logoutButton.tsx | 3 +- src/components/login/MyECLButton.tsx | 128 +++++++--- .../register/CreateAccountFormField.tsx | 10 +- src/components/ui/badge.tsx | 14 +- src/components/ui/breadcrumb.tsx | 48 ++-- src/components/ui/input-otp.tsx | 38 +-- src/components/ui/menubar.tsx | 98 ++++---- src/components/ui/pagination.tsx | 44 ++-- src/components/ui/scroll-area.tsx | 20 +- src/components/ui/sheet.tsx | 3 +- src/components/ui/switch.tsx | 18 +- src/components/ui/table.tsx | 44 ++-- src/components/ui/tabs.tsx | 30 +-- src/components/ui/tooltip.tsx | 22 +- src/hooks/useAdminTeam.ts | 16 +- src/hooks/useAuth.ts | 229 ------------------ src/hooks/useCreateAccount.ts | 10 - src/hooks/useDocument.ts | 16 +- src/hooks/useDriveFolder.ts | 16 +- src/hooks/useInformation.ts | 16 +- src/hooks/useInviteToken.ts | 9 - src/hooks/useMergeTeams.ts | 5 - src/hooks/useParticipant.ts | 24 +- src/hooks/usePayment.ts | 9 - src/hooks/usePaymentUrl.ts | 9 +- src/hooks/usePrice.ts | 15 +- src/hooks/useProfilePicture.ts | 10 +- src/hooks/useRecoverPassword.ts | 9 - src/hooks/useSecurityFile.ts | 11 +- src/hooks/useTeam.ts | 20 +- src/hooks/useTeams.ts | 10 +- src/hooks/useToken.ts | 79 ++++++ src/hooks/useUser.ts | 12 +- src/infra/teamUtils.ts | 2 +- src/stores/codeVerifier.ts | 2 +- yarn.lock | 30 +-- 45 files changed, 457 insertions(+), 727 deletions(-) delete mode 100644 src/hooks/useAuth.ts create mode 100644 src/hooks/useToken.ts diff --git a/next.config.mjs b/next.config.mjs index 90620340..3026cde5 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,7 +3,7 @@ import { withPlausibleProxy } from "next-plausible"; const nextConfig = { - output: 'export', + output: "export", webpack: (config, { isServer }) => { config.resolve.alias.canvas = false; return config; diff --git a/package.json b/package.json index c9407873..2bb30118 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "next": "14.2.3", "next-plausible": "^3.12.0", "next-themes": "^0.3.0", - "openid-client": "^5.6.5", + "oauth4webapi": "^2.10.4", "react": "^18", "react-circular-progressbar": "^2.1.0", "react-currency-input-field": "^3.8.0", diff --git a/src/api/hyperionContext.ts b/src/api/hyperionContext.ts index 9def6af7..4c40775c 100644 --- a/src/api/hyperionContext.ts +++ b/src/api/hyperionContext.ts @@ -1,5 +1,6 @@ import type { QueryKey, UseQueryOptions } from "@tanstack/react-query"; import { QueryOperation } from "./hyperionComponents"; +import { useToken } from "../hooks/useToken"; export type HyperionContext = { fetcherOptions: { @@ -11,6 +12,7 @@ export type HyperionContext = { * Query params to inject in the fetcher */ queryParams?: {}; + getToken?: () => Promise; }; queryOptions: { /** @@ -41,8 +43,13 @@ export function useHyperionContext< "queryKey" | "queryFn" >, ): HyperionContext { + + const { getToken } = useToken(); + return { - fetcherOptions: {}, + fetcherOptions: { + getToken + }, queryOptions: {}, queryKeyFn, }; diff --git a/src/api/hyperionFetcher.ts b/src/api/hyperionFetcher.ts index 9c1a70e3..84a1214a 100644 --- a/src/api/hyperionFetcher.ts +++ b/src/api/hyperionFetcher.ts @@ -33,6 +33,7 @@ export async function hyperionFetch< pathParams, queryParams, signal, + getToken, }: HyperionFetcherOptions< TBody, THeaders, @@ -59,6 +60,13 @@ export async function hyperionFetch< delete requestHeaders["Content-Type"]; } + if (getToken) { + const token = await getToken(); + if (token) { + requestHeaders["Authorization"]= `Bearer ${token}`; + } + } + const response = await window.fetch( `${baseUrl}${resolveUrl(url, queryParams, pathParams)}`, { diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 65dac9e1..6766cd44 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -13,40 +13,12 @@ import { } from "@/src/components/ui/card"; import MyECLButton from "../../components/login/MyECLButton"; import Link from "next/link"; -import { Issuer } from "openid-client"; -import { generators } from "openid-client"; +import * as auth from "oauth4webapi"; import { useRouter } from "next/navigation"; import { useCodeVerifierStore } from "@/src/stores/codeVerifier"; import { LoadingButton } from "@/src/components/custom/LoadingButton"; function Login() { - const router = useRouter(); - const { setCodeVerifier } = useCodeVerifierStore(); - - async function openSSO() { - console.log("dscover", process.env.NEXT_PUBLIC_BACKEND_URL ?? ""); - const hyperionIssuer = await Issuer.discover( - "https://hyperion-3.dev.eclair.ec-lyon.fr", - ); - console.log('ezgrshgj') - - const client = new hyperionIssuer.Client({ - client_id: process.env.NEXT_PUBLIC_CLIENT_ID ?? "", - redirect_uris: [process.env.NEXT_PUBLIC_FRONTEND_URL ?? ""], - response_types: ["code"], - }); - - const codeVerifier = generators.codeVerifier(); - setCodeVerifier(codeVerifier); - const codeChallenge = generators.codeChallenge(codeVerifier); - - const url = client.authorizationUrl({ - scope: "API", - code_challenge: codeChallenge, - code_challenge_method: "SHA256", - }); - router.push(url); - } return (
@@ -60,11 +32,7 @@ function Login() {
- +
@@ -79,6 +47,6 @@ function Login() {
); -}; +} export default Login; diff --git a/src/app/page.tsx b/src/app/page.tsx index f3c39263..fab9e6de 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -4,7 +4,6 @@ import { EmptyParticipantCard } from "../components/home/participantView/EmptyPa import { ParticipantCard } from "../components/home/participantView/ParicipantCard"; import { TeamCard } from "../components/home/teamCard/TeamCard"; import { TopBar } from "../components/home/TopBar"; -import { useAuth } from "../hooks/useAuth"; import { useRouter, useSearchParams } from "next/navigation"; import { useTeam } from "../hooks/useTeam"; import { CreateParticipant } from "../components/home/CreateParticipant"; @@ -22,7 +21,6 @@ import { Button } from "../components/ui/button"; import { RegisteringCompleteDialog } from "../components/home/RegisteringCompleteDialog"; const Home = () => { - const { isTokenQueried, token } = useAuth(); const { me, isFetched, refetch } = useParticipant(); const { me: user, isAdmin } = useUser(); const { team, createTeam, refetchTeam, isLoading: isTeamLoading } = useTeam(); @@ -47,9 +45,9 @@ const Home = () => { router.replace("/"); } - if (isTokenQueried && token === null) { - router.replace("/login"); - } + // if (token === null) { + // router.replace("/login"); + // } if (isAdmin() && typeof window !== "undefined") { const redirection = searchParams.get("redirect"); diff --git a/src/app/recover/page.tsx b/src/app/recover/page.tsx index 8fca9aa1..86aeea9a 100644 --- a/src/app/recover/page.tsx +++ b/src/app/recover/page.tsx @@ -9,9 +9,7 @@ const RecoverPage = () => { return showRecover ? ( setShowRecover(false)} /> ) : ( - setShowRecover(true) - }/> + setShowRecover(true)} /> ); }; diff --git a/src/components/custom/PdfViewer.tsx b/src/components/custom/PdfViewer.tsx index e495c609..dd73bfe8 100644 --- a/src/components/custom/PdfViewer.tsx +++ b/src/components/custom/PdfViewer.tsx @@ -30,21 +30,21 @@ export const PdfViewer = ({ file, width }: PdfViewerProps) => { // get the width of the parent element const maxWidth = self?.innerWidth ?? width ?? 550; return ( - } - > - {Array.from(new Array(numPages), (el, index) => ( - - ))} - + } + > + {Array.from(new Array(numPages), (el, index) => ( + + ))} + ); }; diff --git a/src/components/home/RegisteringCompleteDialog.tsx b/src/components/home/RegisteringCompleteDialog.tsx index ed98d050..de70821d 100644 --- a/src/components/home/RegisteringCompleteDialog.tsx +++ b/src/components/home/RegisteringCompleteDialog.tsx @@ -29,7 +29,7 @@ export const RegisteringCompleteDialog = ({ toast({ title: "Erreur", description: "Impossible de télécharger le fichier", - variant: "destructive" + variant: "destructive", }); setIsFileLoading(false); return; diff --git a/src/components/home/userSheet/logoutButton.tsx b/src/components/home/userSheet/logoutButton.tsx index d3ada9f2..14150486 100644 --- a/src/components/home/userSheet/logoutButton.tsx +++ b/src/components/home/userSheet/logoutButton.tsx @@ -1,9 +1,8 @@ -import { useAuth } from "@/src/hooks/useAuth"; import { HiLogout } from "react-icons/hi"; import { Button } from "../../ui/button"; export const LogoutButton = () => { - const { logout } = useAuth(); + function logout() {} return (