# Your website

> Connect your website to your workspace, so its pages can show the buyer's account and sell your tools.

`toolaby-web.js` is a script your website loads from your workspace. It gives your pages the buyer's account: an account button, content shown by sign-in state or by what the buyer owns, checkout, trials and the customer portal.

This page covers connecting your site and adding the script. [Website components](https://toolaby.app/docs/web-components.md) covers `<toolaby-user>` and the `data-toolaby-*` attributes; [Website JavaScript](https://toolaby.app/docs/web-javascript.md) covers `window.Toolaby`, React and Next.js.

## Before you begin

* A tool on your workspace, with its plans set under **Pricing**.
* A website you can edit, and for the proxy, a server route you can add.
* On Live, the Pro plan: your own domain, your own pages and the proxy are Pro features. On Test every feature is on. See [Plans](https://toolaby.app/docs/plans.md).

## 1. Choose how your site connects

A page can see who is signed in only when the buyer's session reaches it. There are three ways. The script picks the one that applies; you never set it.

| Way             | Where         | What it needs                                                 | The session on your site                                                                              |
| --------------- | ------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Your domain** | Test and Live | Your site on the same domain as your workspace's address      | The cookie on your workspace's address, which the browser sends because your page is on the same site |
| **Proxy**       | Test and Live | A route on your server that forwards to Toolaby with a secret | An `HttpOnly` cookie on your own domain that no script can read                                       |
| **Development** | Test only     | Nothing: any site, `localhost` included                       | A token your page keeps in its own storage                                                            |

With none of them on Live, the page cannot see the buyer. It says so in the console, and every button opens the hosted pages instead: sign-in, checkout and the account page all still work, as links. **Your domain** is the way for production. The proxy is for a site whose domain cannot hold your workspace's address.

`Toolaby.state.mode` says which way a page is using: `cookie`, `proxy`, `dev` or `link`.

### What works in each way

|                                                                       | Your domain                   | Proxy                                           | Development                   | None (`link`)                              |
| --------------------------------------------------------------------- | ----------------------------- | ----------------------------------------------- | ----------------------------- | ------------------------------------------ |
| `<toolaby-user>`                                                      | Yes                           | Yes                                             | Yes                           | An **Account** link                        |
| Content by state (`data-toolaby-signed-in`, `-signed-out`, `-owns`)   | Yes                           | Yes                                             | Yes                           | Neither shown: who is signed in is unknown |
| Checkout, trial, customer portal                                      | Yes                           | Yes                                             | Yes                           | The hosted pages, as links                 |
| A sign-in form of your own: `data-toolaby-signin` with an email field | Sends the link from your page | Opens the hosted sign-in page                   | Opens the hosted sign-in page | Opens the hosted sign-in page              |
| A sign-in page of your own (`sign-in-url`)                            | Yes                           | Opens; its sign-in goes through the hosted page | The same                      | Not reached                                |
| `Toolaby.session()`                                                   | The sign-in library's session | `{ user }`                                      | `{ user }`                    | `null`                                     |

Through a proxy or in development, sign-in always passes through the hosted sign-in page: that is where the one-time code that brings the session to your site is made.

## 2. Connect your site

Do one of the three.

### With your domain

1. Under **Configure → Domain**, verify an address such as `accounts.example.com`.
2. Serve your pages on the same registrable domain: `example.com` or `www.example.com` for `accounts.example.com`.
3. Under **Configure → Your own pages**, add each origin your pages are served from, such as `https://www.example.com`. Up to five. These are also the only audiences your extension may ask a site token for, with [`siteToken()`](https://toolaby.app/docs/reference/client.md#sitetoken).
4. Load the script from that address, on every page that uses it:

```html
<script src="https://accounts.example.com/client/toolaby-web.js" defer></script>
```

When it worked, `Toolaby.state.mode` is `cookie` on your pages.

### Through a proxy

For a site that cannot share your workspace's domain. Your server forwards one path, `/__toolaby`, to Toolaby.

1. Under **Configure → Your own pages → Proxy**, select **Make a proxy secret**. It is shown once; Toolaby keeps only its hash. Put it in your server's environment as `TOOLABY_PROXY_SECRET`.
2. Under the same heading, add your site's origin, such as `https://www.example.com`. It may be any domain.
3. Add the route the dashboard shows, filled in with your workspace's address. In Next.js it is `app/__toolaby/[...path]/route.ts`. It uses web-standard requests, so the same function runs in Remix, SvelteKit, Hono, Cloudflare Workers, Deno and Bun.
4. Load the script through the proxy, from your own domain:

```html
<script src="/__toolaby/client/toolaby-web.js" defer></script>
```

When it worked, `Toolaby.state.mode` is `proxy`.

The route sends Toolaby five request headers (`accept`, `accept-language`, `content-type`, `origin`, `user-agent`) and Toolaby's own cookie, never your site's other cookies or its `Authorization` header. It passes back only Toolaby's own cookie, so nothing from Toolaby can overwrite a cookie of yours. It adds the secret, its own address and the buyer's IP address, which Toolaby trusts only when the secret is right, and then only for your workspace's own limits.

It reads the address from `x-real-ip`, which Vercel and nginx set; behind Cloudflare, send `cf-connecting-ip` instead. A request with a wrong secret, or from an origin that is not listed, is refused with a `401`. Through a proxy Toolaby answers only what the script calls: the site's endpoints and the sign-in handshake. Anything else is a `404`. The route sends only to Toolaby's address: a path that names another host, such as `/__toolaby//example.com/`, is a `404` before anything is sent.

After sign-in, the session reaches your site as `__Host-toolaby.session`: `HttpOnly`, `SameSite=Lax`, `Secure`, and on your host alone, so no other host of your domain — a blog, a shop, anything with scripts of its own — can set one in its place. It works only through your proxy, on the site's endpoints: sent anywhere else, it is no session. Through the proxy, a request that changes something must come from your site's own pages: one from another origin is a `403`. Select **Replace secret** to make a new one; the old one stops working at once. **Revoke** refuses every request through the proxy.

A buyer already signed in on your workspace who selects **Sign in** on your site comes straight back, signed in. A buyer who arrives at the sign-in from anywhere else, such as a link in a message, is asked **Continue to** your site first.

### In development

On **Test**, the script works on any site with no setup: `localhost`, a preview deployment, a site you have not given a domain yet. Load it from your workspace's Test address, the one **Configure → Domain** shows:

```html
<script src="https://quiet-otter-4172.toolaby.app/test/client/toolaby-web.js" defer></script>
```

When it worked, `Toolaby.state.mode` is `dev`. Signing in works like this:

1. The page makes a random secret, keeps it in its own storage, and opens the sign-in page with its hash (PKCE).
2. The sign-in page names your site and marks it **Test mode**.
3. After signing in, the buyer confirms **Continue to your site** on a page of Toolaby that cannot be shown inside another page.
4. The page receives a one-time code in its address's fragment, which browsers never send to a server, and removes it at once.
5. The page exchanges the code, with its secret, for a session token. The code works once, for two minutes, and only with that secret.

The token works on the endpoints a page needs (the account and what it holds, checkout, the trial, the customer portal and sign-out) and on nothing else. A script on your page can read it, which is why this exists only on Test. **Live never accepts it**: use your domain or a proxy there.

## 3. Add the account button

```html
<toolaby-user></toolaby-user>
```

Signed out, it is a **Sign in** button; signed in, the buyer's picture with a menu. The script reads the buyer's account when the page loads, and again when the page becomes visible, at most once every ten seconds, so signing in or out in another tab shows here too. See [Website components](https://toolaby.app/docs/web-components.md) for everything it takes.

## 4. Sell from your page

```html
<button data-toolaby-checkout="word-count">Buy</button>
<div data-toolaby-owns="word-count" hidden>Thanks for buying Word Count.</div>
```

See [Website components](https://toolaby.app/docs/web-components.md#buttons) for checkout, trials and the customer portal, and [Website JavaScript](https://toolaby.app/docs/web-javascript.md) to do the same from code.

## When a page always shows the buyer as signed out

Open the browser's console: the script says which way it is using and why.

| What you see                                                               | Cause                                                                                                                                                                                                                                                                                          |
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Toolaby: … is not on the same domain as … and has no proxy`               | On Live, the page has none of the three ways. Connect your domain, or add a proxy.                                                                                                                                                                                                             |
| A CORS error                                                               | The page's origin is not listed under **Configure → Your own pages**, or the page calls a route the script does not: a listed page reaches the session, a sign-in link, signing out, entitlements, the portal, a trial and checkout. The origin is the scheme, the host and the port, exactly. |
| `This request did not come from a page that may make it` (`403`)           | A request that changes something for the signed-in buyer came from a page that is not listed, and not through your proxy.                                                                                                                                                                      |
| After signing in, the buyer lands on the account page instead of your page | The page is under no listed origin and no proxy origin, and it is not on Test.                                                                                                                                                                                                                 |
| `This proxy is not recognised` (`401`)                                     | The proxy's secret is wrong or was replaced, or its origin is not listed under **Proxy**.                                                                                                                                                                                                      |
| `This sign-in has expired or was started somewhere else`                   | The one-time code was used, took more than two minutes, or came back to a different browser than the one that started the sign-in. Sign in again.                                                                                                                                              |

## Security

* **Your domain:** the session cookie stays on your workspace's address. Your page never holds it; the browser sends it with each request the script makes. Toolaby answers with your page's origin only if it is listed, and only on the routes the script calls; a request that changes something for the buyer from any other page is refused, even one on another workspace's address.
* **Proxy:** the session is an `HttpOnly` `__Host-` cookie on your site's host alone, path `/`. Toolaby trusts a proxied request only with the right secret, compared as a hash in constant time, and from a listed origin. It answers only the site's endpoints through a proxy, and the buyer's address a proxy names counts only toward your workspace's limits.
* **Development:** Test only. The code is single use, lasts two minutes, travels in the fragment and is bound by PKCE to a secret the page keeps, so a code seen by anyone else is worth nothing. The buyer confirms the site first. The token reaches only the endpoints a page needs.
* **Everywhere:** the sign-in page returns a buyer only to a listed origin, a proxy origin or, on Test, a site the buyer confirmed. Any other `next` goes to the account page. A site session never reaches the endpoints that change an account.
* **Content by state is shown by the browser.** `data-toolaby-owns` hides an element from a buyer who does not hold the tool; it does not keep its content from them. Keep what must stay private on your server, and check there: see [Sync customers](https://toolaby.app/docs/sync.md).

## See also

* [Website components](https://toolaby.app/docs/web-components.md)
* [Website JavaScript](https://toolaby.app/docs/web-javascript.md)
* [Buyer pages](https://toolaby.app/docs/buyer-pages.md)
