@@ -1379,5 +1379,119 @@ describe("Tier 1 - Feature Coverage", () => {
13791379 ) ;
13801380 assert . equal ( tokenData . token_type , "Bearer" ) ;
13811381 } ) ;
1382+
1383+ it ( "7.15 astro.config.mjs disables checkOrigin CSRF blocking and subpath /.well-known/oauth-protected-resource/mcp resolves" , async ( ) => {
1384+ const astroConfig = fs . readFileSync (
1385+ path . resolve ( SRC_DIR , "../astro.config.mjs" ) ,
1386+ "utf-8" ,
1387+ ) ;
1388+ assert . ok (
1389+ astroConfig . includes ( "checkOrigin: false" ) ,
1390+ "astro.config.mjs must disable checkOrigin so back-channel POST /oauth2/token requests without Origin header are not blocked with 403" ,
1391+ ) ;
1392+
1393+ const subpathPrmModule = loadModule (
1394+ "src/pages/.well-known/oauth-protected-resource/[...slug].ts" ,
1395+ ) ;
1396+ const res = await subpathPrmModule . GET ( {
1397+ params : { slug : "mcp" } ,
1398+ locals,
1399+ } ) ;
1400+ assert . equal ( res . status , 200 ) ;
1401+ const data = await res . json ( ) ;
1402+ assert . equal ( data . resource , "https://codingcat.dev/mcp" ) ;
1403+ assert . deepEqual ( data . authorization_servers , [ "https://codingcat.dev" ] ) ;
1404+ } ) ;
1405+
1406+ it ( "7.16 /oauth2/token supports full OIDC + PKCE S256 scope exchange (openid profile email offline_access) for Google Gemini" , async ( ) => {
1407+ const oauth2Module = loadModule ( "src/pages/oauth2/[...slug].ts" ) ;
1408+ const regReq = new Request ( "https://codingcat.dev/oauth2/register" , {
1409+ method : "POST" ,
1410+ headers : { "Content-Type" : "application/json" } ,
1411+ body : JSON . stringify ( {
1412+ client_name : "Google Gemini OIDC PKCE Test" ,
1413+ redirect_uris : [
1414+ "https://vertexaisearch.cloud.google.com/oauth-redirect" ,
1415+ ] ,
1416+ grant_types : [ "authorization_code" , "refresh_token" ] ,
1417+ response_types : [ "code" ] ,
1418+ token_endpoint_auth_method : "client_secret_basic" ,
1419+ } ) ,
1420+ } ) ;
1421+ const regRes = await oauth2Module . ALL ( { request : regReq , locals } ) ;
1422+ const regData = await regRes . json ( ) ;
1423+ assert . ok (
1424+ regData . scope . includes ( "openid" ) &&
1425+ regData . scope . includes ( "offline_access" ) ,
1426+ "Registered client must default to allowing openid and offline_access scopes" ,
1427+ ) ;
1428+
1429+ const { hashToken, getAuth } = loadModule ( "src/lib/auth.ts" ) ;
1430+ const auth = getAuth ( "https://codingcat.dev" ) ;
1431+ const ctx = await auth . $context ;
1432+
1433+ const codeVerifier =
1434+ "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk_gemini_pkce_verifier" ;
1435+ const codeChallenge = await hashToken ( codeVerifier ) ;
1436+ const code = `test_code_oidc_${ Date . now ( ) } ` ;
1437+ const identifier = await hashToken ( code ) ;
1438+
1439+ const user = await ctx . internalAdapter . createUser ( {
1440+ email : "gemini_oidc@codingcat.dev" ,
1441+ emailVerified : true ,
1442+ name : "Gemini OIDC User" ,
1443+ createdAt : new Date ( ) ,
1444+ updatedAt : new Date ( ) ,
1445+ } ) ;
1446+ const session = await ctx . internalAdapter . createSession ( user . id ) ;
1447+
1448+ await ctx . internalAdapter . createVerificationValue ( {
1449+ identifier,
1450+ value : JSON . stringify ( {
1451+ type : "authorization_code" ,
1452+ query : {
1453+ client_id : regData . client_id ,
1454+ redirect_uri :
1455+ "https://vertexaisearch.cloud.google.com/oauth-redirect" ,
1456+ response_type : "code" ,
1457+ scope :
1458+ "openid profile email offline_access read:content search mcp:tools agent" ,
1459+ state : "state_gemini_pkce" ,
1460+ code_challenge : codeChallenge ,
1461+ code_challenge_method : "S256" ,
1462+ } ,
1463+ userId : user . id ,
1464+ sessionId : session . id ,
1465+ authTime : Date . now ( ) ,
1466+ resource : [ "https://codingcat.dev/mcp" ] ,
1467+ } ) ,
1468+ expiresAt : new Date ( Date . now ( ) + 600000 ) ,
1469+ createdAt : new Date ( ) ,
1470+ updatedAt : new Date ( ) ,
1471+ } ) ;
1472+
1473+ const basicAuth = btoa ( `${ regData . client_id } :${ regData . client_secret } ` ) ;
1474+ const tokenReq = new Request ( "https://codingcat.dev/oauth2/token" , {
1475+ method : "POST" ,
1476+ headers : {
1477+ "Content-Type" : "application/x-www-form-urlencoded" ,
1478+ Authorization : `Basic ${ basicAuth } ` ,
1479+ } ,
1480+ body : new URLSearchParams ( {
1481+ grant_type : "authorization_code" ,
1482+ code,
1483+ code_verifier : codeVerifier ,
1484+ redirect_uri :
1485+ "https://vertexaisearch.cloud.google.com/oauth-redirect" ,
1486+ } ) ,
1487+ } ) ;
1488+
1489+ const tokenRes = await oauth2Module . ALL ( { request : tokenReq , locals } ) ;
1490+ assert . equal ( tokenRes . status , 200 ) ;
1491+ const tokenData = await tokenRes . json ( ) ;
1492+ assert . ok ( tokenData . access_token , "Must return access_token" ) ;
1493+ assert . ok ( tokenData . refresh_token , "Must return refresh_token" ) ;
1494+ assert . ok ( tokenData . id_token , "Must return id_token for openid scope" ) ;
1495+ } ) ;
13821496 } ) ;
13831497} ) ;
0 commit comments