From 006113ebc7e2c432b32bede7e5888ed05aa468d6 Mon Sep 17 00:00:00 2001 From: kyy Date: Mon, 15 Jun 2026 14:42:02 +0900 Subject: [PATCH] =?UTF-8?q?ID=20Token=EC=97=90=20rt=5Fexpires=5Fat=20?= =?UTF-8?q?=ED=81=B4=EB=A0=88=EC=9E=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth_handler_dynamic_claims_test.go | 32 +++++++++++++++++++ test/ory_v26_compose_policy_test.sh | 17 ++++++++++ 2 files changed, 49 insertions(+) diff --git a/backend/internal/handler/auth_handler_dynamic_claims_test.go b/backend/internal/handler/auth_handler_dynamic_claims_test.go index 5b919ef2..f634f568 100644 --- a/backend/internal/handler/auth_handler_dynamic_claims_test.go +++ b/backend/internal/handler/auth_handler_dynamic_claims_test.go @@ -36,6 +36,38 @@ func assertRefreshTokenExpiryClaimWithin(t *testing.T, claims map[string]any, is assert.False(t, expiresAt.After(issuedBefore.Add(ttl).Add(time.Second)), "rt_expires_at should be before or equal to request end + ttl") } +func TestHydraRefreshTokenTTL_DefaultAndFallback(t *testing.T) { + t.Run("uses explicit env value", func(t *testing.T) { + t.Setenv("HYDRA_REFRESH_TOKEN_TTL", "96h") + assert.Equal(t, 96*time.Hour, hydraRefreshTokenTTL()) + }) + + t.Run("uses default when env is empty", func(t *testing.T) { + t.Setenv("HYDRA_REFRESH_TOKEN_TTL", "") + assert.Equal(t, defaultRefreshTokenTTL, hydraRefreshTokenTTL()) + }) + + t.Run("uses default when env is invalid", func(t *testing.T) { + t.Setenv("HYDRA_REFRESH_TOKEN_TTL", "not-a-duration") + assert.Equal(t, defaultRefreshTokenTTL, hydraRefreshTokenTTL()) + }) + + t.Run("uses default when env is non-positive", func(t *testing.T) { + t.Setenv("HYDRA_REFRESH_TOKEN_TTL", "0h") + assert.Equal(t, defaultRefreshTokenTTL, hydraRefreshTokenTTL()) + }) +} + +func TestWithRefreshTokenExpiryClaim_UsesHydraRefreshTokenTTL(t *testing.T) { + t.Setenv("HYDRA_REFRESH_TOKEN_TTL", "36h") + + issuedAt := time.Date(2026, time.June, 15, 14, 0, 0, 0, time.UTC) + claims := withRefreshTokenExpiryClaim(map[string]any{"email": "user@test.com"}, issuedAt) + + assert.Equal(t, "user@test.com", claims["email"]) + assert.Equal(t, issuedAt.Add(36*time.Hour).Unix(), claims["rt_expires_at"]) +} + func TestBuildOidcClaimsFromTraits_DynamicClaims(t *testing.T) { traits := map[string]any{ "email": "user@baron.com", diff --git a/test/ory_v26_compose_policy_test.sh b/test/ory_v26_compose_policy_test.sh index 8c440416..04eae91d 100644 --- a/test/ory_v26_compose_policy_test.sh +++ b/test/ory_v26_compose_policy_test.sh @@ -306,6 +306,17 @@ if ! grep -q 'scripts/render_ory_config.sh' "$repo_root/.gitea/workflows/staging exit 1 fi +for workflow_file in \ + "$repo_root/.gitea/workflows/staging_code_pull.yml" \ + "$repo_root/.gitea/workflows/staging_release.yml" \ + "$repo_root/.gitea/workflows/production_release.yml" +do + if ! grep -q 'HYDRA_REFRESH_TOKEN_TTL' "$workflow_file"; then + echo "ERROR: workflow must propagate HYDRA_REFRESH_TOKEN_TTL into deployment env: $workflow_file" >&2 + exit 1 + fi +done + if ! grep -q 'up -d --force-recreate kratos hydra keto oathkeeper' "$repo_root/.gitea/workflows/staging_code_pull.yml"; then echo "ERROR: staging code pull must restart Ory services after rendering static config." >&2 exit 1 @@ -334,11 +345,13 @@ KRATOS_UI_URL=https://sso.hmac.kr KRATOS_BROWSER_URL=https://sso.hmac.kr/auth KRATOS_ADMIN_URL=http://kratos:4434 ORY_POSTGRES_PASSWORD=policy-test +HYDRA_REFRESH_TOKEN_TTL=168h KRATOS_ALLOWED_RETURN_URLS_JSON= KRATOS_ALLOWED_RETURN_URLS_EXTRA= EOF ORY_CONFIG_ENV_FILES="$stage_render_env" ORY_CONFIG_OUTPUT_DIR="$stage_render_dir/ory" "$repo_root/scripts/render_ory_config.sh" >/dev/null stage_rendered_kratos="$stage_render_dir/ory/kratos/kratos.yml" +stage_rendered_hydra="$stage_render_dir/ory/hydra/hydra.yml" if ! awk '/allowed_return_urls:/ { in_block=1; next } in_block && /^[[:space:]]+methods:/ { exit } in_block { print }' "$stage_rendered_kratos" | grep -q 'https://sso.hmac.kr'; then echo "ERROR: rendered stage Kratos config must include the public userfront URL in allowed_return_urls." >&2 exit 1 @@ -351,6 +364,10 @@ if ! awk '/session:/ { in_session=1 } in_session && /domain:/ { print; exit }' " echo "ERROR: rendered stage Kratos config must derive hmac.kr as session.cookie.domain." >&2 exit 1 fi +if ! awk '/ttl:/ { in_ttl=1; next } in_ttl && /^[^[:space:]]/ { exit } in_ttl { print }' "$stage_rendered_hydra" | grep -q 'refresh_token: 168h'; then + echo "ERROR: rendered stage Hydra config must include HYDRA_REFRESH_TOKEN_TTL as ttl.refresh_token." >&2 + exit 1 +fi rm -rf "$stage_render_dir" "$stage_render_env" for generated_config in \