Compare commits

..
Author SHA1 Message Date
donghyeon-ka 5a8bc9b145 test(ap1): reproduce issuer mismatch 2026-07-25 14:12:23 +09:00
donghyeon-ka 994bef0edd merge(ap1): resource audience validation 2026-07-25 14:11:31 +09:00
4 changed files with 48 additions and 2 deletions
+6
View File
@@ -116,3 +116,9 @@ Keycloak의 dedicated audience mapper는 `spa-public` access token에
아니라 이 `aud`도 검사합니다. `verify-pattern1.sh`는 같은 정상 토큰을 아니라 이 `aud`도 검사합니다. `verify-pattern1.sh`는 같은 정상 토큰을
`deliberately-wrong-audience`를 기대하는 진단 인스턴스에도 제출해 `401` `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`을 반환합니다.
+26
View File
@@ -112,6 +112,32 @@ services:
networks: networks:
- keycloak-net - 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: nginx:
build: build:
context: ./frontend context: ./frontend
+12 -1
View File
@@ -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.reload();
await page.locator('[data-authenticated="false"]').waitFor(); await page.locator('[data-authenticated="false"]').waitFor();
assert.equal( assert.equal(
@@ -83,7 +94,7 @@ try {
); );
console.log( 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 { } finally {
await browser.close(); await browser.close();
+4 -1
View File
@@ -12,12 +12,15 @@ set +a
docker compose down --volumes --remove-orphans docker compose down --volumes --remove-orphans
docker compose up --build -d --wait 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 npm --prefix e2e ci
E2E_USERNAME=regular-user \ E2E_USERNAME=regular-user \
E2E_PASSWORD="$REGULAR_USER_PASSWORD" \ E2E_PASSWORD="$REGULAR_USER_PASSWORD" \
WRONG_AUDIENCE_URL=http://localhost:18081/api/me \ WRONG_AUDIENCE_URL=http://localhost:18081/api/me \
WRONG_ISSUER_URL=http://localhost:18082/api/me \
npm --prefix e2e run test:pattern1 npm --prefix e2e run test:pattern1
echo "AP1 verified end to end" echo "AP1 verified end to end"