auth

Sentivex Incidents Ticket API

GET/api/auth/callback

index-pm OIDC 콜백. code→token→userinfo → 이메일로 user find/create →

/api/auth/callback — index-pm OIDC 콜백. code→token→userinfo → 이메일로 user find/create → 기존 signToken()/COOKIE_OPTIONS 재사용해 `token` 세션 쿠키 발급 → next 로 복귀. 게이트: 인증되면 로그인(자체 로그인과 동일 세션). entitlement 세분 게이트는 후속.

Responses

200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X GET https://sit.in-bridge.com/api/auth/callback \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/callback", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${token}`
  }
});
const data = await res.json();
POST/api/auth/exchange

패밀리 BFF(sentivex-web 등) 세션 교환. (서버↔서버 전용)

/api/auth/exchange — 패밀리 BFF(sentivex-web 등) 세션 교환. (서버↔서버 전용) 목적(왜): sentivex-web 은 자체 OIDC 세션(svx_session)으로 로그인한다. 그 사용자가 index-pm 의 인시던트/티켓/알림 데이터를 **자기 신원으로** 읽도록, sentivex-web BFF 가 svx_session 을 검증해 얻은 사용자(email+roles)를 이 엔드포인트로 넘기면, index-pm 이 동일 사용자(이메일 find/create)의 pm 세션 토큰을 발급해 돌려준다. BFF 는 그 토큰을 index-pm 호출의 `token` 쿠키로 실어 per-user 접근을 한다. → **OPEN 모드(익명 admin) 의존을 제거**하고 운영에서도 동작(docs/integrations/24·25). 인증: `Authorization: Bearer <BFF_EXCHANGE_SECRET>` — 서버간 공유 시크릿(constant-time 비교). 미설정 시 503(교환 비활성, fail-closed). 사용자 신원의 진위는 호출 BFF 가 svx_session 검증으로 보증하고, 이 엔드포인트는 그 BFF 를 시크릿으로 신뢰한다(브라우저 직접 호출 불가 — 쿠키 게이트 면제 경로이므로 PUBLIC_PATHS 에 등재하되 자체 bearer 로 보호). 보안: 발급 토큰 scopes 는 roles→pm:* 로컬 매핑(role-scopes.ts, IdP 시드 미러)으로 산정한다. 읽기 데이터(GET)는 스코프 게이트가 없어 누구나 통과하고, 쓰기/HITL(process/resume 등)은 가드(requireScope)가 토큰 scopes 로 통제한다(역할에 pm:respond 없으면 거부 = fail-closed). ★테넌트 격리(#203): SOC 테넌트 프로젝트(in-bridge/kumho/hanssem)는 공유 워크스페이스를 써서 멤버십만으론 구분이 안 되고 tenantScope(JWT allowedTenants)가 유일한 per-tenant 경계다. 따라서 BFF 는 svx_session 의 allowed_tenants 를 이 교환에 함께 넘겨야 하며, 그 값으로 pm 토큰이 서버강제된다 (OIDC callback 과 동일). 미전달 시 BFF_EXCHANGE_TENANT_STRICT 로 fail-closed([]) 또는 레거시(전체)+경고. 과거엔 이 클레임을 아예 안 실어 교환 세션이 전 테넌트 인시던트를 읽는 크로스테넌트 유출이 있었다(라이브 실증).

Request body

emailstring<email>required
namestring
rolesstring
allowed_tenantsstring

Responses

200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X POST https://sit.in-bridge.com/api/auth/exchange \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","name":"string","roles":"string","allowed_tenants":"string"}'
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/exchange", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "email": "[email protected]",
    "name": "string",
    "roles": "string",
    "allowed_tenants": "string"
  })
});
const data = await res.json();
POST/api/auth/login

이메일 + 비밀번호 기반 로그인 엔드포인트.

