#453·shannon

[FEATURE]: Token-based auth for API-only, non-browser targets (OAuth client_credentials / bearer)

Author: ysichrisdagCreated Sep 8, 2026Updated Sep 8, 2026

Problem

Shannon's authentication is entirely browser-session based. The validate-authentication preflight drives Playwright through the login flow and saves a Playwright storageState (cookies + origins) to auth-state.json; downstream agents restore it with playwright-cli state-load. login_type accepts api and basic, but prompts/shared/login-instructions.txt only defines FORM and SSO sections, so those two enum values produce empty login instructions and no behavior.

That leaves a gap for a common real-world target: an API with no browser login, protected by an OAuth bearer token obtained from a token endpoint (client_credentials, resource-owner password, or refresh_token grant). For these targets there is no form to fill and no cookie session to capture. The token must be:

  1. fetched from a token endpoint over HTTP (no browser),
  2. attached as Authorization: Bearer <token> to the requests the recon/exploit agents make (they issue raw curl in bash), and
  3. refreshed when it expires — a scan runs ~1–1.5 h while access tokens often live 5–60 min, so a single up-front fetch goes stale mid-run.

A Playwright storageState file cannot express "add this header to curl," so the existing preflight and shared-session artifact do not cover this case.

Proposed solution

Mirror the existing generate-totp helper pattern with a get-oauth-token CLI baked into the worker container. It reads the OAuth config, calls the token endpoint, caches the token with its expiry, and prints a currently-valid access token (refreshing transparently when near expiry). Agents call it inline exactly as they already call generate-totp:

curl -H "Authorization: Bearer $(get-oauth-token)" https://api.target/endpoint

Sketch of the required pieces:

  1. Schema (configs/config-schema.json, types/config.ts): a token-based auth variant carrying token_url, grant_type (client_credentials | password | refresh_token), client_id, client_secret, scope, audience, and optional token_header / token_prefix. Make login_url / success_condition optional for this variant, or add a token-oriented success check (e.g. token endpoint returns 200 with access_token).
  2. Helper CLI get-oauth-token under apps/worker/src/scripts/, symlinked into PATH in the Dockerfile next to generate-totp.
  3. Preflight: a token-acquisition branch analogous to runAuthenticationValidation that fetches once and fails the run early with a classified error if the grant fails; skips the Playwright state-save.
  4. Prompts: an OAUTH/API section in login-instructions.txt and a token block in _shared-session.txt telling agents to use $(get-oauth-token) instead of restoring a browser session; guard the browser state-load behind the browser auth types.
  5. Secret handling: pass client_secret via env/file rather than prompt text (the same concern raised in #264), and run token_url through the existing login-URL safety checks.