Skip to main content
Vigolium supports multi-session authenticated scanning via the --auth and --auth-file flags. This enables scanning behind login walls and detecting authorization bypass vulnerabilities (IDOR/BOLA).
The --auth / --auth-file flags were previously named --session / --session-file, and a single bundle file holding multiple sessions used the separate --auth-config flag. The old names still work as deprecated aliases (--session → --auth, --session-file → --auth-file), and --auth-file now also accepts the multi-session sessions: bundle that used to require --auth-config. Update your commands to the new names.
Session config files can be written in YAML or JSON, the format is auto-detected by file extension (.json) or by content sniffing (leading { or [).
Separate from configured sessions, the browser crawler can also try a short list of common default credentials (admin:admin, …) against a confirmed local login form during spidering, so the crawl reaches authenticated areas without an --auth session. It’s off by default and auto-enabled at balanced/deep intensity, negative-control gated, and never a brute-force. See Default-credential login attempts.
When --spider runs, any session the browser establishes — cookies from a cleared WAF/bot challenge plus its User-Agent — is carried forward into the discovery and scanning phases (per host), so later probes reuse the cleared session. It’s on by default; disable with --no-carry-browser-session, and your own -H/--header overrides always win. See Session carry-forward.

Quick Start

Authentication Flags

Both flags can be combined. If no session is explicitly marked as primary, the first session loaded is used as the primary.

Session Roles

Each session has a role that determines how it is used during the scan:
  • primary: The main session. Used for discovery, spidering, and as the default requester during the audit phase. There should be exactly one primary session.
  • compare: Comparison sessions for IDOR/BOLA testing. During the audit phase, every request made by the primary session is replayed with each compare session’s credentials. If a compare session can access resources it shouldn’t, the authz-compare module flags it.

Inline Sessions

The --auth flag accepts sessions in name:Header:value format:
Values containing colons are handled correctly, only the first two colons are used as delimiters.

Session Files

For sessions with multiple headers or login flows, use files. Both YAML and JSON formats are supported.

Static Headers

YAML:
JSON:
Use with:
Session files are resolved from the configured session_dir (default ~/.vigolium/sessions/) if the path is not absolute. See Session Strategy Configuration below.

Login Flows

Session files can define automated login flows. The scanner executes the login request at scan start and extracts credentials from the response. YAML:
JSON:

Login Request Headers

Added in v0.5.3. login.headers adds request headers to every request the login sends — the single request, each step of a multi-step flow, or the OAuth2 token request. A step’s own headers are applied after the flow’s, so a step header wins on a clash. Values take {name} step variables and the templates below, exactly like url and body; a Host header sets the request’s Host.

Templates: environment variables and TOTP

Added in v0.5.3. Login url, body, header values and the OAuth2 fields accept function templates, evaluated every time the login runs — including an in-scan re-login — so a TOTP code is always current and a secret never has to live in the file.
This is what makes MFA-gated targets scannable without a human in the loop. A variable that is not set, an unreadable secret file or an invalid secret fails the login and names the template — nothing is ever sent with an empty value.
Templates expand before {name} step variables, so a value the server returned is never itself treated as a template.vigolium auth lint reports an unknown template function or malformed name as an error, and warns when {{totp:…}} holds what looks like a literal base32 secret rather than the name of a variable — the easy mistake. vigolium auth totp prints the code a template would produce, so you can check it against your authenticator before a scan depends on it.The older ${NAME} syntax still works; it is expanded once, when the file is loaded, which is why it cannot carry a rotating code.

OAuth2 token endpoints

Added in v0.5.3. type: oauth2_client_credentials and type: oauth2_refresh_token log in against an OAuth2 token endpoint instead of a login form.
The access token is sent as Authorization: <token_type> <access_token> (Bearer for a bearer token).
The token is refreshed before it expires, not after a 401. The response’s expires_in becomes the session’s expiry, and during a native scan the token is renewed shortly ahead of it — a tenth of its lifetime, between 5 s and 60 s, at most half of it — without waiting for a rejection and without holding scan traffic, even when the session has no check block. A refresh token the server rotates replaces the configured one for the next refresh.auth load -j reports the expiry as expires_at. A token-endpoint error (invalid_client, invalid_grant, …) fails the login with that error. auth lint checks the required fields, the token_url scheme and auth_style, and warns about request-flow fields (url, method, body, extract, …) that an OAuth2 login ignores.

Extraction Sources

The apply_as field defines how the extracted value is applied as a request header. Use {value} as a placeholder. apply_as: "var:<name>" instead stores the value as a step variable for a later step to reference as {<name>}.

Multi-Session Bundle File

A bundle file defines all sessions in one place under a sessions key, and is loaded with --auth-file (this is what the removed --auth-config flag used to do).

YAML Format

JSON Format

Use with:

When to Use JSON

JSON is a good choice when:
  • AI agents generate session configs: most LLMs produce cleaner JSON than YAML, and agent modes (swarm, autopilot) already output session config as JSON natively.
  • Programmatic generation: scripts, CI pipelines, or tools that build session configs are often simpler in JSON.
  • Embedding in other JSON payloads: e.g., the REST API POST /api/agent/run/swarm body includes session config as a nested JSON object.
YAML remains convenient for hand-written configs where comments and multi-line strings help readability.

Format Detection

The format is detected automatically:
  1. File extension: .json files are parsed as JSON; .yaml / .yml as YAML.
  2. Content sniffing: if the extension is ambiguous (or missing), content starting with { or [ (after whitespace trimming) is parsed as JSON.
  3. Fallback: everything else is parsed as YAML.
This means extensionless files work too, pipe JSON directly and it will be detected:

Session Config Schema Reference

Both YAML and JSON use the same field names. Here is the full schema:
Only one of headers, login, or login_request can be set per session. check and reauth were added in v0.5.3; scanning_strategy.session.validate_url, reauth_on_status and reauth_interval fill check.url, check.logged_out.status and check.interval for every session that leaves them unset.

Logged-in check and in-scan re-login

A session with a check block is kept live for the whole scan (v0.5.3). This matters because a long scan routinely outlives its own session, and every request after that point tests the logged-out surface while reporting nothing wrong.
  1. Sampling. Every response received with the session’s credentials is matched against logged_out: status and redirect_regex (against Location) on all of them, body_regex on one in sample_every (first 64 KiB). Cached responses are not fresh evidence and are not sampled.
  2. Confirming. A match marks the session suspect and emits auth.session_suspect. With a check.url, one probe goes out with the current credentials — if it is still logged in, the session stays valid, which is the common case of a module that provoked a 401 on purpose. Without a check.url, three matches within 10 s confirm the logout.
  3. Re-login. The login flow runs again on a copy of the session, through the same login transport and the egress gate’s priority lane, so it never queues behind the scan’s own traffic. Up to reauth.max_attempts, waiting backoff after the first failure and doubling it. Success publishes the new credentials as the next generation and emits auth.relogin with ok: true.
  4. One login per expiry. However many workers see the 401 at once, only the first confirms and re-logs-in. A response to an older generation’s credentials is never evidence against the new ones.
  5. Pausing. With pause_traffic: true (the default) this session’s requests wait from the first logged-out match through the re-login — at most the 2-minute login budget — and leave with the new credentials. Only requests already in flight come back logged out.
  6. Proactive probe. check.interval re-runs the probe at most that often even when nothing looked logged out.
  7. Expiry. When the login reported when its credentials expire (an OAuth2 expires_in), the session is kept live even without a check block, and renews shortly before that time — no confirm probe, no paused traffic, since the old token is still good.
When every re-login attempt fails, the session is lost and the scan says so. auth.relogin reports ok: false; the running phase and every later phase that sends with that session are recorded partial with reason auth_lost; and the scan carries on unauthenticated rather than pretending otherwise. With reauth.on_failure: abort the scan stops instead — the remaining phases are skipped with auth_lost and the command exits 1 with error.code: "login_failed".Responses received while the session was logged out are reported as the responses_while_logged_out limit on the phase, with the count under counts.responses_while_logged_out. They are not re-queued, so treat that number as the size of the hole in your coverage.
--require-auth (or spidering.require_auth, v0.5.2) makes a failed initial login fatal for that target, rather than silently scanning it anonymously. Credential-stripped requests — the authorization-differential modules’ anonymous replays — are never sampled, so their deliberate 401s cannot trigger a re-login. A header you set explicitly with -H is never taken over by a session.All login flows together are bounded by a 2-minute budget (v0.5.2), and login flows now honour --proxy and the scan’s TLS stance like every other request — they used to ignore both.

Managing Sessions with the auth Command

The --auth/--auth-file flags load sessions for a single scan. The vigolium auth subcommand manages reusable, persisted sessions and provides helpers for validating configs and automating 2FA.

List stored sessions

Each row shows hostname, session name, role (primary/compare), position, a token preview, and extract rules. Sessions are scoped to the active project.

Load sessions into the database

auth load persists sessions so subsequent scans pick them up automatically. It accepts a native config file, an agent-produced session-config.json, stdin, or even a raw HTTP login request (which it sends, then auto-discovers tokens from the JSON body, Set-Cookie, and auth headers):
By default, load executes each login flow once to verify the credentials work; pass --no-validate to skip. Agent format is auto-detected when the path contains agent-sessions/ (or force it with --agent-format). The hostname is derived from the first login URL when --host is omitted. --force stores a session whose login failed anyway, and --timeout bounds the round-trip.
auth load exits 1 on a failed login as of v0.5.3, and runs the full scan login flow. It used to validate only part of a multi-step flow, ignore expect conditions and --proxy, and then save the failed session anyway — so a broken credential set looked loaded and every scan after it ran unauthenticated against a target that looked clean.Under -j each session reports its own login result, so a bundle with one bad session says which.

Lint a session config

Catch mistakes before a scan — missing/invalid fields, bad extract rules, unknown login or extract types, multiple primary sessions, or duplicate names:
Since v0.5.3 it takes multiple files, reports unknown fields with line and column, and supports -j (one item per file, with each issue’s severity, line, column, message and rule; ok is false exactly when there are errors). auth load, auth lint and --auth-file share one session-file parser, so a file that lints clean is a file the scan can load.

Check that a session really authenticates

Added in v0.5.3, and the answer to the most expensive failure mode in authenticated scanning: a session that loads, lints and looks fine, but is not actually logged in — so the whole scan crawls the public surface and reports a small, clean result. It logs each session in exactly as a scan does (the scan’s login transport: --proxy, the scan’s TLS stance, --timeout), then requests the URL twice through the scan’s HTTP stack — once with the session’s credentials and once without — following no redirects. The two responses decide the verdict: The URL is --url, else check.url, else validate_url; a path resolves against the login URL’s origin. It exits 0 only when every checked session is authenticated, otherwise 1 with error.code login_failed (the login itself did not work) or not_authenticated (any other verdict) — so it drops straight into CI ahead of the scan. -j gives one item per session: session, url, method, verdict, authenticated, reason, signals (name, side, detail), similarity, auth_response and anon_response (status, length, location, error), and login.
The database has no columns for check / reauth, so a stored session is checked with --url (or validate_url) and the heuristics alone.

Generate a TOTP code for 2FA

For automating two-factor login flows, auth totp turns a base32 secret into the current code:
auth totp prints the bare code as of v0.5.3; JSON is behind -j. A script doing vigolium auth totp --secret "$S" | jq -r .code now gets a jq parse error. Drop the jq, or add -j.
--secret-env, --secret-file and --secret - were added in the same release so the secret never appears in ps output or shell history. In a session file, prefer the {{totp:NAME}} template over shelling out at all — it re-derives the code on every re-login, which a value captured once cannot do.

Environment Variables

Session files (both YAML and JSON) support ${VAR} syntax for secrets. This keeps credentials out of config files:
All ${VAR} references are expanded from the environment at load time, before format parsing.

IDOR/BOLA Testing

To test for authorization bypass vulnerabilities, define at least two sessions, one primary and one or more compare sessions. YAML:
JSON:
The built-in authz-compare module automatically activates when compare sessions are present. It replays primary session requests with compare session credentials and flags responses that indicate broken access control.

How Detection Works

  1. The primary session makes a request and gets a response (e.g., GET /api/users/42 -> 200 OK with user data).
  2. The same request is replayed with each compare session’s credentials.
  3. If a compare session also receives a successful response with similar content, the module reports a potential IDOR/BOLA finding with High severity.

Filtering to Auth Modules Only

To run only authorization testing without other active modules:

How Sessions Affect Scan Phases

Session Strategy Configuration

Session behavior is configured under scanning_strategy.session in vigolium-configs.yaml (see public/vigolium-configs.example.yaml for the full annotated example).

Field Reference

Session Directory Resolution

When --auth-file receives a bare name (no path separators), the scanner resolves it from session_dir. Extensions are tried in order: .yaml, .yml, .json.
If the bare name has no matching file with any extension, .yaml is appended as the default. Absolute paths and relative paths with directory separators (e.g. ./sessions/myapp.json) bypass session_dir and are used as-is. To change the lookup directory:

Common Patterns

Unauthenticated discovery, authenticated scanning:
Crawls the public-facing site first, then applies session headers only during the audit phase. This is useful when you want to see what an unauthenticated attacker can discover before testing the authenticated surface. Authenticated scanning without IDOR testing:
Useful when you only need to scan behind a login wall but don’t have multiple user roles to compare. The primary session’s credentials are applied to all phases, but no compare requesters are created and the authz-compare module stays inactive. Long-running scan with token refresh:
Re-executes login flows every 30 minutes proactively, and also reactively when a 401 or 403 is received. The validate_url confirms credentials work after each login before resuming scanning. Team shared sessions directory:
Point all team members to a shared directory so --auth-file staging-admin resolves the same file for everyone. Scanning profiles (~/.vigolium/profiles/) can also override session strategy values, useful for having a “quick unauthenticated” profile alongside a “deep authenticated” profile.

Using Session Config with Agent Modes

Agent modes (swarm, autopilot) can auto-generate session configs from source code analysis. The generated configs are always written as JSON to the session directory. When running agent swarm with --source, the source-analysis phase discovers authentication flows in the codebase and produces a session-config.json and auth-config.yaml in the session directory. This config is then fed into subsequent scan phases automatically. Since v0.5.3 both agent swarm and agent autopilot take --auth-file, so a session bundle you already lint, check and version-control drives an agentic run exactly as it drives a native scan — no prompt engineering, no credentials in a transcript. Swarm additionally accepts a pre-built bundle via its own --auth-config flag (it skips browser/CLI auth synthesis and uses the file verbatim). You can still put credentials, roles, login URL and browser requirements in --prompt (or the positional prompt) for either command; browser tooling is always available and the intent parser extracts the auth signals:
The REST agent endpoints still accept structured credentials, credential_sets, auth_required, requires_browser, browser_start_url, and focus_routes fields. Their removal applies to the autopilot/swarm CLI flags only.

Examples

Scan a REST API with Bearer Token

Full IDOR Test with Login Automation (YAML)

Full IDOR Test with Login Automation (JSON)

Combine with Other Scan Options

Auth flags work with all other scan options:

One-Liner JSON Auth Config

For quick testing or CI scripts, you can write a JSON config inline:

Agent-Generated Session Config

When an AI agent discovers auth flows in source code, it produces JSON like:
This can be saved and reused across scans: