Skip to content

SSO-API · Auth design + Endpoint reference (branch: master)


Auth architecture

HTTP request
  → JwtAuthGuard (global, main.ts:26-27)
      ├── @Public() → allow
      └── else → JwtStrategy.validate(payload)
            ├── ExtractJwt.fromAuthHeaderAsBearerToken()
            └── secretOrKey: JWT_SECRET_KEY
  → AppController → AppService

JwtService DI bug

sso-api/src/app.service.ts:27-32:

typescript
private readonly jwtService: JwtService;  // declared as property

constructor(
  @InjectEntityManager('main')
  private readonly entityManager: EntityManager,
) {}  // ← JwtService NEVER injected

แม้จะ uncomment jwtService.sign() ก็จะ crash ที่ runtime เพราะ this.jwtService เป็น undefined


Why JWT issue is commented

sso-api/src/app.service.ts:166-181:

typescript
const { requestAppId, requestId /* ,loginToken */ } = response.data;

// const payload = {
//   username: body.username,
//   requestAppId,
//   requestId,
//   loginToken,
// };
// const token = this.jwtService.sign(payload);

return {
  username: body.username,
  requestAppId,
  requestId,
  // accessToken: token,
};

Inference จากโค้ด (ไม่มี comment อธิบาย intent):

  1. ทีม scaffold JWT infra (JwtModule, JwtStrategy, global guard) ครบ
  2. วางแผน wrap upstream loginToken เข้า JWT payload
  3. หยุดกลางคัน — sign() ถูก comment + JwtService ไม่ได้ inject
  4. // TODO ที่ signOut / verifySession controllers บ่งชี้ incomplete design

ผล: signIn เป็น @Public() และคืน {username, requestAppId, requestId} โดยไม่มี accessToken → client ไม่สามารถเรียก guarded endpoints ต่อได้ผ่าน flow นี้ alone


Endpoint catalog (7 endpoints)

Global prefix: {BASE_PATH}{API_VERSION_PATH} (UNVERIFIED)

MethodPathAuthHandlerService linesBehavior
GET/@Public()controller:18-22service:34-36Running {PROJECT_NAME}
POST/checkEmpLogin@Public()controller:24-28service:38-141Auto-provision SSO username จาก HRMI
POST/signIn@Public()controller:30-34service:143-191Proxy → external /ssoLogin; no JWT out
POST/signOutJWT requiredcontroller:36-40service:193-218// TODO; proxy → /ssoLogout
POST/verifySessionJWT requiredcontroller:42-46service:220-223Stubreturn { body }
POST/request2FAJWT requiredcontroller:48-51service:225-254Proxy → /ssoRequest2FA
POST/verify2FAJWT requiredcontroller:53-56service:256-284Proxy → /ssoVerify2FA

Flow A — checkEmpLogin (provisioning)

POST /checkEmpLogin { username }
  → SELECT SSOUsers WHERE empCode OR ssoId = username
  ├── exists → return { isVerify: verify|success|error }
  └── missing
        → SELECT temp_pee_na_table JOIN emPerson (dbHRMI_Center cross-schema)
        → POST {SSO_END_POINT}/ssoGetNewUsername (Basic Auth)
        → INSERT SSOUsers
        → return { isVerify: verify }

Evidence — app.service.ts:38-141, cross-schema query ที่ app.service.ts:78-96:

typescript
const empData = await this.entityManager.query(
  `SELECT ... FROM dbHRMI_Center.dbo.temp_pee_na_table t
   JOIN dbHRMI_Center.dbo.emPerson p ON ...
   WHERE ... = @0`,
  [username],
);

ใช้ parameterized @0ไม่มี SQL injection ที่จุดนี้

temp_pee_na_table shared กับ chat-center-api — ไม่มี ownership contract (SEC-SSO-08)


Flow B — signIn (broken JWT chain)

POST /signIn { username, password }
  → AES-256-CBC encrypt credentials (aes-encrypt.ts)
  → POST {SSO_END_POINT}/ssoLogin (Basic Auth: SSO_USERNAME/SSO_PASSWORD)
  → upstream returns { requestAppId, requestId, loginToken }
  → loginToken DISCARDED (commented out)
  → return { username, requestAppId, requestId }  // NO accessToken

Evidence — app.service.ts:143-191

AES keys จาก AES_SECRET_KEY + AES_INIT_VECTOR env


Flow C — 2FA (real proxy, blocked by JWT)

request2FA และ verify2FA ไม่ใช่ stub — เป็น HTTP proxy จริงไป upstream:

StepUpstreamEvidence
request2FAPOST /ssoRequest2FAapp.service.ts:225-254
verify2FAPOST /ssoVerify2FA + header tokenapp.service.ts:256-284

ปัญหา: ทั้งคู่ต้องการ Bearer JWT แต่ signIn ไม่ออก token → client ต้องได้ JWT จากแหล่งอื่น (หรือ flow นี้ใช้ไม่ได้)


The real stub — verifySession

sso-api/src/app.service.ts:220-223:

typescript
async verifySession(body: verifySessionDto) {
  return { body };
  //
}

คืน request body กลับมาเฉย ๆ — ไม่ verify อะไรเลย


External SSO integration

Upstream pathCalled fromAuth
/ssoLoginsignInBasic Auth + AES body
/ssoLogoutsignOutBasic Auth + AES
/ssoRequest2FArequest2FABasic Auth
/ssoVerify2FAverify2FABasic Auth + token header
/ssoGetNewUsernameget-new-username.tsBasic Auth

Inferred URLs จาก sibling repos (ไม่ใช่จาก sso-api เอง):

  • UAT: https://uatssoapi.redcross.or.th/SSOService_dev/api (2way-api/.env.example:41)
  • Prod: https://ssoapi.redcross.or.th/SSOService/api (2way-api/.env.prod:40)

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

ไป 03 Persistence + scorecard