후속 개선: OIDC scope(groups/address/organization)·SAML IdP-initiated/POST·i18n(서버+admin)·검증 공통화 - #75
Merged
Merged
Conversation
…imit + userinfo Vary
- groups scope: 활성 조직 멤버십(department/team/part code|name)을 groups 배열로 매핑
(membershipToGroups). token id_token·userinfo 공유. discovery scopes/claims_supported 에 groups 추가.
- organization scope 를 admin ALLOWED_OIDC_SCOPES 에 추가(discovery/userinfo 는 이미 처리했으나
admin UI 화이트리스트 누락으로 클라이언트 설정 불가였음).
- token 엔드포인트에 per-client rate-limit(token-client:${clientId}, 60/분) 추가 — 남용 client 격리.
- userinfo 응답에 Vary: Authorization 헤더(캐시 오염 방지).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- generateIdpMetadataXml 에 wantAuthnRequestsSigned 파라미터 추가(하드코딩 false 제거). ?sp=<entityId> 쿼리 시 findSp 로 SP 별 값 반영 — 동작(런타임)과 광고(metadata) 불일치 해소. - saml/metadata 응답에 Vary 헤더(테넌트/SP별 캐시 키 분리, Cache-Control 유지). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- lib/server/db/errors.ts: 방언 무관 isUniqueViolation(err) 헬퍼(sqlite/d1 UNIQUE, pg 23505, mysql 1062). - enroll/confirm: unique 위반만 409, 그 외 DB 에러는 재던져 500 전파(관측성 회복). - admin/schemas.ts intField: 빈 문자열/공백을 undefined 로 정규화해 조용한 0 저장 대신 검증 실패. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- users 3방언 스키마에 nullable 주소 5컬럼(address_street/locality/region/postal_code/country). - lib/oidc/claims.ts: buildAddressClaim — 하위 필드 매핑 + formatted 조합, 전부 null 이면 클레임 생략. token id_token·userinfo 공유. address scope 요청 시에만 발급. - discovery scopes/claims_supported 에 address 추가. - admin users/[id] updateProfile 에 주소 5필드 저장(tenant 스코프) + 편집 폼 입력. - 마이그레이션 생성만(d1 0020, pg/mysql/sqlite 0003) — 적용은 사용자. vitest 54 passed, svelte-check 0 errors, build 성공. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- IdP-initiated: GET /saml/sso?sp=<entityId> (SAMLRequest 없음) → findSp → 로그인 → 서비스권한
게이트 → InResponseTo 없는 Response(SAML core §3.4) → 등록된 sp.acsUrl 로 POST auto-submit.
response.ts 의 inResponseTo 를 옵셔널화(null 이면 Response·SubjectConfirmationData 의 InResponseTo 생략).
- HTTP-POST 바인딩: POST /saml/sso, SAMLRequest=base64(XML, deflate 없음). 미로그인 시 Redirect
바인딩으로 재인코딩해 로그인 후 GET 재개.
※ POST 바인딩 서명 AuthnRequest 는 enveloped XML-dsig 검증기가 없어 명시적으로 거부(가짜 통과 없음,
미서명만 지원). wantAuthnRequestsSigned SP 의 POST 요청도 거부.
- SP-initiated Redirect 흐름은 출력·보안검증 100% 보존. 공통 함수 추출(ssoPreflight/Destination/ACS/
게이트·발급) 로 세 흐름 공유.
- replay-ID 소비를 "Assertion 발급 직전"으로 이동 — 1 AuthnRequest=1 Assertion 보장 유지하면서
forceAuthn 재진입(동일 요청 복귀)이 replay 오탐되던 결함 수정.
svelte-check 0 errors, vitest 54 passed, build 성공.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- validation.ts 에 validateSamlUrl, validateLdapHost, validateLdapPort, isCloudMetadataHost,
isLinkLocalHost 추가. ldap-providers/skins 에 중복되던 link-local(169.254/16) 정규식 통합.
- saml-sps/ldap-providers 는 CRUD 팩토리 미적용(의도적): UI 성공 계약({create:true}) 불일치,
before/after diff audit(H-SAML-4)·bindPassword 암호화(H-ADMIN-4)가 팩토리 훅으로 표현 불가 →
강제 적용 시 회귀 확정이라 검증 함수 중복 제거만 수행. 각 라우트 고유 보안 검증 전부 보존.
svelte-check 0 errors, vitest 54 passed, build 성공.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- i18n/core.ts: 클라이언트 t() 와 서버 translate() 가 공유하는 lookup/폴백(현재로케일→ko→key) 로직 추출.
- i18n/server.ts: translate(locale, key, params) — event.locals.locale 로 SSR/action 에서 번역.
- (auth)/{login,signup,find-id,find-password,mfa,reset-password}/+page.server.ts 의 사용자 노출
fail/error 메시지를 키 기반 translate 로 전환(errors 공통 섹션 + 각 섹션 키). ko.json 에 키 추가.
운영자향 로그(IDP_ISSUER_URL 미설정 등)는 제외.
vitest 54 passed, svelte-check 0 errors, eslint·prettier 통과.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- en.json 을 ko.json 과 1:1 대칭으로 완성(30개 최상위 섹션, 476키 커버). admin/users/oidc/saml/ ldap/skins/signing_keys/조직/audit/profile/passkeys/mfa_manage 등 admin 섹션 영어 번역 추가. → locale=en 사용자가 admin 콘솔을 영어로 사용 가능(기존엔 ko 폴백). - admin svelte 잔존 하드코딩 한국어를 t() 로 전환: dashboard, admin/login, 조직 CRUD placeholder, oidc-clients/[id], saml-sps/[id], users/[id] 서비스권한 섹션, skins guide 등 11+파일. 신규 키는 ko.json 에도 한국어 값 추가(ko 회귀 없음). - ※ en.json 번역은 자동 작성 — 배포 전 원어민 검수 권장. vitest 54 passed, svelte-check 0 errors, eslint·prettier 통과, build 성공, JSON 유효. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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.
개요
개선 리포트 후속 검토에서 남았던 항목들을 배치로 처리했습니다(argon2·HSTS preload·의도적 설계 항목 제외). 각 배치는 독립 Opus 에이전트로 구현 후 게이트로 검증했습니다.
최종 검증:
vitest54 passed(9 files) ·svelte-check0 errors(1357 files) ·prettier·eslint통과 ·bun run build(adapter-cloudflare) 성공.변경 요약 (8 커밋)
OIDC
SAML
GET /saml/sso?sp=<entityId>, InResponseTo 없는 Response(§3.4), 등록된 ACS로 POST.보안·정합
i18n
locals.locale기반 번역. 6개 auth 서버 파일 전환.drizzle/0020(d1)·drizzle/{pg,mysql,sqlite}/0003이 users에 주소 5컬럼(순수 additive, nullable)을 추가합니다. 규칙상 생성만 했으니 사용하는 방언에 적용해 주세요(예:bun run db:migrate:pg). 프리뷰 선검증 권장.🤖 Generated with Claude Code