# Managed credentials

Save passwords, authenticator seeds, email inboxes, browser sessions and passkeys for Verify runs.

## Add a credential

Managed credentials keep what a run needs to sign in inside Composal, encrypted. Use them with any app. For a Better Auth app, [Better Auth sign-in](/docs/verify/credentials/better-auth) avoids storing anything for your app.

1. Open your project's **Credentials** and choose **Add Credential**.
2. Choose how agents sign in: **Sign in with a browser**, **Password**, **Email link / OTP** or **Session cookies**. **More sign-in methods** lists **Google**, **GitHub**, **Passkey** and **Custom**.
3. Give the credential a **Name** that says who it is, such as `Staging admin`, and fill in the method's fields below.
4. Optional: open the instructions section to add **When to Use**, such as `Use for admin and billing journeys`, and **Login Instructions**, such as `Choose the Acme workspace. Login is complete when the dashboard is visible.` Verify uses the first to choose between credentials and gives the second to the agent.
5. Choose **Save test account**.

## Password and authenticator

Choose **Password** for an email or username and password form. Enter the **Login page**, the **Email / Username** and the **Password**.

For authenticator-app 2FA, open **Instructions and two-factor authentication** and enter the **Authenticator Secret**: the Base32 setup key or `otpauth://` URI your app shows when you enroll an authenticator, not a one-time code. Agents generate a fresh code each time they sign in. Runs that share an authenticator run one at a time, so a code is never reused.

Push approvals, SMS codes and other provider challenges need a person. Use a [saved browser session](#browser-session) for those apps.

## Email link and one-time codes

Choose **Email link / OTP** for apps that sign in with magic links or emailed codes. Saving creates a dedicated Composal email address and shows it on the credential. Register or invite that address in your app before you run scenarios with it.

During a run, the agent requests a sign-in email and then reads that inbox to open the link or enter the code. It can read only messages that arrive after its run started, and only for the credential selected for that run. Scenarios that test signup need their own fresh inbox: assign one email-link credential to the signup persona for each signup you test.

## Google, GitHub and custom login

**Google** and **GitHub** describe your app's "Continue with Google" or "Continue with GitHub" button. They do not create a Composal OAuth integration. Enter a dedicated provider test account's email and password, and its authenticator setup key if it uses 2FA. Google may refuse sign-in from an automated browser. If it does, sign in yourself and save the app session instead; provider cookies are never accepted.

**Custom** is a password login with extra steps. Its **Login Instructions** are required; describe each step and how to recognize that sign-in succeeded.

## Saved browser sessions

A saved session lets runs start already signed in, after you complete sign-in yourself, including SSO, CAPTCHAs or push approvals. Composal saves only the app's cookies, local storage and IndexedDB for one origin. Sessions expire after 24 hours, or earlier if your app signs them out, so refresh them before you run.

To sign in from Composal:

1. Choose **Add Credential → Sign in with a browser**. Enter the **Account Name** and the app's HTTPS **App Login URL**, then choose **Start Login Browser**.
2. Choose **Open Login Browser** and sign in, including any MFA. The private browser stays open for 15 minutes.
3. Return to Composal, confirm **I’m signed in to the correct test account**, and choose **Save Browser Session**.

To paste cookies instead, choose **Session cookies**. Enter the **App URL** and paste one cookie per line as `name=value` or `name:"value"`. A copied `Cookie` header also works. Choose **Upload File Instead** to upload a session file saved by the CLI.

To capture a session from your own computer, use the CLI. It opens a fresh Chrome profile, waits for you to sign in, then uploads only that app's state:

```sh
com verify browser-session --org your-org --project your-project --account vaccount_example \
  --url https://app.example.com/login --local-login
```

Type `save` when the app shows the expected account. `--chrome-profile Default` imports from an existing Chrome profile instead, and `--login` opens a private cloud browser. Add `--skip-indexed-db` when your app's IndexedDB caches exceed the 1 MiB limit and authentication does not depend on them.

A session works only on the origin it was saved for. Runs against another origin, such as a pull request preview, fail preparation until you save a session for that origin.

## Passkeys

Choose **Passkey** under **More sign-in methods** to upload an enrolled software passkey's private JSON file. Enroll the passkey yourself in your app's security settings: agents can sign in with it but cannot enroll new passkeys. The upload is valid for 30 days and works only on the origin it was enrolled for. A passkey signs in one run at a time. Some providers reject software authenticators, so test it with one run first.

## Use credentials in runs

When you prepare a run, Verify picks each scenario's credential from the project, as described in [how runs use credentials](/docs/verify/credentials#how-runs-use-credentials). To pin a credential to a persona, choose it as that persona's **Credential**.

Scenario steps refer to credential fields by reference. When you edit a credential, **Agent credential references** lists keys such as `VERIFY_…_USERNAME` and `VERIFY_…_PASSWORD`. Use them as a step's `value_ref` in a scenario pack. Managed-environment blueprints must also declare the keys each persona may use. Only the keys a run requests reach it.

## Rotate, disable and remove

Organization administrators can edit a credential's guidance, rotate its password, and replace or remove its authenticator or browser session. Secret fields are write-only: leave them blank to keep the saved values. The username and guidance are visible metadata.

**Disable** blocks every value on the credential for future runs. It does not sign out a browser that is already signed in. Reconfiguring a disabled password credential requires a new password, and a disabled session or passkey requires a fresh file.

## API and CLI

A project's credentials are available at `/api/v1/organizations/{organization_id}/verification/projects/{project_id}/credentials`: GET lists them, POST creates one, PATCH `/{id}` edits or rotates it, and DELETE `/{id}` disables it. Writes accept the same fields as the form, plus `passkey`, `browser_session` or `cookies` for browser artifacts. Responses return metadata and credential references, never secret values. From the CLI, `com verify admin --help` lists the project credential commands; pass request bodies through a private `--body-file` or `--body-stdin`, never as arguments.

Next, see [Better Auth sign-in](/docs/verify/credentials/better-auth) for apps that can trust Composal directly.
