# Codex can now log in to websites with 1Password

Published: 2026-08-09T00:00:00.000Z
Canonical: https://wow.pjh.is/journal/login-to-websites-with-codex-and-1password

I've made a 1Password vault with credentials I'm happy for my agents to use, and given them access via a [service account](https://developer.1password.com/docs/service-accounts/) and the [CLI](https://developer.1password.com/docs/cli/).

With that, Codex will happily log in to websites on my behalf. Claude, by contrast, refuses to log in to anything due to guardrails.

Codex and Claude have a dedicated Chrome profile. This means Codex can access services I've put in the vault, but not other websites I happen to be logged into on my personal profile (unless it ignores my instructions and switches to Computer Use 😅).

**One to watch:** 1Password are working on an [agentic autofill](https://www.1password.dev/agentic-autofill) feature. Right now it's only available for [Browserbase](https://www.browserbase.com/agents).

## Implementation details

Disclaimer: I only set this up yesterday. I've tested it with 5 different websites, most of which include a TOTP step. I expect this implementation will need some refinement.

My `AGENTS.md` contains:

**Prompt**

```prompt
- **Chrome profile:** use only `Peter (5. Agents)`—directory `Profile 8`, extension profile ID `95e5cb11-2782-4f8b-80ee-f9857cefa562`. Never use or fall back to another Chrome profile, including through Computer Use.

**Website logins:** if a website requires sign-in to complete Peter's task, search the `Agent logins` 1Password vault with `op` and sign in without asking. If no matching login is accessible, ask Peter to move it there; do not bypass this restriction through another browser, profile, or tool.

  - For credentials or OTPs, follow the secret-safe browser-login procedure in `/Users/ph/.agents/references/agent-clis.md`. Validate the target fields and submit control before retrieving secrets, then fill and submit without an intervening page-state read.
  - During login, select any available option to remember the session or trust the device.
```

And my `agent-clis.md` reference specifies a safe procedure:

**Prompt**

````prompt
## op (1Password CLI)

[...]

### Safe patterns

- **Discover items using metadata only:** `op item list --categories <category> --vault <vault-id> --format json`

- **Pass a secret to a command:** put a secret reference in an environment variable and let `op run` resolve it only for the child process:

  ```sh
  API_KEY='op://<vault-id>/<item-id>/<field>' op run -- <command>
  ```

  Use a command that does not print the secret or its environment. Never pass `--no-masking`.

- **Enter secrets in a browser:** follow the procedure below.

- **Never expose secrets through agent tooling:** do not run `op read` or a secret-bearing `op item get` as a standalone tool call, interpolate its output into a tool-call argument, or use the clipboard or a regular temporary file.

### Browser logins in Codex

1. Find the exact login using metadata only. Record the item ID, vault ID, and the item's saved URLs; do not fetch fields yet:

   ```sh
   op item list --categories Login --vault 7gnoy3xakiss55tevxazktcxou --format json |
     jq -r '.[] | [.id, .title, .vault.id, ((.urls // []) | map(.href) | join(" "))] | @tsv'
   ```

2. **Origin check (mandatory):** before filling anything, confirm the registrable domain of the tab's current origin matches one of the item's saved URLs from step 1. On mismatch, abort—do not fill, and do not switch to a different item to make it match. Re-run the check after any navigation or redirect before a fill, including the 2FA page.

3. Before fetching secrets, locate and uniquely validate the required fields and submit control. Keep those handles for submission. If the site splits username and password across pages, repeat steps 3–6 with a fresh FIFO and fetch only the field needed on each page.

4. In one yielded agent-shell command, create a protected FIFO and start the secret writer under a 120-second watchdog. The command prints only the temporary directory path; the writer blocks until the browser-side reader connects; if abandoned, the watchdog kills the writer and trashes the directory so the FIFO cannot deliver credentials to a later reader:

   ```sh
   login_item_id='ITEM_ID_FROM_STEP_1'
   login_vault_id='VAULT_ID_FROM_STEP_1'
   login_fields='label=username,label=password' # Use one label for a split login.
   login_pipe_dir=$(mktemp -d /tmp/op-browser-login.XXXXXX)
   chmod 700 "$login_pipe_dir"
   mkfifo "$login_pipe_dir/secret"
   chmod 600 "$login_pipe_dir/secret"
   printf '%s\n' "$login_pipe_dir"
   /opt/homebrew/bin/op item get "$login_item_id" \
     --vault "$login_vault_id" \
     --fields "$login_fields" \
     --format json > "$login_pipe_dir/secret" &
   login_writer_pid=$!
   ( sleep 120; kill "$login_writer_pid"; trash "$login_pipe_dir" ) 2>/dev/null &
   login_watchdog_pid=$!
   wait "$login_writer_pid"
   login_status=$?
   kill "$login_watchdog_pid" 2>/dev/null
   exit "$login_status"
   ```

   If the watchdog fires before the step-5 read completes, start again from step 4 with a fresh FIFO.

5. In the same Node session that controls the form, read `<printed-directory>/secret` once with `node:fs/promises`, parse the top-level JSON field array, select values by field label rather than array order, and fill the pre-validated controls without emitting the values. Submit immediately with the pre-validated login control—do not take a DOM snapshot, AX/app-state dump, screenshot, field read, or other page-state read between filling and submission. If the read or parse throws, report a sanitised error only—never the buffer contents.

6. Confirm that the yielded writer exited 0, then run `trash "<printed-directory>"`. On any error or interruption, terminate or poll the writer as needed and trash the directory. Before retrying, inspect the page because the previous attempt may already have submitted.

7. For 2FA, first re-run the origin check (step 2), then locate and uniquely validate the OTP field and confirm control. Repeat steps 4–6 with a fresh FIFO, replacing the `op item get` invocation in step 4 with:

   ```sh
   /opt/homebrew/bin/op item get "$login_item_id" \
     --vault "$login_vault_id" \
     --otp > "$login_pipe_dir/secret"
   ```

   Keep the backgrounding, watchdog, and `wait` scaffolding around it unchanged. Trim the FIFO value before filling the OTP field, then confirm immediately without an intervening page-state read.

If retrieval fails, use metadata-only diagnostics such as `op --version` and `op item list ... | jq 'length'`. Never print a secret to test access, spawn `op` from the browser's Node process, switch authentication methods, use the clipboard, or broaden vault access.
````
