Skip to content

2 · API endpoint resolution + auth/login flow + SSO handoff

(branch: uat ทุก citation ในบทนี้)


checkLinkURL() — pattern เดียวกับ canonical-truths บวก origin เพิ่ม

vtrc-backoffice-new/src/config/apollo.js:14-28

javascript
const checkLinkURL = () =>{
	if(window.location.href.includes("https://vtrcbackoffice.redcross.or.th/")){
		return "https://vtrcapi.redcross.or.th/api/graphql"
	}else if(window.location.href.includes("https://uat-vtrcbackoffice.redcross.or.th/")){
		return "https://uat-vtrcapi.redcross.or.th/api-uat/graphql"
      // return "https://uat-rc-vtrcapi.redcross.or.th/api-uat/graphql"
	}else if(window.location.href.includes("https://uat-rc-vtrcbackoffice.redcross.or.th/")){
		// return "https://uat-rc-vtrcapi.redcross.or.th/api-uat/graphql"
      return "https://uat-vtrcapi.redcross.or.th/api-uat/graphql"
	}else if(window.location.href.includes("http://localhost:3000/") || window.location.href.includes("http://localhost:3001/")){
		// return "http://192.168.0.125:8081/api-uat/graphql"
		return "https://uat-vtrcapi.redcross.or.th/api-uat/graphql"
      // return "https://uat-rc-vtrcapi.redcross.or.th/api-uat/graphql"
	}
}

(branch: uat)

การจับคู่ origin → endpoint

Origin ที่เปิดGraphQL endpoint
vtrcbackoffice.redcross.or.thvtrcapi.redcross.or.th/api/graphql (prod)
uat-vtrcbackoffice.redcross.or.thuat-vtrcapi.redcross.or.th/api-uat/graphql (UAT)
uat-rc-vtrcbackoffice.redcross.or.th — origin เพิ่มที่ไม่มีใน vtrc-backofficeuat-vtrcapi.redcross.or.th/api-uat/graphql (ชี้ endpoint UAT เดียวกัน)
localhost:3000/3001UAT (default fallback)
origin อื่น ๆไม่มี branch ตรง → undefined (bug เดียวกับ vtrc-backoffice)

ยืนยันตรงกับ pattern checkLinkURL() แบบ window.location.href-based ที่ canonical-truths ระบุไว้สำหรับ vtrc-web/vtrc-rc-backofficeไม่ใช้ NODE_ENV เหมือนกันทุกประการ ต่างจาก vtrc-backoffice เพียงจุดเดียวคือมี origin เพิ่มสำหรับ uat-rc-vtrcbackoffice (คำนำหน้า rc- บ่งชี้ถึงความเชื่อมโยงกับ vtrc-rc-backoffice — ดูหัวข้อ SSO handoff ด้านล่างซึ่งเป็นหลักฐานตรงของความเชื่อมโยงนี้)

checkLinkURL() ตัวที่สองใน src/utils/auth.js (คืน host เปล่า ไม่ใช่ path GraphQL) ก็มี origin เพิ่มแบบเดียวกัน:

vtrc-backoffice-new/src/utils/auth.js:50-68

javascript
export const checkLinkURL = () =>{
   if(window.location.href.includes("https://vtrcbackoffice.redcross.or.th/")){
		return "https://vtrcapi.redcross.or.th/"
	}else if(window.location.href.includes("https://uat-vtrcbackoffice.redcross.or.th/")){
		return "https://uat-vtrcapi.redcross.or.th/"
	}else if(window.location.href.includes("https://uat-rc-vtrcbackoffice.redcross.or.th/")){
		return "https://uat-vtrcapi.redcross.or.th/"
	}else if(window.location.href.includes("http://localhost:3000/") || window.location.href.includes("http://localhost:3001/")){
		return "https://uat-vtrcapi.redcross.or.th/"
	}
}

(branch: uat) — เช่นเดียวกับ vtrc-backoffice ไม่พบจุด import ใช้งานจริงของฟังก์ชันนี้ในรอบตรวจนี้ (UNVERIFIED)


apiKeyNews() — เพิ่ม origin เดียวกัน

vtrc-backoffice-new/src/utils/apiKey.js:1-16

javascript
export const apiKeyNews = () => {
    if(window.location.href.includes("https://vtrcbackoffice.redcross.or.th/")){
        return 'd1de47c0-07a8-11eb-adc1-0242ac120002'
    }else if(window.location.href.includes("https://uat-vtrcbackoffice.redcross.or.th/")){
        return 'd912dfb3-e2f1-4ff6-be88-29127fd32b60'
    }else if(window.location.href.includes("https://uat-rc-vtrcbackoffice.redcross.or.th/")){
      return 'd912dfb3-e2f1-4ff6-be88-29127fd32b60'
   }else if(window.location.href.includes("http://localhost:3000/") || window.location.href.includes("http://192.168.0.105:3000/")){
        return 'd912dfb3-e2f1-4ff6-be88-29127fd32b60'
    }
}

