diff --git a/vaults/1password.mdx b/vaults/1password.mdx index 5c5d1641..5bae31c9 100644 --- a/vaults/1password.mdx +++ b/vaults/1password.mdx @@ -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. @@ -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) @@ -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). @@ -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 ``` @@ -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. ```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"); @@ -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"): @@ -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. @@ -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, }); } @@ -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": @@ -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. @@ -646,6 +643,9 @@ request to 1Password, so an ended grant fails at the next fill. ```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, }); @@ -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") @@ -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.