Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 34 additions & 31 deletions vaults/1password.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ KERNEL-hosted collection works for any user and is the fallback. create a `crede
brokered approval uses 1Password's
[Agentic Autofill](https://www.1password.dev/agentic-autofill/partners). the user
reviews each access request in the 1Password app on their own device and chooses
which logins to grant. the 1Password browser extension, which KERNEL takes care of loading into
the vault-attached browser, then fills the granted login into the page. credential values are never returned by KERNEL’s api or passed through your application or model context. check 1Password's
which logins to grant. KERNEL creates the access request and reads its status from
1Password directly, so no browser is involved until you fill. the 1Password browser
extension, which KERNEL takes care of loading into the vault-attached browser, then fills
the granted login into the page. credential values are never returned by KERNEL’s api or passed through your application or model context. check 1Password's
[supported accounts, vaults, and apps](https://www.1password.dev/agentic-autofill/partners#supported-today)
before you plan a rollout.

Expand Down Expand Up @@ -89,8 +91,8 @@ the login values. then:

1. create a `credential_account` item to link the user's 1Password account. see [connect a 1Password account](#connect-a-1password-account).
2. create a `credential` item with `account` set to that item's key to describe the login you need. see [define a login request](#define-a-login-request).
3. in a vault-attached browser, invoke `1pw_create_access_request`, give the user the approval link, and poll `1pw_access_request_status`. see [request access and hand off approval](#request-access-and-hand-off-approval).
4. once the user approves, invoke `1pw_fill` on that item in a browser session. see [fill and submit](#fill-and-submit).
3. invoke `1pw_create_access_request`, give the user the approval link, and poll `1pw_access_request_status`. neither needs a browser. see [request access and hand off approval](#request-access-and-hand-off-approval).
4. once the user approves, create a vault-attached browser and invoke `1pw_fill` on that item. see [fill and submit](#fill-and-submit).

#### Your own OAuth client (advanced)

Expand All @@ -105,8 +107,8 @@ then:

1. create a `credential` item with the user's `access_token` and `integration_key` to describe the login you need. see [use your own oauth client](#use-your-own-oauth-client).
2. before each task, refresh the access token on your backend and send it with `1pw_update_access_token`. see [replace a stored access token](#replace-a-stored-access-token).
3. in a vault-attached browser, invoke `1pw_create_access_request`, give the user the approval link, and poll `1pw_access_request_status`. see [request access and hand off approval](#request-access-and-hand-off-approval).
4. once the user approves, invoke `1pw_fill` on that item in a browser session. see [fill and submit](#fill-and-submit).
3. invoke `1pw_create_access_request`, give the user the approval link, and poll `1pw_access_request_status`. neither needs a browser. see [request access and hand off approval](#request-access-and-hand-off-approval).
4. once the user approves, create a vault-attached browser and invoke `1pw_fill` on that item. see [fill and submit](#fill-and-submit).

on either path, if a step doesn't produce a usable login,
[fall back to KERNEL-hosted collection](#fall-back-to-kernel-hosted-collection).
Expand Down Expand Up @@ -136,15 +138,16 @@ sequenceDiagram
App->>OP: exchange code for tokens
App->>K: upsert credential (access_token, integration_key)
end
App->>K: 1pw_create_access_request (browser_id)
K->>B: load extension on demand, create request
App->>K: 1pw_create_access_request
K->>OP: create access request
K-->>App: 1password_access_approval action
App->>U: present approval link
U->>OP: approve in the 1Password app
App->>K: 1pw_access_request_status (browser_id)
App->>K: 1pw_access_request_status
K->>OP: read request status
K-->>App: state ready
App->>K: 1pw_fill (browser_id, page_url, entry_id)
K->>B: extension fills and submits
K->>B: load extension on demand, then fill and submit
K-->>App: fill_submitted, fill_failed, or fill_unknown
```

