import * as redirect from "./redirect"; async function sendRequest(endpoint, body) { const formData = new FormData(); for (var key in body) { formData.append(key, body[key]); } const request = { method: "POST", // headers: { // "Content-Type": "application/x-www-form-urlencoded", // }, body: formData, }; let response; try { response = await fetch(endpoint, request); if (!response.ok) { throw new Error(response.statusText); } } catch (err) { console.error(err); return null; } return await response.json(); } /** * @param {string} code the authorization code received from the OIDC * provider * @returns {Promise} true if the token was fetched, false otherwise */ async function getToken(code) { const auth_url = import.meta.env.VITE_URL; const endpoint = `${auth_url}/auth-api/token`; const formData = { grant_type: "authorization_code", code: code, redirect_uri: `${auth_url}/complete/epita/`, client_id: import.meta.env.VITE_CLIENT_ID, }; const response = await sendRequest(endpoint, formData); if (response === null) { console.error("Failed to retrieve OIDC token"); alert("Failed to retrieve OIDC token"); localStorage.clear(); redirect.redirectToLoginPage(); return false; } localStorage.setItem("token", response.id_token); localStorage.setItem("refresh_token", response.refresh_token); console.debug("Saved token and refresh tokens"); return true; } /** * @param {string} refreshToken the refresh token (optional) * @returns {Promise} whether the token has been refreshed or not */ async function refreshToken(refreshToken) { refreshToken = refreshToken || localStorage.getItem("refresh_token"); if (!refreshToken) { console.error("Unable to retrieve refresh token"); return false; } const auth_url = import.meta.env.VITE_URL; const endpoint = `${auth_url}/auth-api/token`; const formData = { client_id: import.meta.env.VITE_CLIENT_ID, // client_secret: "", grant_type: "authorization_code", refresh_token: refreshToken, scope: "epita profile picture", }; const response = await sendRequest(endpoint, formData); if (response === null) { console.error("Failed to retrieve OIDC token"); alert("Failed to retrieve OIDC token"); localStorage.clear(); redirect.redirectToLoginPage(); return false; } localStorage.setItem("token", response.id_token); localStorage.setItem("refresh_token", response.refresh_token); console.debug("Saved token and refresh tokens"); return true; } /** * @returns {Promise} true if the user is authenticated, false otherwise */ async function authenticate() { const token = localStorage.getItem("token"); if (token !== null) { return true; } const refresh_token = localStorage.getItem("refresh_token"); if (refresh_token !== null) { return await refreshToken(refresh_token); } localStorage.clear(); redirect.redirectToLoginPage(); return false; } /** * @param {string} endpoint * @param {object} options this object should at least contain the method. * @returns {Promise} the response or null * We want a {Promise} so we can read the headers as well as the * body, rather than just the body */ async function authedAPIRequest(endpoint, options) { if (!(await authenticate())) { return null; } // if (!options.method) { // console.error("Invalid parameter: options (missing method)"); // return null; // } // Deep copy req const request = JSON.parse(JSON.stringify(options)); if (!request.headers) { request.headers = {}; } // Pcq ce PUTAIN de js est pas foutu de faire ce qu'on lui demande request.headers.Authorization = "Bearer " + localStorage.getItem("token"); const full_endpoint = import.meta.env.VITE_URL + "/api" + endpoint; const response = await fetch(full_endpoint, request); if (response.status === 401) { const response_err = await response.text(); if (response_err.includes("Token expired")) { if (await refreshToken(null)) { return await authedAPIRequest(endpoint, options); } } localStorage.clear(); alert("Redirecting to logging page"); redirect.redirectToLoginPage(); return null; } return response; } export { getToken, refreshToken, authenticate, authedAPIRequest };