Skip to content

Add SSO session reuse and RP-initiated logout integration tests - #4078

Merged
ThaminduDilshan merged 1 commit into
thunder-id:mainfrom
madurangasiriwardena:add-sso-logout-integration-tests
Jul 16, 2026
Merged

Add SSO session reuse and RP-initiated logout integration tests#4078
ThaminduDilshan merged 1 commit into
thunder-id:mainfrom
madurangasiriwardena:add-sso-logout-integration-tests

Conversation

@madurangasiriwardena

@madurangasiriwardena madurangasiriwardena commented Jul 16, 2026

Copy link
Copy Markdown
Member

Purpose

Adds end-to-end integration tests for the flow-based SSO session lifecycle, closing two gaps in integration coverage that existed after the session sign-out feature (#3973) merged:

  1. SSO session reuse — an established per-flow SSO session is reused on a subsequent /oauth2/authorize, skipping re-authentication.
  2. RP-initiated logout — the OIDC end_session_endpoint terminates the SSO session, clears the per-flow cookie, and lands the browser on a validated post_logout_redirect_uri.

Before this PR, the only integration-level assertion touching this feature was the discovery document advertising end_session_endpoint; the session establish/reuse and full logout flows were covered only by unit/component tests.

Approach

The tests live in a new package tests/integration/oauth/sso/ and run against the live test server, exercising the same observable HTTP behaviour a browser would. A cookiejar-backed HTTP client carries the per-flow SSO cookie (tid_sso_*) across requests so session reuse and cookie clearing are genuinely tested, not mocked.

Shared suite setup (suite_test.go) creates:

  • an OU, user type, and users;
  • an SSO-enabled authentication flow (SSOCheckExecutor → SessionExecutor);
  • a SIGNOUT flow (SessionSignOutExecutor);
  • a resource server; and
  • an application linked to both flows with a registered postLogoutRedirectUris.

TestSSOSessionReuseSkipsAuthentication — after a first credential login establishes the session, a second authorize completes on its initial /flow/execute step (assertion issued) without prompting, proving the SSO skip.

TestRPInitiatedLogoutEndsSession — drives the full RP-initiated logout: POST /oauth2/logout (valid id_token_hint + registered post_logout_redirect_uri) → run the sign-out flow → /oauth2/logout/callback returns the post-logout redirect with state; then asserts the SSO cookie is cleared and a fresh authorize re-prompts for credentials.

Related Issues

Related PRs

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • Tests
    • Added end-to-end coverage for RP-initiated logout, including redirect validation, state handling, sign-out completion, and clearing of SSO cookies.
    • Added coverage confirming SSO session reuse can bypass an additional authentication prompt when re-running an authorization.
    • Added a comprehensive OAuth/SSO lifecycle integration test suite with full flow execution (login, authorization, token exchange) and automated cleanup of created test resources.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@madurangasiriwardena, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cbc262a8-a577-4bd4-ac04-9314029b0cd8

📥 Commits

Reviewing files that changed from the base of the PR and between 8321f3b and 9b0d128.

📒 Files selected for processing (4)
  • backend/dbscripts/runtimedb/postgres.sql
  • tests/integration/oauth/sso/rp_logout_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go
📝 Walkthrough

Walkthrough

Adds a live-server SSO integration suite covering fixture lifecycle, authorization-code login, per-flow SSO reuse, and RP-initiated logout with redirect validation, cookie clearing, and session termination.

Changes

SSO session lifecycle

Layer / File(s) Summary
Fixture and flow setup
tests/integration/oauth/sso/suite_test.go
Defines authentication and sign-out flows, provisions OAuth fixtures and users, configures cleanup, and creates cookie-aware session clients.
Authorization and SSO reuse
tests/integration/oauth/sso/suite_test.go, tests/integration/oauth/sso/sso_reuse_test.go
Implements authorization, flow execution, callback completion, token exchange, initial login, and reuse assertions that skip credential prompting.
RP-initiated logout
tests/integration/oauth/sso/rp_logout_test.go
Exercises logout initiation, sign-out completion, post-logout redirect and state validation, cookie clearing, and subsequent authorization without the terminated session.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RPTestClient
  participant LogoutEndpoint
  participant SignOutFlow
  participant LogoutCallback
  RPTestClient->>LogoutEndpoint: POST id_token_hint, redirect URI, and state
  LogoutEndpoint-->>RPTestClient: 302 redirect with executionId and logoutId
  RPTestClient->>SignOutFlow: Execute sign-out steps
  SignOutFlow-->>RPTestClient: COMPLETE and cleared SSO cookies
  RPTestClient->>LogoutCallback: POST logoutId
  LogoutCallback-->>RPTestClient: Validated redirect_uri and state
Loading

Possibly related PRs

  • thunder-id/thunderid#3779: Adds the flow-centric browser SSO implementation exercised by the session reuse and cookie lifecycle tests.
  • thunder-id/thunderid#3973: Adds the session sign-out behavior exercised by the RP-initiated logout and cookie-clearing tests.

Suggested reviewers: rajithacharith

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: new integration tests for SSO session reuse and RP-initiated logout.
Description check ✅ Passed The description follows the template and includes Purpose, Approach, Related Issues, Related PRs, Checklist, and Security checks with relevant details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integration/oauth/sso/rp_logout_test.go`:
- Around line 64-66: Strengthen the assertion in the reauthorization flow using
reAuthStep: verify the exact credential-prompt status rather than only asserting
it is not COMPLETE, and validate the expected prompt metadata (or complete a
second credential login) to prove logout prevented SSO skipping.

In `@tests/integration/oauth/sso/suite_test.go`:
- Around line 268-277: Update the teardown cleanup block to delete the user type
identified by ts.entityTypeID before deleting the organization unit identified
by testOUID. Preserve the existing error logging and cleanup conditions for both
deletions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 566b6a35-f103-4f72-ae60-4ec4478bec6c

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4e926 and 6e3452e.

📒 Files selected for processing (3)
  • tests/integration/oauth/sso/rp_logout_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go

Comment thread tests/integration/oauth/sso/rp_logout_test.go Outdated
Comment thread tests/integration/oauth/sso/suite_test.go Outdated
@madurangasiriwardena madurangasiriwardena added skip-changelog Skip generating changelog for a particular PR and removed Type/Task labels Jul 16, 2026
@madurangasiriwardena
madurangasiriwardena force-pushed the add-sso-logout-integration-tests branch from 6e3452e to 8321f3b Compare July 16, 2026 14:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integration/oauth/sso/suite_test.go`:
- Around line 265-278: Update the cleanup logic in the suite teardown to use
ts.T().Errorf instead of ts.T().Logf for failures from testutils.DeleteUser and
testutils.DeleteOrganizationUnit, so persistent fixture cleanup failures fail
the suite. Keep the existing error messages and DeleteUserType handling
unchanged.
- Around line 29-40: Update newSessionClient to configure a finite HTTP client
timeout, ensuring stalled SSO requests terminate before the outer test timeout
while preserving the existing client and cookie-jar setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a8d0568-bcbf-49bf-9679-631715c5dbec

📥 Commits

Reviewing files that changed from the base of the PR and between 6e3452e and 8321f3b.

📒 Files selected for processing (3)
  • tests/integration/oauth/sso/rp_logout_test.go
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/suite_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/integration/oauth/sso/sso_reuse_test.go
  • tests/integration/oauth/sso/rp_logout_test.go

Comment on lines +29 to +40
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"testing"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
ast-grep outline tests/integration/oauth/sso/suite_test.go \
  --match newSessionClient --view expanded

rg -n -C3 --glob '*.go' \
  '\bnewSessionClient\s*\(|\bTimeout\s*:' tests/integration/oauth/sso

Repository: thunder-id/thunderid

Length of output: 2469


🏁 Script executed:

#!/bin/bash
sed -n '360,390p' tests/integration/oauth/sso/suite_test.go

Repository: thunder-id/thunderid

Length of output: 1261


Add an HTTP client timeout to newSessionClient. A stalled request can block the SSO integration suite until the outer test timeout.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/oauth/sso/suite_test.go` around lines 29 - 40, Update
newSessionClient to configure a finite HTTP client timeout, ensuring stalled SSO
requests terminate before the outer test timeout while preserving the existing
client and cookie-jar setup.

Source: Linters/SAST tools

Comment on lines +265 to +278
for _, userID := range ts.userIDs {
if err := testutils.DeleteUser(userID); err != nil {
ts.T().Logf("Failed to delete test user %s: %v", userID, err)
}
}
if ts.entityTypeID != "" {
if err := testutils.DeleteUserType(ts.entityTypeID); err != nil {
ts.T().Errorf("Failed to delete test user type: %v", err)
}
}
if testOUID != "" {
if err := testutils.DeleteOrganizationUnit(testOUID); err != nil {
ts.T().Logf("Failed to delete test organization unit %s: %v", testOUID, err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail the suite when cleanup leaves persistent fixtures behind.

Using Logf for user or OU deletion failures lets the suite pass while leaking fixed-name resources that can break subsequent runs.

Proposed fix
 	for _, userID := range ts.userIDs {
 		if err := testutils.DeleteUser(userID); err != nil {
-			ts.T().Logf("Failed to delete test user %s: %v", userID, err)
+			ts.T().Errorf("Failed to delete test user %s: %v", userID, err)
 		}
 	}
...
 	if testOUID != "" {
 		if err := testutils.DeleteOrganizationUnit(testOUID); err != nil {
-			ts.T().Logf("Failed to delete test organization unit %s: %v", testOUID, err)
+			ts.T().Errorf("Failed to delete test organization unit %s: %v", testOUID, err)
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, userID := range ts.userIDs {
if err := testutils.DeleteUser(userID); err != nil {
ts.T().Logf("Failed to delete test user %s: %v", userID, err)
}
}
if ts.entityTypeID != "" {
if err := testutils.DeleteUserType(ts.entityTypeID); err != nil {
ts.T().Errorf("Failed to delete test user type: %v", err)
}
}
if testOUID != "" {
if err := testutils.DeleteOrganizationUnit(testOUID); err != nil {
ts.T().Logf("Failed to delete test organization unit %s: %v", testOUID, err)
}
for _, userID := range ts.userIDs {
if err := testutils.DeleteUser(userID); err != nil {
ts.T().Errorf("Failed to delete test user %s: %v", userID, err)
}
}
if ts.entityTypeID != "" {
if err := testutils.DeleteUserType(ts.entityTypeID); err != nil {
ts.T().Errorf("Failed to delete test user type: %v", err)
}
}
if testOUID != "" {
if err := testutils.DeleteOrganizationUnit(testOUID); err != nil {
ts.T().Errorf("Failed to delete test organization unit %s: %v", testOUID, err)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/oauth/sso/suite_test.go` around lines 265 - 278, Update the
cleanup logic in the suite teardown to use ts.T().Errorf instead of ts.T().Logf
for failures from testutils.DeleteUser and testutils.DeleteOrganizationUnit, so
persistent fixture cleanup failures fail the suite. Keep the existing error
messages and DeleteUserType handling unchanged.

Cover two gaps in integration coverage for the flow-based SSO session
lifecycle: reusing an established per-flow session on a subsequent
authorize (login skip), and ending it via the OIDC RP-initiated logout
end_session_endpoint (sign-out flow, cookie clear, post-logout redirect).

Also add the missing postgres RUNTIME_STORE partition for the 'logout:req'
namespace. The namespace was introduced with the sign-out feature but its
LIST partition was never declared, so persisting a logout request fails on
postgres (no partition of relation "RUNTIME_STORE" found for row). This
surfaced only under the postgres backend, which the new logout test exercises.
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ThaminduDilshan
ThaminduDilshan added this pull request to the merge queue Jul 16, 2026
Merged via the queue into thunder-id:main with commit a52b755 Jul 16, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog Skip generating changelog for a particular PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants