@@ -230,17 +230,91 @@ describe("MCP callback and token routes", () => {
230230 expect ( body . access_token ) . toEqual ( expect . any ( String ) ) ;
231231 } ) ;
232232
233- it ( "validates token exchange failures" , async ( ) => {
233+ it ( "exchanges refresh tokens for new token pairs" , async ( ) => {
234+ const { encodeAuthCode } = await import ( "@/lib/mcp/oauth" ) ;
234235 const { POST } = await import ( "@/app/api/mcp/token/route" ) ;
235236
236- const invalidGrant = await POST (
237+ // First, get tokens via authorization_code
238+ const code = encodeAuthCode ( {
239+ github_access_token : "oauth-access-token" ,
240+ github_user_id : "101" ,
241+ github_login : "owner-user" ,
242+ github_name : "Owner User" ,
243+ github_avatar : "https://example.com/owner.png" ,
244+ code_challenge : "iMnq5o6zALKXGivsnlom_0F5_WYda32GHkxlV7mq7hQ" ,
245+ code_challenge_method : "S256" ,
246+ redirect_uri : "https://client.test/callback" ,
247+ client_id : "client-1" ,
248+ expires_at : Date . now ( ) + 60_000 ,
249+ } ) ;
250+
251+ const initial = await POST (
252+ new NextRequest ( "https://markbase.test/api/mcp/token" , {
253+ method : "POST" ,
254+ body : JSON . stringify ( {
255+ grant_type : "authorization_code" ,
256+ code,
257+ redirect_uri : "https://client.test/callback" ,
258+ client_id : "client-1" ,
259+ code_verifier : "verifier" ,
260+ } ) ,
261+ headers : { "content-type" : "application/json" } ,
262+ } ) ,
263+ ) ;
264+ const initialBody = await initial . json ( ) ;
265+ expect ( initialBody . refresh_token ) . toEqual ( expect . any ( String ) ) ;
266+
267+ // Refresh the token
268+ const refreshed = await POST (
269+ new NextRequest ( "https://markbase.test/api/mcp/token" , {
270+ method : "POST" ,
271+ body : JSON . stringify ( {
272+ grant_type : "refresh_token" ,
273+ refresh_token : initialBody . refresh_token ,
274+ } ) ,
275+ headers : { "content-type" : "application/json" } ,
276+ } ) ,
277+ ) ;
278+ const refreshedBody = await refreshed . json ( ) ;
279+ expect ( refreshed . status ) . toBe ( 200 ) ;
280+ expect ( refreshedBody . access_token ) . toEqual ( expect . any ( String ) ) ;
281+ expect ( refreshedBody . refresh_token ) . toEqual ( expect . any ( String ) ) ;
282+
283+ // Invalid refresh token
284+ const invalid = await POST (
285+ new NextRequest ( "https://markbase.test/api/mcp/token" , {
286+ method : "POST" ,
287+ body : JSON . stringify ( {
288+ grant_type : "refresh_token" ,
289+ refresh_token : "bad-token" ,
290+ } ) ,
291+ headers : { "content-type" : "application/json" } ,
292+ } ) ,
293+ ) ;
294+ expect ( invalid . status ) . toBe ( 400 ) ;
295+
296+ // Missing refresh token
297+ const missing = await POST (
237298 new NextRequest ( "https://markbase.test/api/mcp/token" , {
238299 method : "POST" ,
239300 body : JSON . stringify ( { grant_type : "refresh_token" } ) ,
240301 headers : { "content-type" : "application/json" } ,
241302 } ) ,
242303 ) ;
243- expect ( invalidGrant . status ) . toBe ( 400 ) ;
304+ expect ( missing . status ) . toBe ( 400 ) ;
305+ } ) ;
306+
307+ it ( "validates token exchange failures" , async ( ) => {
308+ const { POST } = await import ( "@/app/api/mcp/token/route" ) ;
309+
310+ const unsupportedGrant = await POST (
311+ new NextRequest ( "https://markbase.test/api/mcp/token" , {
312+ method : "POST" ,
313+ body : JSON . stringify ( { grant_type : "client_credentials" } ) ,
314+ headers : { "content-type" : "application/json" } ,
315+ } ) ,
316+ ) ;
317+ expect ( unsupportedGrant . status ) . toBe ( 400 ) ;
244318
245319 const missingCode = await POST (
246320 new NextRequest ( "https://markbase.test/api/mcp/token" , {
0 commit comments