fix: improve authentication handling and error management
This commit is contained in:
156
index.html
156
index.html
@@ -17,11 +17,6 @@
|
||||
<div id="oidcLoginSection">
|
||||
<button id="oidcLoginBtn" class="btn btn-primary">Se connecter avec Infomaniak</button>
|
||||
</div>
|
||||
<div id="apikeySection" style="display: none;">
|
||||
<label for="apikey" class="form-label">API Key</label>
|
||||
<input type="text" id="apikey" class="form-control" placeholder="Entrez votre API Key">
|
||||
<button id="validateApiKey" class="btn btn-success mt-2">Valider</button>
|
||||
</div>
|
||||
<div id="connectedUser" style="display: none;">
|
||||
<p>Connecté en tant que: <span id="userName"></span></p>
|
||||
<button id="disconnect" class="btn btn-danger">Déconnecter</button>
|
||||
@@ -61,11 +56,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const loginContainer = document.getElementById("loginContainer");
|
||||
const oidcLoginSection = document.getElementById("oidcLoginSection");
|
||||
const apikeySection = document.getElementById("apikeySection");
|
||||
const connectedUser = document.getElementById("connectedUser");
|
||||
const eventFilters = document.getElementById("eventFilters");
|
||||
const accountSelect = document.getElementById("account");
|
||||
@@ -80,22 +74,75 @@
|
||||
let storedAccount = localStorage.getItem("account") || "default";
|
||||
let userInfo = JSON.parse(localStorage.getItem("userInfo") || "null");
|
||||
|
||||
// Handle the static "abc" key case - removed for security
|
||||
/*
|
||||
if (storedApiKey === "abc" && !userInfo) {
|
||||
userInfo = {email: "utilisateur@example.com"};
|
||||
localStorage.setItem("userInfo", JSON.stringify(userInfo));
|
||||
}
|
||||
*/
|
||||
|
||||
// Handle OIDC callback
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const code = urlParams.get('code');
|
||||
if (code) {
|
||||
// We're coming back from OIDC login
|
||||
exchangeCodeForToken(code);
|
||||
// Remove code from URL
|
||||
const error = urlParams.get('error');
|
||||
const hashParams = new URLSearchParams(window.location.hash.substring(1));
|
||||
const accessToken = hashParams.get('access_token');
|
||||
|
||||
// Display error message if present
|
||||
if (error) {
|
||||
alert("Erreur d'authentification: " + decodeURIComponent(error));
|
||||
// Remove error from URL
|
||||
window.history.replaceState({}, document.title, "/");
|
||||
}
|
||||
|
||||
// Handle access token in URL fragment
|
||||
if (accessToken) {
|
||||
// Store the access token
|
||||
localStorage.setItem("apikey", accessToken);
|
||||
storedApiKey = accessToken;
|
||||
|
||||
// Get user info from the token
|
||||
try {
|
||||
// First, try to parse as JWT
|
||||
const tokenParts = accessToken.split('.');
|
||||
let userData = {};
|
||||
|
||||
if (tokenParts.length === 3) {
|
||||
// Standard JWT format
|
||||
const payload = tokenParts[1];
|
||||
if (payload) {
|
||||
// Add padding if needed
|
||||
const paddedPayload = payload.padEnd(payload.length + (4 - payload.length % 4) % 4, '=');
|
||||
const decodedPayload = atob(paddedPayload);
|
||||
userData = JSON.parse(decodedPayload);
|
||||
}
|
||||
} else {
|
||||
// Non-JWT token, treat as opaque token
|
||||
console.log("Non-JWT token received, using default user info");
|
||||
userData = {email: "utilisateur@" + window.location.hostname};
|
||||
}
|
||||
|
||||
userInfo = {email: userData.email || "Utilisateur connecté"};
|
||||
localStorage.setItem("userInfo", JSON.stringify(userInfo));
|
||||
} catch (e) {
|
||||
console.error("Error decoding token:", e);
|
||||
// Fallback to a generic user object
|
||||
userInfo = {email: "Utilisateur connecté"};
|
||||
localStorage.setItem("userInfo", JSON.stringify(userInfo));
|
||||
}
|
||||
|
||||
// Remove token from URL
|
||||
window.history.replaceState({}, document.title, "/");
|
||||
|
||||
// Update UI
|
||||
renderLoginSection();
|
||||
}
|
||||
|
||||
function renderLoginSection() {
|
||||
if (storedApiKey || userInfo) {
|
||||
// User is logged in
|
||||
connectedUser.style.display = "block";
|
||||
oidcLoginSection.style.display = "none";
|
||||
apikeySection.style.display = "none";
|
||||
|
||||
const userNameElement = document.getElementById("userName");
|
||||
if (userInfo && userInfo.email) {
|
||||
@@ -113,16 +160,23 @@
|
||||
// User is not logged in
|
||||
connectedUser.style.display = "none";
|
||||
oidcLoginSection.style.display = "block";
|
||||
apikeySection.style.display = "none";
|
||||
eventFilters.style.display = "none";
|
||||
|
||||
document.getElementById("oidcLoginBtn").addEventListener("click", initiateOIDCLogin);
|
||||
// Remove any existing event listeners to prevent duplicates
|
||||
const oidcLoginBtn = document.getElementById("oidcLoginBtn");
|
||||
const newOidcLoginBtn = oidcLoginBtn.cloneNode(true);
|
||||
oidcLoginBtn.parentNode.replaceChild(newOidcLoginBtn, oidcLoginBtn);
|
||||
|
||||
newOidcLoginBtn.addEventListener("click", initiateOIDCLogin);
|
||||
}
|
||||
|
||||
// Add disconnect handler
|
||||
const disconnectBtn = document.getElementById("disconnect");
|
||||
if (disconnectBtn) {
|
||||
disconnectBtn.addEventListener("click", logout);
|
||||
// Remove any existing event listeners to prevent duplicates
|
||||
const newDisconnectBtn = disconnectBtn.cloneNode(true);
|
||||
disconnectBtn.parentNode.replaceChild(newDisconnectBtn, disconnectBtn);
|
||||
newDisconnectBtn.addEventListener("click", logout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,26 +185,6 @@
|
||||
window.location.href = `${apiBaseUrl}/login`;
|
||||
}
|
||||
|
||||
function exchangeCodeForToken(code) {
|
||||
fetch(`${apiBaseUrl}/callback?code=${code}&state=ignored`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.access_token) {
|
||||
localStorage.setItem("apikey", data.access_token);
|
||||
localStorage.setItem("userInfo", JSON.stringify(data.user));
|
||||
storedApiKey = data.access_token;
|
||||
userInfo = data.user;
|
||||
renderLoginSection();
|
||||
} else {
|
||||
alert("Échec de la connexion OIDC");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Erreur lors de l'échange du code:", error);
|
||||
alert("Échec de la connexion OIDC");
|
||||
});
|
||||
}
|
||||
|
||||
function logout() {
|
||||
localStorage.removeItem("apikey");
|
||||
localStorage.removeItem("account");
|
||||
@@ -160,23 +194,27 @@
|
||||
location.reload();
|
||||
}
|
||||
|
||||
function saveApiKey() {
|
||||
const key = document.getElementById("apikey").value;
|
||||
if (key) {
|
||||
localStorage.setItem("apikey", key);
|
||||
location.reload();
|
||||
} else {
|
||||
alert("Veuillez entrer une clé API valide.");
|
||||
}
|
||||
}
|
||||
|
||||
function updateAccountOptions() {
|
||||
// Fetch available accounts from the server
|
||||
fetch(`${apiBaseUrl}/accounts`, {
|
||||
headers: { "Authorization": `Bearer ${storedApiKey}` }
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then(errorData => {
|
||||
console.error("Accounts error response:", errorData);
|
||||
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.detail || 'Unknown error'}`);
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(accounts => {
|
||||
// Check if accounts is actually an array
|
||||
if (!Array.isArray(accounts)) {
|
||||
console.error("Accounts data is not an array:", accounts);
|
||||
throw new Error("Invalid accounts data format");
|
||||
}
|
||||
|
||||
accountSelect.innerHTML = '';
|
||||
accounts.forEach(account => {
|
||||
const option = document.createElement("option");
|
||||
@@ -190,10 +228,8 @@
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Erreur lors du chargement des comptes:", error);
|
||||
// Fallback to default options
|
||||
accountSelect.innerHTML = `
|
||||
<option value="default" ${storedAccount === "default" ? "selected" : ""}>Compte par défaut</option>
|
||||
`;
|
||||
alert("Accès refusé. Vous n'êtes pas autorisé à accéder à cette application. Veuillez contacter l'administrateur.");
|
||||
logout();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -227,19 +263,35 @@
|
||||
.then(response => {
|
||||
if (response.status === 401) {
|
||||
// Token expired or invalid
|
||||
alert("Votre session a expiré. Veuillez vous reconnecter.");
|
||||
logout();
|
||||
return;
|
||||
}
|
||||
if (!response.ok) {
|
||||
return response.json().then(errorData => {
|
||||
console.error("Schedule error response:", errorData);
|
||||
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.detail || 'Unknown error'}`);
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data) {
|
||||
// Check if data is an array
|
||||
if (!Array.isArray(data)) {
|
||||
console.error("Schedule data is not an array:", data);
|
||||
throw new Error("Invalid schedule data format");
|
||||
}
|
||||
|
||||
lastFetchedEvents = data.filter(event => event.event === "Jeu");
|
||||
updateAgeGroupOptions(lastFetchedEvents);
|
||||
displayEvents(lastFetchedEvents);
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Erreur lors du chargement des événements:", error));
|
||||
.catch(error => {
|
||||
console.error("Erreur lors du chargement des événements:", error);
|
||||
alert("Erreur lors du chargement des événements: " + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function updateAgeGroupOptions(events) {
|
||||
|
||||
Reference in New Issue
Block a user