everyapp 0.2.2 → 0.3.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +6 -77
- package/bin/everyapp.js +235 -0
- package/dist/access-LGEN3ABJ.js +1 -0
- package/dist/activity-RSD4D66F.js +12 -0
- package/dist/apps-4RCFXT74.js +13 -0
- package/dist/chunk-23HJ5I2A.js +2 -0
- package/dist/chunk-2Y5Q6A5K.js +6 -0
- package/dist/chunk-4JRIS6S6.js +1 -0
- package/dist/chunk-5PTNYZK6.js +4 -0
- package/dist/chunk-6OGVO7QK.js +6 -0
- package/dist/chunk-6PGUCTJ6.js +11 -0
- package/dist/chunk-AGTCKFRB.js +22 -0
- package/dist/chunk-BHMRY53V.js +2 -0
- package/dist/chunk-CB4U7H2G.js +4 -0
- package/dist/chunk-CCWBPAKH.js +4 -0
- package/dist/chunk-CZTEDWZ2.js +22 -0
- package/dist/chunk-CZXHKPRZ.js +2 -0
- package/dist/chunk-E3EPK27P.js +10 -0
- package/dist/chunk-EGEWRFVY.js +1 -0
- package/dist/chunk-ELK7QZ2M.js +1 -0
- package/dist/chunk-FJJSSVET.js +33 -0
- package/dist/chunk-GKBPYVP3.js +25 -0
- package/dist/chunk-H6QQMY7B.js +18 -0
- package/dist/chunk-H7PDL3X5.js +1 -0
- package/dist/chunk-HOUHG236.js +20 -0
- package/dist/chunk-IFCPALVC.js +46 -0
- package/dist/chunk-IUOBIB5M.js +30 -0
- package/dist/chunk-J35DDCNU.js +1 -0
- package/dist/chunk-J455FAEN.js +7 -0
- package/dist/chunk-JYKRUS5C.js +1 -0
- package/dist/chunk-LXYMDZER.js +1 -0
- package/dist/chunk-LYNBZ7RV.js +57 -0
- package/dist/chunk-M2QQ2UD7.js +132 -0
- package/dist/chunk-N4E5YE4X.js +5 -0
- package/dist/chunk-N5GQLGEC.js +2 -0
- package/dist/chunk-NFU4YFXB.js +1 -0
- package/dist/chunk-OM2HDWVP.js +1 -0
- package/dist/chunk-ONLNJXVQ.js +32 -0
- package/dist/chunk-OP4DYXYI.js +2 -0
- package/dist/chunk-PNLU5U3P.js +1 -0
- package/dist/chunk-PS7S74DG.js +6 -0
- package/dist/chunk-RMF4YGD6.js +98 -0
- package/dist/chunk-T2GD7RHS.js +10 -0
- package/dist/chunk-TLRM3P7V.js +15 -0
- package/dist/chunk-W3JCQHHU.js +1 -0
- package/dist/chunk-WOVS3GV5.js +10 -0
- package/dist/chunk-YHJZMJYZ.js +1 -0
- package/dist/chunk-ZECBN24L.js +15 -0
- package/dist/chunk-ZEXVHB22.js +2 -0
- package/dist/chunk-ZFCUH5PK.js +7 -0
- package/dist/configure-F7UZCJUG.js +8 -0
- package/dist/create-2E5HVWYK.js +22 -0
- package/dist/db-HEQVTDTM.js +9 -0
- package/dist/delete-XCSIQIRS.js +3 -0
- package/dist/deploy-3QOZEZHC.js +1 -0
- package/dist/dev-JL3ZP5BT.js +86 -0
- package/dist/email-RLZFUFUN.js +10 -0
- package/dist/events-2V5VRBWP.js +15 -0
- package/dist/groups-HDG5DVFY.js +8 -0
- package/dist/index.js +97 -1
- package/dist/install-Z5FYXUYM.js +1 -0
- package/dist/login-KHJBC64Z.js +1 -0
- package/dist/mcp-EFDUKJ5V.js +1 -0
- package/dist/provider-EOI5PTFH.js +13 -0
- package/dist/release-74J5ZIL3.js +1 -0
- package/dist/restore-FFGYV7NO.js +3 -0
- package/dist/secret-7FKKTLHB.js +1 -0
- package/dist/setup-PAKRCAFO.js +1 -0
- package/dist/signin-AP3D4XM4.js +12 -0
- package/dist/status-RMWNHOEK.js +4 -0
- package/dist/teardown-E4JNKAZJ.js +22 -0
- package/dist/update-DWVZFNPS.js +1 -0
- package/dist/workspace-CJC5I66L.js +21 -0
- package/package.json +19 -16
- package/skills/build-every-app/SKILL.md +199 -0
- package/skills/build-every-app/patterns.md +140 -0
- package/skills/everyapp/SKILL.md +292 -0
- package/skills/everyapp/mcp.md +350 -0
- package/skills/everyapp/security-review.md +61 -0
- package/skills/everyapp/setup/README.md +274 -0
- package/skills/everyapp/setup/access.md +178 -0
- package/skills/everyapp/setup/context.md +117 -0
- package/skills/everyapp/setup/dataforseo.md +287 -0
- package/skills/everyapp/setup/email.md +366 -0
- package/skills/everyapp/setup/gateway.md +243 -0
- package/skills/everyapp/setup/google.md +360 -0
- package/skills/everyapp/setup/microsoft-365.md +281 -0
- package/skills/everyapp/setup/openai.md +318 -0
- package/skills/everyapp/setup/openrouter.md +309 -0
- package/skills/everyapp/setup/providers.md +1253 -0
- package/skills/everyapp/setup/remote-mcp.md +199 -0
- package/skills/everyapp/setup/signin-apple.md +178 -0
- package/skills/everyapp/setup/signin-cloudflare.md +148 -0
- package/skills/everyapp/setup/signin-github.md +139 -0
- package/skills/everyapp/setup/signin-google.md +178 -0
- package/skills/everyapp/setup/signin-microsoft.md +183 -0
- package/skills/everyapp/setup/signin-oidc.md +163 -0
- package/skills/everyapp/setup/slack.md +344 -0
- package/skills/everyapp/setup/workers-ai.md +138 -0
- package/bin/__everyapp_bash_complete.js +0 -12
- package/dist/bash-complete.js +0 -3
- package/dist/chunk-7BOQJFI5.js +0 -8
- package/dist/chunk-7YVJFS4A.js +0 -1
- package/dist/chunk-BIXKPCDH.js +0 -2
- package/dist/chunk-DLGPLHBH.js +0 -47
- package/dist/chunk-FD6K2YA7.js +0 -9
- package/dist/chunk-GAJEQYJH.js +0 -39
- package/dist/chunk-O2KDOP5R.js +0 -13
- package/dist/chunk-OI2MBGBG.js +0 -4
- package/dist/chunk-POSDLD3G.js +0 -8
- package/dist/chunk-RB5D2R45.js +0 -1
- package/dist/chunk-U4LIQ5VR.js +0 -1
- package/dist/chunk-VEYSNPDH.js +0 -3
- package/dist/chunk-VP6SKW44.js +0 -7
- package/dist/chunk-WO3NE7ZU.js +0 -7
- package/dist/chunk-XTO2VTPY.js +0 -6
- package/dist/chunk-YJ2DKIZS.js +0 -1
- package/dist/create-DYQ3NBWJ.js +0 -22
- package/dist/deploy-PYTCMDGV.js +0 -7
- package/dist/deploy-RMAHOA4Z.js +0 -55
- package/dist/dev-SAE237FS.js +0 -0
- package/dist/generateConfig-46Z3ERF3.js +0 -1
- package/dist/login-B6EF3SMX.js +0 -1
- package/dist/logout-XMKZBLNV.js +0 -1
- package/dist/remoteD1Shell-A67JMO3H.js +0 -6
- package/dist/setupLocal-TZJUW5ES.js +0 -6
- package/gateway/every-app-gateway-build.tar.gz +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# A remote MCP server as an MCP app
|
|
2
|
+
|
|
3
|
+
Any remote MCP server that speaks Streamable HTTP (a docs search, a
|
|
4
|
+
company's API, a SaaS tool's MCP endpoint) can become an MCP app of the
|
|
5
|
+
Gateway's: `pnpm everyapp mcp add <url>` registers it, the Gateway serves its
|
|
6
|
+
tools on its own `/mcp` to every member it is granted to as `<id>_<tool>`,
|
|
7
|
+
holds the server's credential itself (a Gateway setting, never sent to an AI
|
|
8
|
+
client), sends it only to the server's origin, and attributes, audits and
|
|
9
|
+
counts every call like an app's. A read-only connection sees only the tools
|
|
10
|
+
whose risk is `read`. At the end the person has the app on, its credential
|
|
11
|
+
set and pinned to the server's origin, the people who need it granted, and
|
|
12
|
+
one tool call through the Gateway's `/mcp` that worked.
|
|
13
|
+
|
|
14
|
+
## Settings
|
|
15
|
+
|
|
16
|
+
| NAME | required or optional | what it holds |
|
|
17
|
+
| ------------------------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `<ID>_MCP_TOKEN` (the app id upper-cased, `-` as `_`; `--secret` picks another name ending in `_MCP_TOKEN`) | required, unless the server takes no credential (`--auth none`) | What the server's docs call its API key, token or password. `--auth bearer` sends it as `Authorization: Bearer <value>`; `--auth header --header X-Api-Key` as `X-Api-Key: <value>`; `--auth basic` takes `user:password` and sends HTTP Basic. |
|
|
19
|
+
|
|
20
|
+
The name is a Gateway setting like any provider key, set with
|
|
21
|
+
`pnpm everyapp secret set <NAME>` (any name ending in `_MCP_TOKEN` goes to the
|
|
22
|
+
Gateway; nothing else is accepted for it). When it is set, the CLI pins it to
|
|
23
|
+
the origin of the MCP app that declares it (`https://treg.to` for
|
|
24
|
+
`https://treg.to/mcp/`): the Gateway sends a credential only to the origin it
|
|
25
|
+
was set for. A server re-pointed or re-added at another address needs its
|
|
26
|
+
credential set again; only the operator (the Cloudflare login) can, so an
|
|
27
|
+
owner or admin who changes an app in the Gateway never moves a credential.
|
|
28
|
+
`mcp list` and `mcp show` say "needs setup" until it is set and pinned.
|
|
29
|
+
|
|
30
|
+
`--auth oauth` is accepted and recorded, but the Gateway doesn't sign in to
|
|
31
|
+
MCP servers with OAuth this week: such an app serves no tools ("OAuth: not
|
|
32
|
+
supported yet"). If an OAuth server also takes an API key, add it with
|
|
33
|
+
`--auth bearer` instead.
|
|
34
|
+
|
|
35
|
+
## Costs and plans
|
|
36
|
+
|
|
37
|
+
Every App charges nothing for an MCP app. The server's provider charges what
|
|
38
|
+
it charges for its calls (its own plan and limits apply; read them on its
|
|
39
|
+
site). Every forwarded call is counted in the organization's usage as
|
|
40
|
+
`mcp:<id>`, and an owner or admin can cap an app's calls per month in Admin →
|
|
41
|
+
Usage (off by default): past the cap a tool answers a tool error naming it
|
|
42
|
+
until the next UTC month.
|
|
43
|
+
|
|
44
|
+
## Only a person can
|
|
45
|
+
|
|
46
|
+
- Create an account with the server's provider and accept its terms, when it
|
|
47
|
+
has any.
|
|
48
|
+
- Create the API key or token on the provider's dashboard and type it at the
|
|
49
|
+
hidden prompt.
|
|
50
|
+
- Decide who gets the app beyond the owners and admins (Admin → Apps → Manage
|
|
51
|
+
access), and whether a tool the server says is safe should count as a write
|
|
52
|
+
(you suggest; they decide).
|
|
53
|
+
- Decide to replace a credential the Gateway already holds.
|
|
54
|
+
|
|
55
|
+
## Before you start
|
|
56
|
+
|
|
57
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed` are
|
|
58
|
+
true; `data.gatewayUrl` is `https://<gateway host>`. Not installed, or no
|
|
59
|
+
owner: [gateway.md](gateway.md) first.
|
|
60
|
+
2. The server's MCP address and how it authenticates, from its docs: an
|
|
61
|
+
`https://` URL (often ending in `/mcp`) that speaks Streamable HTTP. An old
|
|
62
|
+
HTTP+SSE-only endpoint (a separate `/sse` URL from 2024) isn't served. The
|
|
63
|
+
credential is usually an API key sent as a bearer token; some servers read
|
|
64
|
+
a header of their own (`X-Api-Key`); public servers take none. The
|
|
65
|
+
Gateway follows no redirects, so use the address the server answers at,
|
|
66
|
+
not one that redirects to it.
|
|
67
|
+
3. `pnpm everyapp mcp list --json`: the server isn't added already (no entry
|
|
68
|
+
in `data.apps` with its `url`). The id is the domain's name, the label
|
|
69
|
+
before its suffix (`https://mcp.deepwiki.com/mcp` → `deepwiki`,
|
|
70
|
+
`https://treg.to/mcp/` → `treg`), or `--name <id>` (lowercase letters,
|
|
71
|
+
digits and hyphens, at most 32 characters); it is the tools' prefix, so
|
|
72
|
+
keep it short.
|
|
73
|
+
|
|
74
|
+
## Steps
|
|
75
|
+
|
|
76
|
+
One step per message: give the step, wait for the words it ends with
|
|
77
|
+
("done", "I have it"), then the next.
|
|
78
|
+
|
|
79
|
+
1. **You:** add it. `pnpm everyapp mcp add <url> --auth bearer --json` (or
|
|
80
|
+
`--auth header --header <name>`, `--auth basic`; nothing for a public
|
|
81
|
+
server), with `--name <id>` when the host name makes a poor id and
|
|
82
|
+
`--risk read` for a server that only reads and doesn't say so in its tool
|
|
83
|
+
annotations (`--risk write` treats every tool as a write). The app is on
|
|
84
|
+
at once for the person running it (when they are a member), the owners
|
|
85
|
+
and the admins. The `done` event's `data.id`, `data.key` (null for
|
|
86
|
+
`--auth none`; else the credential's `name` and whether it is `set` and
|
|
87
|
+
`bound` to the URL's origin) and `data.probe` (what one look at the server
|
|
88
|
+
from this machine found, without the credential) say what happened.
|
|
89
|
+
`PROVIDER_KEY_UNSET` means the credential comes next.
|
|
90
|
+
`REMOTE_MCP_PROBE_FAILED` and `REMOTE_MCP_WANTS_OAUTH` are warnings, never
|
|
91
|
+
refusals: a 401 with `--auth none` means the server wants a credential
|
|
92
|
+
(`pnpm everyapp mcp remove <id>`, then the same add with `--auth bearer`), a
|
|
93
|
+
redirect means another address, and "Couldn't reach" may be this
|
|
94
|
+
machine's network only (the Gateway fetches from Cloudflare).
|
|
95
|
+
2. **Person:** the credential. They create the key on the provider's
|
|
96
|
+
dashboard, then, in a second terminal in the `every-app` folder:
|
|
97
|
+
`pnpm everyapp secret set <NAME>` (the name from step 1, for example
|
|
98
|
+
`pnpm everyapp secret set TREG_MCP_TOKEN`) and paste it at the hidden
|
|
99
|
+
prompt (for `--auth basic`, `user:password`). It ends with
|
|
100
|
+
`Set <NAME> on the Gateway`. When the person runs `mcp add` themselves at
|
|
101
|
+
a terminal, it asks `Set <NAME> now?` right after adding and, on yes, shows
|
|
102
|
+
the same hidden prompt (`secret set`'s own); No leaves it for `secret set`.
|
|
103
|
+
The README's other two ways work as for any `secret set` (a pipe into
|
|
104
|
+
`pnpm everyapp secret set <NAME> --json`, or the hidden read then
|
|
105
|
+
`--from-env <VAR>`); `mcp add <url> --auth bearer --from-env <VAR> --json`
|
|
106
|
+
takes the variable at the add itself, read before anything is written.
|
|
107
|
+
Wait for "done".
|
|
108
|
+
3. **Person:** who gets it. The owners, the admins and whoever added it have
|
|
109
|
+
it already. Anyone else: Admin → Apps → the app's row → **Manage access**.
|
|
110
|
+
A member sees its tools after granting, in a client connected to the
|
|
111
|
+
Gateway (the next step).
|
|
112
|
+
4. **Person:** connect an AI client to the Gateway if theirs isn't yet:
|
|
113
|
+
`claude mcp add --transport http everyapp https://<gateway host>/mcp`,
|
|
114
|
+
then `/mcp` → **everyapp** → **Authenticate** in Claude Code (other clients:
|
|
115
|
+
the same URL; mcp.md beside this folder, "Connecting a person").
|
|
116
|
+
|
|
117
|
+
## Verify
|
|
118
|
+
|
|
119
|
+
1. `pnpm everyapp mcp show <id> --json`: `data.status` is `"on"` (not
|
|
120
|
+
`"needs setup"`), `data.key.set` and `data.key.bound` are true and
|
|
121
|
+
`data.key.boundOrigin` is the URL's origin (all three skipped for
|
|
122
|
+
`--auth none`: `data.key` is null). `data.snapshot` is null until the
|
|
123
|
+
Gateway lists the tools, on the first request to its `/mcp` after the add
|
|
124
|
+
(or within the hour). From the checkout root,
|
|
125
|
+
`pnpm everyapp secret list --json` agrees: the name is in
|
|
126
|
+
`data.gateway.names`, its `data.gateway.mcpCredentials` entry has
|
|
127
|
+
`set: true`, and the app has left `data.apps` (it is listed there only
|
|
128
|
+
while it needs setup, with `setup.command` the `secret set` that sets it
|
|
129
|
+
up).
|
|
130
|
+
2. One tool call through the Gateway: in the person's connected client, ask
|
|
131
|
+
for something the server does ("use <id> to …", or "list the tools <id>
|
|
132
|
+
offers"); the client shows a call to an `<id>_<tool>` tool and its
|
|
133
|
+
answer. On a Gateway with many tools the client may list them compactly:
|
|
134
|
+
then it finds them with `everyapp_search_tools` (`{ "app": "<id>" }`) and
|
|
135
|
+
runs one through `everyapp_call_read`. Then
|
|
136
|
+
`pnpm everyapp mcp show <id> --json` again: `data.snapshot.tools` is the
|
|
137
|
+
count listed and `data.tools[]` has each with its `name`, `serverName`,
|
|
138
|
+
`risk` and `source` (`hint`: the server's annotation; `default`: no
|
|
139
|
+
annotation, counted as a write; `app`: the `--risk` default;
|
|
140
|
+
`override`: yours).
|
|
141
|
+
3. Risks, with the person: a tool the server marks read-only is callable by
|
|
142
|
+
every connection; one without a hint is a write (hidden from read-only
|
|
143
|
+
connections); one marked destructive is hidden from every client until
|
|
144
|
+
approvals exist. Change one with
|
|
145
|
+
`pnpm everyapp mcp tool <id> <tool> --risk read|write|destructive --json`
|
|
146
|
+
(`--risk annotations` drops the override), or leave a tool out entirely
|
|
147
|
+
with `--off`; the Gateway lists the tools again on first use.
|
|
148
|
+
4. Logs, when a call fails: README's path (**Workers & Pages** →
|
|
149
|
+
`every-app-gateway` → **Observability** → **Live**). The remote proxy logs
|
|
150
|
+
each failed upstream call as `mcp.remote.upstream_failed` with the status
|
|
151
|
+
(never the credential), and a first-use pin as
|
|
152
|
+
`mcp.remote.credential_bound` (names from the design,
|
|
153
|
+
docs/design/2026-10-mcp-by-url.md; not checked against a live Gateway
|
|
154
|
+
here, unverified).
|
|
155
|
+
|
|
156
|
+
## If it fails
|
|
157
|
+
|
|
158
|
+
| error or symptom | meaning | what to do |
|
|
159
|
+
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
160
|
+
| `SECRET_VALUE_REQUIRED` from `secret set --json` | Nothing was piped and there is no terminal to prompt in | One of the README's three ways: the person's own terminal, a pipe, or `--from-env` |
|
|
161
|
+
| `SECRET_EMPTY` | The `--from-env` variable is empty or not exported in that shell (from `mcp add --from-env`, nothing was added) | The hidden read (README's third way) in that same terminal first, then rerun |
|
|
162
|
+
| `REMOTE_MCP_URL_REFUSED` | Not `https://`, an IP address, the Gateway's own host or app domain, or a provider the Gateway holds a key for | The server's own MCP address, by host name, over https |
|
|
163
|
+
| `REMOTE_MCP_EXISTS` | That id is an MCP app already | `pnpm everyapp mcp show <id>`; another address is `mcp remove <id>` then `mcp add` again, or another `--name` |
|
|
164
|
+
| `REMOTE_MCP_SECRET_TAKEN` | Another MCP app at another origin uses that credential name | `--secret <OTHER>_MCP_TOKEN`: one credential name per origin |
|
|
165
|
+
| `REMOTE_MCP_INVALID` | A bad id, risk, header or credential name, or flags that say two things | As the message says |
|
|
166
|
+
| `PROVIDER_APP_UNKNOWN` | The id isn't an MCP app here | `pnpm everyapp mcp list` for the ids |
|
|
167
|
+
| `REMOTE_MCP_SECRET_BOUND_ELSEWHERE` from `mcp add`, or `needs setup` in `mcp list` or `mcp show` with the key set | The credential is pinned to another origin (`data.key.boundOrigin`): it was set for an earlier server | `pnpm everyapp secret set <NAME>` again (Steps 2) |
|
|
168
|
+
| `REMOTE_MCP_TOOL_NOT_LISTED` from `mcp tool` | The tool's name isn't in the last listing: the override was kept under that name, as the server's | `pnpm everyapp mcp show <id>` for both names, then `mcp tool` with the right one |
|
|
169
|
+
| `REMOTE_MCP_SECRET_UNDECLARED` from `secret set` | No MCP app declares that name (a typo, or set before `mcp add`) | Check the name with `mcp show <id>`; `mcp add` first |
|
|
170
|
+
| `REMOTE_MCP_WANTS_OAUTH` | The server wants OAuth, which the Gateway doesn't do yet | If it also takes an API key: `--auth bearer` and set the key; otherwise it can't be served this week |
|
|
171
|
+
| `REMOTE_MCP_GATEWAY_OUTDATED` | The Gateway predates MCP apps by URL | `pnpm everyapp update`, then Verify |
|
|
172
|
+
| `data.snapshot.status` `error`, `lastError` naming 401 | The server rejected the credential | A fresh key from the provider, `secret set` again, then `pnpm everyapp mcp refresh <id>` |
|
|
173
|
+
| `lastError` naming a redirect or a timeout | The address redirects, or the server is slow or down | The address it redirects to (remove, add again); a slow server: retry later |
|
|
174
|
+
| The tools changed on the server, not on the Gateway | The Gateway's snapshot is up to an hour old | `pnpm everyapp mcp refresh <id>` |
|
|
175
|
+
| A tool is missing for one person only | Their connection is read-only (write tools hidden), or they weren't granted the app | Admin → Apps → Manage access; a read & write connection |
|
|
176
|
+
|
|
177
|
+
## Rerun, rotate, remove
|
|
178
|
+
|
|
179
|
+
- **Rerun.** `mcp add` refuses an id that exists (`REMOTE_MCP_EXISTS`), so a
|
|
180
|
+
rerun starts from Verify. `secret set` replaces; `mcp tool` and
|
|
181
|
+
`mcp refresh` can run again.
|
|
182
|
+
- **Rotate.** A new key on the provider's dashboard, then Steps 2 again: the
|
|
183
|
+
Gateway uses the new value on its next request. A value that went through
|
|
184
|
+
the chat is compromised: rotate it.
|
|
185
|
+
- **Off, and back on.** `pnpm everyapp mcp off <id> --json` stops serving it
|
|
186
|
+
and keeps everyone's access, tokens and the snapshot;
|
|
187
|
+
`pnpm everyapp mcp on <id> --json` serves it again at once.
|
|
188
|
+
- **Remove.** `pnpm everyapp mcp remove <id> --json`: a tombstone, with
|
|
189
|
+
everyone's access, its tokens and its place in AI connections gone (adding
|
|
190
|
+
it again starts fresh). The credential and its pin stay;
|
|
191
|
+
`pnpm everyapp secret unset <NAME> --json` removes both (it asks; with
|
|
192
|
+
`--json` add `--yes` only once the person said so). Then revoke the key on
|
|
193
|
+
the provider's side.
|
|
194
|
+
|
|
195
|
+
## Sources
|
|
196
|
+
|
|
197
|
+
- docs/design/2026-10-mcp-by-url.md (the remote block, the credential family and its pin, the proxy, the defaults taken)
|
|
198
|
+
- In code: `packages/sdk/src/internal/remoteMcp.ts` (the URL, id, auth and risk rules: `remoteMcpUrlProblem`, `remoteMcpIdFromUrl`, `remoteToolRisk`), `packages/sdk/src/internal/gatewaySettings.ts` (`isRemoteMcpSecretName`: any name ending in `_MCP_TOKEN` is the Gateway's), `packages/sdk/src/internal/appSettings.ts` (the `remote-mcp` setting kind naming this file), `packages/perimeter/src/registry/remoteMcpApps.ts` (`planRemoteMcpAppAdd` and its refusals), `packages/cli/src/commands/provider/index.ts` (`mcp add`, `show`, `tool`, the probe's warnings), `packages/cli/src/lib/gateway/remoteMcpApps.ts` (the rows, the pin in `remote_mcp_credentials`), `packages/cli/src/commands/secret/index.ts` (the pin on `secret set`, dropped on `secret unset`), `apps/every-app-gateway/src/db/remote-mcp.schema.ts` (migration 0011)
|
|
199
|
+
- https://modelcontextprotocol.io/specification (Streamable HTTP transport, tool annotations `readOnlyHint` and `destructiveHint`, authorization with `resource_metadata`)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Sign in with Apple on the Gateway
|
|
2
|
+
|
|
3
|
+
Read [README.md](README.md) first: the rules every setup follows.
|
|
4
|
+
|
|
5
|
+
Puts **Continue with Apple** on the Gateway's sign-in page, beside the
|
|
6
|
+
password. At the end the person has an App ID, a Services ID and a Sign in
|
|
7
|
+
with Apple key in their Apple Developer account, the Gateway holds the
|
|
8
|
+
Services ID and a client secret signed with that key, and they have signed
|
|
9
|
+
in with Apple once. `pnpm everyapp signin show apple --json` names this
|
|
10
|
+
file as `setup`, and the CLI prints
|
|
11
|
+
`Guide: .claude/skills/everyapp/setup/signin-apple.md`.
|
|
12
|
+
|
|
13
|
+
Who gets in doesn't change: Apple only says who someone is. A member signs
|
|
14
|
+
in; someone with a pending invitation for that exact address joins with
|
|
15
|
+
the invited role; everyone else is refused ("You don't have an account on
|
|
16
|
+
this gateway.") and nothing is created. Someone who picks **Hide My
|
|
17
|
+
Email** on Apple's sheet arrives as a `@privaterelay.appleid.com` address
|
|
18
|
+
that no invitation names: ask them to pick **Share My Email**, or invite
|
|
19
|
+
the relay address. The rules: `docs/runbooks/sign-in-providers.md`.
|
|
20
|
+
|
|
21
|
+
Apple differs from the others twice. The client secret is not copied from
|
|
22
|
+
a page: it is a JWT you sign with the key, valid six months at most, so
|
|
23
|
+
it must be generated again before then or sign-in stops. And Apple returns
|
|
24
|
+
to the Gateway with a POST; behind Cloudflare Access, `everyapp access on`
|
|
25
|
+
leaves `/api/auth/callback/apple` open for it (Verify 4).
|
|
26
|
+
|
|
27
|
+
## Settings
|
|
28
|
+
|
|
29
|
+
| Name | Required | Flag | What it holds |
|
|
30
|
+
| ---------------------------- | -------- | -------------------------------- | ----------------------------------------------------------------------------- |
|
|
31
|
+
| `APPLE_SIGNIN_CLIENT_ID` | required | `--client-id <id>` | the Services ID's identifier (`com.example.gateway`); not a secret |
|
|
32
|
+
| `APPLE_SIGNIN_CLIENT_SECRET` | required | `--client-secret-from-env <VAR>` | the ES256 JWT from step 5; expires within six months |
|
|
33
|
+
|
|
34
|
+
Scopes asked for: `email name`. The email is the ID token's `email`,
|
|
35
|
+
counted only when `email_verified` is true.
|
|
36
|
+
|
|
37
|
+
## Costs and plans
|
|
38
|
+
|
|
39
|
+
A paid Apple Developer Program membership: US$99 a year (Apple's
|
|
40
|
+
fee-waiver page names the 99 USD fee; local prices vary), as an individual
|
|
41
|
+
or an organization; enrolling an organization needs a D-U-N-S number and
|
|
42
|
+
can take days. Sign in with Apple itself costs nothing more. Only the
|
|
43
|
+
Account Holder or an Admin of the team can set up the identifiers.
|
|
44
|
+
|
|
45
|
+
## Only a person can
|
|
46
|
+
|
|
47
|
+
- Enroll in the Apple Developer Program and pay.
|
|
48
|
+
- Create the App ID, the Services ID and the key in Certificates,
|
|
49
|
+
Identifiers & Profiles, and download the key's `.p8` file (once only).
|
|
50
|
+
- Keep the `.p8` file safe: it is the only way to make the next secret.
|
|
51
|
+
- Sign in with Apple to check it.
|
|
52
|
+
|
|
53
|
+
## Before you start
|
|
54
|
+
|
|
55
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed` true;
|
|
56
|
+
`data.gatewayUrl` gives `<gateway host>`. Not installed or no owner:
|
|
57
|
+
[gateway.md](gateway.md) first. `data.access.on` true: see Verify 4.
|
|
58
|
+
2. `pnpm everyapp signin show apple --json` (changes nothing; `Unknown
|
|
59
|
+
command signin`: `git pull` in the `every-app` folder). `data.status`
|
|
60
|
+
`off`: this file applies; `on`: it works, and only the secret may need
|
|
61
|
+
renewing (Rotate). `data.callbackUrl` is the Return URL,
|
|
62
|
+
`https://<gateway host>/api/auth/callback/apple`.
|
|
63
|
+
3. Ask whether they have a paid Apple Developer membership, and their
|
|
64
|
+
Team ID (top right of https://developer.apple.com/account, or
|
|
65
|
+
Membership details: ten characters). The Team ID is not a secret.
|
|
66
|
+
|
|
67
|
+
## Steps
|
|
68
|
+
|
|
69
|
+
1. **You:** give the person `<gateway host>` and `data.callbackUrl`.
|
|
70
|
+
2. **Person:** the App ID. https://developer.apple.com/account/resources/identifiers/list
|
|
71
|
+
→ **+** → **App IDs** → **Continue** → **App** → **Continue** →
|
|
72
|
+
Description (`Every App`), Bundle ID **Explicit** (`com.<their domain
|
|
73
|
+
reversed>.everyapp`) → tick **Sign in with Apple** (as a primary App
|
|
74
|
+
ID) → **Continue** → **Register**.
|
|
75
|
+
3. **Person:** the Services ID, which is the client id. Same page → **+** →
|
|
76
|
+
**Services IDs** → **Continue** → Description (people see it, for
|
|
77
|
+
example `Every App`), Identifier (`com.<their domain reversed>.gateway`;
|
|
78
|
+
not the App ID's) → **Continue** → **Register**. Open it from the list
|
|
79
|
+
(the filter top right switches to **Services IDs**) → tick **Sign in
|
|
80
|
+
with Apple** → **Configure** → Primary App ID: step 2's → the domain
|
|
81
|
+
`<gateway host>` and the return URL `data.callbackUrl` (Apple's help
|
|
82
|
+
calls the field **Website URLs**, a comma-separated list; some screens
|
|
83
|
+
show **Domains and Subdomains** and **Return URLs** apart) → **Done** →
|
|
84
|
+
**Continue** → **Save**. No file to upload to the domain.
|
|
85
|
+
4. **Person:** the key. https://developer.apple.com/account/resources/authkeys/list
|
|
86
|
+
→ **+** → Key Name (`Every App sign-in`) → tick **Sign in with Apple**
|
|
87
|
+
→ **Configure** → step 2's App ID → **Save** → **Continue** →
|
|
88
|
+
**Register** → **Download** (one chance: the file is
|
|
89
|
+
`AuthKey_<Key ID>.p8`, in Downloads) → **Done**. The Key ID is in the
|
|
90
|
+
file name and on the key's page. They tell you the Key ID, the Services
|
|
91
|
+
ID, the Team ID and the file's full path (none of these is a secret;
|
|
92
|
+
the file's contents are, and nobody opens it). Labels in this step are
|
|
93
|
+
from Apple's general key steps plus the runbook (unverified on Apple's
|
|
94
|
+
Sign in with Apple key page, which was missing when checked).
|
|
95
|
+
5. **You:** make the secret and set both at once, from the `every-app`
|
|
96
|
+
folder, in bash or zsh. The JWT goes straight into an environment
|
|
97
|
+
variable of the `signin add` process: it never appears on screen, on a
|
|
98
|
+
command line or in history. Never run the inner command on its own (it
|
|
99
|
+
prints the JWT) and never open the `.p8`. The key path must be absolute
|
|
100
|
+
(the command runs inside `packages/sdk`, which is where `jose` resolves).
|
|
101
|
+
```sh
|
|
102
|
+
APPLE_SIGNIN_CLIENT_SECRET="$(APPLE_KEY_FILE=/Users/<them>/Downloads/AuthKey_<Key ID>.p8 APPLE_KEY_ID=<Key ID> APPLE_TEAM_ID=<Team ID> APPLE_SERVICES_ID=<Services ID> pnpm --filter @every-app/sdk exec node --input-type=module -e "import { importPKCS8, SignJWT } from 'jose'; import { readFileSync } from 'node:fs'; const e = process.env; const key = await importPKCS8(readFileSync(e.APPLE_KEY_FILE, 'utf8'), 'ES256'); process.stdout.write(await new SignJWT({}).setProtectedHeader({ alg: 'ES256', kid: e.APPLE_KEY_ID }).setIssuer(e.APPLE_TEAM_ID).setSubject(e.APPLE_SERVICES_ID).setAudience('https://appleid.apple.com').setIssuedAt().setExpirationTime('180d').sign(key))")" pnpm everyapp signin add apple --client-id <Services ID> --client-secret-from-env APPLE_SIGNIN_CLIENT_SECRET --json
|
|
103
|
+
```
|
|
104
|
+
The JWT: header `alg` ES256, `kid` the Key ID; claims `iss` the Team ID,
|
|
105
|
+
`sub` the Services ID, `aud` `https://appleid.apple.com`, `iat` now,
|
|
106
|
+
`exp` 180 days out (Apple's limit is 15777000 seconds, about six
|
|
107
|
+
months). A wrong path or key fails inside `$(…)`, the variable is
|
|
108
|
+
empty and `signin add` stops with `SECRET_EMPTY`: nothing is written.
|
|
109
|
+
The done event's `data.status` is `"on"`.
|
|
110
|
+
6. **You:** tell the person to set a reminder for five months from today
|
|
111
|
+
(Rotate), and to move the `.p8` out of Downloads into a password
|
|
112
|
+
manager or another safe place outside the repository: Apple never lets
|
|
113
|
+
it be downloaded again.
|
|
114
|
+
|
|
115
|
+
## Verify
|
|
116
|
+
|
|
117
|
+
1. `pnpm everyapp signin list --json`: the `apple` entry has `status:
|
|
118
|
+
"on"` and `missing` empty.
|
|
119
|
+
2. The person's Apple ID address must be a member's or have a pending
|
|
120
|
+
invitation: `pnpm everyapp workspace invite <email>` for someone new.
|
|
121
|
+
3. **Person:** private window, `https://<gateway host>/sign-in` →
|
|
122
|
+
**Continue with Apple** → sign in with the Apple ID → **Share My
|
|
123
|
+
Email** → **Continue** → the Gateway, signed in.
|
|
124
|
+
4. Behind Cloudflare Access: the Apple address must be the one Access
|
|
125
|
+
knows the person by, and Access must let Apple's POST through. Run
|
|
126
|
+
`pnpm everyapp access show --json`: `data.applications.gateway.inSync`
|
|
127
|
+
false means the ring predates the Apple bypass; `pnpm everyapp access on --json`
|
|
128
|
+
adds it (also after `pnpm everyapp update`). A ring made by hand without
|
|
129
|
+
that bypass blocks Apple sign-in.
|
|
130
|
+
5. Logs (README's Verify 4): `signin.config_problem` for a malformed or
|
|
131
|
+
missing setting.
|
|
132
|
+
|
|
133
|
+
## If it fails
|
|
134
|
+
|
|
135
|
+
| error or symptom | meaning | what to do |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| no **Continue with Apple** button | a setting missing or malformed, or the version isn't live yet | `pnpm everyapp signin list --json`; logs' `signin.config_problem` |
|
|
138
|
+
| Apple's page says the redirect URL or client is invalid (wording unverified) | the Return URL isn't exactly `data.callbackUrl`, or the client id isn't the Services ID | step 3; the client id is the Services ID's identifier, not the App ID |
|
|
139
|
+
| `invalid_client` after Apple's sheet | the JWT is expired, signed with another key, or names another Team ID, Key ID or Services ID | step 5 again with the right values (Rotate) |
|
|
140
|
+
| works for months, then stops | the JWT expired | Rotate |
|
|
141
|
+
| Access's login page, or a 403, right after Apple's sheet | the outer ring blocks Apple's POST to `/api/auth/callback/apple` | `pnpm everyapp access on --json` |
|
|
142
|
+
| "You don't have an account on this gateway." | the address (often a `@privaterelay.appleid.com` one) isn't a member's or invited | **Share My Email**, or invite the address Apple returns |
|
|
143
|
+
| `SECRET_EMPTY` | the JWT command failed (path not absolute, wrong file, a typo in an id) | run step 5 again with corrected values; its stderr says what failed |
|
|
144
|
+
| `SIGNIN_VALUE_REQUIRED` | unattended, and the client id is neither given nor set | `data.rerun`, with `--client-id <Services ID>` |
|
|
145
|
+
|
|
146
|
+
## Rerun, rotate, remove
|
|
147
|
+
|
|
148
|
+
- Rerun: step 5 any time; values given replace, settings not given are
|
|
149
|
+
kept.
|
|
150
|
+
- Rotate, before six months pass: the same command with the same `.p8`,
|
|
151
|
+
leaving out `--client-id` (it is kept), or pipe the JWT into the one
|
|
152
|
+
setting (same environment variables and `node … -e "…"` as step 5):
|
|
153
|
+
`APPLE_KEY_FILE=… pnpm --filter @every-app/sdk exec node --input-type=module -e "…" | pnpm everyapp secret set APPLE_SIGNIN_CLIENT_SECRET --json`.
|
|
154
|
+
A lost `.p8`: the person makes a new key (step 4), revokes the old one,
|
|
155
|
+
and step 5 runs with the new Key ID.
|
|
156
|
+
- Remove, only when asked: `pnpm everyapp signin remove apple --json --yes`
|
|
157
|
+
(`--yes` only when they said so). Accounts made with it stay. Then the
|
|
158
|
+
person revokes the key and deletes the Services ID in Apple's console.
|
|
159
|
+
|
|
160
|
+
## Sources
|
|
161
|
+
|
|
162
|
+
- https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret
|
|
163
|
+
(the JWT's header and claims; at most 15777000 seconds; the page didn't
|
|
164
|
+
render when fetched, so the claim list is checked against the runbook
|
|
165
|
+
and the test below)
|
|
166
|
+
- https://developer.apple.com/help/account/capabilities/configure-sign-in-with-apple-for-the-web
|
|
167
|
+
(Services ID → Sign in with Apple → Configure → primary App ID → Website
|
|
168
|
+
URLs → Done → Continue → Save; no domain file)
|
|
169
|
+
- https://developer.apple.com/help/account/capabilities/about-sign-in-with-apple
|
|
170
|
+
(primary App ID; Account Holder or Admin)
|
|
171
|
+
- https://developer.apple.com/help/account/keys/create-a-private-key
|
|
172
|
+
(Keys → +; download once, `.p8` in Downloads)
|
|
173
|
+
- https://developer.apple.com/support/fee-waiver (the 99 USD yearly fee)
|
|
174
|
+
- Step 5's command was run on 2026-10-24 against a throwaway P-256 key:
|
|
175
|
+
the JWT verified with `jose` (ES256, `kid`, `iss`, `sub`, `aud`, `exp -
|
|
176
|
+
iat` = 15552000), and pnpm prints nothing else on stdout.
|
|
177
|
+
- `docs/runbooks/sign-in-providers.md` ("Apple") and
|
|
178
|
+
`packages/sdk/src/internal/signInProviders.ts`
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Sign in with Cloudflare on the Gateway
|
|
2
|
+
|
|
3
|
+
Read [README.md](README.md) first: the rules every setup follows.
|
|
4
|
+
|
|
5
|
+
Puts **Continue with Cloudflare** on the Gateway's sign-in page, beside
|
|
6
|
+
the password, for the members of the person's Cloudflare account. At the
|
|
7
|
+
end the person has a private OAuth client in that account, its two
|
|
8
|
+
settings on the Gateway, and has signed in with Cloudflare once.
|
|
9
|
+
`pnpm everyapp signin show cloudflare --json` names this file as `setup`,
|
|
10
|
+
and the CLI prints `Guide: .claude/skills/everyapp/setup/signin-cloudflare.md`.
|
|
11
|
+
|
|
12
|
+
Who gets in doesn't change: Cloudflare only says who someone is. A member
|
|
13
|
+
of the Gateway signs in; someone with a pending invitation for that exact
|
|
14
|
+
address joins with the invited role; everyone else is refused ("You don't
|
|
15
|
+
have an account on this gateway.") and nothing is created. An existing
|
|
16
|
+
password account connects Cloudflare once, by signing in with the password
|
|
17
|
+
when the page asks. The rules: `docs/runbooks/sign-in-providers.md`.
|
|
18
|
+
|
|
19
|
+
**Keep the client private** (the default). Cloudflare's `/user` answer has
|
|
20
|
+
no "verified" flag; the address is trusted only because a private client
|
|
21
|
+
can be authorized by nobody but the Cloudflare account's members, who got
|
|
22
|
+
in by accepting an invitation sent to that address. Making a client public
|
|
23
|
+
is permanent and would let any Cloudflare user present an address nobody
|
|
24
|
+
checked. Everyone outside the Cloudflare account uses another provider, a
|
|
25
|
+
password, or Access's one-time PIN.
|
|
26
|
+
|
|
27
|
+
Not the same as Access's "Sign in with Cloudflare" ([access.md](access.md)):
|
|
28
|
+
that one is a login *in front of* the Gateway, at Cloudflare's edge, for
|
|
29
|
+
every page and app. This one is a button *on* the Gateway's sign-in page,
|
|
30
|
+
like Google's. Both can be on; with Access on, this button is rarely needed.
|
|
31
|
+
|
|
32
|
+
## Settings
|
|
33
|
+
|
|
34
|
+
| Name | Required | Flag | What it holds |
|
|
35
|
+
| --------------------------------- | -------- | -------------------------------- | ------------------------------------------------ |
|
|
36
|
+
| `CLOUDFLARE_SIGNIN_CLIENT_ID` | required | `--client-id <id>` | the OAuth client's Client ID; not a secret |
|
|
37
|
+
| `CLOUDFLARE_SIGNIN_CLIENT_SECRET` | required | `--client-secret-from-env <VAR>` | the OAuth client's Client Secret, shown once |
|
|
38
|
+
|
|
39
|
+
Scope asked for: `user-details.read` (User Details Read), nothing an app
|
|
40
|
+
could act with. The email is the Cloudflare API's `/user` `email`.
|
|
41
|
+
|
|
42
|
+
## Costs and plans
|
|
43
|
+
|
|
44
|
+
No price on Cloudflare's OAuth client page, and no plan named (free as far
|
|
45
|
+
as checked; unverified). Creating a client needs the Super Administrator,
|
|
46
|
+
Administrator or OAuth Client Write role on the account.
|
|
47
|
+
|
|
48
|
+
## Only a person can
|
|
49
|
+
|
|
50
|
+
- Sign in to the Cloudflare dashboard and create the OAuth client.
|
|
51
|
+
- Copy the Client Secret off the screen (shown once).
|
|
52
|
+
- Type it at the hidden prompt of `pnpm everyapp signin add cloudflare`.
|
|
53
|
+
- Sign in with Cloudflare to check it; decide never to make the client
|
|
54
|
+
public.
|
|
55
|
+
|
|
56
|
+
## Before you start
|
|
57
|
+
|
|
58
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed` true;
|
|
59
|
+
`data.gatewayUrl` gives `<gateway host>`. Not installed or no owner:
|
|
60
|
+
[gateway.md](gateway.md) first. `data.access.on` true: see Verify 4.
|
|
61
|
+
2. `pnpm everyapp signin show cloudflare --json` (changes nothing;
|
|
62
|
+
`Unknown command signin`: `git pull` in the `every-app` folder).
|
|
63
|
+
`data.status` `off`: this file applies; `needs setup`: `data.missing`
|
|
64
|
+
says what is left (step 3); `on`: ask before replacing.
|
|
65
|
+
`data.callbackUrl` is `https://<gateway host>/api/auth/callback/cloudflare`.
|
|
66
|
+
3. Ask who should use it: only people who are members of their Cloudflare
|
|
67
|
+
account can. For anyone else, suggest Google, Microsoft or the password.
|
|
68
|
+
|
|
69
|
+
## Steps
|
|
70
|
+
|
|
71
|
+
1. **You:** give the person `data.callbackUrl`.
|
|
72
|
+
2. **Person:** in the Cloudflare dashboard, the account the Gateway runs
|
|
73
|
+
in: https://dash.cloudflare.com/?to=/:account/oauth-clients (or
|
|
74
|
+
**Manage Account** → **OAuth clients**) → **Create client**. Client name
|
|
75
|
+
(`Every App`); response type code, grant type **Authorization Code**;
|
|
76
|
+
token authentication method `client_secret_basic`; redirect URL the
|
|
77
|
+
exact URL from step 1 → **Continue** → scopes: **User Details Read**,
|
|
78
|
+
left **Required** (an optional one can be declined, and then sign-in
|
|
79
|
+
can't finish) → **Create client**. The page shows the **Client ID** and
|
|
80
|
+
the **Client Secret**, the secret only now. Leave its visibility
|
|
81
|
+
private: never **Change Visibility**.
|
|
82
|
+
3. **Person:** in a second terminal in the `every-app` folder:
|
|
83
|
+
```sh
|
|
84
|
+
pnpm everyapp signin add cloudflare
|
|
85
|
+
```
|
|
86
|
+
It asks for the Client ID (shown as typed) and the Client Secret
|
|
87
|
+
(hidden: pasting works, nothing is echoed), and writes both in one new
|
|
88
|
+
Gateway Worker version, live on the next request. They tell you when
|
|
89
|
+
it's done.
|
|
90
|
+
Another way: in that terminal, the hidden read (bash or zsh:
|
|
91
|
+
`read -rs CLOUDFLARE_SIGNIN_CLIENT_SECRET && export CLOUDFLARE_SIGNIN_CLIENT_SECRET`;
|
|
92
|
+
fish: `read -s -x CLOUDFLARE_SIGNIN_CLIENT_SECRET`), paste, Enter, then
|
|
93
|
+
`pnpm everyapp signin add cloudflare --client-id <Client ID> --client-secret-from-env CLOUDFLARE_SIGNIN_CLIENT_SECRET --json`
|
|
94
|
+
and `unset CLOUDFLARE_SIGNIN_CLIENT_SECRET` (fish: `set -e CLOUDFLARE_SIGNIN_CLIENT_SECRET`).
|
|
95
|
+
|
|
96
|
+
## Verify
|
|
97
|
+
|
|
98
|
+
1. `pnpm everyapp signin list --json`: the `cloudflare` entry has `status:
|
|
99
|
+
"on"` and `missing` empty.
|
|
100
|
+
2. The person's Cloudflare login address must be a Gateway member's or
|
|
101
|
+
have a pending invitation: `pnpm everyapp workspace invite <email>` for
|
|
102
|
+
someone new (who must also be a member of the Cloudflare account).
|
|
103
|
+
3. **Person:** private window, `https://<gateway host>/sign-in` →
|
|
104
|
+
**Continue with Cloudflare** → Cloudflare's login → the consent screen
|
|
105
|
+
→ allow → the Gateway, signed in.
|
|
106
|
+
4. Behind Cloudflare Access: nothing else changes, but the Cloudflare
|
|
107
|
+
address must be the one Access knows the person by.
|
|
108
|
+
5. Logs (README's Verify 4): `signin.config_problem` for a malformed or
|
|
109
|
+
missing setting.
|
|
110
|
+
|
|
111
|
+
## If it fails
|
|
112
|
+
|
|
113
|
+
| error or symptom | meaning | what to do |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| no **Continue with Cloudflare** button | a setting missing or malformed, or the version isn't live yet | `pnpm everyapp signin list --json`; logs' `signin.config_problem` |
|
|
116
|
+
| Cloudflare refuses the redirect (wording unverified) | the client's redirect URL isn't exactly `data.callbackUrl` | fix it on the client |
|
|
117
|
+
| Cloudflare refuses someone at its consent screen | they aren't a member of the Cloudflare account (a private client) | invite them to the Cloudflare account, or they use another way in |
|
|
118
|
+
| the Gateway refuses after consent, or the profile can't be read | **User Details Read** missing or made optional and declined | edit the client: the scope, **Required** |
|
|
119
|
+
| "You don't have an account on this gateway." | their Cloudflare address isn't a member's or invited | `pnpm everyapp workspace invite <email>` with the Cloudflare address |
|
|
120
|
+
| `SIGNIN_VALUE_REQUIRED` / `SECRET_EMPTY` | unattended, a required setting neither given nor set / the env variable empty in that shell | `data.rerun` with the flag / the hidden read again in the same terminal |
|
|
121
|
+
| Create client is missing or refused | the person's role on the account lacks OAuth Client Write | an Administrator creates it, or grants the role |
|
|
122
|
+
|
|
123
|
+
## Rerun, rotate, remove
|
|
124
|
+
|
|
125
|
+
- Rerun: `pnpm everyapp signin add cloudflare` any time; values given
|
|
126
|
+
replace, settings not given are kept (Enter at a prompt keeps it).
|
|
127
|
+
- Rotate: rotate the secret on the client's page in the dashboard (a lost
|
|
128
|
+
secret can only be rotated), then `pnpm everyapp signin add cloudflare`
|
|
129
|
+
with Enter at the Client ID prompt, or the hidden read and
|
|
130
|
+
`pnpm everyapp signin add cloudflare --client-secret-from-env CLOUDFLARE_SIGNIN_CLIENT_SECRET --json`.
|
|
131
|
+
- Remove, only when asked: `pnpm everyapp signin remove cloudflare --json --yes`
|
|
132
|
+
(`--yes` only when they said so). Accounts made with it stay. Then
|
|
133
|
+
delete the client in **Manage Account** → **OAuth clients**.
|
|
134
|
+
|
|
135
|
+
## Sources
|
|
136
|
+
|
|
137
|
+
- https://developers.cloudflare.com/fundamentals/oauth/create-an-oauth-client/
|
|
138
|
+
(Manage Account → OAuth clients → Create client; the three roles;
|
|
139
|
+
Authorization Code only; `client_secret_basic`; Required scopes; secret
|
|
140
|
+
shown once; private by default, public permanent, Change Visibility; no
|
|
141
|
+
price on the page)
|
|
142
|
+
- https://developers.cloudflare.com/api/resources/user/methods/get/
|
|
143
|
+
(`GET /user`, User Details Read)
|
|
144
|
+
- https://better-auth.com/docs/authentication/cloudflare (`/user` for the
|
|
145
|
+
email; keep `user-details.read` required)
|
|
146
|
+
- `docs/runbooks/sign-in-providers.md` ("Cloudflare", "Sign in with
|
|
147
|
+
Cloudflare: Access's identity provider versus this") and
|
|
148
|
+
`packages/sdk/src/internal/signInProviders.ts`
|