Files
zellij/zellij-client/assets/auth.js
T
Aram Drevekenin 3239d37b40 Native web mobile UI (#5441)
* initial chrome

* mobile state

* feat(mobile-web): implement browser→server actions (fit toggle, single-pane fullscreen, pane/tab focus) with fullscreen-truth reconciliation

* panning

* remove mobile plugin

* fix first load race

* improve web handshake

* rustfmt

* cleanups

* moar cleanups

* add bundle asset check to ci

* fix(mobile-web): tab-scope co-presence, yield single-pane to desktop clients, fix render-mode desync

* make mobile use no-ui fullscreen

* client side welcome screen for mobile

* attach web clients to first tab on tiles surface

* fix: opening new pane in mobile single mode no longer exits mobile single mode

* mobile chrome light theme support

* fix server races and flaky tests

* rustfmt

* fix flakes

* add pr
2026-08-05 15:32:53 +02:00

131 lines
3.7 KiB
JavaScript

/**
* Authentication logic and token management
*/
import { getBaseUrl } from "./utils.js";
/**
* Wait for user to provide a security token
* @returns {Promise<{token: string, remember: boolean}>}
*/
async function waitForSecurityToken() {
let token = null;
let remember = null;
while (!token) {
let result = await getSecurityToken();
if (result) {
token = result.token;
remember = result.remember;
} else {
await showErrorModal(
"Error",
"Must provide security token in order to log in."
);
}
}
return { token, remember };
}
/**
* Perform the login exchange with the server
* @param {string} token - Authentication token
* @param {boolean} rememberMe - Remember login preference
* @returns {Promise<boolean>} true when the login succeeded
*/
async function login(token, rememberMe) {
const baseUrl = getBaseUrl();
let login_res = await fetch(`${baseUrl}/command/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
auth_token: token,
remember_me: rememberMe ? true : false,
}),
credentials: "include",
});
if (login_res.status === 401) {
await showErrorModal("Error", "Unauthorized or revoked login token.");
return false;
} else if (!login_res.ok) {
await showErrorModal(
"Error",
`Error ${login_res.status} connecting to server.`
);
return false;
}
return true;
}
/**
* Perform a request, authenticating and retrying for as long as it is rejected.
* @param {string} url - Absolute URL to request
* @param {Object} options - fetch options, merged over the credentialed defaults
* @returns {Promise<Response>} The successful response
*/
export async function authorizedFetch(url, options = {}) {
while (true) {
const response = await fetch(url, {
credentials: "include",
...options,
});
if (response.ok) {
return response;
}
if (response.status !== 401) {
await showErrorModal(
"Error",
`Error ${response.status} connecting to server.`
);
}
const { token, remember } = await waitForSecurityToken();
await login(token, remember);
}
}
/**
* Request a session from the server, authenticating first if required.
* @param {string} sessionFromPath - Session name taken from the URL path
* @param {Object} options - `{ welcome: boolean }`, welcome defaulting to true
* @returns {Promise<Object>} The full session bootstrap payload
*/
export async function fetchSession(sessionFromPath, options = {}) {
const baseUrl = getBaseUrl();
const params = new URLSearchParams();
if (sessionFromPath) {
params.set("session", sessionFromPath);
}
if (options.welcome === false) {
params.set("welcome", "false");
}
const query = params.toString() ? `?${params.toString()}` : "";
const response = await authorizedFetch(`${baseUrl}/session${query}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
return await response.json();
}
/**
* Fetch the list of live sessions without attaching to any of them.
* @returns {Promise<Array<Object>>} The session descriptors
*/
export async function fetchSessionList() {
const baseUrl = getBaseUrl();
const response = await authorizedFetch(`${baseUrl}/session-list`, {
method: "GET",
});
const payload = await response.json();
return payload.sessions || [];
}