← back to Grant

lib/sister-auth.ts

50 lines

import { NextResponse } from 'next/server';

/**
 * lib/sister-auth.ts — outbound credentials for cross-service calls into
 * sibling DW services (Patty, PoppyPetitions, Freddy, etc.).
 *
 * 2026-05-05 (tick 30 + architect-reviewer reverse port): copied from
 * Patty's lib/sister-auth.ts. Grant doesn't currently make sister-service
 * calls, but the moment it does (e.g. → Patty for "find petitions matching
 * this grant's cause", → Freddy for "lookup foundations in the marketplace
 * catalog"), this is the canonical helper to use.
 *
 * Falls back to AUTH_PASSWORD which Grant's own login already requires.
 * If neither password env is set, SISTER_AUTH is '' and SISTER_OK is false;
 * route handlers MUST gate on SISTER_OK and return sisterUnconfigured()
 * instead of fetching with empty creds (which would silently 401 and look
 * like a bug).
 *
 * NOTE on `?? 'admin'`: the username default is intentional and SAFE —
 * 'admin' is not a secret, just the conventional username across all DW
 * Express services. The anti-pattern rule (no literal fallbacks) targets
 * PASSWORDS and SECRETS specifically. Using `??` (not `||`) so an explicit
 * empty-string SISTER_AUTH_USERNAME is preserved (treated as "unset" only
 * when actually undefined/null).
 */

const SISTER_PASS = process.env.SISTER_AUTH_PASSWORD ?? process.env.AUTH_PASSWORD ?? '';
const SISTER_USER = process.env.SISTER_AUTH_USERNAME ?? 'admin';

export const SISTER_AUTH: string = SISTER_PASS
  ? 'Basic ' + Buffer.from(`${SISTER_USER}:${SISTER_PASS}`).toString('base64')
  : '';

export const SISTER_OK: boolean = SISTER_AUTH !== '';

export function sisterUnconfigured(): NextResponse {
  return NextResponse.json(
    { error: 'sister auth not configured (SISTER_AUTH_PASSWORD or AUTH_PASSWORD env required)' },
    { status: 503 },
  );
}

/**
 * Raw credentials for sister services that use form/JSON login (e.g. cookie
 * auth via /api/auth/login) rather than a Basic Authorization header.
 */
export function sisterCredentials(): { username: string; password: string } {
  return { username: SISTER_USER, password: SISTER_PASS };
}