(branch: uat) — device UUID เดียวกันกับ vtrc-backoffice เป๊ะ (ทั้ง prod และ UAT) ยืนยันว่า vtrc-backoffice-new ใช้ device key เดียวกันกับ vtrc-backoffice เมื่อคุย vtrc-api — backend คงมองสอง frontend นี้เป็น APP_TYPE เดียวกัน (ไม่ได้แยก key ต่างหาก)


Token refresh — อัปเกรดเป็น refreshTokenBO (รองรับ "UAM")

ต่างจาก vtrc-backoffice ที่ใช้ apolloFetch + query refreshToken, vtrc-backoffice-new เปลี่ยนมาใช้ apolloClient.query + mutation/query refreshTokenBO — pattern เดียวกับที่ v1 archive บันทึกไว้สำหรับ vtrc-rc-backoffice:

vtrc-backoffice-new/src/config/apollo.js:59-97

javascript
const errorLink = onError(({ graphQLErrors, operation, forward }) => {
	if (graphQLErrors && graphQLErrors[0].extensions.code === 'TOKEN_EXPIRED') {
		return new Observable(async observer => {
			const oldHeaders = operation.getContext().headers
			const { errors, data } = await apolloClient.query({
				query: Queries.refreshTokenBO,
				variables: {
					refreshToken: getToken().refreshToken,
					token: getToken().token
				},
			});
			console.log('result refreshTokenBO : appllo :', { errors, data })
			if (!errors) {
				// change endpoint to support UAM
				// setToken(data.refreshToken)
				setToken(data.refreshTokenBO)
				operation.setContext({
					headers: {
						...oldHeaders,
						// change endpoint to support UAM
						// Authorization: `Bearer ${data.refreshToken.accessToken}`
						Authorization: `Bearer ${data.refreshTokenBO.accessToken}`
					}
				})

(branch: uat)

comment // change endpoint to support UAM (ปรากฏ 2 ครั้งในไฟล์นี้) เป็นหลักฐานตรงว่าโค้ดนี้ถูกแก้ไขทีหลังจาก baseline ที่เหมือน vtrc-backoffice เพื่อรองรับระบบที่ทีมเรียกว่า "UAM" (ขยายคำเต็มไม่ปรากฏในโค้ดที่ตรวจ — น่าจะเป็น Unified/User Access Management แต่ไม่มีหลักฐานยืนยันชัดเจนในรอบนี้ ทำเครื่องหมาย UNVERIFIED สำหรับความหมายเต็มของคำนี้) — เป็นหลักฐานที่ชัดเจนที่สุดว่า vtrc-backoffice-new ได้รับการแก้ไข/พัฒนาต่อหลังจากจุด fork จาก vtrc-backoffice ในขณะที่ตัวหลังไม่มีการเปลี่ยนแปลงนี้เลย (vtrc-backoffice/src/config/apollo.js ยังใช้ query refreshToken เดิม)

console.log(...) ที่บรรทัด 70 ของไฟล์นี้ยังไม่ถูกลบ (แม้ App/index.js override console.log เป็น no-op ไปแล้วที่ระดับ mount — ดังนั้น log นี้จะไม่ปรากฏจริงหลัง App mount แต่ยังเป็นโค้ดค้าง)


Login flow — เหมือน vtrc-backoffice จนถึงจุดหนึ่ง แล้วแตกไปที่ checkEmployeeCU

ขั้นตอน 1-2 (กรอกรหัสบุคลากร → captcha → รหัสผ่าน → mutation loginBackoffice) เหมือนกับ vtrc-backoffice ทุกประการ ยกเว้น variables ของ mutation ที่เพิ่ม field empCode และเอา lsid ออก (comment ทิ้งไว้):

vtrc-backoffice-new/src/pages/Login/index.js:51-62

javascript
const { data, errors } = await client.mutate({
	mutation: Mutations.loginBackoffice,
	variables: {
      empCode: username,
      // lsid: sessionStorage.getItem('lsid'),
      password: password.password,
      appVersion: "v1.0",
      apiKey: API_DEVICE_KEY,
      // organization: sessionStorage.getItem('organization'),
      organization: getOrganization(),
	}
})

(branch: uat)

หลัง login สำเร็จ (setToken(data.loginBackoffice, 1)bug เดียวกับ vtrc-backoffice: hardcode isRemember = 1 เสมอ ไม่สนใจ checkbox "จำฉันไว้" ที่ไม่ถูก render เช่นกัน) แอปเรียก query ใหม่ checkEmployeeCU ที่ไม่มีอยู่ใน vtrc-backoffice:

vtrc-backoffice-new/src/pages/Login/index.js:77-103

javascript
if (data) {
	setToken(data.loginBackoffice, 1)
	sessionStorage.removeItem('lsid')
	sessionStorage.removeItem('organization')
	sessionStorage.removeItem('reCaptchaCheck')
	let checkEmployeeCU = await client.mutate({
      mutation: Queries.checkEmployeeCU,
      variables: {
         empCode: username,
      }
   })

   if(checkEmployeeCU.data.checkEmployeeCU.status){
      window.location.href = '/';
   }else{
      let pathUrl = 'https://uat-rc-vtrcbackoffice.redcross.or.th'
      if(window.location.href.includes("https://vtrcbackoffice.redcross.or.th/")){
         pathUrl = "https://rc-vtrcbackoffice.redcross.or.th"
      }
      
      clearAllStorage()
      window.location.href = `${pathUrl}/pathLogin/${data.loginBackoffice.accessToken}/${data.loginBackoffice.refreshToken}`;
   }
}

(branch: uat)

สิ่งที่เกิดขึ้นจริง — SSO handoff ไปยัง vtrc-rc-backoffice

  1. หลัง login สำเร็จ เรียก query/mutation checkEmployeeCU(empCode) ที่ vtrc-api — คืนค่า { status: boolean }
  2. ถ้า status === true → อยู่ต่อใน vtrc-backoffice-new (window.location.href = '/')
  3. ถ้า status เป็น falsy → เคลียร์ storage ทั้งหมดของ vtrc-backoffice-new แล้ว redirect ไปยัง vtrc-rc-backoffice พร้อม access/refresh token ฝังใน URL path ตรง: https://{rc-|uat-rc-}vtrcbackoffice.redcross.or.th/pathLogin/{accessToken}/{refreshToken}

นี่คือหลักฐานตรงของ SSO bypass ที่ v1 archive บันทึกไว้ฝั่งผู้รับ (vtrc-rc-backoffice มี route /pathLogin/:token/:refreshToken ตามที่ v1 Volume 7 ระบุไว้ — ดู docs-v1-archive/volume-07-vtrc-rc-backoffice/10-scorecard.md แถว "SSO bypass") — บทนี้ยืนยันฝั่งผู้ส่งคือ vtrc-backoffice-new ทำให้เห็นภาพครบว่า สภากาชาดมีระบบ backoffice 2 ตัวที่ทำงานคู่กันโดยแบ่งผู้ใช้ตามผลลัพธ์ของ checkEmployeeCU ไม่ใช่แอปที่แยกกันเด็ดขาด

ความหมายของ "CU" ใน checkEmployeeCU — ไม่มีหลักฐานในโค้ดที่ตรวจว่าคำนี้ย่อจากอะไร (อาจเกี่ยวกับประเภทบุคลากร/องค์กร) ทำเครื่องหมาย UNVERIFIED ห้ามเดาความหมายทางธุรกิจ

ข้อสังเกตด้าน security ของ handoff นี้ — access token และ refresh token ถูกส่งเป็น path segment ใน URL แบบข้าม origin (window.location.href = ...) ซึ่งจะถูกบันทึกใน browser history, server access log ของฝั่งรับ (vtrc-rc-backoffice's nginx), และอาจรั่วผ่าน Referer header ถ้าหน้าปลายทางโหลด resource ข้าม origin เพิ่มเติม — ตรงกับ finding เดิมของ v1 ("SSO bypass token ใน URL") แต่รอบนี้มีหลักฐานฝั่งผู้ส่งเพิ่มเข้ามาสมบูรณ์กว่าเดิม ดู SEC-BONEW-01 ใน 04-scorecard

Login/index_prd.js (ไฟล์ backup พิเศษที่มีเฉพาะใน repo นี้) มีโค้ด checkEmployeeCU เหมือนกันทุกประการกับ Login/index.js ปัจจุบัน (ตรวจด้วยการอ่านเนื้อหาไฟล์ตรง) — บ่งชี้ว่าเป็น snapshot ที่ถ่ายไว้ใกล้กับเวอร์ชันปัจจุบันมาก ไม่ใช่ "เวอร์ชันก่อนมี feature นี้" ตามที่ชื่อไฟล์อาจสื่อ


Auth gate + fetchPolicy

เหมือนกับ vtrc-backoffice ทุกประการ — isLoggedIn() boolean check ใน App/index.js, fetchPolicy: 'no-cache' เป็น default ทั้ง query/watchQuery (vtrc-backoffice-new/src/config/apollo.js:165-174, branch: uat)


ขั้นตอนถัดไป

ไป 3 · Feature/module list