@comradeweb/browser-auth-kit
v1.0.0
Published
Auth-state monitor for headful browser automation: probes login state, pauses/resumes a BullMQ queue as auth is lost/restored, and emits state-change notifications through a pluggable port. Layers on @comradeweb/browser-service-kit.
Maintainers
Readme
browser-auth-kit
Auth-state supervisor for a long-lived, logged-in headful browser session.
Layers on @comradeweb/browser-service-kit: it probes
the operator's session on an interval, pauses a BullMQ queue when auth is
lost (so jobs don't retry into oblivion against a logged-out browser),
resumes it when the session recovers, and toggles ambient human-behaviour
simulation to keep the session warm.
Most browser-automation tooling stops at "we ran some steps." This kit deals with the operational reality of automating an authenticated session: the session expires, captchas appear, the operator has to reauthenticate — and during that window the job queue must be gated, not drained into failures.
What you get:
BrowserAuthModule—AuthMonitorService+ interval prober + BullMQ queue gate + ambient-simulation switch, wired from oneregister({ … })call.- Two narrow DI ports the host implements:
AuthStatePort(where the per-feature state record is stored) andAuthNotificationPort(a one-method surface for "auth state changed"). AuthProbe— the feature's own "are we still authenticated?" policy.AuthLostError— throw from any step to escalate mid-run; the processor pauses the queue and re-enqueues.
Installation
pnpm add @comradeweb/browser-auth-kit @comradeweb/browser-service-kit \
@nestjs/bullmq bullmq playwright@comradeweb/browser-service-kit, @nestjs/common, @nestjs/bullmq,
bullmq, and playwright are peer dependencies. The monitor injects
BrowserManager and AmbientHumanBehaviorService from
browser-service-kit, so that kit must be wired into the same DI container.
Quick start
A feature implements one policy — AuthProbe — and supplies two thin
adapters (storage + notification). Everything else is the module.
import type { Page } from 'playwright';
import {
BrowserAuthModule,
AUTH_PROBE,
AUTH_NOTIFICATION_PORT,
type AuthProbe,
type ProbeResult,
} from '@comradeweb/browser-auth-kit';
// 1. The feature's "are we logged in?" policy. Cheap, never throws.
class MyAuthProbe implements AuthProbe {
async probe(page: Page): Promise<ProbeResult> {
try {
await page.goto('https://app.example.com/account', {
waitUntil: 'domcontentloaded',
});
const loggedIn = await page.locator('[data-account-menu]').count();
return loggedIn
? { state: 'ok' }
: { state: 'lost', reason: 'no account menu' };
} catch (err) {
// Never leak into the interval loop — degrade to 'unknown'.
return { state: 'unknown', reason: (err as Error).message };
}
}
}
// 2. Opt the feature into supervision.
@Module({
imports: [
// BrowserServiceModule (from your composition layer) provides
// BrowserManager + AmbientHumanBehaviorService into DI first.
BrowserAuthModule.register({
queue: 'feature.scenario',
probe: { provide: AUTH_PROBE, useClass: MyAuthProbe },
notification: {
provide: AUTH_NOTIFICATION_PORT,
useClass: MyAuthNotificationAdapter, // host bridge — see below
},
intervals: { okMs: 5 * 60_000, fallbackMs: 30_000 },
imports: [
// Host supplies AUTH_STATE_PORT — typically a Mongo/Postgres adapter.
MyAuthStateStorageModule,
],
}),
],
})
export class FeatureModule {}What AuthMonitorService does, in one read
- Probes the operator's tab on an interval — fast cadence (
fallbackMs) when unhealthy, slow cadence (okMs) whenok. Probes share the operator page with running jobs viaBrowserManager.acquireOperatorPage()— no parallelpage.gotoclobbering. - Writes state through
AuthStatePort—recordProbe(feature, state, reason?)returns the previous state, so the monitor only fires side-effects on an actual transition. - Pauses / resumes the BullMQ queue on transition —
okresumes, anything else pauses. Idempotent: pause-when-paused is a no-op, so the monitor and a processor racing on the same signal don't fight. - Notifies via the narrow
AuthNotificationPort—notifyAuthStateChanged({ feature, state, reason?, changedAt }). The host wraps its broader notification bus behind this single-method adapter. - Drives ambient human-behaviour —
start()whileok,stop()otherwise. Keeps the session warm against idle-timeout heuristics without faking activity during a blocked state. - Coalesces concurrent
probeNow()calls — an operator hittingPOST /auth/probewhile the timer is mid-flight gets the same promise. No double-navigation.
Why a narrow AuthNotificationPort?
A typical NestJS app already has a wide NotificationPort (WebSocket
gateway, Slack relay, …) carrying a discriminated union of every event the
host emits. This kit emits exactly one of them. Coupling to that wide union
would make the kit awkward to publish standalone — every consumer would
inherit the whole event taxonomy. Instead the kit defines its own minimal
port:
export interface AuthNotificationPort {
notifyAuthStateChanged(payload: AuthStateChangedNotification): void;
}…and the host writes a tiny bridge:
@Injectable()
export class MyAuthNotificationAdapter implements AuthNotificationPort {
constructor(@Inject(NOTIFICATION_PORT) private inner: NotificationPort) {}
notifyAuthStateChanged(data: AuthStateChangedNotification) {
this.inner.notify({ type: 'auth-state-changed', data });
}
}The kit stays publishable; the host keeps one notification bus.
Concepts
| Type / Symbol | Purpose |
| --- | --- |
| BrowserAuthModule.register(opts) | Wires the monitor + queue gate + probe + notification adapter. |
| AuthMonitorService | Active prober, queue gatekeeper, ambient toggle. |
| AuthProbe / AUTH_PROBE | Feature policy: "are we still authenticated?". probe(page) → ProbeResult. |
| AuthStatePort / AUTH_STATE_PORT | Storage contract for the per-feature auth-state record. |
| AuthNotificationPort / AUTH_NOTIFICATION_PORT | Narrow notification surface — one method, one event. |
| AuthLostError | Throw from any step to escalate; processor pauses the queue and re-enqueues. |
| AuthStateValue | 'ok' \| 'lost' \| 'challenge' \| 'unknown'. |
| ProbeResult | { state: AuthStateValue, reason?: string }. |
| AuthStateChangedNotification | Payload handed to AuthNotificationPort. |
API reference
- Module —
BrowserAuthModule.register({ queue, probe, notification, intervals?, imports? }) - Service —
AuthMonitorService - Ports + tokens
AuthProbe,AUTH_PROBEAuthStatePort,AUTH_STATE_PORT,AuthStateRecord,AuthStateRecordProbeResultAuthNotificationPort,AUTH_NOTIFICATION_PORT,AuthStateChangedNotification
- Errors —
AuthLostError - Types —
AuthStateValue,ProbeResult,AuthMonitorIntervals
License
MIT © Vyacheslav D.
