refacto refresh token

This commit is contained in:
OxSaitama 2023-10-11 12:33:04 +02:00
parent 167ebe246b
commit 0eed23f7d0
14 changed files with 132 additions and 136 deletions

View File

@ -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<any> {
public async loginCallback(callbackToken: string | string[]): Promise<ICustomerTokens> {
const url = new URL(this.baseURl.concat(`/login-callback/${callbackToken}`));
try {
return await this.postRequest<any>(url);
return await this.postRequest<ICustomerTokens>(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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");

View File

@ -259,7 +259,6 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
return Customer.hydrate<Customer>(customer);
}),
});
console.log(body);
await Folders.getInstance().put(this.props.selectedFolderUid, body);
this.props.router.push(`/folders/${this.props.selectedFolderUid}`);
}

View File

@ -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,7 +20,11 @@ export default function LoginCallBack() {
useEffect(() => {
async function getUser() {
const code = router.query["code"];
if (!code) return;
const refreshedTokens = await JwtService.getInstance().refreshToken();
if (refreshedTokens) {
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
}
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);
@ -30,6 +35,8 @@ export default function LoginCallBack() {
return;
}
}
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
}
getUser();
}),
[router];

View File

@ -4,33 +4,40 @@ 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);
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;
}
if (!token) return router.reload();
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();
}),

View File

@ -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 (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}>
@ -41,6 +57,20 @@ export default function Login() {
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link>
</div>
<Confirm
isOpen={isErrorModalOpen}
onClose={closeErrorModal}
showCancelButton={false}
onAccept={closeErrorModal}
closeBtn
header={"Erreur"}
confirmText={"OK"}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
Une erreur est survenue lors de la connexion. Veuillez réessayer.
</Typography>
</div>
</Confirm>
</DefaultDoubleSidePage>
);
}

View File

@ -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: {

View File

@ -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,24 +51,56 @@ 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();
if (!decodedToken) return;
const now = Math.floor(Date.now() / 1000);
if (decodedToken.exp < now) {
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 (!refreshToken) return;
const newAccessToken: { accessToken: string } = await User.getInstance().refreshToken(refreshToken);
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) {
CookieService.getInstance().setCookie("leCoffreAccessToken", newAccessToken.accessToken);
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) {
const token = this.decodeJwt();

View File

@ -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

View File

@ -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

View File

@ -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();