Expand Down Expand Up @@ -471,22 +474,17 @@ kernel.vaults.items.upsert(

### Request access and hand off approval

create a browser with the vault attached, then invoke
`1pw_create_access_request` with its `browser_id`. you don't install anything:
KERNEL loads the 1Password extension into that browser on demand the first time
an operation needs it, then creates the access request through the extension.
`goal`, `reason`, and `keywords` in the operation override the item's values
for this request.
invoke `1pw_create_access_request` on the item. KERNEL creates the access
request with 1Password directly, so you don't need a browser yet; you create
one when you're ready to [fill](#fill-and-submit). `goal`, `reason`, and
`keywords` in the operation override the item's values for this request.

<CodeGroup>

```typescript TypeScript
const browser = await kernel.browsers.create({ vaults: [{ id: vault.id }] });

const requested = await kernel.vaults.items.performOperation("github-login", {
id_or_name: vault.id,
type: "1pw_create_access_request",
browser_id: browser.session_id,
});
if (requested.type !== "credential" || requested.action?.name !== "1password_access_approval") {
throw new Error("1Password didn't return an approval link");
Expand All @@ -495,13 +493,10 @@ if (requested.type !== "credential" || requested.action?.name !== "1password_acc
```

```python Python
browser = kernel.browsers.create(vaults=[{"id": vault.id}])

requested = kernel.vaults.items.perform_operation(
"github-login",
id_or_name=vault.id,
type="1pw_create_access_request",
browser_id=browser.session_id,
)
if (requested.type != "credential" or requested.action is None or
requested.action.name != "1password_access_approval"):
Expand Down Expand Up @@ -531,17 +526,19 @@ authenticated channel. see 1Password's
it supplies them.

don't automatically retry a failed request. if the call fails before KERNEL
dispatches the request to 1Password, the item still advertises
`1pw_create_access_request`. if it fails after dispatch, the outcome is
uncertain: a request might exist in 1Password. the item stays blocked in
dispatches the request to 1Password, or 1Password rejects it (`provider_rejected`
with a `4xx` status), nothing was created and the item still advertises
`1pw_create_access_request`; fix the cause, such as an expired token or a
disconnected account, before you try again. if it fails after dispatch with a
`5xx` or a network error, the outcome is uncertain: a request might exist in 1Password. the item stays blocked in
`pending_authorization` with no approval action and no available operations,
and KERNEL doesn't offer a way to retry or reset it. ask the account owner to
check the 1Password app for a pending request instead of sending another.

### Poll for the decision

after you present the approval link, invoke `1pw_access_request_status` with a
vault-attached `browser_id` until the status leaves `pending_authorization`.
after you present the approval link, invoke `1pw_access_request_status` until
the status leaves `pending_authorization`. it doesn't need a browser.
`timeout_seconds` accepts 0–120 and defaults to 10. KERNEL records the decision
only when you poll; a `wait` read of the item doesn't observe approval.

Expand All @@ -553,7 +550,6 @@ while (item.type === "credential" && item.state.status === "pending_authorizatio
item = await kernel.vaults.items.performOperation("github-login", {
id_or_name: vault.id,
type: "1pw_access_request_status",
browser_id: browser.session_id,
timeout_seconds: 60,
});
}
Expand All @@ -569,7 +565,6 @@ while item.type == "credential" and item.state.status == "pending_authorization"
"github-login",
id_or_name=vault.id,
type="1pw_access_request_status",
browser_id=browser.session_id,
timeout_seconds=60,
)
if item.type != "credential" or item.state.status != "ready":
Expand Down Expand Up @@ -612,7 +607,9 @@ see 1Password's [how long a grant lasts](https://www.1password.dev/agentic-autof

### Fill and submit

navigate the attached browser to the page with the username and password form,
create a browser with the vault attached; you don't install anything, because
KERNEL loads the 1Password extension into it on demand the first time you fill.
navigate it to the page with the username and password form,
not a page that asks the user to choose a sign-in method. then invoke `1pw_fill` with
the browser's session id and the exact current top-level `page_url`. the url
must match exactly one open page and share the origin of an approved entry.
Expand Down Expand Up @@ -646,6 +643,9 @@ request to 1Password, so an ended grant fails at the next fill.
<CodeGroup>

```typescript TypeScript
const browser = await kernel.browsers.create({ vaults: [{ id: vault.id }] });
// Navigate the browser to https://portal.example.com/login before filling.

const approved = await kernel.vaults.items.retrieve("example-portal-logins", {
id_or_name: vault.id,
});
Expand All @@ -671,6 +671,9 @@ if (result.status === "fill_failed") {
```

```python Python
browser = kernel.browsers.create(vaults=[{"id": vault.id}])
# Navigate the browser to https://portal.example.com/login before filling.

approved = kernel.vaults.items.retrieve("example-portal-logins", id_or_name=vault.id)
if approved.type != "credential" or approved.state.provider != "1password":
raise RuntimeError("unexpected item")
Expand Down Expand Up @@ -742,7 +745,7 @@ disconnects; see 1Password's

## Limitations

- **not secret isolation:** for each access request, status check, and fill, KERNEL gives the extension inside the attached browser access to the connected account or supplied token, and it doesn't remove the extension afterward. use a dedicated browser for 1Password operations, don't give untrusted automation access to it, and don't load extensions you don't trust alongside it. the filled values are in the page between fill and submit.
- **not secret isolation:** for each fill, KERNEL gives the extension inside the attached browser access to the connected account or supplied token, and it doesn't remove the extension afterward. access requests and status checks don't involve the browser. use a dedicated browser for 1Password operations, don't give untrusted automation access to it, and don't load extensions you don't trust alongside it. the filled values are in the page between fill and submit.
- **private vaults only, no passkeys:** 1Password's api can grant only logins stored in the user's private, non-shared vault, and it doesn't support passkeys. see 1Password's [supported list](https://www.1password.dev/agentic-autofill/partners#supported-today).
- **grants expire:** 1Password grants last 30 days at launch and end sooner if the connection ends. `ready` doesn't expire with them; see [how long a grant lasts](#how-long-a-grant-lasts).
- **up to five logins per item:** each `credential` item carries one to five https login entries.
Expand Down
Loading