# Require Web Login Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Block the self-hosted web app UI until a user is authenticated. **Architecture:** Add a small web-only root extension component registered by `packages/hoppscotch-selfhost-web/src/main.ts`. The component observes the existing auth platform state and renders a full-screen login gate only for web mode while no confirmed user exists. **Tech Stack:** Vue 3, Hoppscotch platform auth streams, RxJS `BehaviorSubject`, Vitest for focused state logic. --- ### Task 1: Auth Gate State Logic **Files:** - Create: `packages/hoppscotch-common/src/helpers/appLoginGate.ts` - Test: `packages/hoppscotch-common/src/helpers/__tests__/appLoginGate.spec.ts` - [ ] **Step 1: Write the failing test** ```ts import { describe, expect, test } from "vitest" import { shouldBlockAppForLogin } from "../appLoginGate" describe("shouldBlockAppForLogin", () => { const user = { uid: "user-1", displayName: "User", email: "user@example.com", photoURL: null, emailVerified: true, } test("blocks the web app while auth is still being checked", () => { expect( shouldBlockAppForLogin({ platform: "web", isAuthInitComplete: false, currentUser: null, }) ).toBe(true) }) test("blocks the web app when auth is confirmed anonymous", () => { expect( shouldBlockAppForLogin({ platform: "web", isAuthInitComplete: true, currentUser: null, }) ).toBe(true) }) test("does not block the web app once a user is authenticated", () => { expect( shouldBlockAppForLogin({ platform: "web", isAuthInitComplete: true, currentUser: user, }) ).toBe(false) }) test("does not block desktop", () => { expect( shouldBlockAppForLogin({ platform: "desktop", isAuthInitComplete: true, currentUser: null, }) ).toBe(false) }) }) ``` - [ ] **Step 2: Run test to verify it fails** Run: `pnpm --dir packages/hoppscotch-common exec vitest --run src/helpers/__tests__/appLoginGate.spec.ts` Expected: FAIL because `../appLoginGate` does not exist. - [ ] **Step 3: Write minimal implementation** ```ts type KernelMode = "web" | "desktop" export type LoginGateState = { platform: KernelMode isAuthInitComplete: boolean currentUser: unknown | null } export function shouldBlockAppForLogin(state: LoginGateState) { return ( state.platform === "web" && (!state.isAuthInitComplete || !state.currentUser) ) } ``` - [ ] **Step 4: Run test to verify it passes** Run: `pnpm --dir packages/hoppscotch-common exec vitest --run src/helpers/__tests__/appLoginGate.spec.ts` Expected: PASS. ### Task 2: Web Login Gate UI **Files:** - Create: `packages/hoppscotch-selfhost-web/src/components/WebLoginGate.vue` - Create: `packages/hoppscotch-selfhost-web/src/services/webLoginGate.service.ts` - Modify: `packages/hoppscotch-selfhost-web/src/main.ts` - [ ] **Step 1: Create the root UI extension registration service** Create `packages/hoppscotch-selfhost-web/src/services/webLoginGate.service.ts`: ```ts import { Service } from "dioc" import { getService } from "@hoppscotch/common/modules/dioc" import { UIExtensionService } from "@hoppscotch/common/services/ui-extension.service" import WebLoginGate from "@app/components/WebLoginGate.vue" export class WebLoginGateService extends Service { public static readonly ID = "WEB_LOGIN_GATE_SERVICE" override onServiceInit() { getService(UIExtensionService).addRootUIExtension(WebLoginGate) } } ``` - [ ] **Step 2: Register the service for web only** In `packages/hoppscotch-selfhost-web/src/main.ts`, import the service: ```ts import { WebLoginGateService } from "@app/services/webLoginGate.service" ``` Then change: ```ts addedServices: [], ``` to: ```ts addedServices: platform === "web" ? [WebLoginGateService] : [], ``` - [ ] **Step 3: Implement the blocking component** Create `packages/hoppscotch-selfhost-web/src/components/WebLoginGate.vue`: ```vue ``` - [ ] **Step 4: Run targeted checks** Run: `pnpm --dir packages/hoppscotch-common exec vitest --run src/helpers/__tests__/appLoginGate.spec.ts` Expected: PASS. Run: `pnpm --dir packages/hoppscotch-selfhost-web run lint` Expected: PASS or only pre-existing unrelated failures. - [ ] **Step 5: Commit** ```bash git add docs/superpowers/plans/2026-05-06-require-web-login.md packages/hoppscotch-common/src/helpers/appLoginGate.ts packages/hoppscotch-common/src/helpers/__tests__/appLoginGate.spec.ts packages/hoppscotch-selfhost-web/src/components/WebLoginGate.vue packages/hoppscotch-selfhost-web/src/services/webLoginGate.service.ts packages/hoppscotch-selfhost-web/src/main.ts git commit -m "feat: require login for web app" ```