feat(oidc): role 변경 시 서명된 Security Event Token(SET) 발행 - #81
Merged
Conversation
관리자가 사용자의 OIDC 서비스 role 을 부여/회수하면, back-channel logout 과
동일한 서명키·JWKS·SET 봉투로 서명된 토큰을 대상 RP 의 role_change_uri 로 push
한다. RP 는 세션을 끊지 않고 members.role 만 갱신하므로 재로그인 없이 다음 요청부터
반영된다(강등 시 낡은 권한 유지 보안 이슈 해소).
- schema: oidcClients 에 role_change_uri(nullable) 추가 (sqlite/pg/mysql 3방언 + 마이그레이션)
- oidc/role-change.ts: getRoleChangeTarget + sendRoleChangeSet (logout.ts 미러)
- RS256, typ=secevent+jwt, iss/aud(=clientId)/sub/iat/jti/events, nonce 금지
- events = { "https://idp.hyochan.site/event/role-change": { roles } }
- user-actions/service.ts: addAssignment(→[role.key]) / revokeAssignment(→[]) 직후
fire-and-forget 발행. 변경 후 getActiveAssignment 로 권위 있는 최종 roles 스냅샷.
전송 성공/실패를 role_change_set_sent audit(outcome)로 기록.
- admin UI: oidc-clients 생성/수정 폼에 Role Change URI 입력 + validateSingleUri 검증
- test: role-change-set.test.ts (계약/서명/roles/skip/실패삼킴/audit 6케이스)
발행은 best-effort(재시도 없음), oidc 배정에만, role_change_uri 미설정 시 skip.
만료 기반 강등은 push 하지 않음(후속 과제).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
배경
RP 는 로그인 시 IdP roles 클레임을 세션에 스냅샷해 저장한다. 그래서 KeyStone 에서 role 을 바꿔도 사용자가 다시 로그인하기 전까지 RP 에 반영되지 않아, 강등 시 최대 세션 수명(14일) 동안 낡은 권한이 유지되는 보안 이슈가 있었다.
이를 back-channel logout 과 동일한 메커니즘(같은 서명키·JWKS·SET 봉투)으로 push 해, 세션을 끊지 않고 RP 의
members.role만 갱신한다. RP 는 매 요청 DB role 을 읽으므로 재로그인 없이 다음 요청부터 반영된다. 새 서명 스킴·시크릿은 필요 없다.변경 사항
oidcClients에role_change_uri(text, nullable) 추가 — sqlite/pg/mysql 3방언 + 마이그레이션(d10029, pg/mysql/sqlite0012). URI 설정된 클라이언트만 SET 수신.src/lib/server/oidc/role-change.ts(신규,logout.ts미러):getRoleChangeTarget— 배정 대상 클라이언트 1곳만 조회(enabled + role_change_uri).sendRoleChangeSet— RS256,typ=secevent+jwt,iss/aud(=clientId)/sub/iat/jti/events, nonce 금지, formrole_change_token=. 오류 삼킴(재시도 없음).events = { "https://idp.hyochan.site/event/role-change": { roles } }— RP 상수와 바이트 일치.admin/user-actions/service.ts:addAssignment(→[role.key]) /revokeAssignment(→[]) DB 변경 직후 fire-and-forget 발행. 변경 후getActiveAssignment로 권위 있는 최종 roles 스냅샷(로그인 token/userinfo 와 동일 경로). 전송 성공/실패를role_change_set_sentaudit(outcome)로 기록.validateSingleUri검증, load 쿼리·컬럼 반영.test/integration/role-change-set.test.ts6케이스 — 계약/서명 검증, roles[key]/[], URI 미설정·saml skip, 전송 실패 삼킴, audit 기록.계약 (RP 수신 측과 일치)
POST https://<rp>/auth/oidc/role-change,application/x-www-form-urlencoded, bodyrole_change_token=<JWT>.aud는clientId문자열(uuid 아님). 부여→roles: [role.key], 회수→roles: [](RP 가 user 로 강등).알려진 한계
검증
bun run check0 errors, prettier+eslint 통과, back-channel logout 회귀 없음.role_change_uri컬럼 추가).배포 후 남은 작업
34be2cd7512f458f9a59)의 Role Change URI 를 admin UI 에서https://drone.devday.one/auth/oidc/role-change로 등록.🤖 Generated with Claude Code