diff --git a/README.md b/README.md index 1f790fe..670617c 100644 --- a/README.md +++ b/README.md @@ -116,3 +116,9 @@ Keycloak의 dedicated audience mapper는 `spa-public` access token에 아니라 이 `aud`도 검사합니다. `verify-pattern1.sh`는 같은 정상 토큰을 `deliberately-wrong-audience`를 기대하는 진단 인스턴스에도 제출해 `401`을 확인합니다. + +Keycloak은 `KC_HOSTNAME=http://localhost:8080`을 기준으로 token의 `iss`를 +발급합니다. 정상 Resource Server는 이 외부 issuer 문자열을 검증하되 JWKS는 +Docker 내부의 `http://keycloak:8080`에서 가져옵니다. 진단 인스턴스는 일부러 +`http://wrong-issuer.invalid`를 기대하도록 구성되어, 서명과 audience가 +정상이더라도 issuer mismatch로 `401`을 반환합니다. diff --git a/docker-compose.yml b/docker-compose.yml index ee74fcf..0262b60 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -112,6 +112,32 @@ services: networks: - keycloak-net + app-wrong-issuer: + profiles: + - diagnostics + build: + context: ./backend + environment: + SERVER_PORT: "8081" + SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://wrong-issuer.invalid/realms/keycloak-patterns + SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: http://keycloak:8080/realms/keycloak-patterns/protocol/openid-connect/certs + SECURITY_EXPECTED_AUDIENCE: keycloak-pattern-api + ports: + - "127.0.0.1:18082:8081" + depends_on: + keycloak: + condition: service_healthy + healthcheck: + test: + - CMD-SHELL + - wget -q -O - http://127.0.0.1:8081/actuator/health | grep -q '"status":"UP"' + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s + networks: + - keycloak-net + nginx: build: context: ./frontend diff --git a/e2e/pattern1.mjs b/e2e/pattern1.mjs index bfdb312..ea20a31 100644 --- a/e2e/pattern1.mjs +++ b/e2e/pattern1.mjs @@ -74,6 +74,17 @@ try { ); } + if (process.env.WRONG_ISSUER_URL) { + const response = await fetch(process.env.WRONG_ISSUER_URL, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + assert.equal( + response.status, + 401, + "the same signed token must fail when the Resource Server expects another issuer", + ); + } + await page.reload(); await page.locator('[data-authenticated="false"]').waitFor(); assert.equal( @@ -83,7 +94,7 @@ try { ); console.log( - "pattern1 browser verified: code+PKCE S256, audience positive 200/negative 401, Web Storage token 0, reload clears token", + "pattern1 browser verified: code+PKCE S256, audience/issuer negatives 401, Web Storage token 0, reload clears token", ); } finally { await browser.close(); diff --git a/scripts/verify-pattern1.sh b/scripts/verify-pattern1.sh index 9cfa026..e806543 100755 --- a/scripts/verify-pattern1.sh +++ b/scripts/verify-pattern1.sh @@ -12,12 +12,15 @@ set +a docker compose down --volumes --remove-orphans docker compose up --build -d --wait -docker compose --profile diagnostics up -d --wait app-wrong-audience +docker compose --profile diagnostics up -d --wait \ + app-wrong-audience \ + app-wrong-issuer npm --prefix e2e ci E2E_USERNAME=regular-user \ E2E_PASSWORD="$REGULAR_USER_PASSWORD" \ WRONG_AUDIENCE_URL=http://localhost:18081/api/me \ +WRONG_ISSUER_URL=http://localhost:18082/api/me \ npm --prefix e2e run test:pattern1 echo "AP1 verified end to end"