107 lines
2.9 KiB
JavaScript

import Keycloak from "keycloak-js";
import { reactive } from "vue";
export const auth = reactive({
ready: false,
enabled: false,
authenticated: false,
username: "",
email: "",
groups: [],
loginUrl: "",
resetUrl: "",
token: "",
});
let keycloak = null;
let initPromise = null;
function normalizeGroups(groups) {
if (!Array.isArray(groups)) return [];
return groups
.filter((g) => typeof g === "string")
.map((g) => g.replace(/^\//, ""))
.filter(Boolean);
}
function updateFromToken() {
const parsed = keycloak?.tokenParsed || {};
auth.authenticated = Boolean(keycloak?.authenticated);
auth.token = keycloak?.token || "";
auth.username = parsed.preferred_username || "";
auth.email = parsed.email || "";
auth.groups = normalizeGroups(parsed.groups);
}
export async function initAuth() {
if (initPromise) return initPromise;
initPromise = (async () => {
try {
const resp = await fetch("/api/auth/config", { headers: { Accept: "application/json" } });
if (!resp.ok) throw new Error(`auth config ${resp.status}`);
const cfg = await resp.json();
auth.enabled = Boolean(cfg.enabled);
auth.loginUrl = cfg.login_url || "";
auth.resetUrl = cfg.reset_url || "";
if (!auth.enabled) return;
keycloak = new Keycloak({
url: cfg.url,
realm: cfg.realm,
clientId: cfg.client_id,
});
const authenticated = await keycloak.init({
onLoad: "check-sso",
pkceMethod: "S256",
silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`,
checkLoginIframe: true,
scope: "openid profile email",
});
auth.authenticated = authenticated;
updateFromToken();
keycloak.onAuthSuccess = () => updateFromToken();
keycloak.onAuthLogout = () => updateFromToken();
keycloak.onAuthRefreshSuccess = () => updateFromToken();
keycloak.onTokenExpired = () => {
keycloak
.updateToken(30)
.then(() => updateFromToken())
.catch(() => updateFromToken());
};
window.setInterval(() => {
if (!keycloak?.authenticated) return;
keycloak.updateToken(60).then(updateFromToken).catch(() => {});
}, 30_000);
} catch {
auth.enabled = false;
} finally {
auth.ready = true;
}
})();
return initPromise;
}
export async function login(redirectPath = window.location.pathname + window.location.search + window.location.hash) {
if (!keycloak) return;
const redirectUri = new URL(redirectPath, window.location.origin).toString();
await keycloak.login({ redirectUri });
}
export async function logout() {
if (!keycloak) return;
await keycloak.logout({ redirectUri: window.location.origin });
}
export async function authFetch(url, options = {}) {
const headers = new Headers(options.headers || {});
if (auth.token) headers.set("Authorization", `Bearer ${auth.token}`);
return fetch(url, { ...options, headers });
}