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,139 @@
|
|
|
1
|
+
# Sign in with GitHub on the Gateway
|
|
2
|
+
|
|
3
|
+
Read [README.md](README.md) first: the rules every setup follows.
|
|
4
|
+
|
|
5
|
+
Puts **Continue with GitHub** on the Gateway's sign-in page, beside the
|
|
6
|
+
password. At the end the person has a GitHub OAuth App, its two settings
|
|
7
|
+
on the Gateway, and has signed in with GitHub once.
|
|
8
|
+
`pnpm everyapp signin show github --json` names this file as `setup`, and
|
|
9
|
+
the CLI prints `Guide: .claude/skills/everyapp/setup/signin-github.md`.
|
|
10
|
+
|
|
11
|
+
Who gets in doesn't change: GitHub only says who someone is, by their
|
|
12
|
+
primary address, and only when GitHub marks it verified. A member signs
|
|
13
|
+
in; someone with a pending invitation for that exact address joins with
|
|
14
|
+
the invited role; everyone else is refused ("You don't have an account on
|
|
15
|
+
this gateway.") and nothing is created. An existing password account
|
|
16
|
+
connects GitHub once, by signing in with the password when the page asks.
|
|
17
|
+
The rules: `docs/runbooks/sign-in-providers.md`, "Who gets in".
|
|
18
|
+
|
|
19
|
+
An **OAuth App**, not a GitHub App: the two live side by side under
|
|
20
|
+
Developer settings, and only an OAuth App fits here.
|
|
21
|
+
|
|
22
|
+
## Settings
|
|
23
|
+
|
|
24
|
+
| Name | Required | Flag | What it holds |
|
|
25
|
+
| ----------------------------- | -------- | -------------------------------- | ---------------------------------------------- |
|
|
26
|
+
| `GITHUB_SIGNIN_CLIENT_ID` | required | `--client-id <id>` | the OAuth App's Client ID; not a secret |
|
|
27
|
+
| `GITHUB_SIGNIN_CLIENT_SECRET` | required | `--client-secret-from-env <VAR>` | a client secret generated on the OAuth App |
|
|
28
|
+
|
|
29
|
+
Scopes asked for: `read:user user:email`. The email is the person's
|
|
30
|
+
primary address from GitHub's emails API, counted only when verified.
|
|
31
|
+
|
|
32
|
+
## Costs and plans
|
|
33
|
+
|
|
34
|
+
Free, on any GitHub account. An OAuth App can belong to the person's own
|
|
35
|
+
account or to an organization they administer (up to 100 per owner); an
|
|
36
|
+
organization owner keeps it from depending on one person.
|
|
37
|
+
|
|
38
|
+
## Only a person can
|
|
39
|
+
|
|
40
|
+
- Sign in to GitHub and register the OAuth App.
|
|
41
|
+
- Generate the client secret and copy it off the screen.
|
|
42
|
+
- Type the secret at the hidden prompt of `pnpm everyapp signin add github`.
|
|
43
|
+
- Sign in with GitHub to check it.
|
|
44
|
+
|
|
45
|
+
## Before you start
|
|
46
|
+
|
|
47
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed` true;
|
|
48
|
+
`data.gatewayUrl` gives `<gateway host>`. Not installed or no owner:
|
|
49
|
+
[gateway.md](gateway.md) first. `data.access.on` true: see Verify 4.
|
|
50
|
+
2. `pnpm everyapp signin show github --json` (changes nothing; `Unknown
|
|
51
|
+
command signin`: `git pull` in the `every-app` folder). `data.status`
|
|
52
|
+
`off`: this file applies; `needs setup`: `data.missing` says what is
|
|
53
|
+
left (step 4); `on`: ask before replacing. `data.callbackUrl` is
|
|
54
|
+
`https://<gateway host>/api/auth/callback/github`.
|
|
55
|
+
3. Ask whether the app should belong to their own account or to an
|
|
56
|
+
organization they administer.
|
|
57
|
+
|
|
58
|
+
## Steps
|
|
59
|
+
|
|
60
|
+
1. **You:** give the person `data.callbackUrl`.
|
|
61
|
+
2. **Person:** open https://github.com/settings/developers (for an
|
|
62
|
+
organization: the organization's **Settings** → **Developer settings**
|
|
63
|
+
→ **OAuth Apps**) → **New OAuth App** (**Register a new application**
|
|
64
|
+
when there is none yet). Application name (people see it, for example
|
|
65
|
+
`Every App`), Homepage URL `https://<gateway host>`, **Authorization
|
|
66
|
+
callback URL** the exact URL from step 1. Leave **Enable Device Flow**
|
|
67
|
+
off. **Register application**. The app's page shows the **Client ID**.
|
|
68
|
+
3. **Person:** on that page, **Generate a new client secret** (GitHub may
|
|
69
|
+
ask for their password or 2FA again). The secret is shown once; keep
|
|
70
|
+
the page open for step 4.
|
|
71
|
+
4. **Person:** in a second terminal in the `every-app` folder:
|
|
72
|
+
```sh
|
|
73
|
+
pnpm everyapp signin add github
|
|
74
|
+
```
|
|
75
|
+
It asks for the Client ID (shown as typed) and the client secret
|
|
76
|
+
(hidden: pasting works, nothing is echoed), and writes both in one new
|
|
77
|
+
Gateway Worker version, live on the next request. They tell you when
|
|
78
|
+
it's done.
|
|
79
|
+
Another way: in that terminal, the hidden read (bash or zsh:
|
|
80
|
+
`read -rs GITHUB_SIGNIN_CLIENT_SECRET && export GITHUB_SIGNIN_CLIENT_SECRET`;
|
|
81
|
+
fish: `read -s -x GITHUB_SIGNIN_CLIENT_SECRET`), paste, Enter, then
|
|
82
|
+
`pnpm everyapp signin add github --client-id <Client ID> --client-secret-from-env GITHUB_SIGNIN_CLIENT_SECRET --json`
|
|
83
|
+
and `unset GITHUB_SIGNIN_CLIENT_SECRET` (fish: `set -e GITHUB_SIGNIN_CLIENT_SECRET`).
|
|
84
|
+
|
|
85
|
+
## Verify
|
|
86
|
+
|
|
87
|
+
1. `pnpm everyapp signin list --json`: the `github` entry has `status:
|
|
88
|
+
"on"` and `missing` empty.
|
|
89
|
+
2. The person's GitHub primary address (https://github.com/settings/emails,
|
|
90
|
+
marked Primary, not Unverified) must be a member's or have a pending
|
|
91
|
+
invitation: `pnpm everyapp workspace invite <email>` for someone new.
|
|
92
|
+
3. **Person:** private window, `https://<gateway host>/sign-in` →
|
|
93
|
+
**Continue with GitHub** → **Authorize** on GitHub's page → the
|
|
94
|
+
Gateway, signed in.
|
|
95
|
+
4. Behind Cloudflare Access: nothing else changes, but the GitHub primary
|
|
96
|
+
address must be the one Access knows the person by.
|
|
97
|
+
5. Logs (README's Verify 4): `signin.config_problem` for a malformed or
|
|
98
|
+
missing setting.
|
|
99
|
+
|
|
100
|
+
## If it fails
|
|
101
|
+
|
|
102
|
+
| error or symptom | meaning | what to do |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| no **Continue with GitHub** button | a setting missing or malformed, or the version isn't live yet | `pnpm everyapp signin list --json`; logs' `signin.config_problem` |
|
|
105
|
+
| `redirect_uri_mismatch`: "The redirect_uri MUST match the registered callback URL for this application." | the Authorization callback URL isn't exactly `data.callbackUrl` | fix it on the OAuth App's page → **Update application** |
|
|
106
|
+
| "The sign-in provider hasn't verified this email address." | the primary address is unverified at GitHub | the person verifies it in GitHub's Emails settings, or makes a verified one primary |
|
|
107
|
+
| "You don't have an account on this gateway." | the primary address isn't a member's or invited | invite that address (people often sign up to GitHub with a personal one) |
|
|
108
|
+
| `incorrect_client_credentials` | the secret doesn't belong to this Client ID | Rotate |
|
|
109
|
+
| `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 |
|
|
110
|
+
| `SIGNIN_FLAG_NOT_APPLICABLE` | `--tenant`, `--issuer` or `--label` given for GitHub | drop it |
|
|
111
|
+
|
|
112
|
+
## Rerun, rotate, remove
|
|
113
|
+
|
|
114
|
+
- Rerun: `pnpm everyapp signin add github` any time; values given replace,
|
|
115
|
+
settings not given are kept (Enter at a prompt keeps it).
|
|
116
|
+
- Rotate: **Generate a new client secret** on the OAuth App (an app holds
|
|
117
|
+
several at once), then `pnpm everyapp signin add github` with Enter at
|
|
118
|
+
the Client ID prompt, or the hidden read and
|
|
119
|
+
`pnpm everyapp signin add github --client-secret-from-env GITHUB_SIGNIN_CLIENT_SECRET --json`.
|
|
120
|
+
Then **Delete** the old secret on GitHub.
|
|
121
|
+
- Remove, only when asked: `pnpm everyapp signin remove github --json --yes`
|
|
122
|
+
(`--yes` only when they said so). Accounts made with it stay. Then delete
|
|
123
|
+
the OAuth App (its page → **Advanced** → **Delete application**).
|
|
124
|
+
|
|
125
|
+
## Sources
|
|
126
|
+
|
|
127
|
+
- https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app
|
|
128
|
+
(Settings → Developer settings → OAuth apps → New OAuth App; fields;
|
|
129
|
+
organization owners; 100 apps per owner)
|
|
130
|
+
- https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps
|
|
131
|
+
(`read:user`, `user:email`)
|
|
132
|
+
- https://docs.github.com/en/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-authorization-request-errors
|
|
133
|
+
and https://docs.github.com/en/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-oauth-app-access-token-request-errors
|
|
134
|
+
(redirect URI mismatch, `incorrect_client_credentials`)
|
|
135
|
+
- The **Generate a new client secret**, **Advanced** and **Delete
|
|
136
|
+
application** labels are from GitHub's UI as the runbook records them;
|
|
137
|
+
GitHub's docs pages above don't show them (unverified).
|
|
138
|
+
- `docs/runbooks/sign-in-providers.md` ("GitHub") and
|
|
139
|
+
`packages/sdk/src/internal/signInProviders.ts`
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Sign in with Google on the Gateway
|
|
2
|
+
|
|
3
|
+
Read [README.md](README.md) first: the rules every setup follows.
|
|
4
|
+
|
|
5
|
+
Puts **Continue with Google** on the Gateway's sign-in page, beside the
|
|
6
|
+
password. At the end the person has an OAuth client in a Google Cloud
|
|
7
|
+
project, its two settings on the Gateway, and has signed in with Google
|
|
8
|
+
once. `pnpm everyapp signin show google --json` names this file as
|
|
9
|
+
`setup`, and the CLI prints `Guide: .claude/skills/everyapp/setup/signin-google.md`.
|
|
10
|
+
|
|
11
|
+
Who gets in doesn't change: Google only says who someone is (an email it
|
|
12
|
+
verified). A member signs in; someone with a pending invitation for that
|
|
13
|
+
exact address joins with the invited role, no link or password needed;
|
|
14
|
+
everyone else is refused ("You don't have an account on this gateway.")
|
|
15
|
+
and nothing is created. An existing password account connects Google once:
|
|
16
|
+
the first "Continue with Google" says an account exists, the person signs
|
|
17
|
+
in with the password and the page connects Google (or **Connect Google** in
|
|
18
|
+
the account menu). The full rules: `docs/runbooks/sign-in-providers.md`,
|
|
19
|
+
"Who gets in".
|
|
20
|
+
|
|
21
|
+
This is not the Google client apps use for Gmail and Calendar
|
|
22
|
+
(`GOOGLE_WORKSPACE_*`, [google.md](google.md)). Make a separate client
|
|
23
|
+
here. When that other client's project is *External* and still in Testing
|
|
24
|
+
(its Gmail scope waits on Google's verification), make this one in a
|
|
25
|
+
separate project too: publishing is per project, and this one must be
|
|
26
|
+
published (step 2).
|
|
27
|
+
|
|
28
|
+
## Settings
|
|
29
|
+
|
|
30
|
+
| Name | Required | Flag | What it holds |
|
|
31
|
+
| ----------------------------- | -------- | -------------------------------- | --------------------------------------------------------------- |
|
|
32
|
+
| `GOOGLE_SIGNIN_CLIENT_ID` | required | `--client-id <id>` | the client's Client ID, ending in `.apps.googleusercontent.com`; not a secret |
|
|
33
|
+
| `GOOGLE_SIGNIN_CLIENT_SECRET` | required | `--client-secret-from-env <VAR>` | the client's Client secret, shown once at creation |
|
|
34
|
+
|
|
35
|
+
Scopes asked for: `openid email profile`, identity only. The email is the
|
|
36
|
+
ID token's `email`, and only when `email_verified` is true (every Google
|
|
37
|
+
account's own address is).
|
|
38
|
+
|
|
39
|
+
## Costs and plans
|
|
40
|
+
|
|
41
|
+
Free: a Google Cloud project and an OAuth client for sign-in need no
|
|
42
|
+
billing account. The identity scopes are not sensitive, so the consent
|
|
43
|
+
screen can be published without Google's sensitive-scope verification
|
|
44
|
+
(whether Google still asks for brand verification before showing the
|
|
45
|
+
app's name and logo: unverified).
|
|
46
|
+
|
|
47
|
+
## Only a person can
|
|
48
|
+
|
|
49
|
+
- Sign in to Google Cloud, create the project and the consent screen.
|
|
50
|
+
- Create the OAuth client and copy its secret off the screen.
|
|
51
|
+
- Publish the consent screen (In production).
|
|
52
|
+
- Type the secret at the hidden prompt of `pnpm everyapp signin add google`.
|
|
53
|
+
- Sign in with Google to check it.
|
|
54
|
+
|
|
55
|
+
## Before you start
|
|
56
|
+
|
|
57
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed` true;
|
|
58
|
+
`data.gatewayUrl` gives `<gateway host>`. Not installed, or no owner:
|
|
59
|
+
[gateway.md](gateway.md) first (nobody signs in with a provider before
|
|
60
|
+
the Gateway is claimed). `data.access.on` true: see Verify 4.
|
|
61
|
+
2. `pnpm everyapp signin show google --json` (changes nothing). `Unknown
|
|
62
|
+
command signin` (exit 251): the checkout predates sign-in providers;
|
|
63
|
+
`git pull` in the `every-app` folder, then again. Read `data.status`:
|
|
64
|
+
`off`, this file applies; `needs setup`, `data.missing` names what is
|
|
65
|
+
left (go to step 3); `on`, it works already: ask before replacing (a new
|
|
66
|
+
secret for the same client is harmless, see Rotate). `data.callbackUrl`
|
|
67
|
+
is the redirect URL, `https://<gateway host>/api/auth/callback/google`.
|
|
68
|
+
3. Ask whether a Google Cloud project for this exists already, and whether
|
|
69
|
+
everyone who will sign in is in one Google Workspace organization
|
|
70
|
+
(then the consent screen can be *Internal*: no publishing needed).
|
|
71
|
+
|
|
72
|
+
## Steps
|
|
73
|
+
|
|
74
|
+
1. **You:** give the person `data.callbackUrl` from Before you start 2.
|
|
75
|
+
2. **Person:** the consent screen, once per project. Open
|
|
76
|
+
https://console.cloud.google.com/auth/overview (pick or create the
|
|
77
|
+
project in the top bar) → **Get started**: App name (what people see,
|
|
78
|
+
for example `Every App`), User support email → Next; Audience
|
|
79
|
+
*Internal* (one Workspace organization) or *External* → Next; Contact
|
|
80
|
+
Information → Next; agree to the User Data Policy → **Create**.
|
|
81
|
+
*External* only: https://console.cloud.google.com/auth/audience →
|
|
82
|
+
**Publish app** → Confirm. While it says Testing, only the test users
|
|
83
|
+
listed there can sign in. Done when the Audience page says "In
|
|
84
|
+
production" (or the app is *Internal*).
|
|
85
|
+
3. **Person:** the client. https://console.cloud.google.com/auth/clients →
|
|
86
|
+
**Create client** → Application type **Web application** → Name (for
|
|
87
|
+
example `Every App sign-in`) → **Authorized redirect URIs** → **Add
|
|
88
|
+
URI** → the exact URL from step 1 → **Create**. No JavaScript origins.
|
|
89
|
+
A dialog shows the Client ID and the Client secret; keep it open (the
|
|
90
|
+
secret is shown only now; afterwards only its last four characters).
|
|
91
|
+
Redirect URI changes can take 5 minutes to a few hours to apply.
|
|
92
|
+
4. **Person:** set both on the Gateway, in a second terminal in the
|
|
93
|
+
`every-app` folder:
|
|
94
|
+
```sh
|
|
95
|
+
pnpm everyapp signin add google
|
|
96
|
+
```
|
|
97
|
+
It prints where the client is made and the redirect URL, then asks for
|
|
98
|
+
the Client ID (shown as typed) and the Client secret (hidden: pasting
|
|
99
|
+
works, nothing is echoed). It ends with Google on and the sign-in URL.
|
|
100
|
+
They tell you when it's done. Both go on in one new version of the
|
|
101
|
+
Gateway Worker, live on the next request.
|
|
102
|
+
Another way, when they'd rather not use the prompts: in that terminal
|
|
103
|
+
they read the secret in hidden (bash or zsh:
|
|
104
|
+
`read -rs GOOGLE_SIGNIN_CLIENT_SECRET && export GOOGLE_SIGNIN_CLIENT_SECRET`;
|
|
105
|
+
fish: `read -s -x GOOGLE_SIGNIN_CLIENT_SECRET`), paste, press Enter, then
|
|
106
|
+
run `pnpm everyapp signin add google --client-id <Client ID> --client-secret-from-env GOOGLE_SIGNIN_CLIENT_SECRET --json`
|
|
107
|
+
and `unset GOOGLE_SIGNIN_CLIENT_SECRET` (fish: `set -e GOOGLE_SIGNIN_CLIENT_SECRET`).
|
|
108
|
+
The Client ID isn't a secret, so it may sit on the command line.
|
|
109
|
+
5. **Person:** close the client dialog (Google keeps no copy of the secret
|
|
110
|
+
to show again; a lost one is replaced in Rotate).
|
|
111
|
+
|
|
112
|
+
## Verify
|
|
113
|
+
|
|
114
|
+
1. `pnpm everyapp signin list --json`: the `google` entry has `status:
|
|
115
|
+
"on"` and `missing` empty.
|
|
116
|
+
2. The person's address must be a member's or have a pending invitation.
|
|
117
|
+
For someone new: `pnpm everyapp workspace invite <email>` (it prints an
|
|
118
|
+
invitation link; for Google sign-in they don't need it, the pending
|
|
119
|
+
invitation for that exact address is enough).
|
|
120
|
+
3. **Person:** in a private window open `https://<gateway host>/sign-in`:
|
|
121
|
+
**Continue with Google** is there. Select it, choose the Google account,
|
|
122
|
+
and they land on the Gateway signed in. An existing password account
|
|
123
|
+
first sees "An account with this email already exists": sign in with
|
|
124
|
+
the password once, and the page connects Google.
|
|
125
|
+
4. Behind Cloudflare Access (`data.access.on`): nothing else changes, but
|
|
126
|
+
the Google account's address must be the one Access knows the person
|
|
127
|
+
by; otherwise the page says Access knows them by another email.
|
|
128
|
+
5. Logs (README's Verify 4): `signin.config_problem` names a malformed or
|
|
129
|
+
missing setting, the reason the button stays off.
|
|
130
|
+
|
|
131
|
+
## If it fails
|
|
132
|
+
|
|
133
|
+
| error or symptom | meaning | what to do |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| no **Continue with Google** button | the provider isn't on: a setting missing or malformed, or the new version isn't live yet | `pnpm everyapp signin list --json` (`missing`); the logs' `signin.config_problem`; `signin add google` again |
|
|
136
|
+
| Google's "Error 400: redirect_uri_mismatch" | the client's redirect URI isn't exactly `data.callbackUrl` | fix it under Authorized redirect URIs (no trailing slash; `https`); wait a few minutes |
|
|
137
|
+
| Google refuses someone before the Gateway sees them (its wording for people not on Test users: unverified) | the consent screen is *External* and still in Testing: only test users get in, and their grants expire after seven days | **Publish app** (step 2) |
|
|
138
|
+
| Google's `org_internal` error | the consent screen is *Internal* and the person's Google account is outside that organization | they use their Workspace account, or the audience becomes *External* and published |
|
|
139
|
+
| "You don't have an account on this gateway." | the address Google returned isn't a member's and has no pending invitation | `pnpm everyapp workspace invite <email>` with the Google address, exactly as Google spells it |
|
|
140
|
+
| "The sign-in provider hasn't verified this email address." | Google reports the address unverified (a secondary, unverified address on a non-Gmail account) | the person verifies it at Google, or uses another account |
|
|
141
|
+
| "An account with this email already exists." | a password account has that address and isn't verified yet | sign in with the password once; the page connects Google |
|
|
142
|
+
| `SIGNIN_VALUE_REQUIRED` | unattended, and a required setting has no flag and isn't set yet | `data.missing` names it; rerun `data.rerun` with the flag |
|
|
143
|
+
| `SECRET_EMPTY` | the `--client-secret-from-env` variable is empty or unset in that shell | the hidden read again, in the same terminal |
|
|
144
|
+
| `SIGNIN_FLAG_NOT_APPLICABLE` | a flag Google doesn't take (`--tenant`, `--issuer`, `--label`) | drop it |
|
|
145
|
+
| Google's "invalid_client" | the secret doesn't belong to this Client ID (a typo, or another client's) | Rotate |
|
|
146
|
+
|
|
147
|
+
## Rerun, rotate, remove
|
|
148
|
+
|
|
149
|
+
- Rerun: `pnpm everyapp signin add google` any time. A value given
|
|
150
|
+
replaces; a setting not given and already set is kept (at the prompt,
|
|
151
|
+
Enter keeps it). The done event's `data.set` and `data.kept` say which.
|
|
152
|
+
- Rotate: on the client's page (https://console.cloud.google.com/auth/clients
|
|
153
|
+
→ the client) add a new secret, then the person runs
|
|
154
|
+
`pnpm everyapp signin add google` and presses Enter at the Client ID
|
|
155
|
+
prompt, pasting only the new secret; or the hidden read and
|
|
156
|
+
`pnpm everyapp signin add google --client-secret-from-env GOOGLE_SIGNIN_CLIENT_SECRET --json`
|
|
157
|
+
(the Client ID is kept). Then disable and delete the old secret there.
|
|
158
|
+
- Remove, only when the person asks:
|
|
159
|
+
`pnpm everyapp signin remove google --json --yes` (`--yes` only when they
|
|
160
|
+
said so; at a terminal it asks). Every `GOOGLE_SIGNIN_*` setting goes in
|
|
161
|
+
one version; people who signed in with Google keep their accounts and
|
|
162
|
+
use another way in. Then delete the client in the console.
|
|
163
|
+
|
|
164
|
+
## Sources
|
|
165
|
+
|
|
166
|
+
- https://support.google.com/cloud/answer/15549257 (Clients page, Create
|
|
167
|
+
client, Web application, Authorized redirect URIs, the secret shown once,
|
|
168
|
+
5 minutes to a few hours)
|
|
169
|
+
- https://developers.google.com/identity/openid-connect/openid-connect
|
|
170
|
+
(`openid email profile`, `email_verified`)
|
|
171
|
+
- https://support.google.com/cloud/answer/13464321 (which scopes need
|
|
172
|
+
verification; the identity scopes are not sensitive)
|
|
173
|
+
- https://support.google.com/cloud/answer/15549945 (Manage App Audience:
|
|
174
|
+
Internal, External, Testing and test users, seven-day grants, Publish
|
|
175
|
+
app, `org_internal`)
|
|
176
|
+
- `docs/runbooks/sign-in-providers.md` ("Who gets in", "Existing accounts",
|
|
177
|
+
"Google", "Troubleshooting") and the catalog,
|
|
178
|
+
`packages/sdk/src/internal/signInProviders.ts`
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Sign in with Microsoft on the Gateway
|
|
2
|
+
|
|
3
|
+
Read [README.md](README.md) first: the rules every setup follows.
|
|
4
|
+
|
|
5
|
+
Puts **Continue with Microsoft** on the Gateway's sign-in page, beside the
|
|
6
|
+
password: work or school accounts from the person's Microsoft Entra
|
|
7
|
+
directory (Microsoft 365), or any Microsoft account. At the end the person
|
|
8
|
+
has an app registration in Entra, its settings on the Gateway, and has
|
|
9
|
+
signed in with Microsoft once. `pnpm everyapp signin show microsoft --json`
|
|
10
|
+
names this file as `setup`, and the CLI prints
|
|
11
|
+
`Guide: .claude/skills/everyapp/setup/signin-microsoft.md`.
|
|
12
|
+
|
|
13
|
+
Who gets in doesn't change: Microsoft only says who someone is. A member
|
|
14
|
+
signs in; someone with a pending invitation for that exact address joins
|
|
15
|
+
with the invited role; everyone else is refused ("You don't have an
|
|
16
|
+
account on this gateway.") and nothing is created. An existing password
|
|
17
|
+
account connects Microsoft once, by signing in with the password when the
|
|
18
|
+
page asks. The rules: `docs/runbooks/sign-in-providers.md`, "Who gets in".
|
|
19
|
+
|
|
20
|
+
Not the Outlook mail and calendar client apps use (`MICROSOFT_365_*`,
|
|
21
|
+
[microsoft-365.md](microsoft-365.md)): make a separate registration here.
|
|
22
|
+
|
|
23
|
+
**Which tenant decides which addresses count as verified.** Recommended:
|
|
24
|
+
the registration is single tenant and `MICROSOFT_SIGNIN_TENANT` is the
|
|
25
|
+
Directory (tenant) ID, so only the person's own directory signs in, and
|
|
26
|
+
its addresses are trusted. Otherwise (unset, `common`, `organizations`,
|
|
27
|
+
`consumers`) any tenant's admin can put any address on their users
|
|
28
|
+
(Microsoft calls the attack "nOAuth"), so the Gateway trusts an address
|
|
29
|
+
only with the token's own word for it: the optional claims `email` and
|
|
30
|
+
`xms_edov` (step 4). Without them every sign-in is refused as unverified.
|
|
31
|
+
|
|
32
|
+
## Settings
|
|
33
|
+
|
|
34
|
+
| Name | Required | Flag | What it holds |
|
|
35
|
+
| -------------------------------- | -------- | -------------------------------- | ---------------------------------------------------------------- |
|
|
36
|
+
| `MICROSOFT_SIGNIN_CLIENT_ID` | required | `--client-id <id>` | the registration's Application (client) ID; not a secret |
|
|
37
|
+
| `MICROSOFT_SIGNIN_CLIENT_SECRET` | required | `--client-secret-from-env <VAR>` | a client secret's **Value** (it expires; 24 months at most) |
|
|
38
|
+
| `MICROSOFT_SIGNIN_TENANT` | optional | `--tenant <GUID\|common\|organizations\|consumers>` | the Directory (tenant) ID; unset means any Microsoft account (`common`) |
|
|
39
|
+
|
|
40
|
+
Scopes asked for: `openid profile email`; no Graph scope, no refresh token
|
|
41
|
+
kept. The email is the ID token's `email`, verified when the tenant is
|
|
42
|
+
pinned, or the token carries `xms_edov: true`, `email_verified: true`, a
|
|
43
|
+
matching `verified_primary_email`, or comes from a personal account.
|
|
44
|
+
|
|
45
|
+
## Costs and plans
|
|
46
|
+
|
|
47
|
+
No charge for an app registration: any Microsoft Entra tenant has them,
|
|
48
|
+
Entra ID Free included (a Microsoft 365 business subscription comes with a
|
|
49
|
+
tenant). Microsoft's quickstart lists an Azure account as a prerequisite;
|
|
50
|
+
whether a tenant with no Azure subscription can register apps: unverified.
|
|
51
|
+
The client secret expires (24 months at most; Microsoft recommends under
|
|
52
|
+
12), so a reminder is part of the setup.
|
|
53
|
+
|
|
54
|
+
## Only a person can
|
|
55
|
+
|
|
56
|
+
- Sign in to the Microsoft Entra admin center (at least the Application
|
|
57
|
+
Developer role to register; adding optional claims is documented for
|
|
58
|
+
Cloud Application Administrator, or the app's owner).
|
|
59
|
+
- Create the registration and the client secret, and copy its Value.
|
|
60
|
+
- Type the secret at the hidden prompt of `pnpm everyapp signin add microsoft`.
|
|
61
|
+
- Choose single tenant or any account; sign in with Microsoft to check.
|
|
62
|
+
|
|
63
|
+
## Before you start
|
|
64
|
+
|
|
65
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed` true;
|
|
66
|
+
`data.gatewayUrl` gives `<gateway host>`. Not installed or no owner:
|
|
67
|
+
[gateway.md](gateway.md) first. `data.access.on` true: see Verify 4.
|
|
68
|
+
2. `pnpm everyapp signin show microsoft --json` (changes nothing; `Unknown
|
|
69
|
+
command signin`: `git pull` in the `every-app` folder). `data.status`
|
|
70
|
+
`off`: this file applies; `needs setup`: `data.missing` names what is
|
|
71
|
+
left (step 5); `on`: ask before replacing. `data.callbackUrl` is the
|
|
72
|
+
redirect URL, `https://<gateway host>/api/auth/callback/microsoft`;
|
|
73
|
+
`data.set` says whether `MICROSOFT_SIGNIN_TENANT` is set.
|
|
74
|
+
3. Ask: only people in your own Microsoft 365 directory (recommended:
|
|
75
|
+
single tenant), or any Microsoft account (guests from other companies,
|
|
76
|
+
outlook.com addresses)? It decides steps 2 and 4.
|
|
77
|
+
|
|
78
|
+
## Steps
|
|
79
|
+
|
|
80
|
+
1. **You:** give the person `data.callbackUrl`.
|
|
81
|
+
2. **Person:** https://entra.microsoft.com → **Entra ID** → **App
|
|
82
|
+
registrations** → **New registration**. Name (what people see, for
|
|
83
|
+
example `Every App`). **Supported account types**:
|
|
84
|
+
**Single tenant only - <your tenant>** (recommended), or **Any Entra ID
|
|
85
|
+
Tenant + Personal Microsoft accounts** for anyone. **Register**. The **Overview** page
|
|
86
|
+
shows **Application (client) ID** and **Directory (tenant) ID**: both
|
|
87
|
+
are plain ids, not secrets.
|
|
88
|
+
3. **Person:** the redirect URL. Under **Manage** → **Authentication** →
|
|
89
|
+
**Redirect URI configuration** → **Add Redirect URI** → **Web** → the
|
|
90
|
+
exact URL from step 1 → **Configure**.
|
|
91
|
+
4. **Person,** only when not single tenant: **Token configuration** → **Add
|
|
92
|
+
optional claim** → token type **ID** → tick `email` and `xms_edov` →
|
|
93
|
+
**Add** (accept the prompt to turn on the Microsoft Graph email
|
|
94
|
+
permission if it appears). If `xms_edov` isn't in the list (reported
|
|
95
|
+
for some apps while the claim was in preview; unverified today):
|
|
96
|
+
**Manifest** → under `optionalClaims` → `idToken` add
|
|
97
|
+
`{ "name": "xms_edov", "essential": false }` beside `email` → **Save**.
|
|
98
|
+
5. **Person:** the secret. **Certificates & secrets** → **Client secrets**
|
|
99
|
+
→ **New client secret** → a description, an expiry (the longest is 24
|
|
100
|
+
months) → **Add**. Copy the **Value** column now (not the Secret ID): it
|
|
101
|
+
is never shown again after leaving the page. Set a calendar reminder
|
|
102
|
+
for a week before it expires.
|
|
103
|
+
6. **Person:** in a second terminal in the `every-app` folder:
|
|
104
|
+
```sh
|
|
105
|
+
pnpm everyapp signin add microsoft
|
|
106
|
+
```
|
|
107
|
+
It asks for the Application (client) ID (shown as typed), the client
|
|
108
|
+
secret Value (hidden: pasting works, nothing is echoed) and the tenant
|
|
109
|
+
(shown as typed: the Directory (tenant) ID for single tenant; Enter
|
|
110
|
+
leaves it unset). One new Gateway Worker version, live on the next
|
|
111
|
+
request. They tell you when it's done.
|
|
112
|
+
Another way: in that terminal, the hidden read (bash or zsh:
|
|
113
|
+
`read -rs MICROSOFT_SIGNIN_CLIENT_SECRET && export MICROSOFT_SIGNIN_CLIENT_SECRET`;
|
|
114
|
+
fish: `read -s -x MICROSOFT_SIGNIN_CLIENT_SECRET`), paste, Enter, then
|
|
115
|
+
`pnpm everyapp signin add microsoft --client-id <Application (client) ID> --tenant <Directory (tenant) ID> --client-secret-from-env MICROSOFT_SIGNIN_CLIENT_SECRET --json`
|
|
116
|
+
(leave `--tenant` off for any account), then `unset MICROSOFT_SIGNIN_CLIENT_SECRET`
|
|
117
|
+
(fish: `set -e MICROSOFT_SIGNIN_CLIENT_SECRET`).
|
|
118
|
+
|
|
119
|
+
## Verify
|
|
120
|
+
|
|
121
|
+
1. `pnpm everyapp signin list --json`: the `microsoft` entry has `status:
|
|
122
|
+
"on"` and `missing` empty; for single tenant, `pnpm everyapp signin show microsoft --json`
|
|
123
|
+
lists `MICROSOFT_SIGNIN_TENANT` in `data.set`.
|
|
124
|
+
2. The person's address must be a member's or have a pending invitation:
|
|
125
|
+
`pnpm everyapp workspace invite <email>` for someone new (the pending
|
|
126
|
+
invitation for that exact address is enough; the printed link isn't
|
|
127
|
+
needed for this).
|
|
128
|
+
3. **Person:** private window, `https://<gateway host>/sign-in` →
|
|
129
|
+
**Continue with Microsoft** → their work account → (first time) the
|
|
130
|
+
consent screen → **Accept** → the Gateway, signed in.
|
|
131
|
+
4. Behind Cloudflare Access: nothing else changes, but the Microsoft
|
|
132
|
+
address must be the one Access knows the person by.
|
|
133
|
+
5. Logs (README's Verify 4): `signin.config_problem` names a malformed
|
|
134
|
+
setting (a tenant that isn't a GUID or one of the three words).
|
|
135
|
+
|
|
136
|
+
## If it fails
|
|
137
|
+
|
|
138
|
+
| error or symptom | meaning | what to do |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| no **Continue with Microsoft** button | a setting missing or malformed, or the version isn't live yet | `pnpm everyapp signin list --json`; logs' `signin.config_problem` |
|
|
141
|
+
| `AADSTS50011` (redirect URI mismatch) | the Web redirect URI isn't exactly `data.callbackUrl` | fix it under Authentication (Web, not Single-page application) |
|
|
142
|
+
| "The sign-in provider hasn't verified this email address." | not single tenant, and the token lacks `xms_edov`/`email` | step 4, or pin the tenant (single-tenant registration plus `--tenant`) |
|
|
143
|
+
| `AADSTS50020` or "user account … does not exist in tenant" | single tenant, and the person's account is from another directory | invite them as a guest in Entra, or use any-account mode with step 4 |
|
|
144
|
+
| `AADSTS7000215` (invalid client secret) | the Secret ID was copied instead of the Value, or the secret expired | Rotate |
|
|
145
|
+
| "You don't have an account on this gateway." | no member or invitation for that address | `pnpm everyapp workspace invite <email>` with the address Microsoft returns |
|
|
146
|
+
| `SIGNIN_TENANT_INVALID` | `--tenant` isn't a GUID, `common`, `organizations` or `consumers` | the Directory (tenant) ID from Overview |
|
|
147
|
+
| `SIGNIN_VALUE_REQUIRED` / `SECRET_EMPTY` | unattended with 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 |
|
|
148
|
+
| sign-in stops after months | the client secret expired | Rotate |
|
|
149
|
+
|
|
150
|
+
## Rerun, rotate, remove
|
|
151
|
+
|
|
152
|
+
- Rerun: `pnpm everyapp signin add microsoft` any time; values given
|
|
153
|
+
replace, settings not given are kept (Enter at a prompt keeps it).
|
|
154
|
+
- Rotate, before the expiry: **Certificates & secrets** → **New client
|
|
155
|
+
secret**, then `pnpm everyapp signin add microsoft` (Enter at the client
|
|
156
|
+
id and tenant prompts, paste the new Value), or the hidden read and
|
|
157
|
+
`pnpm everyapp signin add microsoft --client-secret-from-env MICROSOFT_SIGNIN_CLIENT_SECRET --json`.
|
|
158
|
+
Then delete the old secret in Entra.
|
|
159
|
+
- Drop the tenant pin (any account, after step 4):
|
|
160
|
+
`pnpm everyapp secret unset MICROSOFT_SIGNIN_TENANT --json` (asks at a
|
|
161
|
+
terminal; with `--json`, `--yes` only when the person said so).
|
|
162
|
+
- Remove, only when asked: `pnpm everyapp signin remove microsoft --json --yes`
|
|
163
|
+
(`--yes` only when they said so). Accounts made with it stay. Then delete
|
|
164
|
+
the registration in Entra.
|
|
165
|
+
|
|
166
|
+
## Sources
|
|
167
|
+
|
|
168
|
+
- https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app
|
|
169
|
+
(Entra ID → App registrations → New registration; Supported account
|
|
170
|
+
types; Application (client) ID; Application Developer role)
|
|
171
|
+
- https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-redirect-uri
|
|
172
|
+
(Authentication → Redirect URI configuration → Add Redirect URI → Web)
|
|
173
|
+
- https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials
|
|
174
|
+
(New client secret; 24 months at most, under 12 recommended; the Value
|
|
175
|
+
is never shown again)
|
|
176
|
+
- https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims
|
|
177
|
+
(Token configuration → Add optional claim; the manifest's `optionalClaims`)
|
|
178
|
+
- https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference
|
|
179
|
+
(`xms_edov`: needs the `email` claim; `verified_primary_email`)
|
|
180
|
+
- https://learn.microsoft.com/en-us/entra/identity-platform/migrate-off-email-claim-authorization
|
|
181
|
+
(nOAuth: never trust `email` alone)
|
|
182
|
+
- `docs/runbooks/sign-in-providers.md` ("Microsoft", "Troubleshooting") and
|
|
183
|
+
`packages/sdk/src/internal/signInProviders.ts`
|