@agent-native/core 0.201.0-nightly-20261005153421 → 0.201.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/README.md +22 -22
- package/dist/a2a/server.d.ts +2 -2
- package/dist/a2a/server.js +13 -2
- package/dist/agent/thread-data-builder.d.ts +4 -0
- package/dist/agent/thread-data-builder.js +8 -0
- package/dist/agent-sql/analysis.d.ts +31 -0
- package/dist/agent-sql/analysis.js +169 -0
- package/dist/agent-sql/index.d.ts +4 -0
- package/dist/agent-sql/index.js +4 -0
- package/dist/agent-sql/lexer.d.ts +37 -0
- package/dist/agent-sql/lexer.js +357 -0
- package/dist/agent-sql/postgres.d.ts +51 -0
- package/dist/agent-sql/postgres.js +197 -0
- package/dist/agent-sql/query.d.ts +30 -0
- package/dist/agent-sql/query.js +514 -0
- package/dist/catalog.json +24 -24
- package/dist/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.js +17 -2
- package/dist/client/session-replay.js +3 -2
- package/dist/identity/offboard.js +31 -3
- package/dist/identity/rekey.d.ts +12 -1
- package/dist/identity/rekey.js +77 -6
- package/dist/identity/retired-emails.d.ts +23 -0
- package/dist/identity/retired-emails.js +55 -0
- package/dist/mcp/actions/create-org-service-token.js +20 -8
- package/dist/mcp/actions/service-token-access.d.ts +1 -0
- package/dist/mcp/actions/service-token-access.js +41 -24
- package/dist/mcp/build-server.d.ts +20 -6
- package/dist/mcp/build-server.js +131 -35
- package/dist/mcp/connect-route.d.ts +4 -3
- package/dist/mcp/connect-route.js +140 -82
- package/dist/mcp/connect-store.d.ts +17 -11
- package/dist/mcp/connect-store.js +88 -60
- package/dist/mcp/credential-issuance.d.ts +23 -0
- package/dist/mcp/credential-issuance.js +77 -0
- package/dist/mcp/credential-membership.d.ts +26 -0
- package/dist/mcp/credential-membership.js +67 -0
- package/dist/mcp/oauth-route.js +133 -45
- package/dist/mcp/oauth-store.d.ts +15 -5
- package/dist/mcp/oauth-store.js +119 -34
- package/dist/mcp/oauth-token.d.ts +4 -0
- package/dist/mcp/oauth-token.js +5 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/server.js +14 -0
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/index.d.ts +2 -2
- package/dist/org/membership.d.ts +3 -1
- package/dist/org/membership.js +5 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +10 -10
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resource-changes/store.d.ts +2 -0
- package/dist/resource-changes/store.js +4 -0
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/db/exec.js +28 -101
- package/dist/scripts/db/patch.js +14 -34
- package/dist/scripts/db/postgres-client.d.ts +8 -1
- package/dist/scripts/db/postgres-client.js +20 -6
- package/dist/scripts/db/query.js +31 -33
- package/dist/scripts/db/safety.d.ts +47 -2
- package/dist/scripts/db/safety.js +383 -137
- package/dist/scripts/db/scoping.d.ts +1 -0
- package/dist/scripts/db/scoping.js +3 -2
- package/dist/search/index-store.d.ts +21 -0
- package/dist/search/index-store.js +47 -0
- package/dist/search/indexer.js +35 -32
- package/dist/search/registry.d.ts +6 -3
- package/dist/search/registry.js +30 -8
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat-plugin.js +18 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/attribution.d.ts +2 -0
- package/dist/server/attribution.js +7 -1
- package/dist/server/auth.js +9 -0
- package/dist/server/credential-membership-unavailable.d.ts +13 -0
- package/dist/server/credential-membership-unavailable.js +22 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/recap-image-route.js +4 -0
- package/dist/server/release-schema.js +4 -0
- package/dist/shared/docs-url.d.ts +3 -1
- package/dist/shared/docs-url.js +6 -3
- package/dist/shared/environment-lanes.d.ts +8 -0
- package/dist/shared/environment-lanes.js +26 -0
- package/dist/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/chat/README.md +2 -2
- package/dist/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/default/app/routes/home.tsx +5 -1
- package/dist/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/tracking/providers.js +13 -1
- package/docs/content/authentication.mdx +5 -1
- package/docs/content/durable-background-runs.mdx +17 -8
- package/docs/content/external-agents.mdx +3 -1
- package/docs/content/mcp-protocol.mdx +6 -2
- package/docs/content/security.mdx +1 -1
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/chat/README.md +2 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/default/app/routes/home.tsx +5 -1
- package/src/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
package/dist/mcp/oauth-route.js
CHANGED
|
@@ -3,9 +3,12 @@ import { getHeader, getMethod, getQuery, setResponseStatus } from "h3";
|
|
|
3
3
|
import { getOrgDomain } from "../org/context.js";
|
|
4
4
|
import { getConfiguredLoginHtml, getSession } from "../server/auth.js";
|
|
5
5
|
import { getAuthSecret } from "../server/better-auth-instance.js";
|
|
6
|
+
import { CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE } from "../server/credential-membership-unavailable.js";
|
|
7
|
+
import { getOrigin } from "../server/google-oauth.js";
|
|
6
8
|
import { readBody } from "../server/h3-helpers.js";
|
|
9
|
+
import { McpCredentialIssuanceError, withMcpCredentialIssuance, } from "./credential-issuance.js";
|
|
7
10
|
import { applicationTypeForRedirectUris, isAllowedOAuthRedirectUri, isUrlBasedOAuthClientId, matchesRegisteredRedirectUri, resolveOAuthClientMetadataDocument, } from "./oauth-client-metadata.js";
|
|
8
|
-
import { createOAuthCode, createOAuthRefreshToken, consumeOAuthCode, generateOpaqueToken, getOAuthClient, getOAuthCode, getOAuthRefreshToken, registerOAuthClient, touchOAuthRefreshToken, } from "./oauth-store.js";
|
|
11
|
+
import { createOAuthCode, createOAuthRefreshToken, consumeOAuthCode, ensureOAuthTables, generateOpaqueToken, getOAuthClient, getOAuthCode, getOAuthRefreshToken, registerOAuthClient, revokeOAuthRefreshToken, touchOAuthRefreshToken, } from "./oauth-store.js";
|
|
9
12
|
import { MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS, MCP_OAUTH_SCOPES, normalizeOAuthScope, signMcpOAuthAccessToken, } from "./oauth-token.js";
|
|
10
13
|
import { resolveMcpOrgChoices } from "./org-choice.js";
|
|
11
14
|
import { MCP_DIRECTORY_ROUTE_PREFIX, MCP_LEGACY_ROUTE_PREFIX, MCP_PUBLIC_ROUTE_PREFIX, MCP_ROUTE_PREFIXES, } from "./route-paths.js";
|
|
@@ -700,17 +703,37 @@ async function handleAuthorize(event, options) {
|
|
|
700
703
|
const orgDomain = selectedOrganization
|
|
701
704
|
? await resolveOrgDomain(selectedOrganization.id)
|
|
702
705
|
: undefined;
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
706
|
+
let code;
|
|
707
|
+
try {
|
|
708
|
+
await ensureOAuthTables();
|
|
709
|
+
code = await withMcpCredentialIssuance({
|
|
710
|
+
email: session.email,
|
|
711
|
+
orgId: selectedOrganization?.id ?? null,
|
|
712
|
+
requestOrigin: getOrigin(event),
|
|
713
|
+
}, (tx) => createOAuthCode({
|
|
714
|
+
clientId,
|
|
715
|
+
redirectUri,
|
|
716
|
+
codeChallenge: params.code_challenge,
|
|
717
|
+
codeChallengeMethod: "S256",
|
|
718
|
+
ownerEmail: session.email,
|
|
719
|
+
orgId: selectedOrganization?.id ?? null,
|
|
720
|
+
orgDomain: orgDomain ?? null,
|
|
721
|
+
scope,
|
|
722
|
+
resource,
|
|
723
|
+
}, tx));
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
if (error instanceof McpCredentialIssuanceError &&
|
|
727
|
+
error.reason === "not-member") {
|
|
728
|
+
return redirectWithOAuthError({
|
|
729
|
+
redirectUri,
|
|
730
|
+
issuer,
|
|
731
|
+
state,
|
|
732
|
+
error: "access_denied",
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
736
|
+
}
|
|
714
737
|
let isDeepLinkRedirect = false;
|
|
715
738
|
try {
|
|
716
739
|
const protocol = new URL(redirectUri).protocol;
|
|
@@ -741,7 +764,7 @@ async function handleAuthorize(event, options) {
|
|
|
741
764
|
code: code.code,
|
|
742
765
|
});
|
|
743
766
|
}
|
|
744
|
-
async function issueTokenSet(params) {
|
|
767
|
+
async function issueTokenSet(params, tx) {
|
|
745
768
|
const refreshToken = generateOpaqueToken();
|
|
746
769
|
await createOAuthRefreshToken({
|
|
747
770
|
refreshToken,
|
|
@@ -751,7 +774,7 @@ async function issueTokenSet(params) {
|
|
|
751
774
|
orgDomain: params.orgDomain ?? null,
|
|
752
775
|
scope: params.scope,
|
|
753
776
|
resource: params.resource,
|
|
754
|
-
});
|
|
777
|
+
}, tx);
|
|
755
778
|
const accessToken = await signMcpOAuthAccessToken(params);
|
|
756
779
|
return {
|
|
757
780
|
access_token: accessToken,
|
|
@@ -761,6 +784,13 @@ async function issueTokenSet(params) {
|
|
|
761
784
|
scope: params.scope,
|
|
762
785
|
};
|
|
763
786
|
}
|
|
787
|
+
const NOT_A_MEMBER_DESCRIPTION = "The user is no longer a member of the organization this grant was issued for";
|
|
788
|
+
/** Retryable: the client must keep its refresh token, not start over. */
|
|
789
|
+
function grantUnavailableError(description) {
|
|
790
|
+
const response = oauthError("temporarily_unavailable", description, 503);
|
|
791
|
+
response.headers.set("Retry-After", "5");
|
|
792
|
+
return response;
|
|
793
|
+
}
|
|
764
794
|
async function handleAuthorizationCodeGrant(event, body) {
|
|
765
795
|
const code = body.code;
|
|
766
796
|
const clientId = body.client_id;
|
|
@@ -769,7 +799,14 @@ async function handleAuthorizationCodeGrant(event, body) {
|
|
|
769
799
|
if (!code || !clientId || !redirectUri || !isValidCodeVerifier(verifier)) {
|
|
770
800
|
return oauthError("invalid_request", "Missing authorization-code fields");
|
|
771
801
|
}
|
|
772
|
-
|
|
802
|
+
let row;
|
|
803
|
+
try {
|
|
804
|
+
await ensureOAuthTables();
|
|
805
|
+
row = await getOAuthCode(code);
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
809
|
+
}
|
|
773
810
|
if (!row)
|
|
774
811
|
return oauthError("invalid_grant", "Invalid or expired code");
|
|
775
812
|
if (row.clientId !== clientId || row.redirectUri !== redirectUri) {
|
|
@@ -779,21 +816,42 @@ async function handleAuthorizationCodeGrant(event, body) {
|
|
|
779
816
|
if (!safeEqual(expectedChallenge, row.codeChallenge)) {
|
|
780
817
|
return oauthError("invalid_grant", "PKCE verification failed");
|
|
781
818
|
}
|
|
782
|
-
const consumed = await consumeOAuthCode(code);
|
|
783
|
-
if (!consumed)
|
|
784
|
-
return oauthError("invalid_grant", "Invalid or expired code");
|
|
785
819
|
const issuer = getMcpOAuthIssuer(event);
|
|
786
820
|
if (!issuer)
|
|
787
821
|
return oauthError("server_error", "Unable to derive issuer", 500);
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
822
|
+
try {
|
|
823
|
+
return await withMcpCredentialIssuance({
|
|
824
|
+
email: row.ownerEmail,
|
|
825
|
+
orgId: row.orgId,
|
|
826
|
+
requestOrigin: getOrigin(event),
|
|
827
|
+
}, async (tx) => {
|
|
828
|
+
const consumed = await consumeOAuthCode(code, row.ownerEmail, tx);
|
|
829
|
+
if (!consumed)
|
|
830
|
+
return oauthError("invalid_grant", "Invalid or expired code");
|
|
831
|
+
return json(await issueTokenSet({
|
|
832
|
+
ownerEmail: consumed.ownerEmail,
|
|
833
|
+
orgId: consumed.orgId,
|
|
834
|
+
orgDomain: consumed.orgDomain,
|
|
835
|
+
clientId,
|
|
836
|
+
scope: consumed.scope,
|
|
837
|
+
resource: consumed.resource,
|
|
838
|
+
issuer,
|
|
839
|
+
}, tx));
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
catch (error) {
|
|
843
|
+
if (error instanceof McpCredentialIssuanceError &&
|
|
844
|
+
error.reason === "not-member") {
|
|
845
|
+
try {
|
|
846
|
+
await consumeOAuthCode(code, row.ownerEmail);
|
|
847
|
+
}
|
|
848
|
+
catch {
|
|
849
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
850
|
+
}
|
|
851
|
+
return oauthError("invalid_grant", NOT_A_MEMBER_DESCRIPTION);
|
|
852
|
+
}
|
|
853
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
854
|
+
}
|
|
797
855
|
}
|
|
798
856
|
async function handleRefreshTokenGrant(event, body) {
|
|
799
857
|
const refreshToken = body.refresh_token;
|
|
@@ -804,32 +862,62 @@ async function handleRefreshTokenGrant(event, body) {
|
|
|
804
862
|
if (!clientId) {
|
|
805
863
|
return oauthError("invalid_request", "client_id is required");
|
|
806
864
|
}
|
|
807
|
-
|
|
865
|
+
let existing;
|
|
866
|
+
try {
|
|
867
|
+
await ensureOAuthTables();
|
|
868
|
+
existing = await getOAuthRefreshToken(refreshToken);
|
|
869
|
+
}
|
|
870
|
+
catch {
|
|
871
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
872
|
+
}
|
|
808
873
|
if (!existing)
|
|
809
874
|
return oauthError("invalid_grant", "Invalid refresh token");
|
|
810
875
|
if (existing.clientId !== clientId) {
|
|
811
876
|
return oauthError("invalid_grant", "Refresh token belongs to another client");
|
|
812
877
|
}
|
|
813
|
-
await touchOAuthRefreshToken(refreshToken).catch(() => undefined);
|
|
814
878
|
const issuer = getMcpOAuthIssuer(event);
|
|
815
879
|
if (!issuer)
|
|
816
880
|
return oauthError("server_error", "Unable to derive issuer", 500);
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
881
|
+
try {
|
|
882
|
+
return await withMcpCredentialIssuance({
|
|
883
|
+
email: existing.ownerEmail,
|
|
884
|
+
orgId: existing.orgId,
|
|
885
|
+
requestOrigin: getOrigin(event),
|
|
886
|
+
}, async (tx) => {
|
|
887
|
+
const renewal = await touchOAuthRefreshToken(refreshToken, existing.ownerEmail, tx);
|
|
888
|
+
if (renewal !== "renewed")
|
|
889
|
+
return oauthError("invalid_grant", "Invalid refresh token");
|
|
890
|
+
const accessToken = await signMcpOAuthAccessToken({
|
|
891
|
+
ownerEmail: existing.ownerEmail,
|
|
892
|
+
orgId: existing.orgId,
|
|
893
|
+
orgDomain: existing.orgDomain,
|
|
894
|
+
clientId: existing.clientId,
|
|
895
|
+
scope: existing.scope,
|
|
896
|
+
resource: existing.resource,
|
|
897
|
+
issuer,
|
|
898
|
+
});
|
|
899
|
+
return json({
|
|
900
|
+
access_token: accessToken,
|
|
901
|
+
token_type: "Bearer",
|
|
902
|
+
expires_in: MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS,
|
|
903
|
+
refresh_token: refreshToken,
|
|
904
|
+
scope: existing.scope,
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
catch (error) {
|
|
909
|
+
if (error instanceof McpCredentialIssuanceError &&
|
|
910
|
+
error.reason === "not-member") {
|
|
911
|
+
try {
|
|
912
|
+
await revokeOAuthRefreshToken(refreshToken, existing.ownerEmail);
|
|
913
|
+
}
|
|
914
|
+
catch {
|
|
915
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
916
|
+
}
|
|
917
|
+
return oauthError("invalid_grant", NOT_A_MEMBER_DESCRIPTION);
|
|
918
|
+
}
|
|
919
|
+
return grantUnavailableError(CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE);
|
|
920
|
+
}
|
|
833
921
|
}
|
|
834
922
|
async function handleToken(event) {
|
|
835
923
|
if (getMethod(event) !== "POST") {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* short-lived authorization codes, and hashed refresh tokens. Access tokens
|
|
6
6
|
* are signed JWTs and are never persisted.
|
|
7
7
|
*/
|
|
8
|
+
import { type DbExec } from "../db/client.js";
|
|
8
9
|
export declare const MCP_OAUTH_CODE_TTL_MS: number;
|
|
9
10
|
export declare const MCP_OAUTH_ACCESS_TOKEN_TTL: string;
|
|
10
11
|
export declare const MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS: number;
|
|
@@ -12,6 +13,7 @@ export declare const MCP_OAUTH_REFRESH_TOKEN_TTL_MS: number;
|
|
|
12
13
|
export declare const MCP_OAUTH_REGISTER_MAX = 60;
|
|
13
14
|
export declare const MCP_OAUTH_REGISTER_WINDOW_MS = 60000;
|
|
14
15
|
export declare function ensureTable(): Promise<void>;
|
|
16
|
+
export declare const ensureOAuthTables: typeof ensureTable;
|
|
15
17
|
export interface OAuthClientRow {
|
|
16
18
|
clientId: string;
|
|
17
19
|
clientName: string | null;
|
|
@@ -29,6 +31,7 @@ export interface OAuthCodeRow {
|
|
|
29
31
|
codeChallenge: string;
|
|
30
32
|
codeChallengeMethod: string;
|
|
31
33
|
ownerEmail: string;
|
|
34
|
+
issuedForEmail: string | null;
|
|
32
35
|
orgId: string | null;
|
|
33
36
|
orgDomain: string | null;
|
|
34
37
|
scope: string;
|
|
@@ -42,6 +45,7 @@ export interface OAuthRefreshTokenRow {
|
|
|
42
45
|
tokenHash: string;
|
|
43
46
|
clientId: string;
|
|
44
47
|
ownerEmail: string;
|
|
48
|
+
issuedForEmail: string | null;
|
|
45
49
|
orgId: string | null;
|
|
46
50
|
orgDomain: string | null;
|
|
47
51
|
scope: string;
|
|
@@ -73,9 +77,9 @@ export declare function createOAuthCode(params: {
|
|
|
73
77
|
orgDomain?: string | null;
|
|
74
78
|
scope: string;
|
|
75
79
|
resource: string;
|
|
76
|
-
}): Promise<OAuthCodeRow>;
|
|
80
|
+
}, db?: DbExec): Promise<OAuthCodeRow>;
|
|
77
81
|
export declare function getOAuthCode(code: string): Promise<OAuthCodeRow | null>;
|
|
78
|
-
export declare function consumeOAuthCode(code: string): Promise<OAuthCodeRow | null>;
|
|
82
|
+
export declare function consumeOAuthCode(code: string, expectedOwnerEmail?: string, db?: DbExec): Promise<OAuthCodeRow | null>;
|
|
79
83
|
export declare function createOAuthRefreshToken(params: {
|
|
80
84
|
refreshToken: string;
|
|
81
85
|
clientId: string;
|
|
@@ -84,15 +88,21 @@ export declare function createOAuthRefreshToken(params: {
|
|
|
84
88
|
orgDomain?: string | null;
|
|
85
89
|
scope: string;
|
|
86
90
|
resource: string;
|
|
87
|
-
}): Promise<OAuthRefreshTokenRow>;
|
|
91
|
+
}, db?: DbExec): Promise<OAuthRefreshTokenRow>;
|
|
88
92
|
export declare function rotateOAuthRefreshToken(params: {
|
|
89
93
|
oldRefreshToken: string;
|
|
90
94
|
newRefreshToken: string;
|
|
91
|
-
}): Promise<OAuthRefreshTokenRow | null>;
|
|
95
|
+
}, db?: DbExec): Promise<OAuthRefreshTokenRow | null>;
|
|
92
96
|
export declare function getOAuthRefreshToken(refreshToken: string): Promise<OAuthRefreshTokenRow | null>;
|
|
93
97
|
/**
|
|
94
98
|
* Slide the refresh-token's expiry window on each successful use so that active
|
|
95
99
|
* users never hit the TTL. Records `last_used_at` and extends `expires_at` to
|
|
96
100
|
* `now + MCP_OAUTH_REFRESH_TOKEN_TTL_MS`.
|
|
101
|
+
* The caller supplies the owner whose membership it verified.
|
|
97
102
|
*/
|
|
98
|
-
export declare function touchOAuthRefreshToken(refreshToken: string): Promise<
|
|
103
|
+
export declare function touchOAuthRefreshToken(refreshToken: string, expectedOwnerEmail: string, db?: DbExec): Promise<"renewed" | "invalid">;
|
|
104
|
+
/**
|
|
105
|
+
* Revoke one refresh token so it can never mint another access token.
|
|
106
|
+
* Idempotent: an already-revoked token keeps its first timestamp.
|
|
107
|
+
*/
|
|
108
|
+
export declare function revokeOAuthRefreshToken(refreshToken: string, expectedOwnerEmail?: string): Promise<void>;
|
package/dist/mcp/oauth-store.js
CHANGED
|
@@ -75,6 +75,7 @@ export async function ensureTable() {
|
|
|
75
75
|
code_challenge TEXT NOT NULL,
|
|
76
76
|
code_challenge_method TEXT NOT NULL,
|
|
77
77
|
owner_email TEXT NOT NULL,
|
|
78
|
+
issued_for_email TEXT,
|
|
78
79
|
org_id TEXT,
|
|
79
80
|
org_domain TEXT,
|
|
80
81
|
scope TEXT NOT NULL,
|
|
@@ -90,6 +91,7 @@ export async function ensureTable() {
|
|
|
90
91
|
token_hash TEXT UNIQUE NOT NULL,
|
|
91
92
|
client_id TEXT NOT NULL,
|
|
92
93
|
owner_email TEXT NOT NULL,
|
|
94
|
+
issued_for_email TEXT,
|
|
93
95
|
org_id TEXT,
|
|
94
96
|
org_domain TEXT,
|
|
95
97
|
scope TEXT NOT NULL,
|
|
@@ -104,7 +106,10 @@ export async function ensureTable() {
|
|
|
104
106
|
await ensureTableExists("mcp_oauth_clients", createClientsSql);
|
|
105
107
|
await ensureColumnExists("mcp_oauth_clients", "application_type", `ALTER TABLE mcp_oauth_clients ADD COLUMN IF NOT EXISTS application_type TEXT`);
|
|
106
108
|
await ensureTableExists("mcp_oauth_codes", createCodesSql);
|
|
109
|
+
// Legacy owners may already have been transferred; leave their bindings unset.
|
|
110
|
+
await ensureColumnExists("mcp_oauth_codes", "issued_for_email", `ALTER TABLE mcp_oauth_codes ADD COLUMN IF NOT EXISTS issued_for_email TEXT`);
|
|
107
111
|
await ensureTableExists("mcp_oauth_refresh_tokens", createRefreshTokensSql);
|
|
112
|
+
await ensureColumnExists("mcp_oauth_refresh_tokens", "issued_for_email", `ALTER TABLE mcp_oauth_refresh_tokens ADD COLUMN IF NOT EXISTS issued_for_email TEXT`);
|
|
108
113
|
})().catch((err) => {
|
|
109
114
|
_initPromise = undefined;
|
|
110
115
|
throw err;
|
|
@@ -112,6 +117,7 @@ export async function ensureTable() {
|
|
|
112
117
|
}
|
|
113
118
|
return _initPromise;
|
|
114
119
|
}
|
|
120
|
+
export const ensureOAuthTables = ensureTable;
|
|
115
121
|
export function generateOpaqueToken() {
|
|
116
122
|
return randomBytes(32).toString("base64url");
|
|
117
123
|
}
|
|
@@ -164,6 +170,7 @@ function mapCodeRow(row) {
|
|
|
164
170
|
codeChallenge: row.code_challenge ?? row.codeChallenge,
|
|
165
171
|
codeChallengeMethod: row.code_challenge_method ?? row.codeChallengeMethod,
|
|
166
172
|
ownerEmail: row.owner_email ?? row.ownerEmail,
|
|
173
|
+
issuedForEmail: row.issued_for_email ?? row.issuedForEmail ?? null,
|
|
167
174
|
orgId: row.org_id ?? row.orgId ?? null,
|
|
168
175
|
orgDomain: row.org_domain ?? row.orgDomain ?? null,
|
|
169
176
|
scope: row.scope,
|
|
@@ -179,6 +186,7 @@ function mapRefreshRow(row) {
|
|
|
179
186
|
tokenHash: row.token_hash ?? row.tokenHash,
|
|
180
187
|
clientId: row.client_id ?? row.clientId,
|
|
181
188
|
ownerEmail: row.owner_email ?? row.ownerEmail,
|
|
189
|
+
issuedForEmail: row.issued_for_email ?? row.issuedForEmail ?? null,
|
|
182
190
|
orgId: row.org_id ?? row.orgId ?? null,
|
|
183
191
|
orgDomain: row.org_domain ?? row.orgDomain ?? null,
|
|
184
192
|
scope: row.scope,
|
|
@@ -190,6 +198,11 @@ function mapRefreshRow(row) {
|
|
|
190
198
|
replacedByHash: row.replaced_by_hash ?? row.replacedByHash ?? null,
|
|
191
199
|
};
|
|
192
200
|
}
|
|
201
|
+
function hasIssuanceOwner(row) {
|
|
202
|
+
return (typeof row.issuedForEmail === "string" &&
|
|
203
|
+
row.issuedForEmail.trim().length > 0 &&
|
|
204
|
+
row.issuedForEmail === row.ownerEmail);
|
|
205
|
+
}
|
|
193
206
|
export async function registerOAuthClient(params) {
|
|
194
207
|
await ensureTable();
|
|
195
208
|
const client = getDbExec();
|
|
@@ -259,14 +272,15 @@ export async function getOAuthClient(clientId) {
|
|
|
259
272
|
throw err;
|
|
260
273
|
}
|
|
261
274
|
}
|
|
262
|
-
export async function createOAuthCode(params) {
|
|
263
|
-
|
|
264
|
-
|
|
275
|
+
export async function createOAuthCode(params, db) {
|
|
276
|
+
if (!db)
|
|
277
|
+
await ensureTable();
|
|
278
|
+
const client = db ?? getDbExec();
|
|
265
279
|
const code = generateOpaqueToken();
|
|
266
280
|
const now = Date.now();
|
|
267
281
|
const expiresAt = now + MCP_OAUTH_CODE_TTL_MS;
|
|
268
|
-
await client.execute({
|
|
269
|
-
sql: `INSERT INTO mcp_oauth_codes (code, client_id, redirect_uri, code_challenge, code_challenge_method, owner_email, org_id, org_domain, scope, resource, created_at, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
282
|
+
const result = await client.execute({
|
|
283
|
+
sql: `INSERT INTO mcp_oauth_codes (code, client_id, redirect_uri, code_challenge, code_challenge_method, owner_email, issued_for_email, org_id, org_domain, scope, resource, created_at, expires_at, consumed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
270
284
|
args: [
|
|
271
285
|
code,
|
|
272
286
|
params.clientId,
|
|
@@ -274,6 +288,7 @@ export async function createOAuthCode(params) {
|
|
|
274
288
|
params.codeChallenge,
|
|
275
289
|
params.codeChallengeMethod,
|
|
276
290
|
params.ownerEmail,
|
|
291
|
+
params.ownerEmail,
|
|
277
292
|
params.orgId ?? null,
|
|
278
293
|
params.orgDomain ?? null,
|
|
279
294
|
params.scope,
|
|
@@ -283,6 +298,9 @@ export async function createOAuthCode(params) {
|
|
|
283
298
|
null,
|
|
284
299
|
],
|
|
285
300
|
});
|
|
301
|
+
if (result.rowsAffected !== 1) {
|
|
302
|
+
throw new Error("Authorization-code creation returned an invalid row count");
|
|
303
|
+
}
|
|
286
304
|
return {
|
|
287
305
|
code,
|
|
288
306
|
clientId: params.clientId,
|
|
@@ -290,6 +308,7 @@ export async function createOAuthCode(params) {
|
|
|
290
308
|
codeChallenge: params.codeChallenge,
|
|
291
309
|
codeChallengeMethod: params.codeChallengeMethod,
|
|
292
310
|
ownerEmail: params.ownerEmail,
|
|
311
|
+
issuedForEmail: params.ownerEmail,
|
|
293
312
|
orgId: params.orgId ?? null,
|
|
294
313
|
orgDomain: params.orgDomain ?? null,
|
|
295
314
|
scope: params.scope,
|
|
@@ -309,14 +328,17 @@ export async function getOAuthCode(code) {
|
|
|
309
328
|
if (rows.length === 0)
|
|
310
329
|
return null;
|
|
311
330
|
const row = mapCodeRow(rows[0]);
|
|
312
|
-
if (
|
|
331
|
+
if (!hasIssuanceOwner(row) ||
|
|
332
|
+
row.consumedAt != null ||
|
|
333
|
+
(row.expiresAt ?? 0) < Date.now()) {
|
|
313
334
|
return null;
|
|
314
335
|
}
|
|
315
336
|
return row;
|
|
316
337
|
}
|
|
317
|
-
export async function consumeOAuthCode(code) {
|
|
318
|
-
|
|
319
|
-
|
|
338
|
+
export async function consumeOAuthCode(code, expectedOwnerEmail, db) {
|
|
339
|
+
if (!db)
|
|
340
|
+
await ensureTable();
|
|
341
|
+
const client = db ?? getDbExec();
|
|
320
342
|
const { rows } = await client.execute({
|
|
321
343
|
sql: `SELECT * FROM mcp_oauth_codes WHERE code = ?`,
|
|
322
344
|
args: [code],
|
|
@@ -324,24 +346,35 @@ export async function consumeOAuthCode(code) {
|
|
|
324
346
|
if (rows.length === 0)
|
|
325
347
|
return null;
|
|
326
348
|
const row = mapCodeRow(rows[0]);
|
|
327
|
-
|
|
349
|
+
const now = Date.now();
|
|
350
|
+
if (!hasIssuanceOwner(row) ||
|
|
351
|
+
(expectedOwnerEmail !== undefined &&
|
|
352
|
+
row.ownerEmail !== expectedOwnerEmail) ||
|
|
353
|
+
row.consumedAt != null ||
|
|
354
|
+
(row.expiresAt ?? 0) < now) {
|
|
328
355
|
return null;
|
|
329
356
|
}
|
|
330
357
|
const result = await client.execute({
|
|
331
|
-
sql: `UPDATE mcp_oauth_codes SET consumed_at = ? WHERE code = ? AND consumed_at IS NULL
|
|
332
|
-
args: [
|
|
358
|
+
sql: `UPDATE mcp_oauth_codes SET consumed_at = ? WHERE code = ? AND consumed_at IS NULL AND expires_at >= ? AND owner_email = ? AND issued_for_email = ?`,
|
|
359
|
+
args: [now, code, now, row.ownerEmail, row.issuedForEmail],
|
|
333
360
|
});
|
|
334
|
-
|
|
361
|
+
if (result.rowsAffected === 0)
|
|
362
|
+
return null;
|
|
363
|
+
if (result.rowsAffected === 1)
|
|
364
|
+
return row;
|
|
365
|
+
throw new Error("Authorization-code consumption returned an invalid row count");
|
|
335
366
|
}
|
|
336
|
-
export async function createOAuthRefreshToken(params) {
|
|
337
|
-
|
|
338
|
-
|
|
367
|
+
export async function createOAuthRefreshToken(params, db) {
|
|
368
|
+
if (!db)
|
|
369
|
+
await ensureTable();
|
|
370
|
+
const client = db ?? getDbExec();
|
|
339
371
|
const now = Date.now();
|
|
340
372
|
const row = {
|
|
341
373
|
id: randomUUID(),
|
|
342
374
|
tokenHash: hashOAuthToken(params.refreshToken),
|
|
343
375
|
clientId: params.clientId,
|
|
344
376
|
ownerEmail: params.ownerEmail,
|
|
377
|
+
issuedForEmail: params.ownerEmail,
|
|
345
378
|
orgId: params.orgId ?? null,
|
|
346
379
|
orgDomain: params.orgDomain ?? null,
|
|
347
380
|
scope: params.scope,
|
|
@@ -352,13 +385,14 @@ export async function createOAuthRefreshToken(params) {
|
|
|
352
385
|
revokedAt: null,
|
|
353
386
|
replacedByHash: null,
|
|
354
387
|
};
|
|
355
|
-
await client.execute({
|
|
356
|
-
sql: `INSERT INTO mcp_oauth_refresh_tokens (id, token_hash, client_id, owner_email, org_id, org_domain, scope, resource, created_at, expires_at, last_used_at, revoked_at, replaced_by_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
388
|
+
const result = await client.execute({
|
|
389
|
+
sql: `INSERT INTO mcp_oauth_refresh_tokens (id, token_hash, client_id, owner_email, issued_for_email, org_id, org_domain, scope, resource, created_at, expires_at, last_used_at, revoked_at, replaced_by_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
357
390
|
args: [
|
|
358
391
|
row.id,
|
|
359
392
|
row.tokenHash,
|
|
360
393
|
row.clientId,
|
|
361
394
|
row.ownerEmail,
|
|
395
|
+
row.issuedForEmail,
|
|
362
396
|
row.orgId,
|
|
363
397
|
row.orgDomain,
|
|
364
398
|
row.scope,
|
|
@@ -370,11 +404,15 @@ export async function createOAuthRefreshToken(params) {
|
|
|
370
404
|
row.replacedByHash,
|
|
371
405
|
],
|
|
372
406
|
});
|
|
407
|
+
if (result.rowsAffected !== 1) {
|
|
408
|
+
throw new Error("Refresh-token creation returned an invalid row count");
|
|
409
|
+
}
|
|
373
410
|
return row;
|
|
374
411
|
}
|
|
375
|
-
export async function rotateOAuthRefreshToken(params) {
|
|
376
|
-
|
|
377
|
-
|
|
412
|
+
export async function rotateOAuthRefreshToken(params, db) {
|
|
413
|
+
if (!db)
|
|
414
|
+
await ensureTable();
|
|
415
|
+
const client = db ?? getDbExec();
|
|
378
416
|
const oldHash = hashOAuthToken(params.oldRefreshToken);
|
|
379
417
|
const newHash = hashOAuthToken(params.newRefreshToken);
|
|
380
418
|
const { rows } = await client.execute({
|
|
@@ -384,15 +422,20 @@ export async function rotateOAuthRefreshToken(params) {
|
|
|
384
422
|
if (rows.length === 0)
|
|
385
423
|
return null;
|
|
386
424
|
const old = mapRefreshRow(rows[0]);
|
|
387
|
-
if (old.revokedAt != null || (old.expiresAt ?? 0) < Date.now())
|
|
388
|
-
return null;
|
|
389
425
|
const now = Date.now();
|
|
426
|
+
if (!hasIssuanceOwner(old) ||
|
|
427
|
+
old.revokedAt != null ||
|
|
428
|
+
(old.expiresAt ?? 0) < now)
|
|
429
|
+
return null;
|
|
390
430
|
const update = await client.execute({
|
|
391
|
-
sql: `UPDATE mcp_oauth_refresh_tokens SET revoked_at = ?, last_used_at = ?, replaced_by_hash = ? WHERE token_hash = ? AND revoked_at IS NULL
|
|
392
|
-
args: [now, now, newHash, oldHash],
|
|
431
|
+
sql: `UPDATE mcp_oauth_refresh_tokens SET revoked_at = ?, last_used_at = ?, replaced_by_hash = ? WHERE token_hash = ? AND revoked_at IS NULL AND expires_at >= ? AND owner_email = ? AND issued_for_email = ?`,
|
|
432
|
+
args: [now, now, newHash, oldHash, now, old.ownerEmail, old.issuedForEmail],
|
|
393
433
|
});
|
|
394
434
|
if (update.rowsAffected === 0)
|
|
395
435
|
return null;
|
|
436
|
+
if (update.rowsAffected !== 1) {
|
|
437
|
+
throw new Error("Refresh-token rotation returned an invalid row count");
|
|
438
|
+
}
|
|
396
439
|
const next = {
|
|
397
440
|
...old,
|
|
398
441
|
id: randomUUID(),
|
|
@@ -403,13 +446,14 @@ export async function rotateOAuthRefreshToken(params) {
|
|
|
403
446
|
revokedAt: null,
|
|
404
447
|
replacedByHash: null,
|
|
405
448
|
};
|
|
406
|
-
await client.execute({
|
|
407
|
-
sql: `INSERT INTO mcp_oauth_refresh_tokens (id, token_hash, client_id, owner_email, org_id, org_domain, scope, resource, created_at, expires_at, last_used_at, revoked_at, replaced_by_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
449
|
+
const insert = await client.execute({
|
|
450
|
+
sql: `INSERT INTO mcp_oauth_refresh_tokens (id, token_hash, client_id, owner_email, issued_for_email, org_id, org_domain, scope, resource, created_at, expires_at, last_used_at, revoked_at, replaced_by_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
408
451
|
args: [
|
|
409
452
|
next.id,
|
|
410
453
|
next.tokenHash,
|
|
411
454
|
next.clientId,
|
|
412
455
|
next.ownerEmail,
|
|
456
|
+
next.issuedForEmail,
|
|
413
457
|
next.orgId,
|
|
414
458
|
next.orgDomain,
|
|
415
459
|
next.scope,
|
|
@@ -421,6 +465,9 @@ export async function rotateOAuthRefreshToken(params) {
|
|
|
421
465
|
next.replacedByHash,
|
|
422
466
|
],
|
|
423
467
|
});
|
|
468
|
+
if (insert.rowsAffected !== 1) {
|
|
469
|
+
throw new Error("Refresh-token rotation returned an invalid row count");
|
|
470
|
+
}
|
|
424
471
|
return next;
|
|
425
472
|
}
|
|
426
473
|
export async function getOAuthRefreshToken(refreshToken) {
|
|
@@ -434,7 +481,9 @@ export async function getOAuthRefreshToken(refreshToken) {
|
|
|
434
481
|
if (rows.length === 0)
|
|
435
482
|
return null;
|
|
436
483
|
const row = mapRefreshRow(rows[0]);
|
|
437
|
-
if (
|
|
484
|
+
if (!hasIssuanceOwner(row) ||
|
|
485
|
+
row.revokedAt != null ||
|
|
486
|
+
(row.expiresAt ?? 0) < Date.now()) {
|
|
438
487
|
return null;
|
|
439
488
|
}
|
|
440
489
|
return row;
|
|
@@ -443,14 +492,50 @@ export async function getOAuthRefreshToken(refreshToken) {
|
|
|
443
492
|
* Slide the refresh-token's expiry window on each successful use so that active
|
|
444
493
|
* users never hit the TTL. Records `last_used_at` and extends `expires_at` to
|
|
445
494
|
* `now + MCP_OAUTH_REFRESH_TOKEN_TTL_MS`.
|
|
495
|
+
* The caller supplies the owner whose membership it verified.
|
|
446
496
|
*/
|
|
447
|
-
export async function touchOAuthRefreshToken(refreshToken) {
|
|
448
|
-
|
|
449
|
-
|
|
497
|
+
export async function touchOAuthRefreshToken(refreshToken, expectedOwnerEmail, db) {
|
|
498
|
+
if (!db)
|
|
499
|
+
await ensureTable();
|
|
500
|
+
const client = db ?? getDbExec();
|
|
450
501
|
const tokenHash = hashOAuthToken(refreshToken);
|
|
451
502
|
const now = Date.now();
|
|
452
|
-
await client.execute({
|
|
453
|
-
sql: `UPDATE mcp_oauth_refresh_tokens SET last_used_at = ?, expires_at = ? WHERE token_hash = ? AND revoked_at IS NULL`,
|
|
454
|
-
args: [
|
|
503
|
+
const result = await client.execute({
|
|
504
|
+
sql: `UPDATE mcp_oauth_refresh_tokens SET last_used_at = ?, expires_at = ? WHERE token_hash = ? AND revoked_at IS NULL AND expires_at >= ? AND owner_email = ? AND issued_for_email = ? AND BTRIM(issued_for_email) <> ''`,
|
|
505
|
+
args: [
|
|
506
|
+
now,
|
|
507
|
+
now + MCP_OAUTH_REFRESH_TOKEN_TTL_MS,
|
|
508
|
+
tokenHash,
|
|
509
|
+
now,
|
|
510
|
+
expectedOwnerEmail,
|
|
511
|
+
expectedOwnerEmail,
|
|
512
|
+
],
|
|
513
|
+
});
|
|
514
|
+
if (result.rowsAffected === 0)
|
|
515
|
+
return "invalid";
|
|
516
|
+
if (result.rowsAffected === 1)
|
|
517
|
+
return "renewed";
|
|
518
|
+
throw new Error("Refresh-token renewal returned an invalid row count");
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Revoke one refresh token so it can never mint another access token.
|
|
522
|
+
* Idempotent: an already-revoked token keeps its first timestamp.
|
|
523
|
+
*/
|
|
524
|
+
export async function revokeOAuthRefreshToken(refreshToken, expectedOwnerEmail) {
|
|
525
|
+
await ensureTable();
|
|
526
|
+
const client = getDbExec();
|
|
527
|
+
const result = await client.execute({
|
|
528
|
+
sql: `UPDATE mcp_oauth_refresh_tokens SET revoked_at = ? WHERE token_hash = ? AND revoked_at IS NULL${expectedOwnerEmail !== undefined
|
|
529
|
+
? " AND owner_email = ? AND issued_for_email = ?"
|
|
530
|
+
: ""}`,
|
|
531
|
+
args: [
|
|
532
|
+
Date.now(),
|
|
533
|
+
hashOAuthToken(refreshToken),
|
|
534
|
+
...(expectedOwnerEmail !== undefined
|
|
535
|
+
? [expectedOwnerEmail, expectedOwnerEmail]
|
|
536
|
+
: []),
|
|
537
|
+
],
|
|
455
538
|
});
|
|
539
|
+
if (result.rowsAffected !== 0 && result.rowsAffected !== 1)
|
|
540
|
+
throw new Error("Refresh-token revocation returned an invalid row count");
|
|
456
541
|
}
|
|
@@ -2,6 +2,7 @@ import { MCP_OAUTH_ACCESS_TOKEN_TTL, MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS } from "
|
|
|
2
2
|
export { MCP_OAUTH_ACCESS_TOKEN_TTL, MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS };
|
|
3
3
|
export declare const MCP_OAUTH_SCOPES: readonly ["mcp:read", "mcp:write", "mcp:apps", "offline_access"];
|
|
4
4
|
export declare const MCP_OAUTH_DEFAULT_SCOPE: string;
|
|
5
|
+
declare const MCP_OAUTH_CREDENTIAL_VERSION = 2;
|
|
5
6
|
export interface McpOAuthAccessTokenClaims {
|
|
6
7
|
sub: string;
|
|
7
8
|
org_id?: string | null;
|
|
@@ -11,6 +12,7 @@ export interface McpOAuthAccessTokenClaims {
|
|
|
11
12
|
resource: string;
|
|
12
13
|
jti?: string;
|
|
13
14
|
typ: "agent-native-mcp-oauth";
|
|
15
|
+
credential_version: typeof MCP_OAUTH_CREDENTIAL_VERSION;
|
|
14
16
|
}
|
|
15
17
|
export declare function normalizeOAuthScope(input: unknown): string | null;
|
|
16
18
|
export declare function scopeList(scope: string | undefined): string[];
|
|
@@ -38,4 +40,6 @@ export declare function verifyMcpOAuthAccessToken(token: string, resource: strin
|
|
|
38
40
|
clientId: string;
|
|
39
41
|
jti?: string;
|
|
40
42
|
catalogScope?: "full";
|
|
43
|
+
/** `iat`, in seconds. */
|
|
44
|
+
issuedAt?: number;
|
|
41
45
|
} | null>;
|