Ctrl K
ComunidadGuías…

__tests__/sso-only.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SSO-ONLY mode. A platform-managed Ghost Browser (connected from the Tools tab) belongs to ONE
// platform user and must be entered ONLY through the platform SSO handoff. Local password accounts are
// disabled — otherwise anyone reaching the tool's URL before the owner's first sign-in could claim
// ownership via the "create the owner account" form. The full route stack pulls in Playwright etc. and
// isn't bootable headless, so these pin the load-bearing lines directly.
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';

const server = readFileSync(fileURLToPath(new URL('../src/server.js', import.meta.url)), 'utf8');
const consoleJs = readFileSync(fileURLToPath(new URL('../public/js/console.js', import.meta.url)), 'utf8');

describe('SSO-only server gate', () => {
  it('is enabled by SSO_ONLY but only when LEADFLOW is configured (never locks out with no SSO path)', () => {
    expect(server).toMatch(/const SSO_ONLY = .*process\.env\.SSO_ONLY.*&&\s*!!process\.env\.LEADFLOW_JWT_SECRET/s);
  });

  it('blocks local signup when SSO-only, and login only until the owner has set a password', () => {
    /* Signup stays shut: nobody may claim an instance before its owner's first hand-off. Login
       is the fallback that never depends on the platform - it opens the moment a password exists. */
    const signup = server.slice(server.indexOf("app.post('/api/auth/signup'"), server.indexOf("app.post('/api/auth/login'"));
    const login = server.slice(server.indexOf("app.post('/api/auth/login'"), server.indexOf('ONE LOGIN'));
    expect(signup).toMatch(/if \(SSO_ONLY\) return ssoOnlyBlock/);
    expect(login).toMatch(/if \(SSO_ONLY && !accounts\.hasPassword\(\)\)/);
    expect(login).not.toMatch(/if \(SSO_ONLY\) return ssoOnlyBlock/);
  });

  it('lets the signed-in owner set the password that opens the instance without the platform', () => {
    expect(server).toMatch(/app\.post\('\/api\/auth\/password', authed/);
    const state = server.slice(server.indexOf("app.get('/api/auth/state'"), server.indexOf("app.post('/api/auth/signup'"));
    expect(state).toMatch(/hasPassword: accounts\.hasPassword\(\)/);
  });

  it('does NOT block the SSO route (that is the only way in)', () => {
    const ssoRoute = server.slice(server.indexOf("app.post('/api/auth/sso'"), server.indexOf("app.post('/api/auth/logout'"));
    expect(ssoRoute).not.toMatch(/SSO_ONLY/);
  });

  it('reports ssoOnly in /api/auth/state so the frontend can hide the password gate', () => {
    const state = server.slice(server.indexOf("app.get('/api/auth/state'"), server.indexOf("app.post('/api/auth/signup'"));
    expect(state).toMatch(/ssoOnly: SSO_ONLY/);
  });

  it('frontend KEEPS the email/password form when ssoOnly, and says what to do', () => {
    /* A hand-off that failed used to leave a page with nothing to press. */
    expect(consoleJs).toMatch(/if \(s\.ssoOnly\)/);
    expect(consoleJs).toMatch(/password you set here/i);
    expect(consoleJs).not.toMatch(/\['u', 'p', 'gateGo'/);
    expect(consoleJs).toMatch(/\/api\/auth\/password/);
  });
});