@open-everywhere/adapters 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +21 -0
- package/README.md +43 -0
- package/agent/aws/cli.ts +121 -0
- package/agent/aws/config.ts +71 -0
- package/agent/aws/register.ts +65 -0
- package/agent/aws/sso.ts +567 -0
- package/agent/aws/sts.ts +191 -0
- package/agent/blob/hosting.ts +24 -0
- package/agent/browser/config.ts +62 -0
- package/agent/browser/register.ts +66 -0
- package/agent/browser/sandbox.ts +39 -0
- package/agent/contributions.ts +12 -0
- package/agent/directory/config.ts +138 -0
- package/agent/directory/okta.ts +226 -0
- package/agent/directory/register.ts +16 -0
- package/agent/instrumentation.ts +42 -0
- package/agent/model/approval-judge.ts +57 -0
- package/agent/model/dream-judge.ts +105 -0
- package/agent/model/gateway-vision.ts +162 -0
- package/agent/model/gateway.ts +85 -0
- package/agent/oe-oauth/config.ts +246 -0
- package/agent/oe-oauth/errors.ts +77 -0
- package/agent/oe-oauth/oauth-client.ts +376 -0
- package/agent/oe-oauth/pkce.ts +21 -0
- package/agent/oe-oauth/register.ts +32 -0
- package/agent/oe-oauth/token-vault.ts +652 -0
- package/agent/oe-oauth/user-auth-flow.ts +76 -0
- package/agent/postgres/neon.ts +108 -0
- package/agent/sandbox/backend-stop.ts +259 -0
- package/agent/sandbox/created-sandbox-registry.ts +27 -0
- package/agent/sandbox/extend-workspace-time.ts +77 -0
- package/agent/sandbox/vercel-lifecycle.ts +252 -0
- package/dist/agent/aws/cli.d.ts +24 -0
- package/dist/agent/aws/cli.d.ts.map +1 -0
- package/dist/agent/aws/cli.js +89 -0
- package/dist/agent/aws/cli.js.map +1 -0
- package/dist/agent/aws/config.d.ts +27 -0
- package/dist/agent/aws/config.d.ts.map +1 -0
- package/dist/agent/aws/config.js +46 -0
- package/dist/agent/aws/config.js.map +1 -0
- package/dist/agent/aws/register.d.ts +23 -0
- package/dist/agent/aws/register.d.ts.map +1 -0
- package/dist/agent/aws/register.js +39 -0
- package/dist/agent/aws/register.js.map +1 -0
- package/dist/agent/aws/sso.d.ts +109 -0
- package/dist/agent/aws/sso.d.ts.map +1 -0
- package/dist/agent/aws/sso.js +421 -0
- package/dist/agent/aws/sso.js.map +1 -0
- package/dist/agent/aws/sts.d.ts +27 -0
- package/dist/agent/aws/sts.d.ts.map +1 -0
- package/dist/agent/aws/sts.js +135 -0
- package/dist/agent/aws/sts.js.map +1 -0
- package/dist/agent/blob/hosting.d.ts +4 -0
- package/dist/agent/blob/hosting.d.ts.map +1 -0
- package/dist/agent/blob/hosting.js +20 -0
- package/dist/agent/blob/hosting.js.map +1 -0
- package/dist/agent/browser/config.d.ts +24 -0
- package/dist/agent/browser/config.d.ts.map +1 -0
- package/dist/agent/browser/config.js +50 -0
- package/dist/agent/browser/config.js.map +1 -0
- package/dist/agent/browser/register.d.ts +7 -0
- package/dist/agent/browser/register.d.ts.map +1 -0
- package/dist/agent/browser/register.js +47 -0
- package/dist/agent/browser/register.js.map +1 -0
- package/dist/agent/browser/sandbox.d.ts +23 -0
- package/dist/agent/browser/sandbox.d.ts.map +1 -0
- package/dist/agent/browser/sandbox.js +27 -0
- package/dist/agent/browser/sandbox.js.map +1 -0
- package/dist/agent/contributions.d.ts +16 -0
- package/dist/agent/contributions.d.ts.map +1 -0
- package/dist/agent/contributions.js +11 -0
- package/dist/agent/contributions.js.map +1 -0
- package/dist/agent/directory/config.d.ts +39 -0
- package/dist/agent/directory/config.d.ts.map +1 -0
- package/dist/agent/directory/config.js +101 -0
- package/dist/agent/directory/config.js.map +1 -0
- package/dist/agent/directory/okta.d.ts +19 -0
- package/dist/agent/directory/okta.d.ts.map +1 -0
- package/dist/agent/directory/okta.js +178 -0
- package/dist/agent/directory/okta.js.map +1 -0
- package/dist/agent/directory/register.d.ts +6 -0
- package/dist/agent/directory/register.d.ts.map +1 -0
- package/dist/agent/directory/register.js +16 -0
- package/dist/agent/directory/register.js.map +1 -0
- package/dist/agent/instrumentation.d.ts +5 -0
- package/dist/agent/instrumentation.d.ts.map +1 -0
- package/dist/agent/instrumentation.js +39 -0
- package/dist/agent/instrumentation.js.map +1 -0
- package/dist/agent/model/approval-judge.d.ts +15 -0
- package/dist/agent/model/approval-judge.d.ts.map +1 -0
- package/dist/agent/model/approval-judge.js +46 -0
- package/dist/agent/model/approval-judge.js.map +1 -0
- package/dist/agent/model/dream-judge.d.ts +40 -0
- package/dist/agent/model/dream-judge.d.ts.map +1 -0
- package/dist/agent/model/dream-judge.js +80 -0
- package/dist/agent/model/dream-judge.js.map +1 -0
- package/dist/agent/model/gateway-vision.d.ts +53 -0
- package/dist/agent/model/gateway-vision.d.ts.map +1 -0
- package/dist/agent/model/gateway-vision.js +129 -0
- package/dist/agent/model/gateway-vision.js.map +1 -0
- package/dist/agent/model/gateway.d.ts +30 -0
- package/dist/agent/model/gateway.d.ts.map +1 -0
- package/dist/agent/model/gateway.js +55 -0
- package/dist/agent/model/gateway.js.map +1 -0
- package/dist/agent/oe-oauth/config.d.ts +69 -0
- package/dist/agent/oe-oauth/config.d.ts.map +1 -0
- package/dist/agent/oe-oauth/config.js +182 -0
- package/dist/agent/oe-oauth/config.js.map +1 -0
- package/dist/agent/oe-oauth/errors.d.ts +63 -0
- package/dist/agent/oe-oauth/errors.d.ts.map +1 -0
- package/dist/agent/oe-oauth/errors.js +73 -0
- package/dist/agent/oe-oauth/errors.js.map +1 -0
- package/dist/agent/oe-oauth/oauth-client.d.ts +124 -0
- package/dist/agent/oe-oauth/oauth-client.d.ts.map +1 -0
- package/dist/agent/oe-oauth/oauth-client.js +262 -0
- package/dist/agent/oe-oauth/oauth-client.js.map +1 -0
- package/dist/agent/oe-oauth/pkce.d.ts +8 -0
- package/dist/agent/oe-oauth/pkce.d.ts.map +1 -0
- package/dist/agent/oe-oauth/pkce.js +18 -0
- package/dist/agent/oe-oauth/pkce.js.map +1 -0
- package/dist/agent/oe-oauth/register.d.ts +17 -0
- package/dist/agent/oe-oauth/register.d.ts.map +1 -0
- package/dist/agent/oe-oauth/register.js +24 -0
- package/dist/agent/oe-oauth/register.js.map +1 -0
- package/dist/agent/oe-oauth/token-vault.d.ts +107 -0
- package/dist/agent/oe-oauth/token-vault.d.ts.map +1 -0
- package/dist/agent/oe-oauth/token-vault.js +498 -0
- package/dist/agent/oe-oauth/token-vault.js.map +1 -0
- package/dist/agent/oe-oauth/user-auth-flow.d.ts +10 -0
- package/dist/agent/oe-oauth/user-auth-flow.d.ts.map +1 -0
- package/dist/agent/oe-oauth/user-auth-flow.js +66 -0
- package/dist/agent/oe-oauth/user-auth-flow.js.map +1 -0
- package/dist/agent/postgres/neon.d.ts +29 -0
- package/dist/agent/postgres/neon.d.ts.map +1 -0
- package/dist/agent/postgres/neon.js +76 -0
- package/dist/agent/postgres/neon.js.map +1 -0
- package/dist/agent/sandbox/backend-stop.d.ts +98 -0
- package/dist/agent/sandbox/backend-stop.d.ts.map +1 -0
- package/dist/agent/sandbox/backend-stop.js +188 -0
- package/dist/agent/sandbox/backend-stop.js.map +1 -0
- package/dist/agent/sandbox/created-sandbox-registry.d.ts +15 -0
- package/dist/agent/sandbox/created-sandbox-registry.d.ts.map +1 -0
- package/dist/agent/sandbox/created-sandbox-registry.js +26 -0
- package/dist/agent/sandbox/created-sandbox-registry.js.map +1 -0
- package/dist/agent/sandbox/extend-workspace-time.d.ts +31 -0
- package/dist/agent/sandbox/extend-workspace-time.d.ts.map +1 -0
- package/dist/agent/sandbox/extend-workspace-time.js +63 -0
- package/dist/agent/sandbox/extend-workspace-time.js.map +1 -0
- package/dist/agent/sandbox/vercel-lifecycle.d.ts +38 -0
- package/dist/agent/sandbox/vercel-lifecycle.d.ts.map +1 -0
- package/dist/agent/sandbox/vercel-lifecycle.js +244 -0
- package/dist/agent/sandbox/vercel-lifecycle.js.map +1 -0
- package/package.json +204 -0
- package/tsconfig.build.json +22 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Typed errors for the own-rolled OAuth adapter. Each carries a stable `code`, so classification is
|
|
2
|
+
// DUCK-TYPED across processes. SECURITY: no error here carries a token, a code, or a PKCE verifier.
|
|
3
|
+
/** The subject an error is about, as a log-safe label (never an opaque token). */
|
|
4
|
+
export function subjectLabel(subject) {
|
|
5
|
+
return subject.type === "app" ? "app" : `user:${subject.id}`;
|
|
6
|
+
}
|
|
7
|
+
/** The human has NEVER authorized this (provider, subject) — no token is stored. The connector plane
|
|
8
|
+
* turns this into a fresh consent link. */
|
|
9
|
+
export class OeOauthNeedsConsentError extends Error {
|
|
10
|
+
code = "oe_oauth_needs_consent";
|
|
11
|
+
constructor(providerRef, subject) {
|
|
12
|
+
super(`connection "${providerRef}": ${subjectLabel(subject)} has not authorized this account — user authorization required.`);
|
|
13
|
+
this.name = "OeOauthNeedsConsentError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** A previously-working authorization is dead (refresh token rejected / rotated away) — the human
|
|
17
|
+
* must RE-authorize. Distinct from needs-consent so the audit trail can tell a first connect from a
|
|
18
|
+
* lapsed one, but both classify as "user authorization required". */
|
|
19
|
+
export class OeOauthNeedsReconsentError extends Error {
|
|
20
|
+
code = "oe_oauth_needs_reconsent";
|
|
21
|
+
constructor(providerRef, subject, why) {
|
|
22
|
+
super(`connection "${providerRef}": ${subjectLabel(subject)} must re-authorize — ${why}.`);
|
|
23
|
+
this.name = "OeOauthNeedsReconsentError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** The cross-process single-flight LOSER timed out (OPE-91): another instance holds the refresh claim
|
|
27
|
+
* and did not release a rotated token within this caller's wait budget. TRANSIENT and RETRYABLE — a
|
|
28
|
+
* fresh request may win the claim or find the rotated token — so it is DELIBERATELY not classified as
|
|
29
|
+
* user-auth-required (the human need do nothing; nothing is re-consented). Carries no secret material,
|
|
30
|
+
* only the provider ref + subject label. */
|
|
31
|
+
export class OeOauthRefreshContendedError extends Error {
|
|
32
|
+
code = "oe_oauth_refresh_contended";
|
|
33
|
+
constructor(providerRef, subject) {
|
|
34
|
+
super(`connection "${providerRef}": ${subjectLabel(subject)} token refresh is in progress on another instance and did not complete in time — retry.`);
|
|
35
|
+
this.name = "OeOauthRefreshContendedError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** A generic upstream OAuth failure (token endpoint / discovery / DCR returned an error). `oauthError`
|
|
39
|
+
* is the RFC 6749 `error` code when the body carried one (e.g. "invalid_grant", "invalid_client") —
|
|
40
|
+
* a fixed-vocabulary string, never a secret. */
|
|
41
|
+
export class OeOauthError extends Error {
|
|
42
|
+
code = "oe_oauth_error";
|
|
43
|
+
oauthError;
|
|
44
|
+
httpStatus;
|
|
45
|
+
constructor(message, opts = {}) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "OeOauthError";
|
|
48
|
+
if (opts.oauthError)
|
|
49
|
+
this.oauthError = opts.oauthError;
|
|
50
|
+
if (opts.httpStatus !== undefined)
|
|
51
|
+
this.httpStatus = opts.httpStatus;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** True iff `e` is an upstream OAuth failure whose `error` code is `invalid_grant` (the refresh token
|
|
55
|
+
* was rejected — consumed by a rotation, revoked, or expired). Duck-typed for cross-process safety. */
|
|
56
|
+
export function isInvalidGrant(e) {
|
|
57
|
+
if (!(e instanceof Error))
|
|
58
|
+
return false;
|
|
59
|
+
const oauthError = e.oauthError;
|
|
60
|
+
return typeof oauthError === "string" && oauthError.toLowerCase() === "invalid_grant";
|
|
61
|
+
}
|
|
62
|
+
/** The connector-plane classifier (used by UserAuthFlowProvider.isUserAuthRequired). Duck-typed on
|
|
63
|
+
* the stable `code` strings so a cross-process instance still matches. */
|
|
64
|
+
export function isUserAuthRequiredError(e) {
|
|
65
|
+
if (!(e instanceof Error))
|
|
66
|
+
return false;
|
|
67
|
+
const code = e.code;
|
|
68
|
+
return ((typeof code === "string" && (code === "oe_oauth_needs_consent" || code === "oe_oauth_needs_reconsent")) ||
|
|
69
|
+
e.name === "OeOauthNeedsConsentError" ||
|
|
70
|
+
e.name === "OeOauthNeedsReconsentError" ||
|
|
71
|
+
/user[_ ]?authorization[_ ]?required/i.test(e.message));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../agent/oe-oauth/errors.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,oGAAoG;AAEpG,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,OAAkF;IAC7G,OAAO,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,OAAO,CAAC,EAAE,EAAE,CAAC;AAC/D,CAAC;AAED;4CAC4C;AAC5C,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,IAAI,GAAG,wBAAwB,CAAC;IACzC,YAAY,WAAmB,EAAE,OAAkF;QACjH,KAAK,CAAC,eAAe,WAAW,MAAM,YAAY,CAAC,OAAO,CAAC,iEAAiE,CAAC,CAAC;QAC9H,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED;;sEAEsE;AACtE,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAC1C,IAAI,GAAG,0BAA0B,CAAC;IAC3C,YAAY,WAAmB,EAAE,OAAkF,EAAE,GAAW;QAC9H,KAAK,CAAC,eAAe,WAAW,MAAM,YAAY,CAAC,OAAO,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAC;QAC3F,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAED;;;;6CAI6C;AAC7C,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAC5C,IAAI,GAAG,4BAA4B,CAAC;IAC7C,YAAY,WAAmB,EAAE,OAAkF;QACjH,KAAK,CAAC,eAAe,WAAW,MAAM,YAAY,CAAC,OAAO,CAAC,yFAAyF,CAAC,CAAC;QACtJ,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAED;;iDAEiD;AACjD,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,GAAG,gBAAgB,CAAC;IACxB,UAAU,CAAU;IACpB,UAAU,CAAU;IAC7B,YAAY,OAAe,EAAE,IAAI,GAAmE,EAAE;QACpG,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACvD,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACvE,CAAC;CACF;AAED;wGACwG;AACxG,MAAM,UAAU,cAAc,CAAC,CAAU;IACvC,IAAI,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,UAAU,GAAI,CAA8B,CAAC,UAAU,CAAC;IAC9D,OAAO,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC;AACxF,CAAC;AAED;2EAC2E;AAC3E,MAAM,UAAU,uBAAuB,CAAC,CAAU;IAChD,IAAI,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,GAAI,CAAwB,CAAC,IAAI,CAAC;IAC5C,OAAO,CACL,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,KAAK,wBAAwB,IAAI,IAAI,KAAK,0BAA0B,CAAC,CAAC;QACxG,CAAC,CAAC,IAAI,KAAK,0BAA0B;QACrC,CAAC,CAAC,IAAI,KAAK,4BAA4B;QACvC,sCAAsC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CACvD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/** The injectable network seam — the global `fetch` in production, a stub in tests. */
|
|
2
|
+
export type OAuthFetch = (input: string, init?: RequestInit) => Promise<Response>;
|
|
3
|
+
/** The endpoint set discovered for a provider's authorization server. */
|
|
4
|
+
export interface DiscoveredEndpoints {
|
|
5
|
+
readonly authorizationEndpoint: string;
|
|
6
|
+
readonly tokenEndpoint: string;
|
|
7
|
+
readonly registrationEndpoint: string;
|
|
8
|
+
/** RFC 7009 token revocation (OPE-287). OPTIONAL, and the only optional member here: RFC 8414
|
|
9
|
+
* lists `revocation_endpoint` as optional metadata, so a server may legitimately publish none.
|
|
10
|
+
* Absent ⇒ {@link revokeToken} is never called and a purge is local-only (see its doc). The other
|
|
11
|
+
* three are REQUIRED because without them the flow cannot start at all. */
|
|
12
|
+
readonly revocationEndpoint?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A token endpoint response (authorization-code exchange or refresh). Only the fields we persist. */
|
|
15
|
+
export interface OAuthTokenResponse {
|
|
16
|
+
readonly access_token: string;
|
|
17
|
+
readonly refresh_token?: string;
|
|
18
|
+
readonly expires_in?: number;
|
|
19
|
+
readonly scope?: string;
|
|
20
|
+
readonly token_type?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Two-hop discovery: the MCP resource's protected-resource metadata → its authorization server →
|
|
24
|
+
* that server's metadata (authorization/token/registration endpoints). `resource` is the hosted-MCP
|
|
25
|
+
* server URL (also the RFC 8707 resource indicator). Throws OeOauthError on any missing hop.
|
|
26
|
+
*/
|
|
27
|
+
export declare function discoverEndpoints(resource: string, fetchImpl: OAuthFetch): Promise<DiscoveredEndpoints>;
|
|
28
|
+
/**
|
|
29
|
+
* Dynamic Client Registration (RFC 7591) for a PUBLIC PKCE client — no secret, auth method "none".
|
|
30
|
+
* Returns the minted `client_id`. Registers the SINGLE redirect_uri OE owns (the callback route).
|
|
31
|
+
*/
|
|
32
|
+
export declare function registerClient(registrationEndpoint: string, input: {
|
|
33
|
+
readonly clientName: string;
|
|
34
|
+
readonly redirectUri: string;
|
|
35
|
+
readonly scopes?: readonly string[];
|
|
36
|
+
}, fetchImpl: OAuthFetch): Promise<{
|
|
37
|
+
readonly clientId: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Build the authorization URL a human opens — PKCE S256 challenge + RFC 8707 resource indicator. */
|
|
40
|
+
export declare function buildAuthorizationUrl(input: {
|
|
41
|
+
readonly authorizationEndpoint: string;
|
|
42
|
+
readonly clientId: string;
|
|
43
|
+
readonly redirectUri: string;
|
|
44
|
+
readonly state: string;
|
|
45
|
+
readonly codeChallenge: string;
|
|
46
|
+
readonly resource: string;
|
|
47
|
+
readonly scopes?: readonly string[];
|
|
48
|
+
}): string;
|
|
49
|
+
/** Exchange an authorization `code` for tokens (PKCE `code_verifier`, public client, RFC 8707 resource). */
|
|
50
|
+
export declare function exchangeCode(tokenEndpoint: string, input: {
|
|
51
|
+
readonly code: string;
|
|
52
|
+
readonly verifier: string;
|
|
53
|
+
readonly clientId: string;
|
|
54
|
+
readonly redirectUri: string;
|
|
55
|
+
readonly resource: string;
|
|
56
|
+
}, fetchImpl: OAuthFetch): Promise<OAuthTokenResponse>;
|
|
57
|
+
/**
|
|
58
|
+
* RFC 7009 TOKEN REVOCATION — the VENDOR-side half of a purge. Present the REFRESH token, so one call kills
|
|
59
|
+
* the family. RFC 7009 mandates 200 for an invalid token, so a non-200 means the REQUEST was refused.
|
|
60
|
+
*/
|
|
61
|
+
export declare function revokeToken(revocationEndpoint: string, input: {
|
|
62
|
+
readonly token: string;
|
|
63
|
+
readonly clientId: string;
|
|
64
|
+
readonly tokenTypeHint?: "refresh_token" | "access_token";
|
|
65
|
+
}, fetchImpl: OAuthFetch): Promise<void>;
|
|
66
|
+
/** Everything a confidential token call needs, resolved by the caller. The `clientSecret` is the
|
|
67
|
+
* live value read from the credential store at call time — it is passed, used and dropped; nothing
|
|
68
|
+
* in this module stores it, and no function here puts it anywhere but the Basic header. */
|
|
69
|
+
export interface ConfidentialClient {
|
|
70
|
+
readonly clientId: string;
|
|
71
|
+
readonly clientSecret: string;
|
|
72
|
+
/** How the token body is encoded. `json` is Notion's measured contract; `form` is the RFC default. */
|
|
73
|
+
readonly bodyEncoding: "json" | "form";
|
|
74
|
+
/** Vendor-required extra request headers (Notion needs `Notion-Version`). Never `authorization`. */
|
|
75
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The RFC 6749 §2.3.1 HTTP Basic client credential. The RFC's per-half form-encoding is a no-op for the
|
|
79
|
+
* characters vendors issue, and produces a rejected credential where a portal shows the raw value.
|
|
80
|
+
*/
|
|
81
|
+
export declare function basicAuthHeader(clientId: string, clientSecret: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* The authorization URL for a static confidential client — NO PKCE and no RFC 8707 `resource`: the code is
|
|
84
|
+
* bound by the client SECRET instead. `extraParams` apply FIRST, so a parameter this function owns wins.
|
|
85
|
+
*/
|
|
86
|
+
export declare function buildStaticAuthorizationUrl(input: {
|
|
87
|
+
readonly authorizationEndpoint: string;
|
|
88
|
+
readonly clientId: string;
|
|
89
|
+
readonly redirectUri: string;
|
|
90
|
+
readonly state: string;
|
|
91
|
+
readonly scopes?: readonly string[];
|
|
92
|
+
readonly extraParams?: Readonly<Record<string, string>>;
|
|
93
|
+
}): string;
|
|
94
|
+
/**
|
|
95
|
+
* Exchange an authorization `code` for tokens as a CONFIDENTIAL client. The body carries no `client_id`:
|
|
96
|
+
* RFC 6749 §2.3 forbids authenticating by more than one method in one request.
|
|
97
|
+
*/
|
|
98
|
+
export declare function exchangeCodeConfidential(tokenEndpoint: string, input: {
|
|
99
|
+
readonly code: string;
|
|
100
|
+
readonly redirectUri: string;
|
|
101
|
+
}, client: ConfidentialClient, fetchImpl: OAuthFetch): Promise<OAuthTokenResponse>;
|
|
102
|
+
/** Refresh an access token as a CONFIDENTIAL client. Same rotation semantics as the public-client
|
|
103
|
+
* refresh: a rotating server returns a new `refresh_token`, a non-rotating one omits it. */
|
|
104
|
+
export declare function refreshTokenConfidential(tokenEndpoint: string, input: {
|
|
105
|
+
readonly refreshToken: string;
|
|
106
|
+
readonly scopes?: readonly string[];
|
|
107
|
+
}, client: ConfidentialClient, fetchImpl: OAuthFetch): Promise<OAuthTokenResponse>;
|
|
108
|
+
/**
|
|
109
|
+
* RFC 7009 revocation as a CONFIDENTIAL client. The error contract is {@link revokeToken}'s. Revocation is
|
|
110
|
+
* form-encoded whatever the token endpoint uses, because RFC 7009 §2.1 defines the request that way.
|
|
111
|
+
*/
|
|
112
|
+
export declare function revokeTokenConfidential(revocationEndpoint: string, input: {
|
|
113
|
+
readonly token: string;
|
|
114
|
+
readonly tokenTypeHint?: "refresh_token" | "access_token";
|
|
115
|
+
}, client: ConfidentialClient, fetchImpl: OAuthFetch): Promise<void>;
|
|
116
|
+
/** Refresh an access token (public client, RFC 8707 resource). A rotation-safe server returns a NEW
|
|
117
|
+
* refresh_token and invalidates the old one; a non-rotating server omits it (we keep the old). */
|
|
118
|
+
export declare function refreshToken(tokenEndpoint: string, input: {
|
|
119
|
+
readonly refreshToken: string;
|
|
120
|
+
readonly clientId: string;
|
|
121
|
+
readonly resource: string;
|
|
122
|
+
readonly scopes?: readonly string[];
|
|
123
|
+
}, fetchImpl: OAuthFetch): Promise<OAuthTokenResponse>;
|
|
124
|
+
//# sourceMappingURL=oauth-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-client.d.ts","sourceRoot":"","sources":["../../../agent/oe-oauth/oauth-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Vendor-free OAuth 2.0 client: DCR, the PKCE authorization-code flow, RFC 8707 resource indicators and RFC
|
|
2
|
+
// 7009 revocation. Nothing here logs a token, a code or a verifier, and a client secret rides only a header.
|
|
3
|
+
import { OeOauthError } from "./errors.js";
|
|
4
|
+
function trimSlash(u) {
|
|
5
|
+
return u.replace(/\/+$/, "");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* RFC 9728 / RFC 8414 well-known derivation: the `.well-known/<suffix>` is INSERTED at the ORIGIN with the
|
|
9
|
+
* resource's path appended after it. A naive append hits an authed resource path (mcp.notion.com 401s).
|
|
10
|
+
*/
|
|
11
|
+
function wellKnownUrl(base, suffix) {
|
|
12
|
+
const u = new URL(base);
|
|
13
|
+
const path = trimSlash(u.pathname) === "" ? "" : trimSlash(u.pathname);
|
|
14
|
+
return `${u.origin}/.well-known/${suffix}${path}`;
|
|
15
|
+
}
|
|
16
|
+
async function readError(res) {
|
|
17
|
+
// Parse ONLY the RFC 6749 `error` code — never attach the raw body (may echo a submitted value).
|
|
18
|
+
try {
|
|
19
|
+
const body = (await res.json());
|
|
20
|
+
return typeof body?.error === "string" ? { oauthError: body.error } : {};
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Two-hop discovery: the MCP resource's protected-resource metadata → its authorization server →
|
|
28
|
+
* that server's metadata (authorization/token/registration endpoints). `resource` is the hosted-MCP
|
|
29
|
+
* server URL (also the RFC 8707 resource indicator). Throws OeOauthError on any missing hop.
|
|
30
|
+
*/
|
|
31
|
+
export async function discoverEndpoints(resource, fetchImpl) {
|
|
32
|
+
const base = trimSlash(resource);
|
|
33
|
+
// RFC-form first (origin-inserted, path-suffixed); origin-root fallback for servers that publish
|
|
34
|
+
// metadata only at the root regardless of the resource path.
|
|
35
|
+
let prMeta = await fetchImpl(wellKnownUrl(base, "oauth-protected-resource"), { headers: { accept: "application/json" } });
|
|
36
|
+
if (!prMeta.ok) {
|
|
37
|
+
const rootForm = `${new URL(base).origin}/.well-known/oauth-protected-resource`;
|
|
38
|
+
if (wellKnownUrl(base, "oauth-protected-resource") !== rootForm) {
|
|
39
|
+
prMeta = await fetchImpl(rootForm, { headers: { accept: "application/json" } });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!prMeta.ok)
|
|
43
|
+
throw new OeOauthError(`protected-resource discovery failed for ${base}`, { httpStatus: prMeta.status, ...(await readError(prMeta)) });
|
|
44
|
+
const pr = (await prMeta.json().catch(() => ({})));
|
|
45
|
+
const authServer = Array.isArray(pr.authorization_servers) && typeof pr.authorization_servers[0] === "string" ? pr.authorization_servers[0] : undefined;
|
|
46
|
+
if (!authServer)
|
|
47
|
+
throw new OeOauthError(`protected-resource metadata for ${base} names no authorization_servers`);
|
|
48
|
+
const asBase = trimSlash(authServer);
|
|
49
|
+
const asMeta = await fetchImpl(wellKnownUrl(asBase, "oauth-authorization-server"), { headers: { accept: "application/json" } });
|
|
50
|
+
if (!asMeta.ok)
|
|
51
|
+
throw new OeOauthError(`authorization-server discovery failed for ${asBase}`, { httpStatus: asMeta.status, ...(await readError(asMeta)) });
|
|
52
|
+
const as = (await asMeta.json().catch(() => ({})));
|
|
53
|
+
const authorizationEndpoint = typeof as.authorization_endpoint === "string" ? as.authorization_endpoint : undefined;
|
|
54
|
+
const tokenEndpoint = typeof as.token_endpoint === "string" ? as.token_endpoint : undefined;
|
|
55
|
+
const registrationEndpoint = typeof as.registration_endpoint === "string" ? as.registration_endpoint : undefined;
|
|
56
|
+
// RFC 7009/8414 (OPE-287): optional metadata, so its ABSENCE is never an error — it only means a
|
|
57
|
+
// purge cannot tell the vendor and stays local. Parsed at discovery (not at purge time) because a
|
|
58
|
+
// purge must not depend on the authorization server being reachable at the moment of offboarding.
|
|
59
|
+
const revocationEndpoint = typeof as.revocation_endpoint === "string" && as.revocation_endpoint ? as.revocation_endpoint : undefined;
|
|
60
|
+
if (!authorizationEndpoint || !tokenEndpoint || !registrationEndpoint) {
|
|
61
|
+
throw new OeOauthError(`authorization-server metadata for ${asBase} is missing authorization/token/registration endpoints`);
|
|
62
|
+
}
|
|
63
|
+
return { authorizationEndpoint, tokenEndpoint, registrationEndpoint, ...(revocationEndpoint ? { revocationEndpoint } : {}) };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Dynamic Client Registration (RFC 7591) for a PUBLIC PKCE client — no secret, auth method "none".
|
|
67
|
+
* Returns the minted `client_id`. Registers the SINGLE redirect_uri OE owns (the callback route).
|
|
68
|
+
*/
|
|
69
|
+
export async function registerClient(registrationEndpoint, input, fetchImpl) {
|
|
70
|
+
const res = await fetchImpl(registrationEndpoint, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
client_name: input.clientName,
|
|
75
|
+
redirect_uris: [input.redirectUri],
|
|
76
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
77
|
+
response_types: ["code"],
|
|
78
|
+
token_endpoint_auth_method: "none",
|
|
79
|
+
...(input.scopes && input.scopes.length ? { scope: input.scopes.join(" ") } : {}),
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok)
|
|
83
|
+
throw new OeOauthError(`dynamic client registration failed at ${registrationEndpoint}`, { httpStatus: res.status, ...(await readError(res)) });
|
|
84
|
+
const body = (await res.json().catch(() => ({})));
|
|
85
|
+
if (typeof body.client_id !== "string" || !body.client_id)
|
|
86
|
+
throw new OeOauthError(`registration response carried no client_id`);
|
|
87
|
+
return { clientId: body.client_id };
|
|
88
|
+
}
|
|
89
|
+
/** Build the authorization URL a human opens — PKCE S256 challenge + RFC 8707 resource indicator. */
|
|
90
|
+
export function buildAuthorizationUrl(input) {
|
|
91
|
+
const u = new URL(input.authorizationEndpoint);
|
|
92
|
+
u.searchParams.set("response_type", "code");
|
|
93
|
+
u.searchParams.set("client_id", input.clientId);
|
|
94
|
+
u.searchParams.set("redirect_uri", input.redirectUri);
|
|
95
|
+
u.searchParams.set("state", input.state);
|
|
96
|
+
u.searchParams.set("code_challenge", input.codeChallenge);
|
|
97
|
+
u.searchParams.set("code_challenge_method", "S256");
|
|
98
|
+
u.searchParams.set("resource", input.resource);
|
|
99
|
+
if (input.scopes && input.scopes.length)
|
|
100
|
+
u.searchParams.set("scope", input.scopes.join(" "));
|
|
101
|
+
return u.toString();
|
|
102
|
+
}
|
|
103
|
+
/** Turn one token-endpoint response into tokens, or into a typed error. Shared by BOTH dialects: the
|
|
104
|
+
* public PKCE client and the static confidential client differ in how the request is composed and
|
|
105
|
+
* authenticated, never in how the answer is read. The error names the ENDPOINT and the RFC 6749
|
|
106
|
+
* error code only — no request material travels into it, which is what keeps a client secret out of
|
|
107
|
+
* every message on the confidential path. */
|
|
108
|
+
async function readTokenResponse(tokenEndpoint, res) {
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
const { oauthError } = await readError(res);
|
|
111
|
+
throw new OeOauthError(`token endpoint ${tokenEndpoint} returned ${res.status}${oauthError ? ` (${oauthError})` : ""}`, {
|
|
112
|
+
httpStatus: res.status,
|
|
113
|
+
...(oauthError ? { oauthError } : {}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const body = (await res.json().catch(() => ({})));
|
|
117
|
+
if (typeof body.access_token !== "string" || !body.access_token)
|
|
118
|
+
throw new OeOauthError(`token endpoint ${tokenEndpoint} returned no access_token`);
|
|
119
|
+
return body;
|
|
120
|
+
}
|
|
121
|
+
async function postToken(tokenEndpoint, form, fetchImpl) {
|
|
122
|
+
const res = await fetchImpl(tokenEndpoint, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
|
|
125
|
+
body: new URLSearchParams(form).toString(),
|
|
126
|
+
});
|
|
127
|
+
return readTokenResponse(tokenEndpoint, res);
|
|
128
|
+
}
|
|
129
|
+
/** Exchange an authorization `code` for tokens (PKCE `code_verifier`, public client, RFC 8707 resource). */
|
|
130
|
+
export function exchangeCode(tokenEndpoint, input, fetchImpl) {
|
|
131
|
+
return postToken(tokenEndpoint, {
|
|
132
|
+
grant_type: "authorization_code",
|
|
133
|
+
code: input.code,
|
|
134
|
+
redirect_uri: input.redirectUri,
|
|
135
|
+
client_id: input.clientId,
|
|
136
|
+
code_verifier: input.verifier,
|
|
137
|
+
resource: input.resource,
|
|
138
|
+
}, fetchImpl);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* RFC 7009 TOKEN REVOCATION — the VENDOR-side half of a purge. Present the REFRESH token, so one call kills
|
|
142
|
+
* the family. RFC 7009 mandates 200 for an invalid token, so a non-200 means the REQUEST was refused.
|
|
143
|
+
*/
|
|
144
|
+
export async function revokeToken(revocationEndpoint, input, fetchImpl) {
|
|
145
|
+
const res = await fetchImpl(revocationEndpoint, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
|
|
148
|
+
body: new URLSearchParams({
|
|
149
|
+
token: input.token,
|
|
150
|
+
client_id: input.clientId, // public PKCE client — no secret to present (token_endpoint_auth_method "none")
|
|
151
|
+
...(input.tokenTypeHint ? { token_type_hint: input.tokenTypeHint } : {}),
|
|
152
|
+
}).toString(),
|
|
153
|
+
});
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
const { oauthError } = await readError(res);
|
|
156
|
+
throw new OeOauthError(`revocation endpoint ${revocationEndpoint} returned ${res.status}${oauthError ? ` (${oauthError})` : ""}`, {
|
|
157
|
+
httpStatus: res.status,
|
|
158
|
+
...(oauthError ? { oauthError } : {}),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The RFC 6749 §2.3.1 HTTP Basic client credential. The RFC's per-half form-encoding is a no-op for the
|
|
164
|
+
* characters vendors issue, and produces a rejected credential where a portal shows the raw value.
|
|
165
|
+
*/
|
|
166
|
+
export function basicAuthHeader(clientId, clientSecret) {
|
|
167
|
+
return `Basic ${Buffer.from(`${clientId}:${clientSecret}`, "utf-8").toString("base64")}`;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The authorization URL for a static confidential client — NO PKCE and no RFC 8707 `resource`: the code is
|
|
171
|
+
* bound by the client SECRET instead. `extraParams` apply FIRST, so a parameter this function owns wins.
|
|
172
|
+
*/
|
|
173
|
+
export function buildStaticAuthorizationUrl(input) {
|
|
174
|
+
const u = new URL(input.authorizationEndpoint);
|
|
175
|
+
for (const [k, v] of Object.entries(input.extraParams ?? {}))
|
|
176
|
+
u.searchParams.set(k, v);
|
|
177
|
+
u.searchParams.set("response_type", "code");
|
|
178
|
+
u.searchParams.set("client_id", input.clientId);
|
|
179
|
+
u.searchParams.set("redirect_uri", input.redirectUri);
|
|
180
|
+
u.searchParams.set("state", input.state);
|
|
181
|
+
if (input.scopes && input.scopes.length)
|
|
182
|
+
u.searchParams.set("scope", input.scopes.join(" "));
|
|
183
|
+
return u.toString();
|
|
184
|
+
}
|
|
185
|
+
/** Compose and send one confidential token request. The secret reaches exactly one field of this
|
|
186
|
+
* object — the `authorization` header — and `redirect: "error"` stops that header from being
|
|
187
|
+
* replayed to a host a redirect names. Config headers are applied FIRST so neither they nor a
|
|
188
|
+
* vendor quirk can displace the ones this function owns. */
|
|
189
|
+
/** Vendor headers from config, MINUS any case-variant of a header this module owns. HTTP header
|
|
190
|
+
* names are case-insensitive but a JS object's keys are not, so a config `Content-Type` beside our
|
|
191
|
+
* `content-type` is ONE header with two values on the wire. Dropping the config's variant makes the
|
|
192
|
+
* ones we own win, which is the same rule the config parser states for `authorization`. */
|
|
193
|
+
function vendorHeaders(client, owned) {
|
|
194
|
+
const out = {};
|
|
195
|
+
for (const [name, value] of Object.entries(client.headers ?? {})) {
|
|
196
|
+
if (!owned.includes(name.toLowerCase()))
|
|
197
|
+
out[name] = value;
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
const OWNED_TOKEN_HEADERS = ["accept", "content-type", "authorization"];
|
|
202
|
+
async function postTokenConfidential(tokenEndpoint, client, params, fetchImpl) {
|
|
203
|
+
const headers = vendorHeaders(client, OWNED_TOKEN_HEADERS);
|
|
204
|
+
headers.accept = "application/json";
|
|
205
|
+
headers["content-type"] = client.bodyEncoding === "json" ? "application/json" : "application/x-www-form-urlencoded";
|
|
206
|
+
headers.authorization = basicAuthHeader(client.clientId, client.clientSecret);
|
|
207
|
+
return fetchImpl(tokenEndpoint, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers,
|
|
210
|
+
body: client.bodyEncoding === "json" ? JSON.stringify(params) : new URLSearchParams(params).toString(),
|
|
211
|
+
redirect: "error",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Exchange an authorization `code` for tokens as a CONFIDENTIAL client. The body carries no `client_id`:
|
|
216
|
+
* RFC 6749 §2.3 forbids authenticating by more than one method in one request.
|
|
217
|
+
*/
|
|
218
|
+
export async function exchangeCodeConfidential(tokenEndpoint, input, client, fetchImpl) {
|
|
219
|
+
const res = await postTokenConfidential(tokenEndpoint, client, { grant_type: "authorization_code", code: input.code, redirect_uri: input.redirectUri }, fetchImpl);
|
|
220
|
+
return readTokenResponse(tokenEndpoint, res);
|
|
221
|
+
}
|
|
222
|
+
/** Refresh an access token as a CONFIDENTIAL client. Same rotation semantics as the public-client
|
|
223
|
+
* refresh: a rotating server returns a new `refresh_token`, a non-rotating one omits it. */
|
|
224
|
+
export async function refreshTokenConfidential(tokenEndpoint, input, client, fetchImpl) {
|
|
225
|
+
const res = await postTokenConfidential(tokenEndpoint, client, { grant_type: "refresh_token", refresh_token: input.refreshToken, ...(input.scopes && input.scopes.length ? { scope: input.scopes.join(" ") } : {}) }, fetchImpl);
|
|
226
|
+
return readTokenResponse(tokenEndpoint, res);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* RFC 7009 revocation as a CONFIDENTIAL client. The error contract is {@link revokeToken}'s. Revocation is
|
|
230
|
+
* form-encoded whatever the token endpoint uses, because RFC 7009 §2.1 defines the request that way.
|
|
231
|
+
*/
|
|
232
|
+
export async function revokeTokenConfidential(revocationEndpoint, input, client, fetchImpl) {
|
|
233
|
+
const headers = vendorHeaders(client, OWNED_TOKEN_HEADERS);
|
|
234
|
+
headers.accept = "application/json";
|
|
235
|
+
headers["content-type"] = "application/x-www-form-urlencoded";
|
|
236
|
+
headers.authorization = basicAuthHeader(client.clientId, client.clientSecret);
|
|
237
|
+
const res = await fetchImpl(revocationEndpoint, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers,
|
|
240
|
+
body: new URLSearchParams({ token: input.token, ...(input.tokenTypeHint ? { token_type_hint: input.tokenTypeHint } : {}) }).toString(),
|
|
241
|
+
redirect: "error",
|
|
242
|
+
});
|
|
243
|
+
if (!res.ok) {
|
|
244
|
+
const { oauthError } = await readError(res);
|
|
245
|
+
throw new OeOauthError(`revocation endpoint ${revocationEndpoint} returned ${res.status}${oauthError ? ` (${oauthError})` : ""}`, {
|
|
246
|
+
httpStatus: res.status,
|
|
247
|
+
...(oauthError ? { oauthError } : {}),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** Refresh an access token (public client, RFC 8707 resource). A rotation-safe server returns a NEW
|
|
252
|
+
* refresh_token and invalidates the old one; a non-rotating server omits it (we keep the old). */
|
|
253
|
+
export function refreshToken(tokenEndpoint, input, fetchImpl) {
|
|
254
|
+
return postToken(tokenEndpoint, {
|
|
255
|
+
grant_type: "refresh_token",
|
|
256
|
+
refresh_token: input.refreshToken,
|
|
257
|
+
client_id: input.clientId,
|
|
258
|
+
resource: input.resource,
|
|
259
|
+
...(input.scopes && input.scopes.length ? { scope: input.scopes.join(" ") } : {}),
|
|
260
|
+
}, fetchImpl);
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=oauth-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../../../agent/oe-oauth/oauth-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** A fresh PKCE pair: a 43-char base64url `verifier` (32 random bytes) + its S256 `challenge`. */
|
|
2
|
+
export declare function makePkce(): {
|
|
3
|
+
readonly verifier: string;
|
|
4
|
+
readonly challenge: string;
|
|
5
|
+
};
|
|
6
|
+
/** A fresh opaque, unforgeable, single-use state token (256 bits of entropy, base64url). */
|
|
7
|
+
export declare function makeState(): string;
|
|
8
|
+
//# sourceMappingURL=pkce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../agent/oe-oauth/pkce.ts"],"names":[],"mappings":"AAUA,kGAAkG;AAClG,wBAAgB,QAAQ,IAAI;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAIpF;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// PKCE (RFC 7636) and opaque state for the own-rolled OAuth adapter. The verifier never leaves the host, so
|
|
2
|
+
// a stolen `code` is useless; the 256-bit state is the unguessable, single-use CSRF defence.
|
|
3
|
+
import { randomBytes, createHash } from "node:crypto";
|
|
4
|
+
/** base64url with no padding — the encoding RFC 7636 mandates for verifier/challenge. */
|
|
5
|
+
function base64url(buf) {
|
|
6
|
+
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
7
|
+
}
|
|
8
|
+
/** A fresh PKCE pair: a 43-char base64url `verifier` (32 random bytes) + its S256 `challenge`. */
|
|
9
|
+
export function makePkce() {
|
|
10
|
+
const verifier = base64url(randomBytes(32));
|
|
11
|
+
const challenge = base64url(createHash("sha256").update(verifier).digest());
|
|
12
|
+
return { verifier, challenge };
|
|
13
|
+
}
|
|
14
|
+
/** A fresh opaque, unforgeable, single-use state token (256 bits of entropy, base64url). */
|
|
15
|
+
export function makeState() {
|
|
16
|
+
return base64url(randomBytes(32));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../agent/oe-oauth/pkce.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,6FAA6F;AAE7F,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEtD,yFAAyF;AACzF,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,QAAQ;IACtB,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,SAAS;IACvB,OAAO,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type OeOauthProviderConfig } from "./config.js";
|
|
2
|
+
import type { OeOauthVaultDeps } from "./token-vault.js";
|
|
3
|
+
/** Registration input. `providers` defaults to {@link loadOeOauthConfig}; `deps` is a test/DI seam. */
|
|
4
|
+
export interface RegisterOeOauthOptions {
|
|
5
|
+
readonly providers?: ReadonlyMap<string, OeOauthProviderConfig>;
|
|
6
|
+
readonly deps?: OeOauthVaultDeps;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Construct the own-rolled OAuth provider and callback completer and register both. Throws when no provider
|
|
10
|
+
* config is available — the caller invokes this only once it has decided to arm.
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerOeOauth(opts?: RegisterOeOauthOptions): {
|
|
13
|
+
flow: import("@open-everywhere/core/agent/adapter-api.js").UserAuthFlowProvider;
|
|
14
|
+
completer: import("@open-everywhere/core/agent/adapter-api.js").OeOauthCallbackCompleter;
|
|
15
|
+
purge: import("@open-everywhere/core/agent/adapter-api.js").CredentialPurgeHook;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../agent/oe-oauth/register.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,uGAAuG;AACvG,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,GAAE,sBAA2B;;;;EAahE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Register the own-rolled OAuth adapter: the user-auth flow, the callback completer and the credential-purge
|
|
2
|
+
// hook, from one shared vault. Flag-off ⇒ none registers and the callback route fails closed with a 404.
|
|
3
|
+
import { registerUserAuthFlow, registerOeOauthCallbackCompleter, registerCredentialPurgeHook } from "@open-everywhere/core/agent/adapter-api.js";
|
|
4
|
+
import { loadOeOauthConfig } from "./config.js";
|
|
5
|
+
import { makeOeOauth } from "./user-auth-flow.js";
|
|
6
|
+
/**
|
|
7
|
+
* Construct the own-rolled OAuth provider and callback completer and register both. Throws when no provider
|
|
8
|
+
* config is available — the caller invokes this only once it has decided to arm.
|
|
9
|
+
*/
|
|
10
|
+
export function registerOeOauth(opts = {}) {
|
|
11
|
+
const providers = opts.providers ?? loadOeOauthConfig();
|
|
12
|
+
if (!providers || providers.size === 0) {
|
|
13
|
+
throw new Error("registerOeOauth: no oe-oauth provider config (set OE_OAUTH_CONFIG / OE_OAUTH_NOTION_RESOURCE).");
|
|
14
|
+
}
|
|
15
|
+
const { flow, completer, purge } = makeOeOauth(providers, opts.deps ?? {});
|
|
16
|
+
registerUserAuthFlow(flow);
|
|
17
|
+
registerOeOauthCallbackCompleter(completer);
|
|
18
|
+
// (OPE-287) The purge hook registers ALONGSIDE the mint flow, in the same call, deliberately: a
|
|
19
|
+
// deployment that can mint per-user credentials but has no way to destroy them is the state this
|
|
20
|
+
// ticket removed, and keeping the two registrations together is what stops it coming back.
|
|
21
|
+
registerCredentialPurgeHook("oe-oauth", purge);
|
|
22
|
+
return { flow, completer, purge };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../agent/oe-oauth/register.ts"],"names":[],"mappings":"AAAA,6GAA6G;AAC7G,yGAAyG;AAEzG,OAAO,EAAE,oBAAoB,EAAE,gCAAgC,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACjJ,OAAO,EAAE,iBAAiB,EAA8B,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AASlD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAI,GAA2B,EAAE;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iBAAiB,EAAE,CAAC;IACxD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,gGAAgG,CAAC,CAAC;IACpH,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3E,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC3B,gCAAgC,CAAC,SAAS,CAAC,CAAC;IAC5C,gGAAgG;IAChG,iGAAiG;IACjG,2FAA2F;IAC3F,2BAA2B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC"}
|