@every-app/sdk 0.3.0-rc.2 → 0.3.0-rc.3
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/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/sentinel.d.ts +39 -1
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +22 -1
- package/dist/cloudflare/worker.d.ts +119 -2
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +90 -6
- package/dist/internal/aiDefault.d.ts +141 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +204 -0
- package/dist/internal/aiProviders.d.ts +4 -1
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +5 -1
- package/dist/internal/appSettings.d.ts +20 -2
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +53 -7
- package/dist/internal/audit.d.ts +108 -0
- package/dist/internal/audit.d.ts.map +1 -0
- package/dist/internal/audit.js +235 -0
- package/dist/internal/capabilities.d.ts +275 -6
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +111 -0
- package/dist/internal/catalog/index.d.ts +38 -1
- package/dist/internal/catalog/index.d.ts.map +1 -1
- package/dist/internal/catalog/inference.d.ts +38 -1
- package/dist/internal/catalog/inference.d.ts.map +1 -1
- package/dist/internal/catalog/inference.js +33 -1
- package/dist/internal/devProviders.d.ts +2 -2
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +19 -1
- package/dist/internal/events.d.ts +13 -2
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +19 -3
- package/dist/internal/index.d.ts +4 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +4 -0
- package/dist/internal/oauthPresets.d.ts +29 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +534 -0
- package/dist/internal/oauthScopes.d.ts +56 -1
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +98 -4
- package/dist/internal/oauthSpec.d.ts +59 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +676 -0
- package/dist/internal/protocol.d.ts +11 -0
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +9 -0
- package/dist/internal/providerRegistry.d.ts +17 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +47 -2
- package/dist/internal/providerSpec.d.ts +11 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +134 -37
- package/dist/internal/signInProviders.d.ts.map +1 -1
- package/dist/internal/signInProviders.js +11 -4
- package/dist/server/ai.d.ts +45 -0
- package/dist/server/ai.d.ts.map +1 -0
- package/dist/server/ai.js +40 -0
- package/dist/server/connectUrl.d.ts +21 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +37 -0
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +72 -8
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/integrationFetch.d.ts +39 -2
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +177 -13
- package/package.json +1 -1
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth presets (docs/design/2026-10-oauth-providers.md section 4): ready
|
|
3
|
+
* `oauth2-user` specs, in the JSON shape `everyapp provider add` takes,
|
|
4
|
+
* for the providers people most often want a "Connect" button for. A
|
|
5
|
+
* preset becomes a `providers` row when added (`everyapp provider add
|
|
6
|
+
* --oauth github`, the Admin dialog, `everyapp_provider_add` with
|
|
7
|
+
* `preset`), so an operator may `provider show` and edit a copy; nothing
|
|
8
|
+
* here is a hidden catalog entry. Google, Microsoft, Slack and Discord
|
|
9
|
+
* are catalog clients with a `flow` and are offered in the same picker.
|
|
10
|
+
*
|
|
11
|
+
* Each preset parses with `parseProviderSpec` (oauthPresets.test.ts),
|
|
12
|
+
* names a console where the client is made, the API hosts the token is
|
|
13
|
+
* sent to, and scope sets an app declares under `scopes`. Scope strings
|
|
14
|
+
* are the providers' own, verified against their docs on 2026-10-25.
|
|
15
|
+
*/
|
|
16
|
+
import { parseProviderSpec, } from "./providerSpec.js";
|
|
17
|
+
export const OAUTH_PRESETS = {
|
|
18
|
+
"github-oauth": {
|
|
19
|
+
id: "github-oauth",
|
|
20
|
+
name: "GitHub",
|
|
21
|
+
description: "Repositories, issues, pull requests and the person's profile.",
|
|
22
|
+
docs: "https://github.com/settings/developers",
|
|
23
|
+
api: "https://docs.github.com/rest",
|
|
24
|
+
hosts: ["api.github.com"],
|
|
25
|
+
auth: {
|
|
26
|
+
kind: "oauth2-user",
|
|
27
|
+
authorizeUrl: "https://github.com/login/oauth/authorize",
|
|
28
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
29
|
+
revokeUrl: "https://api.github.com/applications/{client_id}/grant",
|
|
30
|
+
revokeMethod: "DELETE",
|
|
31
|
+
clientId: "GITHUB_OAUTH_CLIENT_ID",
|
|
32
|
+
clientSecret: "GITHUB_OAUTH_CLIENT_SECRET",
|
|
33
|
+
inject: "form",
|
|
34
|
+
pkce: true,
|
|
35
|
+
scopeDialect: "comma",
|
|
36
|
+
tokenHeaders: { Accept: "application/json" },
|
|
37
|
+
identity: {
|
|
38
|
+
url: "https://api.github.com/user",
|
|
39
|
+
label: "login",
|
|
40
|
+
id: "id",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
scopeSets: {
|
|
44
|
+
identity: {
|
|
45
|
+
label: "GitHub account (who you are)",
|
|
46
|
+
access: "read",
|
|
47
|
+
scopes: ["read:user", "user:email"],
|
|
48
|
+
},
|
|
49
|
+
"repo.read": {
|
|
50
|
+
label: "Repositories (read, private ones included)",
|
|
51
|
+
access: "read",
|
|
52
|
+
scopes: ["repo"],
|
|
53
|
+
},
|
|
54
|
+
"repo.write": {
|
|
55
|
+
label: "Repositories (read and write)",
|
|
56
|
+
access: "write",
|
|
57
|
+
scopes: ["repo"],
|
|
58
|
+
},
|
|
59
|
+
"org.read": {
|
|
60
|
+
label: "Organizations and teams (read)",
|
|
61
|
+
access: "read",
|
|
62
|
+
scopes: ["read:org"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
access: ["read", "write"],
|
|
66
|
+
hint: "Register an OAuth App (not a GitHub App) and paste its client id and secret. GitHub's `repo` scope is read and write; a read set with it still lets the Gateway refuse writes.",
|
|
67
|
+
},
|
|
68
|
+
"notion-oauth": {
|
|
69
|
+
id: "notion-oauth",
|
|
70
|
+
name: "Notion",
|
|
71
|
+
description: "Pages and databases the person shares with the integration.",
|
|
72
|
+
docs: "https://www.notion.so/my-integrations",
|
|
73
|
+
api: "https://developers.notion.com/reference",
|
|
74
|
+
hosts: ["api.notion.com"],
|
|
75
|
+
auth: {
|
|
76
|
+
kind: "oauth2-user",
|
|
77
|
+
authorizeUrl: "https://api.notion.com/v1/oauth/authorize",
|
|
78
|
+
tokenUrl: "https://api.notion.com/v1/oauth/token",
|
|
79
|
+
clientId: "NOTION_OAUTH_CLIENT_ID",
|
|
80
|
+
clientSecret: "NOTION_OAUTH_CLIENT_SECRET",
|
|
81
|
+
inject: "basic",
|
|
82
|
+
pkce: false,
|
|
83
|
+
scopeDialect: "space",
|
|
84
|
+
authorizeParams: { owner: "user" },
|
|
85
|
+
},
|
|
86
|
+
// Notion reads with GET, and with POST for a search and a database
|
|
87
|
+
// query; the read set lists those two, so a read grant never creates
|
|
88
|
+
// or changes a page (POST /v1/pages, PATCH, DELETE need the write set).
|
|
89
|
+
scopeSets: {
|
|
90
|
+
"workspace.read": {
|
|
91
|
+
label: "Shared pages and databases (read)",
|
|
92
|
+
access: "read",
|
|
93
|
+
scopes: [],
|
|
94
|
+
routes: [
|
|
95
|
+
{
|
|
96
|
+
host: "api.notion.com",
|
|
97
|
+
paths: [
|
|
98
|
+
"/v1/users",
|
|
99
|
+
"/v1/users/",
|
|
100
|
+
"/v1/pages/",
|
|
101
|
+
"/v1/databases/",
|
|
102
|
+
"/v1/blocks/",
|
|
103
|
+
"/v1/comments",
|
|
104
|
+
],
|
|
105
|
+
posts: ["^/v1/search$", "^/v1/databases/[^/]+/query$"],
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
"workspace.write": {
|
|
110
|
+
label: "Shared pages and databases (read and write)",
|
|
111
|
+
access: "write",
|
|
112
|
+
scopes: [],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
access: ["read", "write"],
|
|
116
|
+
hint: "Notion has no scopes: the person picks the pages to share on Notion's consent screen. Make the integration public to get a client id and secret. Apps send `Notion-Version` on every call.",
|
|
117
|
+
},
|
|
118
|
+
"linear-oauth": {
|
|
119
|
+
id: "linear-oauth",
|
|
120
|
+
name: "Linear",
|
|
121
|
+
description: "Issues, projects and teams, over Linear's GraphQL API.",
|
|
122
|
+
docs: "https://linear.app/settings/api/applications",
|
|
123
|
+
api: "https://developers.linear.app/docs/oauth/authentication",
|
|
124
|
+
hosts: ["api.linear.app"],
|
|
125
|
+
auth: {
|
|
126
|
+
kind: "oauth2-user",
|
|
127
|
+
authorizeUrl: "https://linear.app/oauth/authorize",
|
|
128
|
+
tokenUrl: "https://api.linear.app/oauth/token",
|
|
129
|
+
revokeUrl: "https://api.linear.app/oauth/revoke",
|
|
130
|
+
clientId: "LINEAR_OAUTH_CLIENT_ID",
|
|
131
|
+
clientSecret: "LINEAR_OAUTH_CLIENT_SECRET",
|
|
132
|
+
inject: "form",
|
|
133
|
+
pkce: false,
|
|
134
|
+
scopeDialect: "comma",
|
|
135
|
+
},
|
|
136
|
+
// Linear's API is one GraphQL POST, which the Gateway can't tell a
|
|
137
|
+
// query from a mutation on, so the one set is a write set: an app
|
|
138
|
+
// lists Linear with access: "write", and Linear's own `read` scope is
|
|
139
|
+
// what a read-only token holds.
|
|
140
|
+
scopeSets: {
|
|
141
|
+
"workspace.write": {
|
|
142
|
+
label: "Issues, projects and teams (read and write)",
|
|
143
|
+
access: "write",
|
|
144
|
+
scopes: ["read", "write"],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
access: ["write"],
|
|
148
|
+
readMethods: [],
|
|
149
|
+
writeMethods: ["POST"],
|
|
150
|
+
hint: "Linear's API is GraphQL over POST, so every call needs access: \"write\"; a token's scopes decide what a query may change.",
|
|
151
|
+
},
|
|
152
|
+
"gitlab-oauth": {
|
|
153
|
+
id: "gitlab-oauth",
|
|
154
|
+
name: "GitLab",
|
|
155
|
+
description: "Projects, merge requests and issues on gitlab.com.",
|
|
156
|
+
docs: "https://gitlab.com/-/user_settings/applications",
|
|
157
|
+
api: "https://docs.gitlab.com/api/rest/",
|
|
158
|
+
hosts: ["gitlab.com"],
|
|
159
|
+
auth: {
|
|
160
|
+
kind: "oauth2-user",
|
|
161
|
+
authorizeUrl: "https://gitlab.com/oauth/authorize",
|
|
162
|
+
tokenUrl: "https://gitlab.com/oauth/token",
|
|
163
|
+
revokeUrl: "https://gitlab.com/oauth/revoke",
|
|
164
|
+
clientId: "GITLAB_OAUTH_CLIENT_ID",
|
|
165
|
+
clientSecret: "GITLAB_OAUTH_CLIENT_SECRET",
|
|
166
|
+
inject: "form",
|
|
167
|
+
pkce: true,
|
|
168
|
+
scopeDialect: "space",
|
|
169
|
+
identity: {
|
|
170
|
+
url: "https://gitlab.com/api/v4/user",
|
|
171
|
+
label: "username",
|
|
172
|
+
id: "id",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
scopeSets: {
|
|
176
|
+
identity: {
|
|
177
|
+
label: "GitLab account (who you are)",
|
|
178
|
+
access: "read",
|
|
179
|
+
scopes: ["read_user"],
|
|
180
|
+
},
|
|
181
|
+
"api.read": {
|
|
182
|
+
label: "Projects, issues and merge requests (read)",
|
|
183
|
+
access: "read",
|
|
184
|
+
scopes: ["read_api"],
|
|
185
|
+
},
|
|
186
|
+
"api.write": {
|
|
187
|
+
label: "Projects, issues and merge requests (read and write)",
|
|
188
|
+
access: "write",
|
|
189
|
+
scopes: ["api"],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
access: ["read", "write"],
|
|
193
|
+
hint: "A self-managed GitLab needs a custom provider with its own host in place of gitlab.com.",
|
|
194
|
+
},
|
|
195
|
+
atlassian: {
|
|
196
|
+
id: "atlassian",
|
|
197
|
+
name: "Atlassian (Jira and Confluence)",
|
|
198
|
+
description: "Jira issues and Confluence pages on the sites the person grants.",
|
|
199
|
+
docs: "https://developer.atlassian.com/console/myapps/",
|
|
200
|
+
api: "https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/",
|
|
201
|
+
hosts: ["api.atlassian.com"],
|
|
202
|
+
auth: {
|
|
203
|
+
kind: "oauth2-user",
|
|
204
|
+
authorizeUrl: "https://auth.atlassian.com/authorize",
|
|
205
|
+
tokenUrl: "https://auth.atlassian.com/oauth/token",
|
|
206
|
+
clientId: "ATLASSIAN_CLIENT_ID",
|
|
207
|
+
clientSecret: "ATLASSIAN_CLIENT_SECRET",
|
|
208
|
+
inject: "form",
|
|
209
|
+
pkce: false,
|
|
210
|
+
scopeDialect: "space",
|
|
211
|
+
authorizeParams: { audience: "api.atlassian.com", prompt: "consent" },
|
|
212
|
+
identity: {
|
|
213
|
+
url: "https://api.atlassian.com/me",
|
|
214
|
+
label: "email",
|
|
215
|
+
id: "account_id",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
scopeSets: {
|
|
219
|
+
identity: {
|
|
220
|
+
label: "Atlassian account (who you are)",
|
|
221
|
+
access: "read",
|
|
222
|
+
scopes: ["read:me"],
|
|
223
|
+
},
|
|
224
|
+
"jira.read": {
|
|
225
|
+
label: "Jira issues and projects (read)",
|
|
226
|
+
access: "read",
|
|
227
|
+
scopes: ["read:jira-work", "read:jira-user"],
|
|
228
|
+
},
|
|
229
|
+
"jira.write": {
|
|
230
|
+
label: "Jira issues (read and write)",
|
|
231
|
+
access: "write",
|
|
232
|
+
scopes: ["read:jira-work", "write:jira-work"],
|
|
233
|
+
},
|
|
234
|
+
"confluence.read": {
|
|
235
|
+
label: "Confluence pages (read)",
|
|
236
|
+
access: "read",
|
|
237
|
+
scopes: [
|
|
238
|
+
"read:confluence-content.all",
|
|
239
|
+
"read:confluence-space.summary",
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
alwaysRequested: ["offline_access"],
|
|
244
|
+
access: ["read", "write"],
|
|
245
|
+
hint: "Calls go to api.atlassian.com/ex/jira/<cloud id>/… ; the cloud id comes from /oauth/token/accessible-resources.",
|
|
246
|
+
},
|
|
247
|
+
"hubspot-oauth": {
|
|
248
|
+
id: "hubspot-oauth",
|
|
249
|
+
name: "HubSpot",
|
|
250
|
+
description: "CRM contacts, companies and deals.",
|
|
251
|
+
docs: "https://developers.hubspot.com/",
|
|
252
|
+
api: "https://developers.hubspot.com/docs/reference/api",
|
|
253
|
+
hosts: ["api.hubapi.com"],
|
|
254
|
+
auth: {
|
|
255
|
+
kind: "oauth2-user",
|
|
256
|
+
authorizeUrl: "https://app.hubspot.com/oauth/authorize",
|
|
257
|
+
tokenUrl: "https://api.hubapi.com/oauth/v1/token",
|
|
258
|
+
clientId: "HUBSPOT_OAUTH_CLIENT_ID",
|
|
259
|
+
clientSecret: "HUBSPOT_OAUTH_CLIENT_SECRET",
|
|
260
|
+
inject: "form",
|
|
261
|
+
pkce: false,
|
|
262
|
+
scopeDialect: "space",
|
|
263
|
+
},
|
|
264
|
+
scopeSets: {
|
|
265
|
+
"contacts.read": {
|
|
266
|
+
label: "Contacts (read)",
|
|
267
|
+
access: "read",
|
|
268
|
+
scopes: ["crm.objects.contacts.read"],
|
|
269
|
+
},
|
|
270
|
+
"contacts.write": {
|
|
271
|
+
label: "Contacts (read and write)",
|
|
272
|
+
access: "write",
|
|
273
|
+
scopes: ["crm.objects.contacts.read", "crm.objects.contacts.write"],
|
|
274
|
+
},
|
|
275
|
+
"companies.read": {
|
|
276
|
+
label: "Companies (read)",
|
|
277
|
+
access: "read",
|
|
278
|
+
scopes: ["crm.objects.companies.read"],
|
|
279
|
+
},
|
|
280
|
+
"deals.read": {
|
|
281
|
+
label: "Deals (read)",
|
|
282
|
+
access: "read",
|
|
283
|
+
scopes: ["crm.objects.deals.read"],
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
access: ["read", "write"],
|
|
287
|
+
hint: "The app's scopes in HubSpot's developer console must match the sets apps declare, or HubSpot refuses the authorize URL.",
|
|
288
|
+
},
|
|
289
|
+
zoom: {
|
|
290
|
+
id: "zoom",
|
|
291
|
+
name: "Zoom",
|
|
292
|
+
description: "The person's meetings and recordings.",
|
|
293
|
+
docs: "https://marketplace.zoom.us/develop/create",
|
|
294
|
+
api: "https://developers.zoom.us/docs/api/",
|
|
295
|
+
hosts: ["api.zoom.us"],
|
|
296
|
+
auth: {
|
|
297
|
+
kind: "oauth2-user",
|
|
298
|
+
authorizeUrl: "https://zoom.us/oauth/authorize",
|
|
299
|
+
tokenUrl: "https://zoom.us/oauth/token",
|
|
300
|
+
revokeUrl: "https://zoom.us/oauth/revoke",
|
|
301
|
+
clientId: "ZOOM_CLIENT_ID",
|
|
302
|
+
clientSecret: "ZOOM_CLIENT_SECRET",
|
|
303
|
+
inject: "basic",
|
|
304
|
+
pkce: true,
|
|
305
|
+
scopeDialect: "space",
|
|
306
|
+
identity: {
|
|
307
|
+
url: "https://api.zoom.us/v2/users/me",
|
|
308
|
+
label: "email",
|
|
309
|
+
id: "id",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
scopeSets: {
|
|
313
|
+
identity: {
|
|
314
|
+
label: "Zoom account (who you are)",
|
|
315
|
+
access: "read",
|
|
316
|
+
scopes: ["user:read:user"],
|
|
317
|
+
},
|
|
318
|
+
"meetings.read": {
|
|
319
|
+
label: "Meetings (read)",
|
|
320
|
+
access: "read",
|
|
321
|
+
scopes: ["meeting:read:meeting", "meeting:read:list_meetings"],
|
|
322
|
+
},
|
|
323
|
+
"meetings.write": {
|
|
324
|
+
label: "Meetings (read, create and change)",
|
|
325
|
+
access: "write",
|
|
326
|
+
scopes: [
|
|
327
|
+
"meeting:read:meeting",
|
|
328
|
+
"meeting:write:meeting",
|
|
329
|
+
"meeting:update:meeting",
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
"recordings.read": {
|
|
333
|
+
label: "Cloud recordings (read)",
|
|
334
|
+
access: "read",
|
|
335
|
+
scopes: ["cloud_recording:read:list_user_recordings"],
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
access: ["read", "write"],
|
|
339
|
+
hint: "A General app with user-managed OAuth; Zoom's granular scope names are the ones listed here.",
|
|
340
|
+
},
|
|
341
|
+
dropbox: {
|
|
342
|
+
id: "dropbox",
|
|
343
|
+
name: "Dropbox",
|
|
344
|
+
description: "Files and folders in the person's Dropbox.",
|
|
345
|
+
docs: "https://www.dropbox.com/developers/apps",
|
|
346
|
+
api: "https://www.dropbox.com/developers/documentation/http/documentation",
|
|
347
|
+
hosts: ["api.dropboxapi.com", "content.dropboxapi.com"],
|
|
348
|
+
auth: {
|
|
349
|
+
kind: "oauth2-user",
|
|
350
|
+
authorizeUrl: "https://www.dropbox.com/oauth2/authorize",
|
|
351
|
+
tokenUrl: "https://api.dropboxapi.com/oauth2/token",
|
|
352
|
+
revokeUrl: "https://api.dropboxapi.com/2/auth/token/revoke",
|
|
353
|
+
clientId: "DROPBOX_OAUTH_CLIENT_ID",
|
|
354
|
+
clientSecret: "DROPBOX_OAUTH_CLIENT_SECRET",
|
|
355
|
+
inject: "form",
|
|
356
|
+
pkce: true,
|
|
357
|
+
scopeDialect: "space",
|
|
358
|
+
authorizeParams: { token_access_type: "offline" },
|
|
359
|
+
},
|
|
360
|
+
// Dropbox is POST throughout, so each read set lists the read calls
|
|
361
|
+
// it unlocks under `posts`; everything else needs the write set.
|
|
362
|
+
scopeSets: {
|
|
363
|
+
identity: {
|
|
364
|
+
label: "Dropbox account (who you are)",
|
|
365
|
+
access: "read",
|
|
366
|
+
scopes: ["account_info.read"],
|
|
367
|
+
routes: [
|
|
368
|
+
{
|
|
369
|
+
host: "api.dropboxapi.com",
|
|
370
|
+
paths: [],
|
|
371
|
+
posts: [
|
|
372
|
+
"^/2/users/get_current_account$",
|
|
373
|
+
"^/2/users/get_space_usage$",
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
"files.read": {
|
|
379
|
+
label: "Files and folders (read)",
|
|
380
|
+
access: "read",
|
|
381
|
+
scopes: ["files.metadata.read", "files.content.read"],
|
|
382
|
+
routes: [
|
|
383
|
+
{
|
|
384
|
+
host: "api.dropboxapi.com",
|
|
385
|
+
paths: [],
|
|
386
|
+
posts: [
|
|
387
|
+
"^/2/files/list_folder$",
|
|
388
|
+
"^/2/files/list_folder/continue$",
|
|
389
|
+
"^/2/files/get_metadata$",
|
|
390
|
+
"^/2/files/search_v2$",
|
|
391
|
+
"^/2/files/search/continue_v2$",
|
|
392
|
+
"^/2/files/get_temporary_link$",
|
|
393
|
+
"^/2/files/list_revisions$",
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
host: "content.dropboxapi.com",
|
|
398
|
+
paths: [],
|
|
399
|
+
posts: [
|
|
400
|
+
"^/2/files/download$",
|
|
401
|
+
"^/2/files/get_thumbnail_v2$",
|
|
402
|
+
"^/2/files/export$",
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
},
|
|
407
|
+
"files.write": {
|
|
408
|
+
label: "Files and folders (read and write)",
|
|
409
|
+
access: "write",
|
|
410
|
+
scopes: [
|
|
411
|
+
"files.metadata.read",
|
|
412
|
+
"files.content.read",
|
|
413
|
+
"files.metadata.write",
|
|
414
|
+
"files.content.write",
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
access: ["read", "write"],
|
|
419
|
+
hint: 'Dropbox\'s API is POST throughout: a read set unlocks its listed read calls, anything else needs access: "write". `token_access_type=offline` is what yields a refresh token.',
|
|
420
|
+
},
|
|
421
|
+
spotify: {
|
|
422
|
+
id: "spotify",
|
|
423
|
+
name: "Spotify",
|
|
424
|
+
description: "The person's library, playlists and listening.",
|
|
425
|
+
docs: "https://developer.spotify.com/dashboard",
|
|
426
|
+
api: "https://developer.spotify.com/documentation/web-api",
|
|
427
|
+
hosts: ["api.spotify.com"],
|
|
428
|
+
auth: {
|
|
429
|
+
kind: "oauth2-user",
|
|
430
|
+
authorizeUrl: "https://accounts.spotify.com/authorize",
|
|
431
|
+
tokenUrl: "https://accounts.spotify.com/api/token",
|
|
432
|
+
clientId: "SPOTIFY_CLIENT_ID",
|
|
433
|
+
clientSecret: "SPOTIFY_CLIENT_SECRET",
|
|
434
|
+
inject: "basic",
|
|
435
|
+
pkce: true,
|
|
436
|
+
scopeDialect: "space",
|
|
437
|
+
identity: {
|
|
438
|
+
url: "https://api.spotify.com/v1/me",
|
|
439
|
+
label: "display_name",
|
|
440
|
+
id: "id",
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
scopeSets: {
|
|
444
|
+
identity: {
|
|
445
|
+
label: "Spotify account (who you are)",
|
|
446
|
+
access: "read",
|
|
447
|
+
scopes: ["user-read-email", "user-read-private"],
|
|
448
|
+
},
|
|
449
|
+
"library.read": {
|
|
450
|
+
label: "Saved tracks and albums (read)",
|
|
451
|
+
access: "read",
|
|
452
|
+
scopes: ["user-library-read"],
|
|
453
|
+
},
|
|
454
|
+
"playlists.read": {
|
|
455
|
+
label: "Playlists (read)",
|
|
456
|
+
access: "read",
|
|
457
|
+
scopes: ["playlist-read-private", "playlist-read-collaborative"],
|
|
458
|
+
},
|
|
459
|
+
"playlists.write": {
|
|
460
|
+
label: "Playlists (read and change)",
|
|
461
|
+
access: "write",
|
|
462
|
+
scopes: [
|
|
463
|
+
"playlist-read-private",
|
|
464
|
+
"playlist-modify-private",
|
|
465
|
+
"playlist-modify-public",
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
access: ["read", "write"],
|
|
470
|
+
},
|
|
471
|
+
"x-oauth": {
|
|
472
|
+
id: "x-oauth",
|
|
473
|
+
name: "X (OAuth 2)",
|
|
474
|
+
description: "Posts and the person's profile on X, over the v2 API.",
|
|
475
|
+
docs: "https://developer.x.com/en/portal/dashboard",
|
|
476
|
+
api: "https://docs.x.com/x-api",
|
|
477
|
+
hosts: ["api.x.com"],
|
|
478
|
+
auth: {
|
|
479
|
+
kind: "oauth2-user",
|
|
480
|
+
authorizeUrl: "https://x.com/i/oauth2/authorize",
|
|
481
|
+
tokenUrl: "https://api.x.com/2/oauth2/token",
|
|
482
|
+
revokeUrl: "https://api.x.com/2/oauth2/revoke",
|
|
483
|
+
clientId: "X_OAUTH_CLIENT_ID",
|
|
484
|
+
clientSecret: "X_OAUTH_CLIENT_SECRET",
|
|
485
|
+
inject: "basic",
|
|
486
|
+
pkce: true,
|
|
487
|
+
scopeDialect: "space",
|
|
488
|
+
identity: {
|
|
489
|
+
url: "https://api.x.com/2/users/me",
|
|
490
|
+
label: "data.username",
|
|
491
|
+
id: "data.id",
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
scopeSets: {
|
|
495
|
+
identity: {
|
|
496
|
+
label: "X account (who you are)",
|
|
497
|
+
access: "read",
|
|
498
|
+
scopes: ["users.read", "tweet.read"],
|
|
499
|
+
},
|
|
500
|
+
"posts.read": {
|
|
501
|
+
label: "Posts and timelines (read)",
|
|
502
|
+
access: "read",
|
|
503
|
+
scopes: ["tweet.read", "users.read"],
|
|
504
|
+
},
|
|
505
|
+
"posts.write": {
|
|
506
|
+
label: "Posts (read and publish)",
|
|
507
|
+
access: "write",
|
|
508
|
+
scopes: ["tweet.read", "users.read", "tweet.write"],
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
alwaysRequested: ["offline.access"],
|
|
512
|
+
access: ["read", "write"],
|
|
513
|
+
hint: "OAuth 2 with PKCE on X's v2 API; X's OAuth 1.0a (the v1.1 media upload) stays an app secret.",
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
/** The preset ids, in the order the picker shows them. */
|
|
517
|
+
export function oauthPresetIds() {
|
|
518
|
+
return Object.keys(OAUTH_PRESETS);
|
|
519
|
+
}
|
|
520
|
+
/** A preset's parsed capability, for the picker's labels; throws for an unknown id (the test parses every preset). */
|
|
521
|
+
export function oauthPreset(id) {
|
|
522
|
+
const spec = OAUTH_PRESETS[id];
|
|
523
|
+
if (!spec)
|
|
524
|
+
return null;
|
|
525
|
+
const parsed = parseProviderSpec(spec);
|
|
526
|
+
if (!parsed.ok) {
|
|
527
|
+
throw new Error(`preset ${id} doesn't parse: ${JSON.stringify(parsed.problems)}`);
|
|
528
|
+
}
|
|
529
|
+
return parsed.capability;
|
|
530
|
+
}
|
|
531
|
+
/** Whether `capability` is an OAuth provider the Gateway runs the flow for. */
|
|
532
|
+
export function isOAuthProvider(capability) {
|
|
533
|
+
return capability?.mode === "oauth_client" && capability.flow !== undefined;
|
|
534
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* check, the SDK's local-dev twin of it and, later, the proxy and the
|
|
8
8
|
* outbound Worker. Nothing here reads a secret or a token value.
|
|
9
9
|
*/
|
|
10
|
-
import type { OAuthScopePolicy } from "./capabilities.js";
|
|
10
|
+
import type { OAuthScopeDialect, OAuthScopePolicy } from "./capabilities.js";
|
|
11
11
|
import type { TokenRequest } from "./oauthClient.js";
|
|
12
12
|
/** Scopes by the token they belong to: the bot or only token, and Slack's user token. */
|
|
13
13
|
export interface ScopeList {
|
|
@@ -58,6 +58,54 @@ export declare function carriesToken(json: unknown): boolean;
|
|
|
58
58
|
* hold to anything.
|
|
59
59
|
*/
|
|
60
60
|
export declare function grantedScopes(policy: OAuthScopePolicy, json: unknown): GrantedScopes | null;
|
|
61
|
+
/**
|
|
62
|
+
* What a token response granted, for the Gateway-owned flow: as
|
|
63
|
+
* {@link grantedScopes}, except that a `space` or `comma` provider that
|
|
64
|
+
* names no `scope` at all is taken at its word (RFC 6749 §5.1: the field
|
|
65
|
+
* is optional when the grant matches the request), so `requested` stands.
|
|
66
|
+
* The three parsed dialects keep failing closed.
|
|
67
|
+
*/
|
|
68
|
+
export declare function grantedScopesOrRequested(policy: OAuthScopePolicy, json: unknown, requested: ScopeList): GrantedScopes | null;
|
|
69
|
+
/** Why the proxy refuses a held-token call, with what would let it through. */
|
|
70
|
+
export type HeldTokenRefusal = {
|
|
71
|
+
ok: false;
|
|
72
|
+
code: "integration_not_connected";
|
|
73
|
+
/** The declared sets the connection was not granted. */
|
|
74
|
+
missingSets: string[];
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
code: "integration_scope_not_allowed" | "integration_method_not_allowed";
|
|
78
|
+
message: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Whether an app's call with a held token goes through, and under which
|
|
82
|
+
* set (docs/design/2026-10-oauth-providers.md section 7). In order: every
|
|
83
|
+
* set the app declares must be among the connection's (else the person
|
|
84
|
+
* connects again: `integration_not_connected` with the missing sets); the
|
|
85
|
+
* host must be one of the provider's `api` hosts; the path passes the
|
|
86
|
+
* segment rules (`scopeRouteAllows`'s: no batch tunnels, no dot segments,
|
|
87
|
+
* no denied segments); then a declared set with routes must cover the
|
|
88
|
+
* call, or a declared set without routes covers its hosts by access: a
|
|
89
|
+
* read set unlocks `readMethods` (GET and HEAD by default), a write set
|
|
90
|
+
* every method the provider offers. `writes` says whether the method is
|
|
91
|
+
* one the grant allows beyond reads, decided by the caller as for an
|
|
92
|
+
* API-key provider.
|
|
93
|
+
*/
|
|
94
|
+
export declare function heldTokenCallAllowed({ policy, declaredSets, connectionSets, hosts, method, upstream, readMethods, writes, }: {
|
|
95
|
+
policy: OAuthScopePolicy;
|
|
96
|
+
declaredSets: readonly string[];
|
|
97
|
+
connectionSets: readonly string[];
|
|
98
|
+
/** The provider's `api` hosts. */
|
|
99
|
+
hosts: readonly string[];
|
|
100
|
+
method: string;
|
|
101
|
+
upstream: URL;
|
|
102
|
+
readMethods?: readonly string[];
|
|
103
|
+
/** Whether `method` is a write the app's grant allows. */
|
|
104
|
+
writes: boolean;
|
|
105
|
+
}): {
|
|
106
|
+
ok: true;
|
|
107
|
+
set: string;
|
|
108
|
+
} | HeldTokenRefusal;
|
|
61
109
|
/**
|
|
62
110
|
* What `granted` carries beyond `allowed`, per token kind, for a refusal's
|
|
63
111
|
* message and log line: the user token's extras as `user:<scope>`. Both
|
|
@@ -109,6 +157,13 @@ export declare function rewriteTokenRequestScope(policy: OAuthScopePolicy, sets:
|
|
|
109
157
|
* the token to a path this never matched.
|
|
110
158
|
*/
|
|
111
159
|
export declare function scopeRouteAllows(policy: OAuthScopePolicy, sets: readonly string[], method: string, upstream: URL): string | null;
|
|
160
|
+
/** `scopes` in the dialect's one spelling, deduplicated, order kept. */
|
|
161
|
+
/**
|
|
162
|
+
* `scopes` in the dialect's canonical spelling (Google's long names,
|
|
163
|
+
* Microsoft's bare lower-case names), for comparing a set's scopes with
|
|
164
|
+
* what a token response says it granted.
|
|
165
|
+
*/
|
|
166
|
+
export declare function canonicalScopes(dialect: OAuthScopeDialect, scopes: readonly string[]): string[];
|
|
112
167
|
/**
|
|
113
168
|
* `text` as a JSON object, or null for anything else (an array, a scalar,
|
|
114
169
|
* not JSON). Shared with the gateway's read of a provider's token answer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauthScopes.d.ts","sourceRoot":"","sources":["../../src/internal/oauthScopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"oauthScopes.d.ts","sourceRoot":"","sources":["../../src/internal/oauthScopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,yFAAyF;AACzF,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,4FAA4F;AAC5F,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GACtC,MAAM,EAAE,GAAG,IAAI,CAejB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,CASX;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,CASX;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CASnD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,OAAO,GACZ,aAAa,GAAG,IAAI,CAkCtB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,SAAS,GACnB,aAAa,GAAG,IAAI,CAYtB;AAED,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GACxB;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,2BAA2B,CAAC;IAClC,wDAAwD;IACxD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,+BAA+B,GAAG,gCAAgC,CAAC;IACzE,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,MAAM,EACN,YAAY,EACZ,cAAc,EACd,KAAK,EACL,MAAM,EACN,QAAQ,EACR,WAA6B,EAC7B,MAAM,GACP,EAAE;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,kCAAkC;IAClC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CA6C/C;AA+BD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,SAAS,GACjB,MAAM,EAAE,CASV;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,YAAY,GACpB,aAAa,GAAG,IAAI,CAStB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,YAAY,GACpB,YAAY,CA+Bd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,GAAG,GACZ,MAAM,GAAG,IAAI,CAsBf;AAoED,wEAAwE;AACxE;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,MAAM,EAAE,CAEV;AAwFD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMvE"}
|