Skip to content

[oauth] id_token 발급 및 OIDC Discovery 문서 - #438

Open
ZaMan0806 wants to merge 3 commits into
feature/oauth-logoutfrom
feature/oauth-oidc-id-token
Open

[oauth] id_token 발급 및 OIDC Discovery 문서#438
ZaMan0806 wants to merge 3 commits into
feature/oauth-logoutfrom
feature/oauth-oidc-id-token

Conversation

@ZaMan0806

@ZaMan0806 ZaMan0806 commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

개요

SSO 로드맵 우선순위 3(id_token + openid scope)과 우선순위 4(OIDC Discovery) 를 함께 다룹니다. 로드맵이 "묶어서 하나의 OIDC 준수 PR로"를 권장한 항목입니다.

스택 PR입니다. base는 #437입니다.

본문

왜 필요한가

현재 발급하는 JWT는 access token(인가용)뿐이라, SP가 "이 사용자가 누구인지" 알려면 /userinfo를 추가 호출해야 합니다. 동작에는 문제없지만 SP가 표준 OIDC 클라이언트 라이브러리(Spring Security oauth2Login(), next-auth 등)를 쓰려면 id_token이 필요하고, 없으면 각 팀이 수동 연동 코드를 작성해야 합니다.

논의했던 sub 값 — email로 결정

로드맵에서 팀 논의가 필요하다고 표시된 항목입니다. id_token.sub에는 email을 넣습니다.

처음에는 OIDC의 "영구적이고 고유한 식별자" 요구를 근거로 account.id를 넣었지만, 이 시스템의 계정 email은 학교 계정에 묶여 변경되지 않으므로 그 조건을 이미 만족합니다. 바뀌지 않는 값을 두고 별도 식별자를 쓸 이유가 없었습니다.

일관성 쪽 근거가 더 큽니다. access token의 sub도, /userinfo(datagsm-oauth-userinfoJwtProviderclaims.subject를 email로 읽습니다)도 email을 식별자로 씁니다. id_token만 account.id를 쓰면 세 곳의 sub가 어긋나 SP 연동에서 "어느 토큰의 sub인지"를 매번 따져야 합니다.

id_token과 access token의 sub가 실제로 같은 값인지 확인하는 테스트를 넣어 두 값이 갈라지지 않도록 고정했습니다.

변경 내용

1. openid scope

openid는 권한이 아니라 "id_token을 함께 달라"는 프로토콜 지시자입니다. applicationId:scopeName 형식이 아니고 tb_oauth_scope에도 없어서, client 등록 scope 검사와 DB 조회 양쪽에서 제외했습니다. 섞인 채로 조회하면 "권한 데이터가 잘못되었습니다" 500이 납니다.

2. nonce 지원

authorize에서 받아 code에 실어 id_token 클레임까지 전달합니다 (OIDC replay 방지 필수 항목).

3. GET /.well-known/openid-configuration

지원 목록을 손으로 적지 않고 GrantType, PkceChallengeMethod enum에서 끌어옵니다. 그러지 않으면 구현이 바뀌었는데 문서만 남아 SP가 지원하지 않는 값을 쓰게 됩니다.

sdk.response.not-wrapping-urls에 경로를 추가해 CommonApiResponse 래핑 없이 raw JSON이 나가도록 했습니다.

검증

  • 신규 테스트 18건 포함 171건 통과, ktlintCheck build 통과
  • 뮤테이션 테스트:
    • openid를 DB scope 조회에서 제외하지 않음 → 4건 실패
    • id_token 미발급 → 2건 실패
    • sub에 email이 아닌 다른 값 사용 → 2건 실패
    • nonce 클레임 누락 → 1건 실패

nonce 누락 뮤테이션이 처음엔 살아남았습니다. 토큰 서비스 테스트가 JwtProvider를 목으로 두어 "전달했는지"만 보고 "JWT에 실제로 들어갔는지"는 보지 못했기 때문입니다. RSA 키를 생성해 실제로 서명된 토큰을 파싱해 검증하는 JwtProviderIdTokenTest를 추가했습니다.

새 환경변수

OAUTH_USERINFO_URL=https://oauth.userinfo.datagsm.kr/userinfo

Discovery 문서의 userinfo_endpoint에 쓰입니다. UserInfo는 별도 모듈(datagsm-oauth-userinfo)의 다른 호스트라 issuer_url에서 유도할 수 없습니다.

SP가 표준 OIDC 클라이언트 라이브러리(Spring Security oauth2Login,
next-auth 등)를 그대로 쓸 수 있게 한다. 기존에는 사용자를 식별하려면
/userinfo를 추가 호출해야 해서 팀마다 수동 연동 코드가 필요했다.

- openid scope 지원. 권한이 아니라 "id_token을 함께 달라"는 프로토콜
  지시자라 tb_oauth_scope 조회와 client 등록 scope 검사에서 제외한다.
  섞인 채로 조회하면 권한 데이터 오류로 500이 난다.
