@every-app/sdk 0.3.0-rc.9 → 0.3.1
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/README.md +79 -74
- package/dist/cloudflare/catalogSettings.generated.d.ts +19 -6
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
- package/dist/cloudflare/catalogSettings.generated.js +43 -14
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/worker.d.ts +63 -9
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +45 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/aiDefault.d.ts +36 -79
- package/dist/internal/aiDefault.d.ts.map +1 -1
- package/dist/internal/aiDefault.js +47 -149
- package/dist/internal/aiProviders.d.ts +1 -1
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +2 -2
- package/dist/internal/appSettings.d.ts +140 -89
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +264 -156
- package/dist/internal/capabilities.d.ts +109 -503
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +30 -550
- package/dist/internal/catalog/commerce.js +1 -1
- package/dist/internal/catalog/index.d.ts +35 -0
- package/dist/internal/catalog/index.d.ts.map +1 -1
- package/dist/internal/catalog/infra.d.ts +35 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -1
- package/dist/internal/catalog/infra.js +65 -0
- package/dist/internal/devProviders.d.ts +1 -19
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +7 -1
- package/dist/internal/events.d.ts +52 -1
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +66 -4
- package/dist/internal/gatewaySettings.d.ts +6 -16
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +5 -23
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -0
- package/dist/internal/mcpOAuth.d.ts +210 -0
- package/dist/internal/mcpOAuth.d.ts.map +1 -0
- package/dist/internal/mcpOAuth.js +735 -0
- package/dist/internal/oauthClient.d.ts +36 -44
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +30 -113
- package/dist/internal/oauthPresets.d.ts +356 -20
- package/dist/internal/oauthPresets.d.ts.map +1 -1
- package/dist/internal/oauthPresets.js +183 -861
- package/dist/internal/oauthScopes.d.ts +43 -155
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +82 -475
- package/dist/internal/oauthSpec.d.ts +28 -71
- package/dist/internal/oauthSpec.d.ts.map +1 -1
- package/dist/internal/oauthSpec.js +172 -448
- package/dist/internal/protocol.d.ts +24 -14
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +26 -7
- package/dist/internal/providerApps.d.ts +2 -2
- package/dist/internal/providerApps.js +2 -2
- package/dist/internal/providerRegistry.d.ts +5 -64
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +19 -118
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +14 -36
- package/dist/internal/remoteMcp.d.ts +191 -47
- package/dist/internal/remoteMcp.d.ts.map +1 -1
- package/dist/internal/remoteMcp.js +338 -133
- package/dist/internal/signInProviders.d.ts +1 -1
- package/dist/internal/signInProviders.d.ts.map +1 -1
- package/dist/internal/tools.d.ts +15 -8
- package/dist/internal/tools.d.ts.map +1 -1
- package/dist/internal/tools.js +9 -8
- package/dist/manifest.d.ts +96 -40
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +1 -1
- package/dist/server/ai.d.ts +1 -93
- package/dist/server/ai.d.ts.map +1 -1
- package/dist/server/ai.js +17 -111
- package/dist/server/attribution.d.ts +11 -1
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +7 -1
- package/dist/server/connectUrl.d.ts +5 -10
- package/dist/server/connectUrl.d.ts.map +1 -1
- package/dist/server/connectUrl.js +22 -14
- package/dist/server/context.d.ts +12 -14
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +23 -25
- package/dist/server/eventsEndpoint.d.ts +48 -0
- package/dist/server/eventsEndpoint.d.ts.map +1 -0
- package/dist/server/eventsEndpoint.js +145 -0
- package/dist/server/everyApp.d.ts +16 -26
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +22 -46
- package/dist/server/gatewayFetch.d.ts +1 -1
- package/dist/server/gatewayFetch.js +1 -1
- package/dist/server/index.d.ts +6 -9
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -8
- package/dist/server/integrationFetch.d.ts +66 -72
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +120 -392
- package/dist/server/refusal.d.ts +0 -9
- package/dist/server/refusal.d.ts.map +1 -1
- package/dist/server/refusal.js +0 -22
- package/dist/server/tools.d.ts +18 -7
- package/dist/server/tools.d.ts.map +1 -1
- package/dist/server/tools.js +15 -5
- package/dist/server/toolsEndpoint.d.ts +2 -1
- package/dist/server/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +4 -3
- package/dist/server/webhooks.d.ts +56 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +205 -0
- package/dist/testing/index.d.ts +2 -3
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -6
- package/package.json +6 -1
- package/dist/server/aiBinding.d.ts +0 -68
- package/dist/server/aiBinding.d.ts.map +0 -1
- package/dist/server/aiBinding.js +0 -294
- package/dist/server/authorizeUrl.d.ts +0 -43
- package/dist/server/authorizeUrl.d.ts.map +0 -1
- package/dist/server/authorizeUrl.js +0 -106
- package/dist/server/emailBinding.d.ts +0 -72
- package/dist/server/emailBinding.d.ts.map +0 -1
- package/dist/server/emailBinding.js +0 -124
- package/dist/server/fromMcpTools.d.ts +0 -89
- package/dist/server/fromMcpTools.d.ts.map +0 -1
- package/dist/server/fromMcpTools.js +0 -192
|
@@ -1,30 +1,121 @@
|
|
|
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, and no gateway reserves a preset's
|
|
9
|
-
* setting names until it is added. Google, Microsoft and Slack are
|
|
10
|
-
* catalog clients with a `flow` and are offered in the same picker. The
|
|
11
|
-
* social networks' presets carry the tag `social`.
|
|
12
|
-
*
|
|
13
|
-
* Each preset parses with `parseProviderSpec` (oauthPresets.test.ts),
|
|
14
|
-
* names a console where the client is made, the API hosts the token is
|
|
15
|
-
* sent to, and scope sets an app declares under `scopes`. Scope strings
|
|
16
|
-
* are the providers' own, verified against their docs on 2026-10-25;
|
|
17
|
-
* the social presets' come from the catalog entries Postiz used, and
|
|
18
|
-
* none of them has been tried against a live connect yet.
|
|
19
|
-
*/
|
|
1
|
+
import { providerBindingKey } from "./providerRegistry.js";
|
|
20
2
|
import { parseProviderSpec, } from "./providerSpec.js";
|
|
21
|
-
|
|
3
|
+
const PRESETS = {
|
|
4
|
+
// One Google Cloud project's OAuth client: Gmail, Calendar, Search
|
|
5
|
+
// Console, Analytics and any other Google API an app names a scope for.
|
|
6
|
+
"google-workspace": {
|
|
7
|
+
id: "google-workspace",
|
|
8
|
+
name: "Google",
|
|
9
|
+
description: "Gmail, Calendar, Drive, Search Console, Analytics and the other Google APIs, as the person who connects.",
|
|
10
|
+
docs: "https://console.cloud.google.com/apis/credentials",
|
|
11
|
+
api: "https://developers.google.com/identity/protocols/oauth2/scopes",
|
|
12
|
+
hosts: [
|
|
13
|
+
"gmail.googleapis.com",
|
|
14
|
+
"www.googleapis.com",
|
|
15
|
+
"searchconsole.googleapis.com",
|
|
16
|
+
"analyticsdata.googleapis.com",
|
|
17
|
+
"analyticsadmin.googleapis.com",
|
|
18
|
+
"openidconnect.googleapis.com",
|
|
19
|
+
],
|
|
20
|
+
auth: {
|
|
21
|
+
kind: "oauth2-user",
|
|
22
|
+
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
23
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
24
|
+
// A disconnect revokes the grant: POST form `token=` (RFC 7009), the
|
|
25
|
+
// refresh token once the access token has expired.
|
|
26
|
+
revokeUrl: "https://oauth2.googleapis.com/revoke",
|
|
27
|
+
clientId: "GOOGLE_WORKSPACE_CLIENT_ID",
|
|
28
|
+
clientSecret: "GOOGLE_WORKSPACE_CLIENT_SECRET",
|
|
29
|
+
inject: "form",
|
|
30
|
+
pkce: true,
|
|
31
|
+
scopeSeparator: "space",
|
|
32
|
+
// An app writes `gmail.modify`; the authorize URL carries the full
|
|
33
|
+
// `https://www.googleapis.com/auth/gmail.modify`.
|
|
34
|
+
scopePrefix: "https://www.googleapis.com/auth/",
|
|
35
|
+
// A refresh token needs `access_type=offline`; `prompt=consent`
|
|
36
|
+
// makes Google issue one on every connect.
|
|
37
|
+
authorizeParams: { access_type: "offline", prompt: "consent" },
|
|
38
|
+
identity: {
|
|
39
|
+
url: "https://openidconnect.googleapis.com/v1/userinfo",
|
|
40
|
+
label: "email",
|
|
41
|
+
id: "sub",
|
|
42
|
+
scopes: ["openid", "email"],
|
|
43
|
+
},
|
|
44
|
+
// Google answers `email` and `profile` as the userinfo URLs, and may
|
|
45
|
+
// add `profile` beside `openid`.
|
|
46
|
+
tolerated: [
|
|
47
|
+
"profile",
|
|
48
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
49
|
+
"https://www.googleapis.com/auth/userinfo.profile",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
hint: "A Web application OAuth client in Google Cloud, with the Gateway's callback as its redirect URI and the scopes your apps list on its consent screen.",
|
|
53
|
+
},
|
|
54
|
+
// The multi-tenant Entra endpoints: register the client for accounts in
|
|
55
|
+
// any organization and personal accounts.
|
|
56
|
+
"microsoft-365": {
|
|
57
|
+
id: "microsoft-365",
|
|
58
|
+
name: "Microsoft 365",
|
|
59
|
+
description: "Outlook mail and calendar, OneDrive and the rest of Microsoft Graph, as the person who connects.",
|
|
60
|
+
docs: "https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade",
|
|
61
|
+
api: "https://learn.microsoft.com/graph/permissions-reference",
|
|
62
|
+
hosts: ["graph.microsoft.com"],
|
|
63
|
+
auth: {
|
|
64
|
+
kind: "oauth2-user",
|
|
65
|
+
authorizeUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
|
66
|
+
tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
|
67
|
+
clientId: "MICROSOFT_365_CLIENT_ID",
|
|
68
|
+
clientSecret: "MICROSOFT_365_CLIENT_SECRET",
|
|
69
|
+
inject: "form",
|
|
70
|
+
pkce: true,
|
|
71
|
+
scopeSeparator: "space",
|
|
72
|
+
// An app writes `Mail.Read`; Graph's scopes are fully qualified on
|
|
73
|
+
// the wire and may come back either way.
|
|
74
|
+
scopePrefix: "https://graph.microsoft.com/",
|
|
75
|
+
identity: {
|
|
76
|
+
url: "https://graph.microsoft.com/v1.0/me",
|
|
77
|
+
label: "userPrincipalName",
|
|
78
|
+
id: "id",
|
|
79
|
+
// `offline_access` is what makes Entra issue a refresh token.
|
|
80
|
+
scopes: ["User.Read", "offline_access"],
|
|
81
|
+
},
|
|
82
|
+
tolerated: ["openid", "profile", "email"],
|
|
83
|
+
},
|
|
84
|
+
hint: "An app registration in Entra for accounts in any organization and personal accounts, with the Gateway's callback as a Web redirect URI. If two apps share this client with different scopes, Microsoft may hand back both apps' scopes and the Gateway refuses the token: give each app its own client.",
|
|
85
|
+
},
|
|
86
|
+
// A Slack app's bot token: the top-level token of `oauth.v2.access`.
|
|
87
|
+
slack: {
|
|
88
|
+
id: "slack",
|
|
89
|
+
name: "Slack",
|
|
90
|
+
description: "Channels, members and messages, as the Slack app.",
|
|
91
|
+
docs: "https://api.slack.com/apps",
|
|
92
|
+
api: "https://api.slack.com/scopes",
|
|
93
|
+
hosts: ["slack.com"],
|
|
94
|
+
auth: {
|
|
95
|
+
kind: "oauth2-user",
|
|
96
|
+
authorizeUrl: "https://slack.com/oauth/v2/authorize",
|
|
97
|
+
tokenUrl: "https://slack.com/api/oauth.v2.access",
|
|
98
|
+
revokeUrl: "https://slack.com/api/auth.revoke",
|
|
99
|
+
clientId: "SLACK_CLIENT_ID",
|
|
100
|
+
clientSecret: "SLACK_CLIENT_SECRET",
|
|
101
|
+
inject: "form",
|
|
102
|
+
// Slack refuses `code_challenge`.
|
|
103
|
+
pkce: false,
|
|
104
|
+
scopeSeparator: "comma",
|
|
105
|
+
identity: {
|
|
106
|
+
url: "https://slack.com/api/auth.test",
|
|
107
|
+
label: "team",
|
|
108
|
+
id: "team_id",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
hint: "A Slack app with the Gateway's callback as its redirect URL and the bot scopes your apps list. The Gateway takes its Client ID and Client Secret, not a bot token (xoxb-).",
|
|
112
|
+
},
|
|
22
113
|
"github-oauth": {
|
|
23
114
|
id: "github-oauth",
|
|
24
115
|
name: "GitHub",
|
|
25
116
|
description: "Repositories, issues, pull requests and the person's profile.",
|
|
26
117
|
docs: "https://github.com/settings/developers",
|
|
27
|
-
api: "https://docs.github.com/
|
|
118
|
+
api: "https://docs.github.com/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps",
|
|
28
119
|
hosts: ["api.github.com"],
|
|
29
120
|
auth: {
|
|
30
121
|
kind: "oauth2-user",
|
|
@@ -36,38 +127,16 @@ export const OAUTH_PRESETS = {
|
|
|
36
127
|
clientSecret: "GITHUB_OAUTH_CLIENT_SECRET",
|
|
37
128
|
inject: "form",
|
|
38
129
|
pkce: true,
|
|
39
|
-
|
|
130
|
+
scopeSeparator: "comma",
|
|
40
131
|
tokenHeaders: { Accept: "application/json" },
|
|
132
|
+
// GET /user answers with the public profile under any token.
|
|
41
133
|
identity: {
|
|
42
134
|
url: "https://api.github.com/user",
|
|
43
135
|
label: "login",
|
|
44
136
|
id: "id",
|
|
45
137
|
},
|
|
46
138
|
},
|
|
47
|
-
|
|
48
|
-
identity: {
|
|
49
|
-
label: "GitHub account (who you are)",
|
|
50
|
-
access: "read",
|
|
51
|
-
scopes: ["read:user", "user:email"],
|
|
52
|
-
},
|
|
53
|
-
"repo.read": {
|
|
54
|
-
label: "Repositories (read, private ones included)",
|
|
55
|
-
access: "read",
|
|
56
|
-
scopes: ["repo"],
|
|
57
|
-
},
|
|
58
|
-
"repo.write": {
|
|
59
|
-
label: "Repositories (read and write)",
|
|
60
|
-
access: "write",
|
|
61
|
-
scopes: ["repo"],
|
|
62
|
-
},
|
|
63
|
-
"org.read": {
|
|
64
|
-
label: "Organizations and teams (read)",
|
|
65
|
-
access: "read",
|
|
66
|
-
scopes: ["read:org"],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
access: ["read", "write"],
|
|
70
|
-
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.",
|
|
139
|
+
hint: "Register an OAuth App (not a GitHub App) and paste its client id and secret. GitHub's `repo` scope is read and write.",
|
|
71
140
|
},
|
|
72
141
|
"notion-oauth": {
|
|
73
142
|
id: "notion-oauth",
|
|
@@ -84,40 +153,10 @@ export const OAUTH_PRESETS = {
|
|
|
84
153
|
clientSecret: "NOTION_OAUTH_CLIENT_SECRET",
|
|
85
154
|
inject: "basic",
|
|
86
155
|
pkce: false,
|
|
87
|
-
|
|
156
|
+
scopeSeparator: "space",
|
|
88
157
|
authorizeParams: { owner: "user" },
|
|
89
158
|
},
|
|
90
|
-
|
|
91
|
-
// query; the read set lists those two, so a read grant never creates
|
|
92
|
-
// or changes a page (POST /v1/pages, PATCH, DELETE need the write set).
|
|
93
|
-
scopeSets: {
|
|
94
|
-
"workspace.read": {
|
|
95
|
-
label: "Shared pages and databases (read)",
|
|
96
|
-
access: "read",
|
|
97
|
-
scopes: [],
|
|
98
|
-
routes: [
|
|
99
|
-
{
|
|
100
|
-
host: "api.notion.com",
|
|
101
|
-
paths: [
|
|
102
|
-
"/v1/users",
|
|
103
|
-
"/v1/users/",
|
|
104
|
-
"/v1/pages/",
|
|
105
|
-
"/v1/databases/",
|
|
106
|
-
"/v1/blocks/",
|
|
107
|
-
"/v1/comments",
|
|
108
|
-
],
|
|
109
|
-
posts: ["^/v1/search$", "^/v1/databases/[^/]+/query$"],
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
},
|
|
113
|
-
"workspace.write": {
|
|
114
|
-
label: "Shared pages and databases (read and write)",
|
|
115
|
-
access: "write",
|
|
116
|
-
scopes: [],
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
access: ["read", "write"],
|
|
120
|
-
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.",
|
|
159
|
+
hint: "Notion has no scopes: an app lists scopes: [] and 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.",
|
|
121
160
|
},
|
|
122
161
|
"linear-oauth": {
|
|
123
162
|
id: "linear-oauth",
|
|
@@ -135,30 +174,16 @@ export const OAUTH_PRESETS = {
|
|
|
135
174
|
clientSecret: "LINEAR_OAUTH_CLIENT_SECRET",
|
|
136
175
|
inject: "form",
|
|
137
176
|
pkce: false,
|
|
138
|
-
|
|
139
|
-
},
|
|
140
|
-
// Linear's API is one GraphQL POST, which the Gateway can't tell a
|
|
141
|
-
// query from a mutation on, so the one set is a write set: an app
|
|
142
|
-
// lists Linear with access: "write", and Linear's own `read` scope is
|
|
143
|
-
// what a read-only token holds.
|
|
144
|
-
scopeSets: {
|
|
145
|
-
"workspace.write": {
|
|
146
|
-
label: "Issues, projects and teams (read and write)",
|
|
147
|
-
access: "write",
|
|
148
|
-
scopes: ["read", "write"],
|
|
149
|
-
},
|
|
177
|
+
scopeSeparator: "comma",
|
|
150
178
|
},
|
|
151
|
-
|
|
152
|
-
readMethods: [],
|
|
153
|
-
writeMethods: ["POST"],
|
|
154
|
-
hint: "Linear's API is GraphQL over POST, so every call needs access: \"write\"; a token's scopes decide what a query may change.",
|
|
179
|
+
hint: "Linear's scopes are read, write, issues:create and comments:create; its API is GraphQL over POST.",
|
|
155
180
|
},
|
|
156
181
|
"gitlab-oauth": {
|
|
157
182
|
id: "gitlab-oauth",
|
|
158
183
|
name: "GitLab",
|
|
159
184
|
description: "Projects, merge requests and issues on gitlab.com.",
|
|
160
185
|
docs: "https://gitlab.com/-/user_settings/applications",
|
|
161
|
-
api: "https://docs.gitlab.com/
|
|
186
|
+
api: "https://docs.gitlab.com/integration/oauth_provider/#view-all-authorized-applications",
|
|
162
187
|
hosts: ["gitlab.com"],
|
|
163
188
|
auth: {
|
|
164
189
|
kind: "oauth2-user",
|
|
@@ -169,31 +194,14 @@ export const OAUTH_PRESETS = {
|
|
|
169
194
|
clientSecret: "GITLAB_OAUTH_CLIENT_SECRET",
|
|
170
195
|
inject: "form",
|
|
171
196
|
pkce: true,
|
|
172
|
-
|
|
197
|
+
scopeSeparator: "space",
|
|
173
198
|
identity: {
|
|
174
199
|
url: "https://gitlab.com/api/v4/user",
|
|
175
200
|
label: "username",
|
|
176
201
|
id: "id",
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
scopeSets: {
|
|
180
|
-
identity: {
|
|
181
|
-
label: "GitLab account (who you are)",
|
|
182
|
-
access: "read",
|
|
183
202
|
scopes: ["read_user"],
|
|
184
203
|
},
|
|
185
|
-
"api.read": {
|
|
186
|
-
label: "Projects, issues and merge requests (read)",
|
|
187
|
-
access: "read",
|
|
188
|
-
scopes: ["read_api"],
|
|
189
|
-
},
|
|
190
|
-
"api.write": {
|
|
191
|
-
label: "Projects, issues and merge requests (read and write)",
|
|
192
|
-
access: "write",
|
|
193
|
-
scopes: ["api"],
|
|
194
|
-
},
|
|
195
204
|
},
|
|
196
|
-
access: ["read", "write"],
|
|
197
205
|
hint: "A self-managed GitLab needs a custom provider with its own host in place of gitlab.com.",
|
|
198
206
|
},
|
|
199
207
|
atlassian: {
|
|
@@ -201,7 +209,7 @@ export const OAUTH_PRESETS = {
|
|
|
201
209
|
name: "Atlassian (Jira and Confluence)",
|
|
202
210
|
description: "Jira issues and Confluence pages on the sites the person grants.",
|
|
203
211
|
docs: "https://developer.atlassian.com/console/myapps/",
|
|
204
|
-
api: "https://developer.atlassian.com/cloud/jira/platform/oauth-2-
|
|
212
|
+
api: "https://developer.atlassian.com/cloud/jira/platform/scopes-for-oauth-2-3LO-and-forge-apps/",
|
|
205
213
|
hosts: ["api.atlassian.com"],
|
|
206
214
|
auth: {
|
|
207
215
|
kind: "oauth2-user",
|
|
@@ -211,41 +219,16 @@ export const OAUTH_PRESETS = {
|
|
|
211
219
|
clientSecret: "ATLASSIAN_CLIENT_SECRET",
|
|
212
220
|
inject: "form",
|
|
213
221
|
pkce: false,
|
|
214
|
-
|
|
222
|
+
scopeSeparator: "space",
|
|
215
223
|
authorizeParams: { audience: "api.atlassian.com", prompt: "consent" },
|
|
216
224
|
identity: {
|
|
217
225
|
url: "https://api.atlassian.com/me",
|
|
218
226
|
label: "email",
|
|
219
227
|
id: "account_id",
|
|
228
|
+
// `offline_access` is what yields a refresh token.
|
|
229
|
+
scopes: ["read:me", "offline_access"],
|
|
220
230
|
},
|
|
221
231
|
},
|
|
222
|
-
scopeSets: {
|
|
223
|
-
identity: {
|
|
224
|
-
label: "Atlassian account (who you are)",
|
|
225
|
-
access: "read",
|
|
226
|
-
scopes: ["read:me"],
|
|
227
|
-
},
|
|
228
|
-
"jira.read": {
|
|
229
|
-
label: "Jira issues and projects (read)",
|
|
230
|
-
access: "read",
|
|
231
|
-
scopes: ["read:jira-work", "read:jira-user"],
|
|
232
|
-
},
|
|
233
|
-
"jira.write": {
|
|
234
|
-
label: "Jira issues (read and write)",
|
|
235
|
-
access: "write",
|
|
236
|
-
scopes: ["read:jira-work", "write:jira-work"],
|
|
237
|
-
},
|
|
238
|
-
"confluence.read": {
|
|
239
|
-
label: "Confluence pages (read)",
|
|
240
|
-
access: "read",
|
|
241
|
-
scopes: [
|
|
242
|
-
"read:confluence-content.all",
|
|
243
|
-
"read:confluence-space.summary",
|
|
244
|
-
],
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
alwaysRequested: ["offline_access"],
|
|
248
|
-
access: ["read", "write"],
|
|
249
232
|
hint: "Calls go to api.atlassian.com/ex/jira/<cloud id>/… ; the cloud id comes from /oauth/token/accessible-resources.",
|
|
250
233
|
},
|
|
251
234
|
"hubspot-oauth": {
|
|
@@ -253,7 +236,7 @@ export const OAUTH_PRESETS = {
|
|
|
253
236
|
name: "HubSpot",
|
|
254
237
|
description: "CRM contacts, companies and deals.",
|
|
255
238
|
docs: "https://developers.hubspot.com/",
|
|
256
|
-
api: "https://developers.hubspot.com/docs/
|
|
239
|
+
api: "https://developers.hubspot.com/docs/api/working-with-oauth",
|
|
257
240
|
hosts: ["api.hubapi.com"],
|
|
258
241
|
auth: {
|
|
259
242
|
kind: "oauth2-user",
|
|
@@ -263,39 +246,16 @@ export const OAUTH_PRESETS = {
|
|
|
263
246
|
clientSecret: "HUBSPOT_OAUTH_CLIENT_SECRET",
|
|
264
247
|
inject: "form",
|
|
265
248
|
pkce: false,
|
|
266
|
-
|
|
267
|
-
},
|
|
268
|
-
scopeSets: {
|
|
269
|
-
"contacts.read": {
|
|
270
|
-
label: "Contacts (read)",
|
|
271
|
-
access: "read",
|
|
272
|
-
scopes: ["crm.objects.contacts.read"],
|
|
273
|
-
},
|
|
274
|
-
"contacts.write": {
|
|
275
|
-
label: "Contacts (read and write)",
|
|
276
|
-
access: "write",
|
|
277
|
-
scopes: ["crm.objects.contacts.read", "crm.objects.contacts.write"],
|
|
278
|
-
},
|
|
279
|
-
"companies.read": {
|
|
280
|
-
label: "Companies (read)",
|
|
281
|
-
access: "read",
|
|
282
|
-
scopes: ["crm.objects.companies.read"],
|
|
283
|
-
},
|
|
284
|
-
"deals.read": {
|
|
285
|
-
label: "Deals (read)",
|
|
286
|
-
access: "read",
|
|
287
|
-
scopes: ["crm.objects.deals.read"],
|
|
288
|
-
},
|
|
249
|
+
scopeSeparator: "space",
|
|
289
250
|
},
|
|
290
|
-
|
|
291
|
-
hint: "The app's scopes in HubSpot's developer console must match the sets apps declare, or HubSpot refuses the authorize URL.",
|
|
251
|
+
hint: "The app's scopes in HubSpot's developer console must include the scopes apps list, or HubSpot refuses the authorize URL.",
|
|
292
252
|
},
|
|
293
253
|
zoom: {
|
|
294
254
|
id: "zoom",
|
|
295
255
|
name: "Zoom",
|
|
296
256
|
description: "The person's meetings and recordings.",
|
|
297
257
|
docs: "https://marketplace.zoom.us/develop/create",
|
|
298
|
-
api: "https://developers.zoom.us/docs/
|
|
258
|
+
api: "https://developers.zoom.us/docs/integrations/oauth-scopes-overview/",
|
|
299
259
|
hosts: ["api.zoom.us"],
|
|
300
260
|
auth: {
|
|
301
261
|
kind: "oauth2-user",
|
|
@@ -306,48 +266,22 @@ export const OAUTH_PRESETS = {
|
|
|
306
266
|
clientSecret: "ZOOM_CLIENT_SECRET",
|
|
307
267
|
inject: "basic",
|
|
308
268
|
pkce: true,
|
|
309
|
-
|
|
269
|
+
scopeSeparator: "space",
|
|
310
270
|
identity: {
|
|
311
271
|
url: "https://api.zoom.us/v2/users/me",
|
|
312
272
|
label: "email",
|
|
313
273
|
id: "id",
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
scopeSets: {
|
|
317
|
-
identity: {
|
|
318
|
-
label: "Zoom account (who you are)",
|
|
319
|
-
access: "read",
|
|
320
274
|
scopes: ["user:read:user"],
|
|
321
275
|
},
|
|
322
|
-
"meetings.read": {
|
|
323
|
-
label: "Meetings (read)",
|
|
324
|
-
access: "read",
|
|
325
|
-
scopes: ["meeting:read:meeting", "meeting:read:list_meetings"],
|
|
326
|
-
},
|
|
327
|
-
"meetings.write": {
|
|
328
|
-
label: "Meetings (read, create and change)",
|
|
329
|
-
access: "write",
|
|
330
|
-
scopes: [
|
|
331
|
-
"meeting:read:meeting",
|
|
332
|
-
"meeting:write:meeting",
|
|
333
|
-
"meeting:update:meeting",
|
|
334
|
-
],
|
|
335
|
-
},
|
|
336
|
-
"recordings.read": {
|
|
337
|
-
label: "Cloud recordings (read)",
|
|
338
|
-
access: "read",
|
|
339
|
-
scopes: ["cloud_recording:read:list_user_recordings"],
|
|
340
|
-
},
|
|
341
276
|
},
|
|
342
|
-
|
|
343
|
-
hint: "A General app with user-managed OAuth; Zoom's granular scope names are the ones listed here.",
|
|
277
|
+
hint: "A General app with user-managed OAuth, using Zoom's granular scope names.",
|
|
344
278
|
},
|
|
345
279
|
dropbox: {
|
|
346
280
|
id: "dropbox",
|
|
347
281
|
name: "Dropbox",
|
|
348
282
|
description: "Files and folders in the person's Dropbox.",
|
|
349
283
|
docs: "https://www.dropbox.com/developers/apps",
|
|
350
|
-
api: "https://
|
|
284
|
+
api: "https://developers.dropbox.com/oauth-guide",
|
|
351
285
|
hosts: ["api.dropboxapi.com", "content.dropboxapi.com"],
|
|
352
286
|
auth: {
|
|
353
287
|
kind: "oauth2-user",
|
|
@@ -358,76 +292,19 @@ export const OAUTH_PRESETS = {
|
|
|
358
292
|
clientSecret: "DROPBOX_OAUTH_CLIENT_SECRET",
|
|
359
293
|
inject: "form",
|
|
360
294
|
pkce: true,
|
|
361
|
-
|
|
295
|
+
scopeSeparator: "space",
|
|
362
296
|
authorizeParams: { token_access_type: "offline" },
|
|
297
|
+
// Dropbox grants account_info.read to every app.
|
|
298
|
+
tolerated: ["account_info.read"],
|
|
363
299
|
},
|
|
364
|
-
|
|
365
|
-
// it unlocks under `posts`; everything else needs the write set.
|
|
366
|
-
scopeSets: {
|
|
367
|
-
identity: {
|
|
368
|
-
label: "Dropbox account (who you are)",
|
|
369
|
-
access: "read",
|
|
370
|
-
scopes: ["account_info.read"],
|
|
371
|
-
routes: [
|
|
372
|
-
{
|
|
373
|
-
host: "api.dropboxapi.com",
|
|
374
|
-
paths: [],
|
|
375
|
-
posts: [
|
|
376
|
-
"^/2/users/get_current_account$",
|
|
377
|
-
"^/2/users/get_space_usage$",
|
|
378
|
-
],
|
|
379
|
-
},
|
|
380
|
-
],
|
|
381
|
-
},
|
|
382
|
-
"files.read": {
|
|
383
|
-
label: "Files and folders (read)",
|
|
384
|
-
access: "read",
|
|
385
|
-
scopes: ["files.metadata.read", "files.content.read"],
|
|
386
|
-
routes: [
|
|
387
|
-
{
|
|
388
|
-
host: "api.dropboxapi.com",
|
|
389
|
-
paths: [],
|
|
390
|
-
posts: [
|
|
391
|
-
"^/2/files/list_folder$",
|
|
392
|
-
"^/2/files/list_folder/continue$",
|
|
393
|
-
"^/2/files/get_metadata$",
|
|
394
|
-
"^/2/files/search_v2$",
|
|
395
|
-
"^/2/files/search/continue_v2$",
|
|
396
|
-
"^/2/files/get_temporary_link$",
|
|
397
|
-
"^/2/files/list_revisions$",
|
|
398
|
-
],
|
|
399
|
-
},
|
|
400
|
-
{
|
|
401
|
-
host: "content.dropboxapi.com",
|
|
402
|
-
paths: [],
|
|
403
|
-
posts: [
|
|
404
|
-
"^/2/files/download$",
|
|
405
|
-
"^/2/files/get_thumbnail_v2$",
|
|
406
|
-
"^/2/files/export$",
|
|
407
|
-
],
|
|
408
|
-
},
|
|
409
|
-
],
|
|
410
|
-
},
|
|
411
|
-
"files.write": {
|
|
412
|
-
label: "Files and folders (read and write)",
|
|
413
|
-
access: "write",
|
|
414
|
-
scopes: [
|
|
415
|
-
"files.metadata.read",
|
|
416
|
-
"files.content.read",
|
|
417
|
-
"files.metadata.write",
|
|
418
|
-
"files.content.write",
|
|
419
|
-
],
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
access: ["read", "write"],
|
|
423
|
-
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.',
|
|
300
|
+
hint: "`token_access_type=offline` is what yields a refresh token.",
|
|
424
301
|
},
|
|
425
302
|
spotify: {
|
|
426
303
|
id: "spotify",
|
|
427
304
|
name: "Spotify",
|
|
428
305
|
description: "The person's library, playlists and listening.",
|
|
429
306
|
docs: "https://developer.spotify.com/dashboard",
|
|
430
|
-
api: "https://developer.spotify.com/documentation/web-api",
|
|
307
|
+
api: "https://developer.spotify.com/documentation/web-api/concepts/scopes",
|
|
431
308
|
hosts: ["api.spotify.com"],
|
|
432
309
|
auth: {
|
|
433
310
|
kind: "oauth2-user",
|
|
@@ -437,612 +314,28 @@ export const OAUTH_PRESETS = {
|
|
|
437
314
|
clientSecret: "SPOTIFY_CLIENT_SECRET",
|
|
438
315
|
inject: "basic",
|
|
439
316
|
pkce: true,
|
|
440
|
-
|
|
317
|
+
scopeSeparator: "space",
|
|
318
|
+
// GET /v1/me names the account under any token.
|
|
441
319
|
identity: {
|
|
442
320
|
url: "https://api.spotify.com/v1/me",
|
|
443
321
|
label: "display_name",
|
|
444
322
|
id: "id",
|
|
445
323
|
},
|
|
446
324
|
},
|
|
447
|
-
scopeSets: {
|
|
448
|
-
identity: {
|
|
449
|
-
label: "Spotify account (who you are)",
|
|
450
|
-
access: "read",
|
|
451
|
-
scopes: ["user-read-email", "user-read-private"],
|
|
452
|
-
},
|
|
453
|
-
"library.read": {
|
|
454
|
-
label: "Saved tracks and albums (read)",
|
|
455
|
-
access: "read",
|
|
456
|
-
scopes: ["user-library-read"],
|
|
457
|
-
},
|
|
458
|
-
"playlists.read": {
|
|
459
|
-
label: "Playlists (read)",
|
|
460
|
-
access: "read",
|
|
461
|
-
scopes: ["playlist-read-private", "playlist-read-collaborative"],
|
|
462
|
-
},
|
|
463
|
-
"playlists.write": {
|
|
464
|
-
label: "Playlists (read and change)",
|
|
465
|
-
access: "write",
|
|
466
|
-
scopes: [
|
|
467
|
-
"playlist-read-private",
|
|
468
|
-
"playlist-modify-private",
|
|
469
|
-
"playlist-modify-public",
|
|
470
|
-
],
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
access: ["read", "write"],
|
|
474
|
-
},
|
|
475
|
-
"x-oauth": {
|
|
476
|
-
id: "x-oauth",
|
|
477
|
-
name: "X (OAuth 2)",
|
|
478
|
-
description: "Posts and the person's profile on X, over the v2 API.",
|
|
479
|
-
docs: "https://developer.x.com/en/portal/dashboard",
|
|
480
|
-
api: "https://docs.x.com/x-api",
|
|
481
|
-
hosts: ["api.x.com"],
|
|
482
|
-
auth: {
|
|
483
|
-
kind: "oauth2-user",
|
|
484
|
-
authorizeUrl: "https://x.com/i/oauth2/authorize",
|
|
485
|
-
tokenUrl: "https://api.x.com/2/oauth2/token",
|
|
486
|
-
revokeUrl: "https://api.x.com/2/oauth2/revoke",
|
|
487
|
-
clientId: "X_OAUTH_CLIENT_ID",
|
|
488
|
-
clientSecret: "X_OAUTH_CLIENT_SECRET",
|
|
489
|
-
inject: "basic",
|
|
490
|
-
pkce: true,
|
|
491
|
-
scopeDialect: "space",
|
|
492
|
-
identity: {
|
|
493
|
-
url: "https://api.x.com/2/users/me",
|
|
494
|
-
label: "data.username",
|
|
495
|
-
id: "data.id",
|
|
496
|
-
},
|
|
497
|
-
},
|
|
498
|
-
scopeSets: {
|
|
499
|
-
identity: {
|
|
500
|
-
label: "X account (who you are)",
|
|
501
|
-
access: "read",
|
|
502
|
-
scopes: ["users.read", "tweet.read"],
|
|
503
|
-
},
|
|
504
|
-
"posts.read": {
|
|
505
|
-
label: "Posts and timelines (read)",
|
|
506
|
-
access: "read",
|
|
507
|
-
scopes: ["tweet.read", "users.read"],
|
|
508
|
-
},
|
|
509
|
-
"posts.write": {
|
|
510
|
-
label: "Posts (read and publish)",
|
|
511
|
-
access: "write",
|
|
512
|
-
scopes: ["tweet.read", "users.read", "tweet.write"],
|
|
513
|
-
},
|
|
514
|
-
},
|
|
515
|
-
alwaysRequested: ["offline.access"],
|
|
516
|
-
access: ["read", "write"],
|
|
517
|
-
hint: "OAuth 2 with PKCE on X's v2 API; X's OAuth 1.0a (the v1.1 media upload) stays an app secret.",
|
|
518
|
-
},
|
|
519
|
-
// Social networks, tagged `social`: the clients Postiz connected its
|
|
520
|
-
// channels with, once catalog entries every gateway reserved names for.
|
|
521
|
-
linkedin: {
|
|
522
|
-
id: "linkedin",
|
|
523
|
-
name: "LinkedIn",
|
|
524
|
-
description: "Posts on a member's profile and on the company pages they admin.",
|
|
525
|
-
docs: "https://www.linkedin.com/developers/apps",
|
|
526
|
-
api: "https://learn.microsoft.com/linkedin/",
|
|
527
|
-
hosts: ["api.linkedin.com"],
|
|
528
|
-
auth: {
|
|
529
|
-
kind: "oauth2-user",
|
|
530
|
-
authorizeUrl: "https://www.linkedin.com/oauth/v2/authorization",
|
|
531
|
-
tokenUrl: "https://www.linkedin.com/oauth/v2/accessToken",
|
|
532
|
-
clientId: "LINKEDIN_CLIENT_ID",
|
|
533
|
-
clientSecret: "LINKEDIN_CLIENT_SECRET",
|
|
534
|
-
inject: "form",
|
|
535
|
-
// PKCE is for LinkedIn's native apps; a web client sends the secret.
|
|
536
|
-
pkce: false,
|
|
537
|
-
scopeDialect: "space",
|
|
538
|
-
identity: {
|
|
539
|
-
url: "https://api.linkedin.com/v2/userinfo",
|
|
540
|
-
label: "name",
|
|
541
|
-
id: "sub",
|
|
542
|
-
},
|
|
543
|
-
},
|
|
544
|
-
scopeSets: {
|
|
545
|
-
identity: {
|
|
546
|
-
label: "LinkedIn account (who you are)",
|
|
547
|
-
access: "read",
|
|
548
|
-
scopes: ["openid", "profile"],
|
|
549
|
-
},
|
|
550
|
-
"posts.write": {
|
|
551
|
-
label: "Posts on your profile",
|
|
552
|
-
access: "write",
|
|
553
|
-
scopes: ["openid", "profile", "w_member_social"],
|
|
554
|
-
},
|
|
555
|
-
"pages.write": {
|
|
556
|
-
label: "Company pages you admin (read and post)",
|
|
557
|
-
access: "write",
|
|
558
|
-
scopes: [
|
|
559
|
-
"rw_organization_admin",
|
|
560
|
-
"w_organization_social",
|
|
561
|
-
"r_organization_social",
|
|
562
|
-
],
|
|
563
|
-
},
|
|
564
|
-
},
|
|
565
|
-
access: ["read", "write"],
|
|
566
|
-
tags: ["social"],
|
|
567
|
-
hint: "Add the Sign In with LinkedIn and Share on LinkedIn products to the app; company pages need the Community Management API. Refresh tokens come to approved partners only. Not yet tried against a live connect.",
|
|
568
|
-
},
|
|
569
|
-
facebook: {
|
|
570
|
-
id: "facebook",
|
|
571
|
-
name: "Facebook Pages",
|
|
572
|
-
description: "Facebook pages, and the Instagram business accounts linked to them.",
|
|
573
|
-
docs: "https://developers.facebook.com/apps",
|
|
574
|
-
api: "https://developers.facebook.com/docs/graph-api",
|
|
575
|
-
hosts: ["graph.facebook.com"],
|
|
576
|
-
auth: {
|
|
577
|
-
kind: "oauth2-user",
|
|
578
|
-
authorizeUrl: "https://www.facebook.com/v25.0/dialog/oauth",
|
|
579
|
-
tokenUrl: "https://graph.facebook.com/v25.0/oauth/access_token",
|
|
580
|
-
clientId: "FACEBOOK_APP_ID",
|
|
581
|
-
clientSecret: "FACEBOOK_APP_SECRET",
|
|
582
|
-
inject: "form",
|
|
583
|
-
pkce: false,
|
|
584
|
-
scopeDialect: "comma",
|
|
585
|
-
identity: {
|
|
586
|
-
url: "https://graph.facebook.com/v25.0/me",
|
|
587
|
-
label: "name",
|
|
588
|
-
id: "id",
|
|
589
|
-
},
|
|
590
|
-
},
|
|
591
|
-
scopeSets: {
|
|
592
|
-
identity: {
|
|
593
|
-
label: "Facebook account (who you are)",
|
|
594
|
-
access: "read",
|
|
595
|
-
scopes: ["public_profile"],
|
|
596
|
-
},
|
|
597
|
-
"pages.read": {
|
|
598
|
-
label: "Pages you manage and their insights (read)",
|
|
599
|
-
access: "read",
|
|
600
|
-
scopes: ["pages_show_list", "pages_read_engagement", "read_insights"],
|
|
601
|
-
},
|
|
602
|
-
"pages.write": {
|
|
603
|
-
label: "Pages you manage (read, post and reply)",
|
|
604
|
-
access: "write",
|
|
605
|
-
scopes: [
|
|
606
|
-
"pages_show_list",
|
|
607
|
-
"business_management",
|
|
608
|
-
"pages_manage_posts",
|
|
609
|
-
"pages_manage_engagement",
|
|
610
|
-
"pages_read_engagement",
|
|
611
|
-
],
|
|
612
|
-
},
|
|
613
|
-
"instagram.write": {
|
|
614
|
-
label: "Instagram business accounts linked to your pages",
|
|
615
|
-
access: "write",
|
|
616
|
-
scopes: [
|
|
617
|
-
"pages_show_list",
|
|
618
|
-
"instagram_basic",
|
|
619
|
-
"instagram_content_publish",
|
|
620
|
-
"instagram_manage_comments",
|
|
621
|
-
"instagram_manage_insights",
|
|
622
|
-
],
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
access: ["read", "write"],
|
|
626
|
-
tags: ["social"],
|
|
627
|
-
hint: "A Business app with Facebook Login. Meta issues no refresh token: a person reconnects when the token expires. Not yet tried against a live connect.",
|
|
628
|
-
},
|
|
629
|
-
instagram: {
|
|
630
|
-
id: "instagram",
|
|
631
|
-
name: "Instagram",
|
|
632
|
-
description: "An Instagram professional account on its own, without a Facebook page.",
|
|
633
|
-
docs: "https://developers.facebook.com/apps",
|
|
634
|
-
api: "https://developers.facebook.com/docs/instagram-platform",
|
|
635
|
-
hosts: ["graph.instagram.com"],
|
|
636
|
-
auth: {
|
|
637
|
-
kind: "oauth2-user",
|
|
638
|
-
authorizeUrl: "https://www.instagram.com/oauth/authorize",
|
|
639
|
-
tokenUrl: "https://api.instagram.com/oauth/access_token",
|
|
640
|
-
clientId: "INSTAGRAM_APP_ID",
|
|
641
|
-
clientSecret: "INSTAGRAM_APP_SECRET",
|
|
642
|
-
inject: "form",
|
|
643
|
-
pkce: false,
|
|
644
|
-
scopeDialect: "comma",
|
|
645
|
-
identity: {
|
|
646
|
-
url: "https://graph.instagram.com/me?fields=username",
|
|
647
|
-
label: "username",
|
|
648
|
-
id: "id",
|
|
649
|
-
},
|
|
650
|
-
},
|
|
651
|
-
scopeSets: {
|
|
652
|
-
identity: {
|
|
653
|
-
label: "Instagram account (who you are)",
|
|
654
|
-
access: "read",
|
|
655
|
-
scopes: ["instagram_business_basic"],
|
|
656
|
-
},
|
|
657
|
-
"insights.read": {
|
|
658
|
-
label: "Insights (read)",
|
|
659
|
-
access: "read",
|
|
660
|
-
scopes: [
|
|
661
|
-
"instagram_business_basic",
|
|
662
|
-
"instagram_business_manage_insights",
|
|
663
|
-
],
|
|
664
|
-
},
|
|
665
|
-
"posts.write": {
|
|
666
|
-
label: "Posts and comments",
|
|
667
|
-
access: "write",
|
|
668
|
-
scopes: [
|
|
669
|
-
"instagram_business_basic",
|
|
670
|
-
"instagram_business_content_publish",
|
|
671
|
-
"instagram_business_manage_comments",
|
|
672
|
-
],
|
|
673
|
-
},
|
|
674
|
-
},
|
|
675
|
-
access: ["read", "write"],
|
|
676
|
-
tags: ["social"],
|
|
677
|
-
hint: "The Instagram API with Instagram Login, on a Meta app. The token lasts an hour; the long-lived exchange isn't done by the Gateway. Not yet tried against a live connect.",
|
|
678
|
-
},
|
|
679
|
-
threads: {
|
|
680
|
-
id: "threads",
|
|
681
|
-
name: "Threads",
|
|
682
|
-
description: "Posts, replies and insights on a Threads profile.",
|
|
683
|
-
docs: "https://developers.facebook.com/apps",
|
|
684
|
-
api: "https://developers.facebook.com/docs/threads",
|
|
685
|
-
hosts: ["graph.threads.net"],
|
|
686
|
-
auth: {
|
|
687
|
-
kind: "oauth2-user",
|
|
688
|
-
authorizeUrl: "https://threads.net/oauth/authorize",
|
|
689
|
-
tokenUrl: "https://graph.threads.net/oauth/access_token",
|
|
690
|
-
clientId: "THREADS_APP_ID",
|
|
691
|
-
clientSecret: "THREADS_APP_SECRET",
|
|
692
|
-
inject: "form",
|
|
693
|
-
pkce: false,
|
|
694
|
-
scopeDialect: "comma",
|
|
695
|
-
identity: {
|
|
696
|
-
url: "https://graph.threads.net/v1.0/me?fields=id,username",
|
|
697
|
-
label: "username",
|
|
698
|
-
id: "id",
|
|
699
|
-
},
|
|
700
|
-
},
|
|
701
|
-
scopeSets: {
|
|
702
|
-
identity: {
|
|
703
|
-
label: "Threads profile (who you are)",
|
|
704
|
-
access: "read",
|
|
705
|
-
scopes: ["threads_basic"],
|
|
706
|
-
},
|
|
707
|
-
"insights.read": {
|
|
708
|
-
label: "Insights (read)",
|
|
709
|
-
access: "read",
|
|
710
|
-
scopes: ["threads_basic", "threads_manage_insights"],
|
|
711
|
-
},
|
|
712
|
-
"posts.write": {
|
|
713
|
-
label: "Posts and replies",
|
|
714
|
-
access: "write",
|
|
715
|
-
scopes: [
|
|
716
|
-
"threads_basic",
|
|
717
|
-
"threads_content_publish",
|
|
718
|
-
"threads_manage_replies",
|
|
719
|
-
],
|
|
720
|
-
},
|
|
721
|
-
},
|
|
722
|
-
access: ["read", "write"],
|
|
723
|
-
tags: ["social"],
|
|
724
|
-
hint: "The Threads use case on a Meta app. The token lasts an hour; the long-lived exchange isn't done by the Gateway. Not yet tried against a live connect.",
|
|
725
|
-
},
|
|
726
|
-
youtube: {
|
|
727
|
-
id: "youtube",
|
|
728
|
-
name: "YouTube",
|
|
729
|
-
description: "YouTube channels and their analytics, and Google Business Profiles.",
|
|
730
|
-
docs: "https://console.cloud.google.com/apis/credentials",
|
|
731
|
-
api: "https://developers.google.com/youtube/v3",
|
|
732
|
-
hosts: [
|
|
733
|
-
"www.googleapis.com",
|
|
734
|
-
"youtubeanalytics.googleapis.com",
|
|
735
|
-
"mybusinessaccountmanagement.googleapis.com",
|
|
736
|
-
"mybusinessbusinessinformation.googleapis.com",
|
|
737
|
-
"mybusiness.googleapis.com",
|
|
738
|
-
"businessprofileperformance.googleapis.com",
|
|
739
|
-
"openidconnect.googleapis.com",
|
|
740
|
-
],
|
|
741
|
-
auth: {
|
|
742
|
-
kind: "oauth2-user",
|
|
743
|
-
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
744
|
-
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
745
|
-
revokeUrl: "https://oauth2.googleapis.com/revoke",
|
|
746
|
-
clientId: "YOUTUBE_CLIENT_ID",
|
|
747
|
-
clientSecret: "YOUTUBE_CLIENT_SECRET",
|
|
748
|
-
inject: "form",
|
|
749
|
-
pkce: true,
|
|
750
|
-
scopeDialect: "google",
|
|
751
|
-
authorizeParams: { access_type: "offline", prompt: "consent" },
|
|
752
|
-
identity: {
|
|
753
|
-
url: "https://openidconnect.googleapis.com/v1/userinfo",
|
|
754
|
-
label: "email",
|
|
755
|
-
id: "sub",
|
|
756
|
-
},
|
|
757
|
-
},
|
|
758
|
-
scopeSets: {
|
|
759
|
-
identity: {
|
|
760
|
-
label: "Google account (who you are)",
|
|
761
|
-
access: "read",
|
|
762
|
-
scopes: [
|
|
763
|
-
"https://www.googleapis.com/auth/userinfo.profile",
|
|
764
|
-
"https://www.googleapis.com/auth/userinfo.email",
|
|
765
|
-
],
|
|
766
|
-
},
|
|
767
|
-
"youtube.read": {
|
|
768
|
-
label: "YouTube channels and analytics (read)",
|
|
769
|
-
access: "read",
|
|
770
|
-
scopes: [
|
|
771
|
-
"https://www.googleapis.com/auth/youtube.readonly",
|
|
772
|
-
"https://www.googleapis.com/auth/yt-analytics.readonly",
|
|
773
|
-
],
|
|
774
|
-
},
|
|
775
|
-
"youtube.write": {
|
|
776
|
-
label: "YouTube channels (upload and manage)",
|
|
777
|
-
access: "write",
|
|
778
|
-
scopes: [
|
|
779
|
-
"https://www.googleapis.com/auth/youtube",
|
|
780
|
-
"https://www.googleapis.com/auth/youtube.upload",
|
|
781
|
-
"https://www.googleapis.com/auth/youtube.force-ssl",
|
|
782
|
-
],
|
|
783
|
-
},
|
|
784
|
-
"business.write": {
|
|
785
|
-
label: "Google Business Profiles (read and post)",
|
|
786
|
-
access: "write",
|
|
787
|
-
scopes: ["https://www.googleapis.com/auth/business.manage"],
|
|
788
|
-
},
|
|
789
|
-
},
|
|
790
|
-
access: ["read", "write"],
|
|
791
|
-
tags: ["social"],
|
|
792
|
-
hint: "Its own Google Cloud project, apart from google-workspace's: YouTube's scopes need their own verification. Not yet tried against a live connect.",
|
|
793
|
-
},
|
|
794
|
-
reddit: {
|
|
795
|
-
id: "reddit",
|
|
796
|
-
name: "Reddit",
|
|
797
|
-
description: "Reading subreddits and posting as the person.",
|
|
798
|
-
docs: "https://www.reddit.com/prefs/apps",
|
|
799
|
-
api: "https://www.reddit.com/dev/api",
|
|
800
|
-
hosts: ["oauth.reddit.com"],
|
|
801
|
-
auth: {
|
|
802
|
-
kind: "oauth2-user",
|
|
803
|
-
authorizeUrl: "https://www.reddit.com/api/v1/authorize",
|
|
804
|
-
tokenUrl: "https://www.reddit.com/api/v1/access_token",
|
|
805
|
-
clientId: "REDDIT_CLIENT_ID",
|
|
806
|
-
clientSecret: "REDDIT_CLIENT_SECRET",
|
|
807
|
-
inject: "basic",
|
|
808
|
-
pkce: false,
|
|
809
|
-
scopeDialect: "space",
|
|
810
|
-
// A refresh token only with a permanent grant.
|
|
811
|
-
authorizeParams: { duration: "permanent" },
|
|
812
|
-
identity: {
|
|
813
|
-
url: "https://oauth.reddit.com/api/v1/me",
|
|
814
|
-
label: "name",
|
|
815
|
-
id: "id",
|
|
816
|
-
},
|
|
817
|
-
},
|
|
818
|
-
scopeSets: {
|
|
819
|
-
identity: {
|
|
820
|
-
label: "Reddit account (who you are)",
|
|
821
|
-
access: "read",
|
|
822
|
-
scopes: ["identity"],
|
|
823
|
-
},
|
|
824
|
-
"posts.read": {
|
|
825
|
-
label: "Subreddits and posts (read)",
|
|
826
|
-
access: "read",
|
|
827
|
-
scopes: ["identity", "read"],
|
|
828
|
-
},
|
|
829
|
-
"posts.write": {
|
|
830
|
-
label: "Posts (read, submit and flair)",
|
|
831
|
-
access: "write",
|
|
832
|
-
scopes: ["identity", "read", "submit", "flair"],
|
|
833
|
-
},
|
|
834
|
-
},
|
|
835
|
-
access: ["read", "write"],
|
|
836
|
-
tags: ["social"],
|
|
837
|
-
hint: "A web app on Reddit's apps page. Not yet tried against a live connect.",
|
|
838
|
-
},
|
|
839
|
-
pinterest: {
|
|
840
|
-
id: "pinterest",
|
|
841
|
-
name: "Pinterest",
|
|
842
|
-
description: "Boards and pins on a Pinterest account.",
|
|
843
|
-
docs: "https://developers.pinterest.com/apps/",
|
|
844
|
-
api: "https://developers.pinterest.com/docs/api/v5/",
|
|
845
|
-
hosts: ["api.pinterest.com"],
|
|
846
|
-
auth: {
|
|
847
|
-
kind: "oauth2-user",
|
|
848
|
-
authorizeUrl: "https://www.pinterest.com/oauth/",
|
|
849
|
-
tokenUrl: "https://api.pinterest.com/v5/oauth/token",
|
|
850
|
-
clientId: "PINTEREST_CLIENT_ID",
|
|
851
|
-
clientSecret: "PINTEREST_CLIENT_SECRET",
|
|
852
|
-
inject: "basic",
|
|
853
|
-
pkce: false,
|
|
854
|
-
scopeDialect: "comma",
|
|
855
|
-
identity: {
|
|
856
|
-
url: "https://api.pinterest.com/v5/user_account",
|
|
857
|
-
label: "username",
|
|
858
|
-
id: "id",
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
scopeSets: {
|
|
862
|
-
identity: {
|
|
863
|
-
label: "Pinterest account (who you are)",
|
|
864
|
-
access: "read",
|
|
865
|
-
scopes: ["user_accounts:read"],
|
|
866
|
-
},
|
|
867
|
-
"pins.read": {
|
|
868
|
-
label: "Boards and pins (read)",
|
|
869
|
-
access: "read",
|
|
870
|
-
scopes: ["user_accounts:read", "boards:read", "pins:read"],
|
|
871
|
-
},
|
|
872
|
-
"pins.write": {
|
|
873
|
-
label: "Boards and pins (read and write)",
|
|
874
|
-
access: "write",
|
|
875
|
-
scopes: [
|
|
876
|
-
"user_accounts:read",
|
|
877
|
-
"boards:read",
|
|
878
|
-
"boards:write",
|
|
879
|
-
"pins:read",
|
|
880
|
-
"pins:write",
|
|
881
|
-
],
|
|
882
|
-
},
|
|
883
|
-
},
|
|
884
|
-
access: ["read", "write"],
|
|
885
|
-
tags: ["social"],
|
|
886
|
-
hint: "Not yet tried against a live connect.",
|
|
887
|
-
},
|
|
888
|
-
discord: {
|
|
889
|
-
id: "discord",
|
|
890
|
-
name: "Discord",
|
|
891
|
-
description: "The person's Discord account and servers, and installing a bot in a server.",
|
|
892
|
-
docs: "https://discord.com/developers/applications",
|
|
893
|
-
api: "https://discord.com/developers/docs/reference",
|
|
894
|
-
hosts: ["discord.com"],
|
|
895
|
-
auth: {
|
|
896
|
-
kind: "oauth2-user",
|
|
897
|
-
authorizeUrl: "https://discord.com/oauth2/authorize",
|
|
898
|
-
tokenUrl: "https://discord.com/api/oauth2/token",
|
|
899
|
-
revokeUrl: "https://discord.com/api/oauth2/token/revoke",
|
|
900
|
-
clientId: "DISCORD_CLIENT_ID",
|
|
901
|
-
clientSecret: "DISCORD_CLIENT_SECRET",
|
|
902
|
-
inject: "basic",
|
|
903
|
-
pkce: true,
|
|
904
|
-
scopeDialect: "space",
|
|
905
|
-
identity: {
|
|
906
|
-
url: "https://discord.com/api/users/@me",
|
|
907
|
-
label: "username",
|
|
908
|
-
id: "id",
|
|
909
|
-
},
|
|
910
|
-
},
|
|
911
|
-
scopeSets: {
|
|
912
|
-
identity: {
|
|
913
|
-
label: "Discord account (who you are)",
|
|
914
|
-
access: "read",
|
|
915
|
-
scopes: ["identify"],
|
|
916
|
-
},
|
|
917
|
-
"servers.read": {
|
|
918
|
-
label: "Servers you are in (read)",
|
|
919
|
-
access: "read",
|
|
920
|
-
scopes: ["identify", "guilds"],
|
|
921
|
-
},
|
|
922
|
-
"bot.write": {
|
|
923
|
-
label: "Add the bot to a server",
|
|
924
|
-
access: "write",
|
|
925
|
-
scopes: ["identify", "guilds", "bot"],
|
|
926
|
-
},
|
|
927
|
-
},
|
|
928
|
-
access: ["read", "write"],
|
|
929
|
-
tags: ["social"],
|
|
930
|
-
hint: "Posting as the bot uses the bot's own token, an app secret the Gateway doesn't hold. Not yet tried against a live connect.",
|
|
931
|
-
},
|
|
932
|
-
kick: {
|
|
933
|
-
id: "kick",
|
|
934
|
-
name: "Kick",
|
|
935
|
-
description: "A Kick channel and its chat.",
|
|
936
|
-
docs: "https://kick.com/settings/developer",
|
|
937
|
-
api: "https://docs.kick.com/",
|
|
938
|
-
hosts: ["api.kick.com"],
|
|
939
|
-
auth: {
|
|
940
|
-
kind: "oauth2-user",
|
|
941
|
-
authorizeUrl: "https://id.kick.com/oauth/authorize",
|
|
942
|
-
tokenUrl: "https://id.kick.com/oauth/token",
|
|
943
|
-
clientId: "KICK_CLIENT_ID",
|
|
944
|
-
clientSecret: "KICK_CLIENT_SECRET",
|
|
945
|
-
inject: "form",
|
|
946
|
-
pkce: true,
|
|
947
|
-
scopeDialect: "space",
|
|
948
|
-
},
|
|
949
|
-
scopeSets: {
|
|
950
|
-
identity: {
|
|
951
|
-
label: "Kick account (who you are)",
|
|
952
|
-
access: "read",
|
|
953
|
-
scopes: ["user:read"],
|
|
954
|
-
},
|
|
955
|
-
"channel.read": {
|
|
956
|
-
label: "Your channel (read)",
|
|
957
|
-
access: "read",
|
|
958
|
-
scopes: ["user:read", "channel:read"],
|
|
959
|
-
},
|
|
960
|
-
"chat.write": {
|
|
961
|
-
label: "Chat messages",
|
|
962
|
-
access: "write",
|
|
963
|
-
scopes: ["user:read", "chat:write"],
|
|
964
|
-
},
|
|
965
|
-
},
|
|
966
|
-
access: ["read", "write"],
|
|
967
|
-
tags: ["social"],
|
|
968
|
-
hint: "Not yet tried against a live connect.",
|
|
969
|
-
},
|
|
970
|
-
twitch: {
|
|
971
|
-
id: "twitch",
|
|
972
|
-
name: "Twitch",
|
|
973
|
-
description: "A Twitch channel's chat and announcements.",
|
|
974
|
-
docs: "https://dev.twitch.tv/console/apps",
|
|
975
|
-
api: "https://dev.twitch.tv/docs/api/",
|
|
976
|
-
hosts: ["api.twitch.tv"],
|
|
977
|
-
auth: {
|
|
978
|
-
kind: "oauth2-user",
|
|
979
|
-
authorizeUrl: "https://id.twitch.tv/oauth2/authorize",
|
|
980
|
-
tokenUrl: "https://id.twitch.tv/oauth2/token",
|
|
981
|
-
clientId: "TWITCH_CLIENT_ID",
|
|
982
|
-
clientSecret: "TWITCH_CLIENT_SECRET",
|
|
983
|
-
inject: "form",
|
|
984
|
-
pkce: false,
|
|
985
|
-
scopeDialect: "space",
|
|
986
|
-
},
|
|
987
|
-
scopeSets: {
|
|
988
|
-
"chat.read": {
|
|
989
|
-
label: "Chat (read)",
|
|
990
|
-
access: "read",
|
|
991
|
-
scopes: ["user:read:chat"],
|
|
992
|
-
},
|
|
993
|
-
"chat.write": {
|
|
994
|
-
label: "Chat messages and announcements",
|
|
995
|
-
access: "write",
|
|
996
|
-
scopes: [
|
|
997
|
-
"user:read:chat",
|
|
998
|
-
"user:write:chat",
|
|
999
|
-
"moderator:manage:announcements",
|
|
1000
|
-
],
|
|
1001
|
-
},
|
|
1002
|
-
},
|
|
1003
|
-
access: ["read", "write"],
|
|
1004
|
-
tags: ["social"],
|
|
1005
|
-
hint: "Helix also wants the public client id as a Client-Id header on every call. Not yet tried against a live connect.",
|
|
1006
|
-
},
|
|
1007
|
-
tumblr: {
|
|
1008
|
-
id: "tumblr",
|
|
1009
|
-
name: "Tumblr",
|
|
1010
|
-
description: "Posting to the person's Tumblr blogs.",
|
|
1011
|
-
docs: "https://www.tumblr.com/oauth/apps",
|
|
1012
|
-
api: "https://www.tumblr.com/docs/en/api/v2",
|
|
1013
|
-
hosts: ["api.tumblr.com"],
|
|
1014
|
-
auth: {
|
|
1015
|
-
kind: "oauth2-user",
|
|
1016
|
-
authorizeUrl: "https://www.tumblr.com/oauth2/authorize",
|
|
1017
|
-
tokenUrl: "https://api.tumblr.com/v2/oauth2/token",
|
|
1018
|
-
clientId: "TUMBLR_CLIENT_ID",
|
|
1019
|
-
clientSecret: "TUMBLR_CLIENT_SECRET",
|
|
1020
|
-
inject: "form",
|
|
1021
|
-
pkce: false,
|
|
1022
|
-
scopeDialect: "space",
|
|
1023
|
-
identity: {
|
|
1024
|
-
url: "https://api.tumblr.com/v2/user/info",
|
|
1025
|
-
label: "response.user.name",
|
|
1026
|
-
},
|
|
1027
|
-
},
|
|
1028
|
-
scopeSets: {
|
|
1029
|
-
identity: {
|
|
1030
|
-
label: "Tumblr account (who you are)",
|
|
1031
|
-
access: "read",
|
|
1032
|
-
scopes: ["basic"],
|
|
1033
|
-
},
|
|
1034
|
-
"posts.write": {
|
|
1035
|
-
label: "Posts on your blogs",
|
|
1036
|
-
access: "write",
|
|
1037
|
-
scopes: ["basic", "write"],
|
|
1038
|
-
},
|
|
1039
|
-
},
|
|
1040
|
-
alwaysRequested: ["offline_access"],
|
|
1041
|
-
access: ["read", "write"],
|
|
1042
|
-
tags: ["social"],
|
|
1043
|
-
hint: "Not yet tried against a live connect.",
|
|
1044
325
|
},
|
|
1045
326
|
};
|
|
327
|
+
/** Every preset by id, in the order the picker shows them. */
|
|
328
|
+
export const OAUTH_PRESETS = PRESETS;
|
|
329
|
+
/** The agent docs' setup guides for a preset's client, where one exists. */
|
|
330
|
+
const SETUP_GUIDES = {
|
|
331
|
+
"google-workspace": "setup/google.md",
|
|
332
|
+
"microsoft-365": "setup/microsoft-365.md",
|
|
333
|
+
slack: "setup/slack.md",
|
|
334
|
+
};
|
|
335
|
+
/** The setup guide for OAuth provider `id`'s client, when its preset has one (`setup` in an app's settings report). */
|
|
336
|
+
export function oauthSetupGuide(id) {
|
|
337
|
+
return SETUP_GUIDES[id];
|
|
338
|
+
}
|
|
1046
339
|
/** The preset ids, in the order the picker shows them. */
|
|
1047
340
|
export function oauthPresetIds() {
|
|
1048
341
|
return Object.keys(OAUTH_PRESETS);
|
|
@@ -1058,7 +351,36 @@ export function oauthPreset(id) {
|
|
|
1058
351
|
}
|
|
1059
352
|
return parsed.capability;
|
|
1060
353
|
}
|
|
1061
|
-
/**
|
|
1062
|
-
|
|
1063
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Whether `capability`, a Gateway's row for `id`, is that preset's: its
|
|
356
|
+
* client would go to the preset's endpoints and hosts under the preset's
|
|
357
|
+
* setting names (the same binding key). Adding the preset again takes the
|
|
358
|
+
* client for such a row; a row of another spec under a preset's id is the
|
|
359
|
+
* operator's own and is never taken for the preset.
|
|
360
|
+
*/
|
|
361
|
+
export function isPresetRow(id, capability) {
|
|
362
|
+
const preset = oauthPreset(id);
|
|
363
|
+
return (preset !== null &&
|
|
364
|
+
providerBindingKey(capability) === providerBindingKey(preset));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* The preset ids among `ids` (an app's capabilities) that `lookup` (a
|
|
368
|
+
* Gateway's registry) doesn't have: the rows a deploy or an install
|
|
369
|
+
* writes from their presets before it checks the manifest.
|
|
370
|
+
*/
|
|
371
|
+
export function unregisteredPresetIds(ids, lookup) {
|
|
372
|
+
return [...new Set(ids)].filter((id) => Object.hasOwn(OAUTH_PRESETS, id) && lookup.get(id) === undefined);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* `lookup` with every preset it lacks, as the row it would be: what a
|
|
376
|
+
* local run (`everyapp dev`) and a check made before a Gateway is asked
|
|
377
|
+
* read, since a deploy writes the row of any preset an app names.
|
|
378
|
+
*/
|
|
379
|
+
export function withOAuthPresets(lookup) {
|
|
380
|
+
const presets = oauthPresetIds().filter((id) => !lookup.get(id));
|
|
381
|
+
return {
|
|
382
|
+
get: (id) => lookup.get(id) ??
|
|
383
|
+
(presets.includes(id) ? (oauthPreset(id) ?? undefined) : undefined),
|
|
384
|
+
ids: () => [...lookup.ids(), ...presets],
|
|
385
|
+
};
|
|
1064
386
|
}
|