33 * for agent model authentication.
44 */
55import { createHash , randomBytes } from "node:crypto" ;
6+ import { readResponseWithLimit } from "@openclaw/media-core/read-response-with-limit" ;
67import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce" ;
78import { resolveExpiresAtMsFromDurationSeconds } from "../infra/parse-finite-number.js" ;
89import type { OAuthCredentials } from "../llm/oauth.js" ;
910
1011const CHUTES_OAUTH_ISSUER = "https://api.chutes.ai" ;
12+ const CHUTES_OAUTH_RESPONSE_MAX_BYTES = 4 * 1024 * 1024 ;
1113export const CHUTES_AUTHORIZE_ENDPOINT = `${ CHUTES_OAUTH_ISSUER } /idp/authorize` ;
1214export const CHUTES_TOKEN_ENDPOINT = `${ CHUTES_OAUTH_ISSUER } /idp/token` ;
1315export const CHUTES_USERINFO_ENDPOINT = `${ CHUTES_OAUTH_ISSUER } /idp/userinfo` ;
@@ -103,6 +105,13 @@ async function cancelUnreadResponseBody(response: Response): Promise<void> {
103105 }
104106}
105107
108+ async function readChutesOAuthJsonResponse < T > ( response : Response ) : Promise < T > {
109+ const bytes = await readResponseWithLimit ( response , CHUTES_OAUTH_RESPONSE_MAX_BYTES , {
110+ onOverflow : ( { maxBytes } ) => new Error ( `Chutes OAuth response exceeds ${ maxBytes } bytes` ) ,
111+ } ) ;
112+ return JSON . parse ( new TextDecoder ( ) . decode ( bytes ) ) as T ;
113+ }
114+
106115async function fetchChutesUserInfo ( params : {
107116 accessToken : string ;
108117 fetchFn ?: typeof fetch ;
@@ -115,7 +124,7 @@ async function fetchChutesUserInfo(params: {
115124 await cancelUnreadResponseBody ( response ) ;
116125 return null ;
117126 }
118- const data = ( await response . json ( ) ) as unknown ;
127+ const data = ( await readChutesOAuthJsonResponse ( response ) ) as unknown ;
119128 if ( ! data || typeof data !== "object" ) {
120129 return null ;
121130 }
@@ -155,11 +164,11 @@ export async function exchangeChutesCodeForTokens(params: {
155164 throw new Error ( `Chutes token exchange failed: ${ text } ` ) ;
156165 }
157166
158- const data = ( await response . json ( ) ) as {
167+ const data = await readChutesOAuthJsonResponse < {
159168 access_token ?: string ;
160169 refresh_token ?: string ;
161170 expires_in ?: number ;
162- } ;
171+ } > ( response ) ;
163172
164173 const access = data . access_token ?. trim ( ) ;
165174 const refresh = data . refresh_token ?. trim ( ) ;
@@ -226,11 +235,11 @@ export async function refreshChutesTokens(params: {
226235 throw new Error ( `Chutes token refresh failed: ${ text } ` ) ;
227236 }
228237
229- const data = ( await response . json ( ) ) as {
238+ const data = await readChutesOAuthJsonResponse < {
230239 access_token ?: string ;
231240 refresh_token ?: string ;
232241 expires_in ?: number ;
233- } ;
242+ } > ( response ) ;
234243 const access = data . access_token ?. trim ( ) ;
235244 const newRefresh = data . refresh_token ?. trim ( ) ;
236245 const expires = resolveChutesExpiresAt ( data . expires_in , now ) ;
0 commit comments