Run a dynamic scan against a typical SaaS product without credentials and you will test the login page, the password reset flow, the marketing pages and a 401 handler. That is perhaps 5% of the application and none of the parts that hold data.
Authenticated scanning is where DAST becomes worth running. It is also where it becomes capable of doing damage, and the two facts are the same fact.
Getting in
Recorded login sequences work everywhere and break constantly. You record the steps once; they survive until someone changes a selector. Prefer stable attributes — data-testid, name, id — over CSS paths, and treat a broken login as a scan failure rather than falling through to an unauthenticated crawl. A scanner that silently degrades to anonymous is reporting on 5% of the app while looking like it covered all of it.
Direct token injection is more robust when available: obtain a session token or API key out of band and inject it as a header or cookie. No selectors to break. The tradeoff is that you skip the login flow, so it never gets tested, and you have to source the token from somewhere the scanner can reach without storing a password.
Client credentials against your own IdP is the cleanest option for API scanning: a dedicated service account, a token endpoint, a scope narrower than a real user's.
Whatever the mechanism, the scan account should be purpose-built — a real user in a non-production tenant, with realistic but not administrative permissions. Scanning as a superuser tests paths no attacker can reach and skips the authorisation bugs that matter, which are precisely the ones about a normal user reaching something they should not.
Staying in
Getting a session is the easy half. Keeping one through an automated crawl that is deliberately clicking everything is where most scans quietly fail.
Exclude the logout route. Obvious, universally forgotten. The crawler finds /logout, follows it, and every subsequent request is anonymous. The findings that follow are all "unauthenticated access to protected page", which are false, and the real findings are all missing. Exclude by URL pattern and by link text, because logout is often a POST from a menu rather than a link.
Detect logged-out state and re-authenticate. Define a signal — a redirect to the login page, a specific status, an element present only when authenticated — check it periodically, and re-run the login sequence when it trips. Sessions expire, and a long scan will outlive one.
Do not share one session across concurrent workers. Many applications rotate tokens on use, or bind a session to a single client. Parallel workers on one session produce phantom auth failures that look like findings. Give each worker its own session, or serialise.
Handle CSRF properly. If the app issues per-request tokens, the scanner has to read the token from the form and submit it, or every state-changing request fails identically and the app appears to have no attack surface at all.
Not wrecking the app
An authenticated crawler is a user with permissions clicking every control on every page. Assume it will find the destructive ones.
Scope guard first. Enforce the allowed host list in code, on every request, before it leaves the client — not as a config option a misconfiguration can bypass. A scanner that follows an off-scope link is attacking a third party from your infrastructure, which is a legal problem rather than a bug.
Verify ownership before scanning at all. Target verification — DNS record, file at a well-known path, platform-level ownership — should be a precondition the engine checks, not a checkbox the user ticks. The check belongs next to the code that starts the scan.
Refuse destructive controls by default. Buttons and links whose text or target matches delete, remove, revoke, deactivate, cancel, purge, reset. Yes, this means those paths go untested. That is the right default; the alternative is a scanner that empties a staging environment on its first run and is never permitted a second.
Never scan production with a real user's credentials. Use a dedicated account in a dedicated tenant. If production is genuinely the only environment, run read-only: crawl, no active checks, no form submission.
Rate limit deliberately. A scanner is a load test nobody asked for. Concurrency and delay settings exist to keep the target alive, and the default should be polite rather than fast.
Knowing it worked
The single most common failure is a scan that authenticated, lost the session early, and completed happily. Every subsequent request was anonymous, so nothing behind the login was tested — and the report looks normal.
Make the scan prove it stayed in:
- record how many requests were made while authenticated versus not
- assert that at least one known post-login URL returned authenticated content
- fail the scan, loudly, if the authenticated request ratio falls below a threshold
Then put those numbers in the report. "Authenticated for 96% of 1,847 requests" is a coverage statement. A report without one is an unverified claim that the most important part of the scan happened at all.