new-vtrc-mobile — Auth, RSA & AsyncStorage Pipeline
บทนี้ (branch: master) trace flow login จริง, การสร้าง csID, RSA encrypt, และการ persist token ผ่าน AsyncStorage
Boot → navigator gate
index.js → App.tsx
│ initializeI18n() แล้วค่อย render
│ `App.tsx:11-30`
▼
AppProviders
│ Redux Provider + PersistGate(AsyncStorage) + NavigationContainer
│ `src/providers/AppProviders.tsx`
▼
RootNavigator
│ isLoggedIn = state.auth.isLoggedIn
│ true → BottomTabNavigator (Home / Menu / Setting)
│ false → AuthNavigator (Login เท่านั้น)
│ `RootNavigator.tsx:8-19`CheckEmpCode มีไฟล์ครบแต่ถูก comment ออกจาก AuthNavigator (AuthNavigator.tsx:5,12) — ไม่เข้า runtime tree
Persist: AsyncStorage (ไม่ใช่ Keychain)
persistConfig = {
key: 'root',
storage: AsyncStorage, ← plain AsyncStorage
whitelist: ['auth'],
}
`src/redux/store.ts:20-25`สิ่งที่ถูก persist ทั้งก้อนใน auth slice (authSlice.ts:11-22):
| Field | เก็บหลัง login? | ความเสี่ยง |
|---|---|---|
accessToken | ใช่ (setLoginData) | plaintext บน device storage |
refreshToken | ใช่ | plaintext |
password | ใช่ (ค่าที่ encrypt แล้วถูกใส่ใน payload) | ยังอยู่ใน persist bag |
isLoggedIn | ใช่ | — |
ssoId / lsid | ใช่ | — |
profile / employeeProfile / empDetail | ตั้งหลัง fetch | อาจมี PII ใน persist |
เทียบ vtrc-mobile ที่ใช้ sensitive storage — ที่นี่ถอยไปใช้ AsyncStorage ตรง (SEC-NEW-02)
Auth.ts ยังอ่าน/เขียน key แยก accessToken / refreshToken บน AsyncStorage อีกชั้น (Auth.ts:4-16) นอกเหนือจาก redux-persist — สองช่องทาง
RSA — สองเส้นทาง
1) makeCsID() — device proof สำหรับ captcha / verify
DeviceInfo.getUniqueId()
+ moment().valueOf()
+ API_KEY
→ sha256 hex
→ JSON { clientId, createdAt, hashSum }
→ RSA publicEncrypt(RSA_KEY.public)
→ base64 string = csID
`src/utils/CSID.ts:7-27`ใช้ RSA_KEY.public จาก constants/RSAKey.ts:1-10
2) encryptToBase64 / decryptToString — รหัสผ่าน
encryptToBase64(text)
→ publicEncrypt(RSA_KEY.public, JSON.stringify(text))
→ base64
`EncryptCrypto.ts:4-13`
decryptToString(encryptedBase64)
→ privateDecrypt(RSA_KEY.private, ...)
→ JSON.parse
`EncryptCrypto.ts:15-24`Private key อยู่ใน client bundle (RSAKey.ts:11-37) — ใคร unpack APK/IPA ก็ decrypt ได้ → SEC-NEW-01
Login service pipeline (authService.ts)
ลำดับที่พบบน service layer (เรียกจาก LoginScreen):
1. generateCaptcha()
GraphQL GENERATE_CAPTCHA → HOSTGRAHQL
usePinning: true, useAuth: false
`authService.ts:39-55`
2. ssoCheckEmpLogin(username)
POST `${HOST}/SSOService/checkEmpLogin`
zod validate → dispatch setSsoData
`authService.ts:57-96`
3. verifyEmpCodeLogin({ empCode, csID })
GraphQL VERIFY_EMP_CODE_LOGIN
variables: empCode, csId, organization, workflow:'SIGNIN'
`authService.ts:98-117`
4. login(...)
password ผ่าน encryptToBase64 ก่อนส่ง
GraphQL LOGIN
dispatch setLoginData({ accessToken, refreshToken, password, ... })
แล้ว fetch profile / employeeProfile / empDetail
(ต่อใน authService หลังบรรทัด ~120)Host default = UAT (HostAPI.ts:1-6); prod ถูก comment (HostAPI.ts:9-14)
LINK_DOWNLOAD_APP = 'https://vtrc.redcross.or.th/adn/' (HostAPI.ts:16) — ชี้ไป download center เดียวกับ vtrc-application-front
HTTP layer (ApiRequest.ts)
| Function | ใช้เมื่อ | Citation |
|---|---|---|
axiosRequest | REST เช่น SSO | ApiRequest.ts:19-111 |
graphqlRequest | GraphQL ไป HOSTGRAHQL | ApiRequest.ts:113-212 |
ทั้งคู่:
- ใส่ header
apiKey: API_KEYเสมอ (ApiRequest.ts:38,134) - ใส่
Authorization: Bearer {accessToken}เมื่อuseAuth(ApiRequest.ts:39,132) - optional SSL pinning cert name
vtrc_redcross_th(ApiRequest.ts:51-53,145-147) - fallback axios ถ้า pinning module โหลดไม่ได้ (
ApiRequest.ts:12-17,61-66) - บน 401 + message match
/token expired|invalid token|unauthorized/i→ เรียกrefreshToken()แล้ว retry ครั้งเดียว (ApiRequest.ts:83-101,184-206)
refreshToken() ยิง https://yourdomain.com/auth/refresh (Auth.ts:11) — ยังเป็น boilerplate → QUAL-NEW-01
GraphQL surface ที่ port มาแล้ว
| File | Operations | Citation |
|---|---|---|
graphql/mutations/auth.ts | LOGIN, LOGOUT | map ใน 01-repository-map |
graphql/queries/auth.ts | VERIFY_EMP_CODE_LOGIN, FETCH_PROFILE, GENERATE_CAPTCHA, FETCH_EMPLOYEE_PROFILE, FETCH_EMPLOYEE_DETAIL | 同上 |
graphql/queries/news.ts | FETCH_LIST_POST | newsService.ts มีแต่ Home ยังไม่ผูก UI จริง |
ยังไม่มีโดเมน leave / hospital / slip ฯลฯ ของ vtrc-mobile
Data flow กับระบบอื่น
new-vtrc-mobile
│ GraphQL + apiKey + Bearer
▼
uat-vtrcapi.redcross.or.th/api-uat/graphql (default)
│
├── SSOService/checkEmpLogin บน HOST เดียวกัน
└── (workforce host คงที่ใน HostAPI แต่ยังไม่เห็น caller ใน screen ชุดเล็กนี้ — UNVERIFIED usage)ไม่มี Apollo Client — ใช้ axios wrapper เองทั้งหมด
RSA helpers (file:line)
| Function | Behavior | Citation |
|---|---|---|
encryptToBase64 | publicEncrypt(RSA_KEY.public) → base64 | EncryptCrypto.ts:4-13 |
decryptToString | privateDecrypt(RSA_KEY.private) → JSON.parse | EncryptCrypto.ts:15-24 |
makeCsID | SHA256(clientId:createdAt:API_KEY) แล้ว publicEncrypt object | CSID.ts:7-27 |
shim.js polyfill Buffer/process/crypto ก่อน boot — จำเป็นสำหรับ react-native-quick-crypto
Deps js-crypto-rsa / node-forge ใน package.json ไม่พบ import ใน src/ — dead candidates
AsyncStorage key summary
| Key | Writer | Notes |
|---|---|---|
persist:root | redux-persist | auth whitelist รวม tokens + encrypted password |
language | i18n | th / en |
accessToken / refreshToken | Auth.ts helpers | คู่ขนานกับ Redux — ApiRequest อ่านจาก store เป็นหลัก |
ดู scorecard SEC-NEW-02 สำหรับความเสี่ยง plain AsyncStorage