- nonce 파라미터를 authorize에서 받아 code에 실어 id_token까지 전달한다.
- GET /.well-known/openid-configuration 추가. 지원 목록은 GrantType과
  PkceChallengeMethod에서 끌어와, 구현이 바뀌었는데 문서만 남는 상황을 막는다.

id_token의 sub는 account.id다. OIDC는 sub가 영구적이고 재사용되지 않는
값일 것을 요구하는데 email은 변경될 수 있어, 바뀌는 순간 SP가 같은 사람을
다른 사용자로 인식한다. access token의 sub는 기존 /userinfo 소비자와의
호환을 위해 email을 유지하므로, 두 토큰의 sub는 서로 다르다.
@ZaMan0806
ZaMan0806 added this pull request to stack #434 September 9, 2026 14:26

@pr-agent-demo pr-agent-demo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Overview:
    OAuth 인증 서버에 OIDC id_token 발급, openid scope 처리, nonce 전달, 그리고 GET /.well-known/openid-configuration discovery 엔드포인트를 추가합니다.

  • Intent:
    표준 OIDC 클라이언트 라이브러리 연동을 지원하기 위해 id_token을 발급하고, SP가 issuer만 알면 엔드포인트와 지원 기능을 자동 발견할 수 있도록 OIDC Discovery 문서를 제공합니다.

  • Risk:
    위험도: 중간.
    집중 확인 대상:

    • id_tokensubaccount.id이고 access token의 sub는 email로 유지되는 불일치가 기존/향후 SP 연동에 미치는 영향
    • openid scope가 권한이 아닌 프로토콜 지시자로 처리되며, DB scope 조회와 응답 scope 포맷에서 올바르게 분리되는지
    • nonce가 authorize → code → id_token까지 정합적으로 전달되고, 누락/재사용 시 문제가 없는지
    • Discovery 문서의 issuer/endpoint/userinfo URL/지원 목록이 실제 구현 및 환경 설정과 일치하는지
    • 새 환경변수(OAUTH_USERINFO_URL) 누락 시 동작 및 raw JSON 응답 처리가 의도대로 되는지

0 inline comment(s)

기존에는 OIDC의 "영구 식별자" 요구를 근거로 account.id를 넣었으나,
이 시스템의 계정 email은 학교 계정에 묶여 변경되지 않으므로 그 조건을
이미 만족한다. 바뀌지 않는 값을 두고 별도 식별자를 쓸 이유가 없다.

또한 access token과 /userinfo(JwtProvider가 claims.subject를 email로
읽는다)가 모두 email을 식별자로 쓰고 있어, id_token만 account.id를 쓰면
세 곳의 sub가 어긋나 SP 연동에서 혼란을 만든다.

id_token과 access token의 sub가 같은 값인지 확인하는 테스트를 추가했다.
@github-actions github-actions Bot added the waiting for review:검토 대기 확인을 대기하고 있습니다 label Sep 10, 2026
@ZaMan0806

Copy link
Copy Markdown
Collaborator Author

sub 값을 account.id → email로 변경 (b4d3780)

처음 올릴 때는 OIDC의 "영구적이고 고유한 식별자" 요구를 근거로 account.id를 넣었는데, 이 시스템의 계정 email은 학교 계정에 묶여 변경되지 않는다는 전제를 빠뜨린 판단이었습니다. 바뀌지 않는 값이라면 표준 조건을 이미 만족하므로 별도 식별자를 쓸 이유가 없습니다.

일관성 쪽이 더 결정적입니다. datagsm-oauth-userinfoJwtProviderclaims.subject를 email로 읽고 있어(JwtProvider.kt:33), access token과 /userinfo가 이미 email을 식별자로 씁니다. id_token만 account.id를 쓰면 세 곳의 sub가 어긋나 SP 연동에서 매번 어느 토큰인지 따져야 했습니다.

변경 사항

  • JwtProvider.generateIdToken()에서 accountId 파라미터 제거, sub에 email 사용
  • 호출부의 requireNotNull(account.id) 가드도 함께 제거 (sub 조립 용도로만 있던 코드)
  • id_token과 access token의 sub가 같은 값인지 확인하는 테스트 추가 — 앞으로 한쪽만 바뀌면 실패합니다

기존 PR 본문의 "두 토큰의 sub가 서로 다릅니다" 경고와, 그에 딸린 "나중에 통일하려면 SP 동시 배포 필요" 항목은 이제 해당 사항이 없어 삭제했습니다.

검증

171건 통과, ktlintCheck build 통과. 뮤테이션으로 sub에 다른 값을 넣어보면 2건이 실패합니다(값 자체 검증 + 토큰 간 일치 검증).

스택 하위인 #439도 이 변경을 머지해 반영했습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting for review:검토 대기 확인을 대기하고 있습니다

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant