diff --git a/src/front/Api/LeCoffreApi/Id360/BaseId360.ts b/src/front/Api/Auth/Id360/BaseId360.ts similarity index 100% rename from src/front/Api/LeCoffreApi/Id360/BaseId360.ts rename to src/front/Api/Auth/Id360/BaseId360.ts diff --git a/src/front/Api/LeCoffreApi/Id360/Customers/Customers.ts b/src/front/Api/Auth/Id360/Customers/Customers.ts similarity index 85% rename from src/front/Api/LeCoffreApi/Id360/Customers/Customers.ts rename to src/front/Api/Auth/Id360/Customers/Customers.ts index edf23384..a97c6ca0 100644 --- a/src/front/Api/LeCoffreApi/Id360/Customers/Customers.ts +++ b/src/front/Api/Auth/Id360/Customers/Customers.ts @@ -7,6 +7,11 @@ export interface IConnectionUrlResponse { } } +export interface ICustomerTokens { + accessToken: string; + refreshToken: string; +} + export default class Customers extends BaseId360 { private static instance: Customers; private readonly baseURl = this.namespaceUrl.concat("/customers"); @@ -33,10 +38,10 @@ export default class Customers extends BaseId360 { } } - public async loginCallback(callbackToken: string | string[]): Promise { + public async loginCallback(callbackToken: string | string[]): Promise { const url = new URL(this.baseURl.concat(`/login-callback/${callbackToken}`)); try { - return await this.postRequest(url); + return await this.postRequest(url); } catch (err) { this.onError(err); return Promise.reject(err); diff --git a/src/front/Api/Auth/IdNot/User.ts b/src/front/Api/Auth/IdNot/User.ts index 1854979a..706ef3a3 100644 --- a/src/front/Api/Auth/IdNot/User.ts +++ b/src/front/Api/Auth/IdNot/User.ts @@ -35,14 +35,4 @@ export default class User extends BaseApiService { return Promise.reject(err); } } - - public async refreshToken(refreshToken: string): Promise<{ accessToken: string }> { - const url = new URL(`${this.baseURl}/refresh-token`); - try { - return await this.postRequest(url, {}, refreshToken); - } catch (err) { - this.onError(err); - return Promise.reject(err); - } - } } diff --git a/src/front/Api/Auth/franceConnect/Customer.ts b/src/front/Api/Auth/franceConnect/Customer.ts deleted file mode 100644 index b7c60ba5..00000000 --- a/src/front/Api/Auth/franceConnect/Customer.ts +++ /dev/null @@ -1,38 +0,0 @@ -import BaseApiService from "@Front/Api/BaseApiService"; - -export default class Customer extends BaseApiService { - private static instance: Customer; - private readonly baseURl = this.getBaseUrl().concat("/france-connect/customer"); - - private constructor() { - super(); - } - - public static getInstance() { - if (!this.instance) { - return new Customer(); - } else { - return this.instance; - } - } - - public async login(email: string) { - const url = new URL(this.baseURl.concat("/login/").concat(email)); - try { - return await this.postRequest(url); - } catch (err) { - this.onError(err); - return Promise.reject(err); - } - } - - public async refreshToken(refreshToken: string): Promise<{ accessToken: string }> { - const url = new URL(this.baseURl.concat("/refresh-token")); - try { - return await this.postRequest(url, {}, refreshToken); - } catch (err) { - this.onError(err); - return Promise.reject(err); - } - } -} diff --git a/src/front/Components/Layouts/ClientDashboard/index.tsx b/src/front/Components/Layouts/ClientDashboard/index.tsx index 1f7bce1e..6c048e86 100644 --- a/src/front/Components/Layouts/ClientDashboard/index.tsx +++ b/src/front/Components/Layouts/ClientDashboard/index.tsx @@ -31,7 +31,6 @@ export default function ClientDashboard(props: IProps) { } const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } }); - console.log(folder); const actualCustomer = folder?.customers?.find((customer) => customer.uid === jwt?.customerId); if (!actualCustomer) throw new Error("Customer not found"); diff --git a/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx b/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx index ddf1418d..656b7436 100644 --- a/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx +++ b/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx @@ -259,7 +259,6 @@ class AddClientToFolderClass extends BasePage { return Customer.hydrate(customer); }), }); - console.log(body); await Folders.getInstance().put(this.props.selectedFolderUid, body); this.props.router.push(`/folders/${this.props.selectedFolderUid}`); } diff --git a/src/front/Components/Layouts/LoginCallback/index.tsx b/src/front/Components/Layouts/LoginCallback/index.tsx index 3ca30a34..5079e38b 100644 --- a/src/front/Components/Layouts/LoginCallback/index.tsx +++ b/src/front/Components/Layouts/LoginCallback/index.tsx @@ -12,6 +12,7 @@ import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Loader from "@Front/Components/DesignSystem/Loader"; import UserStore from "@Front/Stores/UserStore"; import Link from "next/link"; +import JwtService from "@Front/Services/JwtService/JwtService"; export default function LoginCallBack() { const router = useRouter(); @@ -19,16 +20,22 @@ export default function LoginCallBack() { useEffect(() => { async function getUser() { const code = router.query["code"]; - if (!code) return; - try { - const token = await Auth.getInstance().getIdnotJwt(code as string); - if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path); - await UserStore.instance.connect(token.accessToken, token.refreshToken); + const refreshedTokens = await JwtService.getInstance().refreshToken(); + if (refreshedTokens) { return router.push(Module.getInstance().get().modules.pages.Folder.props.path); - } catch (e) { - router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1"); - return; } + if (code) { + try { + const token = await Auth.getInstance().getIdnotJwt(code as string); + if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path); + await UserStore.instance.connect(token.accessToken, token.refreshToken); + return router.push(Module.getInstance().get().modules.pages.Folder.props.path); + } catch (e) { + router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1"); + return; + } + } + return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1"); } getUser(); }), diff --git a/src/front/Components/Layouts/LoginCallbackCustomer/index.tsx b/src/front/Components/Layouts/LoginCallbackCustomer/index.tsx index 7ec5bf15..214bfca6 100644 --- a/src/front/Components/Layouts/LoginCallbackCustomer/index.tsx +++ b/src/front/Components/Layouts/LoginCallbackCustomer/index.tsx @@ -4,33 +4,39 @@ import CoffreIcon from "@Assets/Icons/coffre.svg"; import { useRouter } from "next/router"; import React, { useEffect } from "react"; import classes from "./classes.module.scss"; -//import Module from "@Front/Config/Module"; -//import Auth from "@Front/Api/Auth/IdNot"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Loader from "@Front/Components/DesignSystem/Loader"; -// import { FrontendVariables } from "@Front/Config/VariablesFront"; -// import CustomerStore from "@Front/Stores/CustomerStore"; -import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers"; +import Customers, { ICustomerTokens } from "@Front/Api/Auth/Id360/Customers/Customers"; import CustomerStore from "@Front/Stores/CustomerStore"; import Module from "@Front/Config/Module"; import Link from "next/link"; +import JwtService from "@Front/Services/JwtService/JwtService"; -export default function LoginCallBack() { +export default function LoginCallBackCustomer() { const router = useRouter(); useEffect(() => { const getReport = async () => { const tokenid360 = router.query["token"]; - if (!tokenid360) return; - // const variables = FrontendVariables.getInstance(); - // console.log(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}/`) - // const reportRes = await fetch(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}`, { method: "GET"}); - // const report = await reportRes.json() as id360ProcessResponse; - const token = await Customers.getInstance().loginCallback(tokenid360); - CustomerStore.instance.connect(token.accessToken, token.refreshToken); - router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path); + const refreshedTokens = await JwtService.getInstance().refreshToken(); + if (refreshedTokens) { + return router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path); + } + if (tokenid360) { + let token: ICustomerTokens | undefined; + try { + token = await Customers.getInstance().loginCallback(tokenid360); + } catch (e) { + console.log(e); + router.push(Module.getInstance().get().modules.pages.CustomersLogin.props.path + "?error=1"); + return; + } + CustomerStore.instance.connect(token.accessToken, token.refreshToken); + router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path); + } + return router.push(Module.getInstance().get().modules.pages.CustomersLogin.props.path + "?error=1"); }; getReport(); }), diff --git a/src/front/Components/Layouts/LoginCustomer/index.tsx b/src/front/Components/Layouts/LoginCustomer/index.tsx index 7968e55c..40cb3edf 100644 --- a/src/front/Components/Layouts/LoginCustomer/index.tsx +++ b/src/front/Components/Layouts/LoginCustomer/index.tsx @@ -5,14 +5,18 @@ import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import Image from "next/image"; import { useRouter } from "next/router"; -import { useCallback } from "react"; -import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers"; +import { useCallback, useEffect, useState } from "react"; +import Customers from "@Front/Api/Auth/Id360/Customers/Customers"; import classes from "./classes.module.scss"; import LandingImage from "./landing-connect.jpeg"; import Link from "next/link"; +import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; export default function Login() { const router = useRouter(); + const error = router.query["error"]; + + const [isErrorModalOpen, setIsErrorModalOpen] = useState(false); const redirectCustomerOnConnection = useCallback(() => { async function getCustomer() { @@ -26,6 +30,18 @@ export default function Login() { getCustomer(); }, [router]); + const openErrorModal = useCallback(() => { + setIsErrorModalOpen(true); + }, []); + + const closeErrorModal = useCallback(() => { + setIsErrorModalOpen(false); + }, []); + + useEffect(() => { + if (error === "1") openErrorModal(); + }, [error, openErrorModal]); + return (
@@ -41,6 +57,20 @@ export default function Login() {
+ +
+ + Une erreur est survenue lors de la connexion. Veuillez réessayer. + +
+
); } diff --git a/src/front/Components/Layouts/SelectFolder/index.tsx b/src/front/Components/Layouts/SelectFolder/index.tsx index 7d59f914..d23a8146 100644 --- a/src/front/Components/Layouts/SelectFolder/index.tsx +++ b/src/front/Components/Layouts/SelectFolder/index.tsx @@ -18,7 +18,6 @@ export default function SelectFolder() { async function getFolders() { const jwt = JwtService.getInstance().decodeCustomerJwt(); if (!jwt) return; - console.log(jwt); const folders = await Folders.getInstance().get({ q: { diff --git a/src/front/Services/JwtService/JwtService.ts b/src/front/Services/JwtService/JwtService.ts index dd1dbf2c..dbe8ca4d 100644 --- a/src/front/Services/JwtService/JwtService.ts +++ b/src/front/Services/JwtService/JwtService.ts @@ -1,6 +1,8 @@ import jwt_decode from "jwt-decode"; import CookieService from "../CookieService/CookieService"; -import User from "@Front/Api/Auth/IdNot/User"; +import UserStore from "@Front/Stores/CustomerStore"; +import CustomerStore from "@Front/Stores/CustomerStore"; +import { FrontendVariables } from "@Front/Config/VariablesFront"; enum PROVIDER_OPENID { idNot = "idNot", @@ -22,6 +24,7 @@ export interface IUserJwtPayload { export interface ICustomerJwtPayload { customerId: string; email: string; + exp: number; } export default class JwtService { @@ -48,23 +51,55 @@ export default class JwtService { * @description : set a cookie with a name and a value that expire in 7 days * @throws {Error} If the name or the value is empty */ - public async checkJwt() { - const decodedToken = this.decodeJwt(); + public async refreshToken() { + const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken"); + const variables = await FrontendVariables.getInstance(); + if (!refreshToken) return false; + const userToken = jwt_decode(refreshToken) as IUserJwtPayload; + const customerToken = jwt_decode(refreshToken) as ICustomerJwtPayload; - if (!decodedToken) return; - - const now = Math.floor(Date.now() / 1000); - - if (decodedToken.exp < now) { - const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken"); - - if (!refreshToken) return; - const newAccessToken: { accessToken: string } = await User.getInstance().refreshToken(refreshToken); - - if (newAccessToken) { - CookieService.getInstance().setCookie("leCoffreAccessToken", newAccessToken.accessToken); + if (userToken?.exp && userToken.exp > Math.floor(Date.now() / 1000)) { + if (userToken?.userId) { + try { + const headers = new Headers(); + headers.append("Authorization", `Bearer ${refreshToken}`); + const response = await fetch( + `${ + variables.BACK_API_PROTOCOL + variables.BACK_API_HOST + variables.BACK_API_ROOT_URL + variables.BACK_API_VERSION + }/idnot/user/auth/refresh-token`, + { method: 'POST', headers: headers }, + ); + const newAccessToken: { accessToken: string } = await response.json(); + if (newAccessToken) { + await UserStore.instance.connect(newAccessToken.accessToken, refreshToken); + return true; + } + } catch (err) { + console.log(err); + return false; + } + } else if (customerToken?.customerId) { + try { + const headers = new Headers(); + headers.append("Authorization", `Bearer ${refreshToken}`); + const response = await fetch( + `${ + variables.BACK_API_PROTOCOL + variables.BACK_API_HOST + variables.BACK_API_ROOT_URL + variables.BACK_API_VERSION + }/id360/customers/refresh-token`, + { method: 'POST', headers: headers }, + ); + const newAccessToken: { accessToken: string } = await response.json(); + if (newAccessToken) { + await CustomerStore.instance.connect(newAccessToken.accessToken, refreshToken); + return true; + } + } catch (err) { + console.log(err); + return false; + } } } + return false; } public hasRule(name: string, action: string) { diff --git a/src/front/Stores/CustomerStore.ts b/src/front/Stores/CustomerStore.ts index b360998c..2a3ff88e 100644 --- a/src/front/Stores/CustomerStore.ts +++ b/src/front/Stores/CustomerStore.ts @@ -1,6 +1,5 @@ "use client"; -import Customer from "@Front/Api/Auth/franceConnect/Customer"; import CookieService from "@Front/Services/CookieService/CookieService"; import EventEmitter from "@Front/Services/EventEmitter"; import JwtService from "@Front/Services/JwtService/JwtService"; @@ -36,23 +35,6 @@ export default class UserStore { return true; } - public async connectCustomer(email: string) { - try { - //call connection function - const customer: any = await Customer.getInstance().login(email); - - //Save tokens in cookies - CookieService.getInstance().setCookie("leCoffreAccessToken", customer.accessToken); - CookieService.getInstance().setCookie("leCoffreRefreshToken", customer.refreshToken); - - this.event.emit("connection", this.accessToken); - } catch (error) { - console.error(error); - return false; - } - return true; - } - public async disconnect() { try { //Remove tokens from cookies diff --git a/src/front/Stores/UserStore.ts b/src/front/Stores/UserStore.ts index b360998c..2a3ff88e 100644 --- a/src/front/Stores/UserStore.ts +++ b/src/front/Stores/UserStore.ts @@ -1,6 +1,5 @@ "use client"; -import Customer from "@Front/Api/Auth/franceConnect/Customer"; import CookieService from "@Front/Services/CookieService/CookieService"; import EventEmitter from "@Front/Services/EventEmitter"; import JwtService from "@Front/Services/JwtService/JwtService"; @@ -36,23 +35,6 @@ export default class UserStore { return true; } - public async connectCustomer(email: string) { - try { - //call connection function - const customer: any = await Customer.getInstance().login(email); - - //Save tokens in cookies - CookieService.getInstance().setCookie("leCoffreAccessToken", customer.accessToken); - CookieService.getInstance().setCookie("leCoffreRefreshToken", customer.refreshToken); - - this.event.emit("connection", this.accessToken); - } catch (error) { - console.error(error); - return false; - } - return true; - } - public async disconnect() { try { //Remove tokens from cookies diff --git a/src/middleware.ts b/src/middleware.ts index 13034279..39b4491e 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -15,14 +15,13 @@ export async function middleware(request: NextRequest) { // If no JWT provided, redirect to login page if (!userDecodedToken && !customerDecodedToken) return NextResponse.redirect(new URL("/login", request.url)); - // If JWT expired, redirect to login page - const token = userDecodedToken ?? customerDecodedToken; - const currentDate = new Date(); - const time = currentDate.getTime(); - const now = Math.floor(time / 1000); - if (token.exp < now) { - console.log("token expired"); - return NextResponse.redirect(new URL("/login", request.url)); + // If JWT expired, redirect to login callback page to refresh tokens + const now = Math.floor(Date.now() / 1000); + if (userDecodedToken.userId && userDecodedToken.exp < now) { + return NextResponse.redirect(new URL("/authorized-client", request.url)); + } + if (customerDecodedToken.customerId && customerDecodedToken.exp < now) { + return NextResponse.redirect(new URL("/id360/customer-callback", request.url)); } return NextResponse.next();