@every-app/sdk 0.1.14 → 0.3.0-rc.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 +608 -2
- package/dist/cloudflare/index.d.ts +21 -1
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +21 -1
- package/dist/cloudflare/sentinel.d.ts +69 -0
- package/dist/cloudflare/sentinel.d.ts.map +1 -0
- package/dist/cloudflare/sentinel.js +50 -0
- package/dist/cloudflare/worker.d.ts +511 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +738 -0
- package/dist/dev/index.d.ts +8 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +7 -0
- package/dist/dev/keypair.d.ts +24 -0
- package/dist/dev/keypair.d.ts.map +1 -0
- package/dist/dev/keypair.js +60 -0
- package/dist/internal/aiProviders.d.ts +67 -0
- package/dist/internal/aiProviders.d.ts.map +1 -0
- package/dist/internal/aiProviders.js +220 -0
- package/dist/internal/appSettings.d.ts +237 -0
- package/dist/internal/appSettings.d.ts.map +1 -0
- package/dist/internal/appSettings.js +374 -0
- package/dist/internal/capabilities.d.ts +1344 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +1521 -0
- package/dist/internal/devProviders.d.ts +49 -0
- package/dist/internal/devProviders.d.ts.map +1 -0
- package/dist/internal/devProviders.js +62 -0
- package/dist/internal/email.d.ts +280 -0
- package/dist/internal/email.d.ts.map +1 -0
- package/dist/internal/email.js +613 -0
- package/dist/internal/events.d.ts +182 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +324 -0
- package/dist/internal/gatewaySettings.d.ts +82 -0
- package/dist/internal/gatewaySettings.d.ts.map +1 -0
- package/dist/internal/gatewaySettings.js +209 -0
- package/dist/internal/identity.d.ts +88 -0
- package/dist/internal/identity.d.ts.map +1 -0
- package/dist/internal/identity.js +231 -0
- package/dist/internal/index.d.ts +19 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +18 -0
- package/dist/internal/members.d.ts +35 -0
- package/dist/internal/members.d.ts.map +1 -0
- package/dist/internal/members.js +7 -0
- package/dist/internal/oauthClient.d.ts +63 -0
- package/dist/internal/oauthClient.d.ts.map +1 -0
- package/dist/internal/oauthClient.js +194 -0
- package/dist/internal/oauthScopes.d.ts +117 -0
- package/dist/internal/oauthScopes.d.ts.map +1 -0
- package/dist/internal/oauthScopes.js +416 -0
- package/dist/internal/organizationName.d.ts +18 -0
- package/dist/internal/organizationName.d.ts.map +1 -0
- package/dist/internal/organizationName.js +24 -0
- package/dist/internal/protocol.d.ts +195 -0
- package/dist/internal/protocol.d.ts.map +1 -0
- package/dist/internal/protocol.js +250 -0
- package/dist/internal/providerApps.d.ts +75 -0
- package/dist/internal/providerApps.d.ts.map +1 -0
- package/dist/internal/providerApps.js +59 -0
- package/dist/internal/providerRegistry.d.ts +147 -0
- package/dist/internal/providerRegistry.d.ts.map +1 -0
- package/dist/internal/providerRegistry.js +262 -0
- package/dist/internal/providerSpec.d.ts +55 -0
- package/dist/internal/providerSpec.d.ts.map +1 -0
- package/dist/internal/providerSpec.js +746 -0
- package/dist/internal/remoteMcp.d.ts +188 -0
- package/dist/internal/remoteMcp.d.ts.map +1 -0
- package/dist/internal/remoteMcp.js +543 -0
- package/dist/internal/tools.d.ts +115 -0
- package/dist/internal/tools.d.ts.map +1 -0
- package/dist/internal/tools.js +80 -0
- package/dist/server/attribution.d.ts +39 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +67 -0
- package/dist/server/authorizeUrl.d.ts +43 -0
- package/dist/server/authorizeUrl.d.ts.map +1 -0
- package/dist/server/authorizeUrl.js +110 -0
- package/dist/server/emit.d.ts +63 -0
- package/dist/server/emit.d.ts.map +1 -0
- package/dist/server/emit.js +101 -0
- package/dist/server/everyApp.d.ts +81 -0
- package/dist/server/everyApp.d.ts.map +1 -0
- package/dist/server/everyApp.js +185 -0
- package/dist/server/gatewayFetch.d.ts +37 -0
- package/dist/server/gatewayFetch.d.ts.map +1 -0
- package/dist/server/gatewayFetch.js +115 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/integrationFetch.d.ts +131 -0
- package/dist/server/integrationFetch.d.ts.map +1 -0
- package/dist/server/integrationFetch.js +526 -0
- package/dist/server/listMembers.d.ts +32 -0
- package/dist/server/listMembers.d.ts.map +1 -0
- package/dist/server/listMembers.js +74 -0
- package/dist/server/nodeServer.d.ts +48 -0
- package/dist/server/nodeServer.d.ts.map +1 -0
- package/dist/server/nodeServer.js +45 -0
- package/dist/server/sendEmail.d.ts +39 -0
- package/dist/server/sendEmail.d.ts.map +1 -0
- package/dist/server/sendEmail.js +114 -0
- package/dist/server/tools.d.ts +184 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +347 -0
- package/dist/server/toolsEndpoint.d.ts +32 -0
- package/dist/server/toolsEndpoint.d.ts.map +1 -0
- package/dist/server/toolsEndpoint.js +231 -0
- package/package.json +21 -31
- package/dist/cloudflare/getLocalD1Url.d.ts +0 -2
- package/dist/cloudflare/getLocalD1Url.d.ts.map +0 -1
- package/dist/cloudflare/getLocalD1Url.js +0 -40
- package/dist/cloudflare/lazyInit.d.ts +0 -22
- package/dist/cloudflare/lazyInit.d.ts.map +0 -1
- package/dist/cloudflare/lazyInit.js +0 -65
- package/dist/cloudflare/server/gateway.d.ts +0 -29
- package/dist/cloudflare/server/gateway.d.ts.map +0 -1
- package/dist/cloudflare/server/gateway.js +0 -69
- package/dist/cloudflare/server/index.d.ts +0 -3
- package/dist/cloudflare/server/index.d.ts.map +0 -1
- package/dist/cloudflare/server/index.js +0 -2
- package/dist/core/authenticatedFetch.d.ts +0 -9
- package/dist/core/authenticatedFetch.d.ts.map +0 -1
- package/dist/core/authenticatedFetch.js +0 -31
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -2
- package/dist/core/sessionManager.d.ts +0 -58
- package/dist/core/sessionManager.d.ts.map +0 -1
- package/dist/core/sessionManager.js +0 -357
- package/dist/shared/bypassGatewayLocalOnly.d.ts +0 -15
- package/dist/shared/bypassGatewayLocalOnly.d.ts.map +0 -1
- package/dist/shared/bypassGatewayLocalOnly.js +0 -45
- package/dist/shared/parseMessagePayload.d.ts +0 -3
- package/dist/shared/parseMessagePayload.d.ts.map +0 -1
- package/dist/shared/parseMessagePayload.js +0 -18
- package/dist/tanstack/EmbeddedAppProvider.d.ts +0 -30
- package/dist/tanstack/EmbeddedAppProvider.d.ts.map +0 -1
- package/dist/tanstack/EmbeddedAppProvider.js +0 -57
- package/dist/tanstack/GatewayRequiredError.d.ts +0 -20
- package/dist/tanstack/GatewayRequiredError.d.ts.map +0 -1
- package/dist/tanstack/GatewayRequiredError.js +0 -91
- package/dist/tanstack/_internal/useEveryAppSession.d.ts +0 -18
- package/dist/tanstack/_internal/useEveryAppSession.d.ts.map +0 -1
- package/dist/tanstack/_internal/useEveryAppSession.js +0 -47
- package/dist/tanstack/index.d.ts +0 -4
- package/dist/tanstack/index.d.ts.map +0 -1
- package/dist/tanstack/index.js +0 -3
- package/dist/tanstack/server/authConfig.d.ts +0 -3
- package/dist/tanstack/server/authConfig.d.ts.map +0 -1
- package/dist/tanstack/server/authConfig.js +0 -13
- package/dist/tanstack/server/authenticateRequest.d.ts +0 -31
- package/dist/tanstack/server/authenticateRequest.d.ts.map +0 -1
- package/dist/tanstack/server/authenticateRequest.js +0 -154
- package/dist/tanstack/server/index.d.ts +0 -4
- package/dist/tanstack/server/index.d.ts.map +0 -1
- package/dist/tanstack/server/index.js +0 -2
- package/dist/tanstack/server/types.d.ts +0 -5
- package/dist/tanstack/server/types.d.ts.map +0 -1
- package/dist/tanstack/server/types.js +0 -1
- package/dist/tanstack/useEveryAppRouter.d.ts +0 -7
- package/dist/tanstack/useEveryAppRouter.d.ts.map +0 -1
- package/dist/tanstack/useEveryAppRouter.js +0 -66
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts +0 -2
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts.map +0 -1
- package/dist/tanstack/useSessionTokenClientMiddleware.js +0 -29
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `oauth_client` capability's wire rules (docs/capabilities.md "OAuth
|
|
3
|
+
* clients"), shared by the gateway, which puts the operator's client on an
|
|
4
|
+
* app's token request, and the SDK's local-dev fallback, which puts the
|
|
5
|
+
* developer's own on it the same way.
|
|
6
|
+
*/
|
|
7
|
+
import { type CapabilitySettings, type OAuthClientCapability, type OAuthTokenEndpoint } from "./capabilities.js";
|
|
8
|
+
/** The largest token request body the gateway reads: real ones are a few hundred bytes. */
|
|
9
|
+
export declare const OAUTH_TOKEN_BODY_LIMIT: number;
|
|
10
|
+
/** An OAuth client as the gateway (or `.dev.vars`) holds it. */
|
|
11
|
+
export interface OAuthClientCredentials {
|
|
12
|
+
clientId: string;
|
|
13
|
+
/** Absent for a public client. */
|
|
14
|
+
clientSecret?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The client `settings` hold for `capability`, or null until every setting
|
|
18
|
+
* it needs is set: the id, the secret (unless it is a public client), and a
|
|
19
|
+
* `hostSetting` that is an https origin when it is set at all.
|
|
20
|
+
*/
|
|
21
|
+
export declare function oauthClientCredentials(capability: OAuthClientCapability, settings: CapabilitySettings): OAuthClientCredentials | null;
|
|
22
|
+
/** A token request: where it goes, its headers, its (buffered) body. */
|
|
23
|
+
export interface TokenRequest {
|
|
24
|
+
url: URL;
|
|
25
|
+
headers: Headers;
|
|
26
|
+
body: string | null;
|
|
27
|
+
}
|
|
28
|
+
export type InjectedTokenRequest = ({
|
|
29
|
+
ok: true;
|
|
30
|
+
} & TokenRequest) | {
|
|
31
|
+
ok: false;
|
|
32
|
+
problem: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* `request` with `client` put where `rule` says (the form body, HTTP Basic,
|
|
36
|
+
* the query string or the JSON body), and every client credential the app
|
|
37
|
+
* sent removed wherever it was: the `Authorization` header, and
|
|
38
|
+
* `client_id`/`client_secret` (or the rule's names for them, in any case) in
|
|
39
|
+
* the query string and the body. Bodies are re-serialised from their parsed
|
|
40
|
+
* values, so a crafted body can't carry a second secret past the parser.
|
|
41
|
+
* Token requests are form-encoded or JSON; anything else is refused.
|
|
42
|
+
*/
|
|
43
|
+
export declare function injectClientCredentials(request: TokenRequest, rule: OAuthTokenEndpoint, client: OAuthClientCredentials): InjectedTokenRequest;
|
|
44
|
+
/**
|
|
45
|
+
* The grants the gateway completes: each ends in a user's own tokens. The
|
|
46
|
+
* client-credentials grant, and anything else a network serves on the same
|
|
47
|
+
* endpoint, would mint a token for the gateway's own application instead,
|
|
48
|
+
* which on Meta, Discord, Twitch or Kick manages the application's
|
|
49
|
+
* settings, its webhooks and every tenant's tokens.
|
|
50
|
+
*/
|
|
51
|
+
export declare const USER_BOUND_GRANT_TYPES: ReadonlySet<string>;
|
|
52
|
+
/**
|
|
53
|
+
* A token request's `grant_type` as the app sent it, from its body or query
|
|
54
|
+
* string, or null when it names none (Meta's code exchange). Never the
|
|
55
|
+
* request's other fields.
|
|
56
|
+
*/
|
|
57
|
+
export declare function requestedGrantType(request: TokenRequest): string | null;
|
|
58
|
+
/**
|
|
59
|
+
* A token request's `grant_type` for logs: one of the known grant types,
|
|
60
|
+
* `other`, or null when it names none.
|
|
61
|
+
*/
|
|
62
|
+
export declare function tokenGrantType(request: TokenRequest): string | null;
|
|
63
|
+
//# sourceMappingURL=oauthClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthClient.d.ts","sourceRoot":"","sources":["../../src/internal/oauthClient.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD,gEAAgE;AAChE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,EACjC,QAAQ,EAAE,kBAAkB,GAC3B,sBAAsB,GAAG,IAAI,CAW/B;AAMD,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,MAAM,oBAAoB,GAC5B,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,YAAY,CAAC,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAKnC;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,kBAAkB,EACxB,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CAyFtB;AAaD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAUvE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAInE"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `oauth_client` capability's wire rules (docs/capabilities.md "OAuth
|
|
3
|
+
* clients"), shared by the gateway, which puts the operator's client on an
|
|
4
|
+
* app's token request, and the SDK's local-dev fallback, which puts the
|
|
5
|
+
* developer's own on it the same way.
|
|
6
|
+
*/
|
|
7
|
+
import { base64Utf8, oauthClientOrigin, } from "./capabilities.js";
|
|
8
|
+
/** The largest token request body the gateway reads: real ones are a few hundred bytes. */
|
|
9
|
+
export const OAUTH_TOKEN_BODY_LIMIT = 64 * 1024;
|
|
10
|
+
/**
|
|
11
|
+
* The client `settings` hold for `capability`, or null until every setting
|
|
12
|
+
* it needs is set: the id, the secret (unless it is a public client), and a
|
|
13
|
+
* `hostSetting` that is an https origin when it is set at all.
|
|
14
|
+
*/
|
|
15
|
+
export function oauthClientCredentials(capability, settings) {
|
|
16
|
+
const clientId = settingValue(settings[capability.secrets.clientId]);
|
|
17
|
+
const secretName = capability.secrets.clientSecret;
|
|
18
|
+
const clientSecret = secretName
|
|
19
|
+
? settingValue(settings[secretName])
|
|
20
|
+
: undefined;
|
|
21
|
+
if (!clientId || (secretName && !clientSecret))
|
|
22
|
+
return null;
|
|
23
|
+
if (capability.hostSetting && !oauthClientOrigin(capability, settings)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return { clientId, ...(clientSecret ? { clientSecret } : {}) };
|
|
27
|
+
}
|
|
28
|
+
function settingValue(value) {
|
|
29
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
30
|
+
}
|
|
31
|
+
const FORM = "application/x-www-form-urlencoded";
|
|
32
|
+
const JSON_TYPE = "application/json";
|
|
33
|
+
/**
|
|
34
|
+
* `request` with `client` put where `rule` says (the form body, HTTP Basic,
|
|
35
|
+
* the query string or the JSON body), and every client credential the app
|
|
36
|
+
* sent removed wherever it was: the `Authorization` header, and
|
|
37
|
+
* `client_id`/`client_secret` (or the rule's names for them, in any case) in
|
|
38
|
+
* the query string and the body. Bodies are re-serialised from their parsed
|
|
39
|
+
* values, so a crafted body can't carry a second secret past the parser.
|
|
40
|
+
* Token requests are form-encoded or JSON; anything else is refused.
|
|
41
|
+
*/
|
|
42
|
+
export function injectClientCredentials(request, rule, client) {
|
|
43
|
+
const idName = rule.params?.clientId ?? "client_id";
|
|
44
|
+
const secretName = rule.params?.clientSecret ?? "client_secret";
|
|
45
|
+
const credentialNames = new Set(["client_id", "client_secret", idName, secretName].map((name) => name.toLowerCase()));
|
|
46
|
+
const isCredential = (name) => credentialNames.has(name.toLowerCase());
|
|
47
|
+
const fields = [
|
|
48
|
+
[idName, client.clientId],
|
|
49
|
+
...(client.clientSecret
|
|
50
|
+
? [[secretName, client.clientSecret]]
|
|
51
|
+
: []),
|
|
52
|
+
];
|
|
53
|
+
// The query keeps the app's other fields as they were sent.
|
|
54
|
+
const url = new URL(request.url);
|
|
55
|
+
const query = url.search
|
|
56
|
+
.slice(1)
|
|
57
|
+
.split("&")
|
|
58
|
+
.filter((pair) => {
|
|
59
|
+
const [name] = new URLSearchParams(pair).keys();
|
|
60
|
+
return pair !== "" && !(name !== undefined && isCredential(name));
|
|
61
|
+
});
|
|
62
|
+
if (rule.inject === "query") {
|
|
63
|
+
query.push(new URLSearchParams(fields).toString());
|
|
64
|
+
}
|
|
65
|
+
url.search = query.join("&");
|
|
66
|
+
const headers = new Headers(request.headers);
|
|
67
|
+
headers.delete("authorization");
|
|
68
|
+
headers.delete("content-length");
|
|
69
|
+
if (rule.inject === "basic") {
|
|
70
|
+
headers.set("authorization", `Basic ${base64Utf8(`${client.clientId}:${client.clientSecret ?? ""}`)}`);
|
|
71
|
+
}
|
|
72
|
+
const type = mediaType(headers.get("content-type"));
|
|
73
|
+
let body = request.body;
|
|
74
|
+
if (body === null || body === "") {
|
|
75
|
+
// Nothing the app sent; the form and JSON rules make the body.
|
|
76
|
+
if (rule.inject === "form") {
|
|
77
|
+
headers.set("content-type", FORM);
|
|
78
|
+
body = new URLSearchParams(fields).toString();
|
|
79
|
+
}
|
|
80
|
+
else if (rule.inject === "json") {
|
|
81
|
+
headers.set("content-type", JSON_TYPE);
|
|
82
|
+
body = JSON.stringify(Object.fromEntries(fields));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (type === FORM) {
|
|
86
|
+
if (rule.inject === "json") {
|
|
87
|
+
return { ok: false, problem: "This token endpoint takes a JSON body." };
|
|
88
|
+
}
|
|
89
|
+
const params = new URLSearchParams(body);
|
|
90
|
+
for (const name of new Set([...params.keys()].filter(isCredential))) {
|
|
91
|
+
params.delete(name);
|
|
92
|
+
}
|
|
93
|
+
if (rule.inject === "form") {
|
|
94
|
+
for (const [name, value] of fields)
|
|
95
|
+
params.set(name, value);
|
|
96
|
+
}
|
|
97
|
+
body = params.toString();
|
|
98
|
+
}
|
|
99
|
+
else if (type === JSON_TYPE) {
|
|
100
|
+
if (rule.inject === "form") {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
problem: `This token endpoint takes a form-encoded body (${FORM}).`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const parsed = jsonObject(body);
|
|
107
|
+
if (!parsed) {
|
|
108
|
+
return { ok: false, problem: "The body is not a JSON object." };
|
|
109
|
+
}
|
|
110
|
+
for (const name of Object.keys(parsed)) {
|
|
111
|
+
if (isCredential(name))
|
|
112
|
+
delete parsed[name];
|
|
113
|
+
}
|
|
114
|
+
if (rule.inject === "json") {
|
|
115
|
+
for (const [name, value] of fields)
|
|
116
|
+
parsed[name] = value;
|
|
117
|
+
}
|
|
118
|
+
body = JSON.stringify(parsed);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
problem: `A token request's body is form-encoded (${FORM}) or JSON (${JSON_TYPE}).`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, url, headers, body };
|
|
127
|
+
}
|
|
128
|
+
/** The grant types logs may name; anything else is logged as `other`. */
|
|
129
|
+
const GRANT_TYPES = new Set([
|
|
130
|
+
"authorization_code",
|
|
131
|
+
"refresh_token",
|
|
132
|
+
"client_credentials",
|
|
133
|
+
"fb_exchange_token",
|
|
134
|
+
"ig_exchange_token",
|
|
135
|
+
"th_exchange_token",
|
|
136
|
+
"urn:ietf:params:oauth:grant-type:token-exchange",
|
|
137
|
+
]);
|
|
138
|
+
/**
|
|
139
|
+
* The grants the gateway completes: each ends in a user's own tokens. The
|
|
140
|
+
* client-credentials grant, and anything else a network serves on the same
|
|
141
|
+
* endpoint, would mint a token for the gateway's own application instead,
|
|
142
|
+
* which on Meta, Discord, Twitch or Kick manages the application's
|
|
143
|
+
* settings, its webhooks and every tenant's tokens.
|
|
144
|
+
*/
|
|
145
|
+
export const USER_BOUND_GRANT_TYPES = new Set([
|
|
146
|
+
"authorization_code",
|
|
147
|
+
"refresh_token",
|
|
148
|
+
"fb_exchange_token",
|
|
149
|
+
"ig_exchange_token",
|
|
150
|
+
"th_exchange_token",
|
|
151
|
+
"urn:ietf:params:oauth:grant-type:token-exchange",
|
|
152
|
+
]);
|
|
153
|
+
/**
|
|
154
|
+
* A token request's `grant_type` as the app sent it, from its body or query
|
|
155
|
+
* string, or null when it names none (Meta's code exchange). Never the
|
|
156
|
+
* request's other fields.
|
|
157
|
+
*/
|
|
158
|
+
export function requestedGrantType(request) {
|
|
159
|
+
let value = null;
|
|
160
|
+
const type = mediaType(request.headers.get("content-type"));
|
|
161
|
+
if (request.body && type === FORM) {
|
|
162
|
+
value = new URLSearchParams(request.body).get("grant_type");
|
|
163
|
+
}
|
|
164
|
+
else if (request.body && type === JSON_TYPE) {
|
|
165
|
+
value = jsonObject(request.body)?.["grant_type"] ?? null;
|
|
166
|
+
}
|
|
167
|
+
value ??= request.url.searchParams.get("grant_type");
|
|
168
|
+
return typeof value === "string" && value ? value : null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* A token request's `grant_type` for logs: one of the known grant types,
|
|
172
|
+
* `other`, or null when it names none.
|
|
173
|
+
*/
|
|
174
|
+
export function tokenGrantType(request) {
|
|
175
|
+
const value = requestedGrantType(request);
|
|
176
|
+
if (value === null)
|
|
177
|
+
return null;
|
|
178
|
+
return GRANT_TYPES.has(value) ? value : "other";
|
|
179
|
+
}
|
|
180
|
+
/** A Content-Type's media type, lowercased and without parameters. */
|
|
181
|
+
function mediaType(contentType) {
|
|
182
|
+
return (contentType ?? "").split(";")[0].trim().toLowerCase();
|
|
183
|
+
}
|
|
184
|
+
function jsonObject(text) {
|
|
185
|
+
try {
|
|
186
|
+
const value = JSON.parse(text);
|
|
187
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
188
|
+
? value
|
|
189
|
+
: null;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope sets for an `oauth_client` capability with a `scopePolicy`
|
|
3
|
+
* (docs/capabilities.md "OAuth clients"): what an app's manifest entry
|
|
4
|
+
* declares, what that lets the app ask the provider for, how each provider
|
|
5
|
+
* spells what it granted, and which API routes a set unlocks. Pure
|
|
6
|
+
* functions over the registry, shared by the gateway's token-endpoint
|
|
7
|
+
* check, the SDK's local-dev twin of it and, later, the proxy and the
|
|
8
|
+
* outbound Worker. Nothing here reads a secret or a token value.
|
|
9
|
+
*/
|
|
10
|
+
import type { OAuthScopePolicy } from "./capabilities.js";
|
|
11
|
+
import type { TokenRequest } from "./oauthClient.js";
|
|
12
|
+
/** Scopes by the token they belong to: the bot or only token, and Slack's user token. */
|
|
13
|
+
export interface ScopeList {
|
|
14
|
+
scopes: string[];
|
|
15
|
+
userScopes: string[];
|
|
16
|
+
}
|
|
17
|
+
/** What a token response says it granted; a field is present when that kind of token is. */
|
|
18
|
+
export interface GrantedScopes {
|
|
19
|
+
scopes?: string[];
|
|
20
|
+
userScopes?: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The scope set names a manifest entry declares, or null when the entry
|
|
24
|
+
* voids its grant: `scopes` missing, not an array, empty, a non-string
|
|
25
|
+
* element, a name listed twice, or any name the policy doesn't offer. An
|
|
26
|
+
* unknown name voids the whole grant rather than narrowing it silently, so
|
|
27
|
+
* a newer CLI's set name reaching an older gateway is refused outright and
|
|
28
|
+
* a message can say so.
|
|
29
|
+
*/
|
|
30
|
+
export declare function declaredScopeSets(policy: OAuthScopePolicy, entry: {
|
|
31
|
+
scopes?: unknown;
|
|
32
|
+
} | undefined): string[] | null;
|
|
33
|
+
/**
|
|
34
|
+
* What an app with `sets` asks the provider for: the sets' scopes in
|
|
35
|
+
* registry order, deduplicated, then `alwaysRequested`. The status hands
|
|
36
|
+
* these out and the authorize URL (Microsoft: the token leg too) carries
|
|
37
|
+
* them verbatim.
|
|
38
|
+
*/
|
|
39
|
+
export declare function requestedScopes(policy: OAuthScopePolicy, sets: readonly string[]): ScopeList;
|
|
40
|
+
/**
|
|
41
|
+
* What a token an app with `sets` is handed may carry: the requested
|
|
42
|
+
* scopes and the tolerated ones, in the dialect's canonical spelling so a
|
|
43
|
+
* granted list canonicalised the same way compares exactly.
|
|
44
|
+
*/
|
|
45
|
+
export declare function allowedScopes(policy: OAuthScopePolicy, sets: readonly string[]): ScopeList;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a token response body carries a token: an access, refresh or ID
|
|
48
|
+
* token, or Slack's user token under `authed_user`. A 2xx without one
|
|
49
|
+
* (Slack's `ok: false`) is an error the app must see, not a grant.
|
|
50
|
+
*/
|
|
51
|
+
export declare function carriesToken(json: unknown): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* The scopes a token response says it granted, canonicalised per dialect,
|
|
54
|
+
* or null when the answer names none for a token it carries, which fails
|
|
55
|
+
* closed: every documented success response of the three providers names
|
|
56
|
+
* them (map-providers: Google and Microsoft `scope`, Slack `scope` and
|
|
57
|
+
* `authed_user.scope`), so a missing field is an answer the gateway can't
|
|
58
|
+
* hold to anything.
|
|
59
|
+
*/
|
|
60
|
+
export declare function grantedScopes(policy: OAuthScopePolicy, json: unknown): GrantedScopes | null;
|
|
61
|
+
/**
|
|
62
|
+
* What `granted` carries beyond `allowed`, per token kind, for a refusal's
|
|
63
|
+
* message and log line: the user token's extras as `user:<scope>`. Both
|
|
64
|
+
* lists are in the dialect's canonical spelling already.
|
|
65
|
+
*/
|
|
66
|
+
export declare function scopeExcess(granted: GrantedScopes, allowed: ScopeList): string[];
|
|
67
|
+
/**
|
|
68
|
+
* The scopes a token request itself asks for (`scope`, and Slack's
|
|
69
|
+
* `user_scope`, in any case) in its form or JSON body, canonicalised per
|
|
70
|
+
* dialect for comparison with `allowedScopes()`, or null when the body
|
|
71
|
+
* names none. The query string is never read: the gateway refuses a
|
|
72
|
+
* request that puts these there, so only one place decides.
|
|
73
|
+
*/
|
|
74
|
+
export declare function requestedTokenScopes(policy: OAuthScopePolicy, request: TokenRequest): GrantedScopes | null;
|
|
75
|
+
/**
|
|
76
|
+
* `request` with its `scope`, in any case, as the policy's `requestScope`
|
|
77
|
+
* says: replaced by the requested list of the app's `sets`, dropped, or
|
|
78
|
+
* kept as sent. A separate step after `injectClientCredentials()`, whose
|
|
79
|
+
* re-serialised form or JSON body this edits the same way; any other body
|
|
80
|
+
* is left alone.
|
|
81
|
+
*/
|
|
82
|
+
export declare function rewriteTokenRequestScope(policy: OAuthScopePolicy, sets: readonly string[], request: TokenRequest): TokenRequest;
|
|
83
|
+
/**
|
|
84
|
+
* The declared set that unlocks `method upstream`, or null. Not wired to
|
|
85
|
+
* any live path yet: it becomes the proxy's boundary once the gateway
|
|
86
|
+
* holds tokens, and the outbound Worker's for dispatched apps before that.
|
|
87
|
+
*
|
|
88
|
+
* In order: https only; the host must be on one of the declared sets'
|
|
89
|
+
* routes; the raw pathname is refused when it holds `\`, `//` or `%00`;
|
|
90
|
+
* each raw segment, and each percent-decoding of it while a `%` remains
|
|
91
|
+
* (a provider's layers may each decode once), is refused when it won't
|
|
92
|
+
* decode, holds a control or format character or a backslash, is one of
|
|
93
|
+
* the policy's denied segments in any case, or splits on `/` into a dot
|
|
94
|
+
* segment: `.` or `..`, with or without a `;` parameter (`..;x`), which
|
|
95
|
+
* Google's front end resolves like the bare one. The denied comparison is
|
|
96
|
+
* of the whole segment, because an encoded slash is part of one id to the
|
|
97
|
+
* provider, never a separator: a Search Console site URL whose own path
|
|
98
|
+
* holds `batch` stays matchable, and `2%2FmeasurementProtocolSecrets` is
|
|
99
|
+
* a data stream id that doesn't exist, not the secrets. Then the raw
|
|
100
|
+
* pathname, case preserved, is matched: GET and HEAD need a `paths` entry
|
|
101
|
+
* (exact, or a prefix when it ends in `/`), POST needs a `posts` pattern,
|
|
102
|
+
* and no other method goes through. Matching the raw path is what lets
|
|
103
|
+
* Search Console's `sites/https%3A%2F%2Fexample.com%2F/searchAnalytics/query`
|
|
104
|
+
* match `[^/]+` while `..`, batch tunnels and secrets stay refused however
|
|
105
|
+
* encoded. `X-HTTP-Method-Override`, `access_token` and `token` parameters
|
|
106
|
+
* are the proxy's to strip before it asks; nothing here reads them. The
|
|
107
|
+
* proxy fetches with `redirect: "manual"`: Google answers a dot segment it
|
|
108
|
+
* resolves with a 302 to the resolved path, and following one would carry
|
|
109
|
+
* the token to a path this never matched.
|
|
110
|
+
*/
|
|
111
|
+
export declare function scopeRouteAllows(policy: OAuthScopePolicy, sets: readonly string[], method: string, upstream: URL): string | null;
|
|
112
|
+
/**
|
|
113
|
+
* `text` as a JSON object, or null for anything else (an array, a scalar,
|
|
114
|
+
* not JSON). Shared with the gateway's read of a provider's token answer.
|
|
115
|
+
*/
|
|
116
|
+
export declare function jsonObject(text: string): Record<string, unknown> | null;
|
|
117
|
+
//# sourceMappingURL=oauthScopes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthScopes.d.ts","sourceRoot":"","sources":["../../src/internal/oauthScopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAEV,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,CA6BtB;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;AAwJD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMvE"}
|