@clearlaunch/agent-auth
v0.1.1
Published
Let a ClearLaunch agent reach an auth-gated page in your app as a real authenticated user — single-use ticket, no long-lived credential, never in production.
Readme
@clearlaunch/agent-auth
Let a ClearLaunch agent — a code reviewer, or the pen-test runtime phase — reach an auth-gated page in your app as a real authenticated user.
No login form to automate, no OAuth popup, no long-lived credential, and nothing that can be switched on in production.
Why this exists
Runtime security checks have to act as a real user of your app. The highest-value ones are impossible otherwise:
| Check | Why it needs a session | |---|---| | IDOR / cross-tenant | Can user A read user B's records by changing an id? Needs two accounts with separate data. | | Privilege escalation | Can a low-privilege user act above their level — or raise their own role? | | Session handling | Does logout truly invalidate? Are cookie flags right? |
Driving a real login is not a workable substitute. Federated sign-in (Google, Microsoft, Apple) actively blocks headless browsers, and scripting a password login tests the login form rather than the authorisation you care about.
Why it is not an auth bypass
It mints a real session for a real account you provisioned. Your RBAC still applies, your session mechanism is still the one in use, and ClearLaunch never sets a cookie on your domain or creates a user.
- No global switch. Each grant is a single-use ticket with a 120-second TTL. There is nothing to leave enabled.
- A ticket, not a key. A leaked URL is inert after two minutes, and inert immediately from any other address.
- Refuses in production. Not a flag you should not set — there is no configuration that enables it there. The middleware throws at startup.
- Scoped to accounts you declared. A grant can only name a synthetic user
listed in your
.clearlaunchconfig. It cannot name a real one.
The honest caveat: a grant is exactly as privileged as the account behind it. Nothing here can protect you from a synthetic user with admin rights. Give the reviewer a read-only account, and the pen-test a sandbox tenant.
Install
npm install @clearlaunch/agent-authUse
import express from 'express';
import { agentAuth } from '@clearlaunch/agent-auth';
const app = express();
app.use(agentAuth({
apiUrl: 'https://api.clearlaunch.ai',
grantSecret: process.env.AGENT_GRANT_SECRET, // from your vault, never inline
// The only part you write. Establish YOUR session, exactly as a real login
// would — same cookie, same claims, same role.
async establishSession(req, res, user) {
req.session.userId = user.synthUserId;
req.session.role = user.synthUserRole;
},
}));That is the whole integration. The middleware exposes one route
(/__cl/agent-session by default), verifies the ticket server-to-server against
ClearLaunch, calls your establishSession, and redirects.
Get establishSession right and nothing else matters much. If the session it
produces differs from a real login's, every check afterwards tests that
difference rather than your app.
Options
| Option | Required | Meaning |
|---|---|---|
| apiUrl | yes | The ClearLaunch API that verifies tickets. |
| grantSecret | yes | Authenticates your app to ClearLaunch. Read from the environment. |
| establishSession | yes | Logs the verified user in, using your own session mechanism. |
| path | no | Route to expose. Default /__cl/agent-session. Must match webapp.sessionGrantUrl in .clearlaunch. |
| defaultRedirect | no | Where to land with no ?next. Default /. |
Declare it in ClearLaunch
cl init asks for this, or add it to .clearlaunch/config.json:
{
"webapp": {
"url": "https://dev.yourapp.com",
"sessionGrantUrl": "/__cl/agent-session",
"synthUsers": [
{ "id": "[email protected]", "role": "viewer" },
{ "id": "[email protected]", "role": "customer" }
],
"grantSecret": "AGENT_GRANT_SECRET"
}
}grantSecret is the name of a vault secret, never the value.
Provisioning the synthetic users
You create these in your own app. ClearLaunch never does.
- Use an unroutable domain (
.invalid) so they can never be emailed. - Give them no usable password — they exist only for key-based entry.
- Give each its own data. Without that, an IDOR check runs and finds nothing, which reads as "secure" when it means "untested".
- Keep them narrow. See the caveat above.
What it refuses to do
Each of these is a test in the suite, not just a claim:
- Run in production. Throws at startup. No environment variable overrides it.
- Trust a ticket locally. Verification is always server-to-server; the ticket is an opaque lookup key. A ticket that verifies itself is one an attacker can forge.
- Fail open. An unreachable verifier is a refusal. A network blip must not become an unauthenticated session.
- Redirect off-origin.
?nextaccepts same-origin paths only. An open redirect here would hand over a browser carrying the session cookie it was just issued. - Explain a failure. Every refusal returns one generic message, so nobody can probe which tickets exist. The reason is logged server-side.
- Redirect after a failed login. If
establishSessionthrows, you get a 500. Landing on the target page without a session looks like the grant worked and your app is broken — much harder to diagnose.
Removing it
Delete the app.use(...) line. There is no state to clean up, and no account
this package created.
