





















@@ -4,10 +4,12 @@ import {
44parseOAuthCallbackInput,
55waitForLocalOAuthCallback,
66} from "openclaw/plugin-sdk/provider-auth-runtime";
7+import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
7889export const GOOGLE_MEET_REDIRECT_URI = "http://localhost:8085/oauth2callback";
910export const GOOGLE_MEET_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
1011export const GOOGLE_MEET_TOKEN_URL = "https://oauth2.googleapis.com/token";
12+const GOOGLE_MEET_TOKEN_HOST = "oauth2.googleapis.com";
1113export const GOOGLE_MEET_SCOPES = [
1214"https://www.googleapis.com/auth/meetings.space.readonly",
1315"https://www.googleapis.com/auth/meetings.conference.media.readonly",
@@ -43,40 +45,49 @@ export function buildGoogleMeetAuthUrl(params: {
4345}
44464547async function executeGoogleTokenRequest(body: URLSearchParams): Promise<GoogleMeetOAuthTokens> {
46-const response = await fetch(GOOGLE_MEET_TOKEN_URL, {
47-method: "POST",
48-headers: {
49-"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
50-Accept: "application/json",
48+const { response, release } = await fetchWithSsrFGuard({
49+url: GOOGLE_MEET_TOKEN_URL,
50+init: {
51+method: "POST",
52+headers: {
53+"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
54+Accept: "application/json",
55+},
56+ body,
5157},
52- body,
58+policy: { allowedHostnames: [GOOGLE_MEET_TOKEN_HOST] },
59+auditContext: "google-meet.oauth.token",
5360});
54-if (!response.ok) {
55-const detail = await response.text();
56-throw new Error(`Google OAuth token request failed (${response.status}): ${detail}`);
57-}
58-const payload = (await response.json()) as {
59-access_token?: string;
60-expires_in?: number;
61-refresh_token?: string;
62-scope?: string;
63-token_type?: string;
64-};
65-const accessToken = payload.access_token?.trim();
66-if (!accessToken) {
67-throw new Error("Google OAuth token response was missing access_token");
61+try {
62+if (!response.ok) {
63+const detail = await response.text();
64+throw new Error(`Google OAuth token request failed (${response.status}): ${detail}`);
65+}
66+const payload = (await response.json()) as {
67+access_token?: string;
68+expires_in?: number;
69+refresh_token?: string;
70+scope?: string;
71+token_type?: string;
72+};
73+const accessToken = payload.access_token?.trim();
74+if (!accessToken) {
75+throw new Error("Google OAuth token response was missing access_token");
76+}
77+const expiresInSeconds =
78+typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in)
79+ ? payload.expires_in
80+ : 3600;
81+return {
82+ accessToken,
83+expiresAt: Date.now() + expiresInSeconds * 1000,
84+refreshToken: payload.refresh_token?.trim() || undefined,
85+scope: payload.scope?.trim() || undefined,
86+tokenType: payload.token_type?.trim() || undefined,
87+};
88+} finally {
89+await release();
6890}
69-const expiresInSeconds =
70-typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in)
71- ? payload.expires_in
72- : 3600;
73-return {
74- accessToken,
75-expiresAt: Date.now() + expiresInSeconds * 1000,
76-refreshToken: payload.refresh_token?.trim() || undefined,
77-scope: payload.scope?.trim() || undefined,
78-tokenType: payload.token_type?.trim() || undefined,
79-};
8091}
81928293function tokenRequestBody(values: Record<string, string | undefined>): URLSearchParams {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。