fix(mosaic): gateway token recovery review remediations (#414)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful

This commit was merged in pull request #414.
This commit is contained in:
2026-04-05 06:13:29 +00:00
parent 3abd63ea5c
commit 119ff0eb1b
5 changed files with 90 additions and 30 deletions

View File

@@ -1,7 +1,6 @@
import { createInterface } from 'node:readline';
import { loadSession, validateSession, signIn, saveSession } from '../../auth.js';
import { readMeta, writeMeta } from './daemon.js';
import { getGatewayUrl } from './login.js';
import { getGatewayUrl, promptLine, promptSecret } from './login.js';
interface MintedToken {
id: string;
@@ -58,6 +57,9 @@ export async function mintAdminToken(
/**
* Persist the new token into meta.json and print the confirmation banner.
*
* Emits a warning when the target gateway differs from the locally installed one,
* so operators are aware that meta.json may not reflect the intended gateway.
*/
export function persistToken(gatewayUrl: string, minted: MintedToken): void {
const meta = readMeta() ?? {
@@ -68,6 +70,15 @@ export function persistToken(gatewayUrl: string, minted: MintedToken): void {
port: parseInt(new URL(gatewayUrl).port || '14242', 10),
};
// Warn when the target gateway does not match the locally installed one
const targetHost = new URL(gatewayUrl).hostname;
if (targetHost !== meta.host) {
console.warn(
`Warning: token was minted against ${gatewayUrl} but is being saved to the local` +
` meta.json (host: ${meta.host}). Copy the token manually if targeting a remote gateway.`,
);
}
writeMeta({ ...meta, adminToken: minted.plaintext });
const preview = `${minted.plaintext.slice(0, 8)}...`;
@@ -108,13 +119,10 @@ export async function ensureSession(gatewayUrl: string): Promise<string> {
console.log(`No session found for ${gatewayUrl}. Please sign in.`);
}
// Prompt for credentials
const rl = createInterface({ input: process.stdin, output: process.stdout });
const ask = (q: string): Promise<string> => new Promise((resolve) => rl.question(q, resolve));
const email = (await ask('Email: ')).trim();
const password = (await ask('Password: ')).trim();
rl.close();
// Prompt for credentials — password must not be echoed to the terminal
const email = await promptLine('Email: ');
// Do not trim password — it may contain intentional leading/trailing whitespace
const password = await promptSecret('Password: ');
const auth = await signIn(gatewayUrl, email, password).catch((err: unknown) => {
console.error(err instanceof Error ? err.message : String(err));