eplace/src/utils/auth.js

175 lines
4.7 KiB
JavaScript
Raw Normal View History

2026-05-15 14:08:38 +02:00
import * as redirect from "./redirect";
2026-05-15 17:27:27 +02:00
async function sendRequest(endpoint, body) {
2026-05-15 18:31:16 +02:00
const formData = new FormData();
for (var key in body) {
formData.append(key, body[key]);
}
const request = {
method: "POST",
2026-05-15 19:00:11 +02:00
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// },
2026-05-15 18:31:16 +02:00
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;
2026-05-15 16:43:17 +02:00
}
2026-05-15 18:31:16 +02:00
return await response.json();
2026-05-15 17:27:27 +02:00
}
/**
2026-05-15 18:31:16 +02:00
* @param {string} code the authorization code received from the OIDC
* provider
* @returns {Promise<boolean>} true if the token was fetched, false otherwise
*/
2026-05-15 17:27:27 +02:00
async function getToken(code) {
2026-05-15 18:31:16 +02:00
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;
}
2026-05-15 17:27:27 +02:00
2026-05-15 18:31:16 +02:00
localStorage.setItem("token", response.id_token);
localStorage.setItem("refresh_token", response.refresh_token);
console.debug("Saved token and refresh tokens");
2026-05-15 16:43:17 +02:00
2026-05-15 18:31:16 +02:00
return true;
2026-05-15 14:08:38 +02:00
}
/**
2026-05-15 18:31:16 +02:00
* @param {string} refreshToken the refresh token (optional)
* @returns {Promise<boolean>} whether the token has been refreshed or not
*/
2026-05-15 16:43:17 +02:00
async function refreshToken(refreshToken) {
2026-05-15 18:31:16 +02:00
refreshToken = refreshToken || localStorage.getItem("refresh_token");
if (!refreshToken) {
console.error("Unable to retrieve refresh token");
return false;
}
2026-05-15 16:43:17 +02:00
2026-05-15 18:31:16 +02:00
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;
}
2026-05-15 16:43:17 +02:00
2026-05-15 18:31:16 +02:00
localStorage.setItem("token", response.id_token);
localStorage.setItem("refresh_token", response.refresh_token);
console.debug("Saved token and refresh tokens");
2026-05-15 16:43:17 +02:00
2026-05-15 18:31:16 +02:00
return true;
2026-05-15 16:43:17 +02:00
}
2026-05-15 14:08:38 +02:00
/**
2026-05-15 18:31:16 +02:00
* @returns {Promise<boolean>} true if the user is authenticated, false otherwise
*/
2026-05-15 14:08:38 +02:00
async function authenticate() {
2026-05-15 18:31:16 +02:00
const token = localStorage.getItem("token");
if (token !== null) {
return true;
}
const refresh_token = localStorage.getItem("refresh_token");
2026-05-15 14:08:38 +02:00
2026-05-15 18:31:16 +02:00
if (refresh_token !== null) {
2026-05-15 22:33:21 +02:00
return await refreshToken(refresh_token);
2026-05-15 18:31:16 +02:00
}
localStorage.clear();
redirect.redirectToLoginPage();
return false;
2026-05-15 14:08:38 +02:00
}
2026-05-15 18:31:16 +02:00
/**
* @param {string} endpoint
* @param {object} options this object should at least contain the method.
* @returns {Promise<Response>} the response or null
* We want a {Promise<Response>} so we can read the headers as well as the
* body, rather than just the body
*/
async function authedAPIRequest(endpoint, options) {
2026-05-15 22:33:21 +02:00
if (!(await authenticate())) {
2026-05-15 18:31:16 +02:00
return null;
}
2026-05-16 11:14:39 +02:00
// if (!options.method) {
// console.error("Invalid parameter: options (missing method)");
// return null;
// }
2026-05-15 18:31:16 +02:00
// Deep copy req
const request = JSON.parse(JSON.stringify(options));
if (!request.headers) {
request.headers = {};
2026-05-15 18:31:16 +02:00
}
// Pcq ce PUTAIN de js est pas foutu de faire ce qu'on lui demande
request.headers.Authorization = "Bearer " + localStorage.getItem("token");
2026-05-15 18:31:16 +02:00
2026-05-15 20:13:34 +02:00
const full_endpoint = import.meta.env.VITE_URL + "/api" + endpoint;
2026-05-15 18:31:16 +02:00
const response = await fetch(full_endpoint, request);
2026-05-16 11:14:39 +02:00
2026-05-15 22:59:15 +02:00
if (response.status === 401) {
const response_err = await response.text();
2026-05-15 14:08:38 +02:00
2026-05-15 22:59:15 +02:00
if (response_err.includes("Token expired")) {
if (await refreshToken(null)) {
return await authedAPIRequest(endpoint, options);
2026-05-15 18:31:16 +02:00
}
}
2026-05-15 22:59:15 +02:00
localStorage.clear();
alert("Redirecting to logging page");
redirect.redirectToLoginPage();
return null;
2026-05-15 18:31:16 +02:00
}
return response;
2026-05-15 14:08:38 +02:00
}
2026-05-15 18:31:16 +02:00
export { getToken, refreshToken, authenticate, authedAPIRequest };