API 라우트: POST /api/auth/login 이메일 + 비밀번호 기반 로그인 엔드포인트. 인증 성공 시 JWT를 httpOnly 쿠키(token)에 설정한다. proxy.ts가 이 쿠키를 검증해 모든 보호된 경로를 게이트한다. 보안 장치: 1. IP+이메일 조합으로 15분 내 5회 시도 제한 (Redis 기반 rate limit) 2. 계정 존재 여부를 노출하지 않도록 사용자 없음/비밀번호 불일치 메시지 통일 3. 로그인 성공 시 rate limit 카운터 초기화 (정상 사용자 보호) 바디: { email: string, password: string } 응답: { user: { id, email, fullName } } + Set-Cookie: token=<JWT> @author jijuta ([email protected]) @since 2026-05-03 @updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용

Request body

emailanyrequired
passwordstringrequired
redirectstring

Responses

200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X POST https://sit.in-bridge.com/api/auth/login \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":null,"password":"string","redirect":"string"}'
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/login", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "email": null,
    "password": "string",
    "redirect": "string"
  })
});
const data = await res.json();
GET/api/auth/logout

GET /api/auth/logout

Responses

200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X GET https://sit.in-bridge.com/api/auth/logout \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/logout", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${token}`
  }
});
const data = await res.json();
POST/api/auth/logout

POST /api/auth/logout

Responses

200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X POST https://sit.in-bridge.com/api/auth/logout \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/logout", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`
  }
});
const data = await res.json();
GET/api/auth/me

현재 로그인 사용자 정보 조회 라우트

GET /api/auth/me — 현재 로그인 사용자 정보 조회 라우트 JWT 쿠키(`token`)로 인증된 사용자의 프로필을 반환한다. 클라이언트가 페이지 로드 시 자신의 세션을 확인하거나, 사용자 정보를 최신화할 때 호출하는 진입점이다. - 권한: 로그인 필수 (`requireUser` — 미인증 시 401) - 부작용 없음 (읽기 전용) - 반환: `{ user: { id, email, fullName, username, avatarUrl } }` @author jijuta ([email protected]) @since 2026-05-03 @updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용

Responses

200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X GET https://sit.in-bridge.com/api/auth/me \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/me", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${token}`
  }
});
const data = await res.json();
GET/api/auth/oidc/start

index-pm OIDC 로그인 시작. state·next 쿠키 굽고 IdP authorize 로 리다이렉트.

/api/auth/oidc/start — index-pm OIDC 로그인 시작. state·next 쿠키 굽고 IdP authorize 로 리다이렉트.

Responses

200 성공401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X GET https://sit.in-bridge.com/api/auth/oidc/start \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/oidc/start", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${token}`
  }
});
const data = await res.json();
POST/api/auth/register

신규 사용자 회원가입 라우트

POST /api/auth/register — 신규 사용자 회원가입 라우트 이메일·비밀번호·이름을 받아 사용자를 생성하고, 즉시 JWT를 발급해 httpOnly 쿠키에 세팅함으로써 가입 후 별도 로그인 없이 바로 인증 상태가 되도록 한다. - 권한: 인증 불필요 (공개 엔드포인트) - 입력(JSON body): `{ email, password, fullName }` - 부작용: users 테이블 INSERT + `token` 쿠키 설정 - 반환: `{ user: { id, email, fullName } }` (HTTP 201) - 실패: 이메일 중복 → 409 ConflictError, 유효성 오류 → 422 ZodError @author jijuta ([email protected]) @since 2026-05-03 @updated 2026-06-07 — JSDoc 표준(@author/@since/@param 등) 적용 @updated 2026-06-07 — #44 IP/사용자별 rate limit 추가(가입 남용·brute-force·bcrypt DoS 차단)

Request body

emailanyrequired
passwordstringrequired
fullNamestringrequired
redirectstring

Responses

200 성공400 입력 검증 실패(zod)401 미인증(JWT 쿠키 필요)403 권한 없음404 없음
cURL
curl -X POST https://sit.in-bridge.com/api/auth/register \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":null,"password":"string","fullName":"string","redirect":"string"}'
JavaScript
const res = await fetch("https://sit.in-bridge.com/api/auth/register", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "email": null,
    "password": "string",
    "fullName": "string",
    "redirect": "string"
  })
});
const data = await res.json();