> ## Documentation Index
> Fetch the complete documentation index at: https://libretto.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Website authentication

> Use credentials for scripted login, auth profiles for saved browser state, and `librettoAuthenticate` to recover when a saved session is missing or expired.

Libretto supports two website authentication tools:

* `credentials` are secrets such as usernames, passwords, API keys, and TOTP shared secrets. Use them when the workflow can log in with Playwright.
* `authProfile` is named browser state for a signed-in session. Use it when a workflow should start from an existing login or preserve login state across runs.

By default, Libretto-generated login workflows combine both: the workflow starts with the named auth profile, checks whether it is signed in, and signs in with credentials when the profile is missing, expired, or logged out.

```typescript theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { librettoAuthenticate, workflow } from "libretto";
import { z } from "zod";

export default workflow("portalReport", {
  credentials: ["username", "password"],
  authProfile: {
    name: "portal",
    refresh: true,
  },
  input: z.object({
    reportId: z.string(),
  }),
  output: z.object({
    title: z.string(),
  }),
  async handler(ctx, input) {
    const { page } = ctx;

    await page.goto("https://portal.example.com");

    await librettoAuthenticate(ctx, {
      credentials: input.credentials,
      isSignedIn: async ({ page }) =>
        await page
          .getByRole("button", { name: /account|sign out/i })
          .isVisible()
          .catch(() => false),
      signIn: async ({ page }, credentials) => {
        await page.goto("https://portal.example.com/login");
        await page.getByLabel("Email").fill(credentials.username);
        await page.getByLabel("Password").fill(credentials.password);
        await page.getByRole("button", { name: /log in/i }).click();
      },
    });

    await page.goto(`https://portal.example.com/reports/${input.reportId}`);
    const title = await page.locator("h1").innerText();
    return { title };
  },
});
```

In this pattern, the auth profile handles the common case where the browser is already signed in. The `signIn` step handles the recovery case where the profile is missing, expired, or logged out. If sign-in succeeds and `refresh: true` is set, the run saves the updated browser state back to the profile for future runs.

### Credentials

Credentials are separate from normal workflow input. Declare the names the workflow needs, then read the resolved values from `input.credentials`. Do not pass secrets through `--params`.

For local runs, store matching variables in `.env`:

```dotenv theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
LIBRETTO_CLOUD_USERNAME=alice@example.com
LIBRETTO_CLOUD_PASSWORD=secret
LIBRETTO_CLOUD_TOTP_SECRET=JBSWY3DPEHPK3PXP
```

Libretto removes the `LIBRETTO_CLOUD_` prefix and lowercases the rest, so `LIBRETTO_CLOUD_USERNAME` becomes `input.credentials.username`.

For hosted runs, store the same credential names in Libretto Cloud. See [Libretto Cloud credentials](/libretto-cloud-api/credentials).

<Warning>
  Never put secrets in workflow params, logs, or source code. Use declared credentials backed by `.env` locally and Libretto Cloud credentials in hosted runs.
</Warning>

### Auth profiles

An auth profile is named browser state for a login session. Local profiles live in `.libretto/profiles/<name>.json`; hosted profiles are provider-native browser profiles managed by Libretto Cloud and the active browser provider.

Use the workflow-level `authProfile` option:

```typescript theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
authProfile: "portal"

authProfile: {
  name: "portal",
  refresh: true,
}
```

Use `{ name, refresh: true }` when successful runs should persist updated browser state back to the profile. On local runs, this updates the local `.libretto/profiles/<name>.json` file. On hosted runs, it asks the browser provider to persist changes to the provider-native profile.

The preferred way to create a local profile is to sign in through a headed Libretto session and save it:

```bash theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx libretto open https://portal.example.com --headed --session portal-login
# Log in manually in the browser window.
npx libretto save portal --session portal-login --sites portal.example.com
```

Use this when the login cannot be scripted with Playwright, such as CAPTCHAs, SMS codes, device approvals, or magic links. Once saved, the workflow can use `authProfile: "portal"` or `authProfile: { name: "portal", refresh: true }` without repeating the manual login every run.

If you already have a signed-in Chrome profile and explicitly want to copy from it, start that Chrome profile with remote debugging enabled and import only the sites you need:

```bash theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx libretto import-chrome-profiles portal --cdp-url http://127.0.0.1:9222 --sites portal.example.com
```

Chrome often needs to be closed and relaunched with a temporary user-data directory before remote debugging can attach. Importing from Chrome can close or relaunch that browser window, so confirm with the user before doing it.

Use `--sites` only when saving or importing a local profile. It controls which site storage is copied into the local profile. See [CLI profiles](/reference/cli/profiles).

### Hosted website authentication

Hosted runs do not upload local `.libretto/profiles/<name>.json` files. They use provider-native profiles with the workflow's `authProfile.name`. `libretto cloud deploy` registers missing profile names, and hosted runs populate or refresh the provider profile when sign-in succeeds.

For the hosted model, provider behavior, and profile persistence details, see [Website authentication in Libretto Cloud](/libretto-cloud-hosting/website-authentication).

### Runtime helper

Use [`librettoAuthenticate`](/reference/runtime/authentication) when a workflow has an auth profile and scripted sign-in. The helper runs your `isSignedIn` check, calls your `signIn` function if needed, and checks again before the workflow continues.
