Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

13KB

Story 1.3: Keycloak Realm Configuration & OIDC Integration

Status: done

Story

As a any team member, I want to authenticate using Keycloak single sign-on via OpenID Connect, so that I can securely access the application with my organizational credentials.

Acceptance Criteria

  1. Given a user navigates to the application while unauthenticated When they load any protected route Then they are redirected to the Keycloak login page for the configured realm
  2. Given a user enters valid Keycloak credentials When authentication succeeds Then they receive a JWT access token, are redirected to their role-specific workspace, and the authentication event is logged to the audit service within 5 seconds (NFR7)
  3. Given a user's access token expires When they make an authenticated request Then the refresh token flow silently renews the session or redirects to login if the refresh token is also expired
  4. Given an invalid or expired token is presented to the API When the request is processed Then the API returns 401 Unauthorized and the failed authentication attempt is captured in the audit log
  5. Given the application is deployed When traffic is inspected Then all communication is over TLS 1.2+ (NFR4) and sensitive token data is not exposed in URLs or logs
  6. Given an authenticated user activates the Logout action When confirmed Then the client clears local token storage, calls the Keycloak end_session_endpoint with the id_token_hint to destroy the server-side session, and the user is redirected to the Keycloak login page
  7. Given a logout event occurs When complete Then it is written to the audit log within 5 seconds including actor identity, timestamp (UTC), action type SESSION_LOGOUT, and outcome (NFR7)
  8. Given the Keycloak end_session_endpoint is unreachable during logout When the error occurs Then client-side tokens are still cleared, the user is redirected to the login page, and the failure is logged — no silent partial-logout state is permitted

Tasks / Subtasks

  • Implement story behavior in aligned backend/frontend modules (AC: #1)
    • Add or update API/service/UI components required by the story scope
    • Keep legacy Access entities read-only and route writes to extension-layer structures
  • Cover acceptance criteria #2 in implementation and tests (AC: #2)
    • Add validation/error handling and UX state updates as needed
  • Cover acceptance criteria #3 in implementation and tests (AC: #3)
    • Add validation/error handling and UX state updates as needed
  • Cover acceptance criteria #4 in implementation and tests (AC: #4)
    • Add validation/error handling and UX state updates as needed
  • Cover acceptance criteria #6 in implementation and tests (AC: #6)
    • Add backend /api/auth/logout endpoint that calls Keycloak end_session_endpoint with id_token_hint
    • Add frontend logout handler that calls backend logout endpoint and clears local token storage
  • Cover acceptance criteria #7 in implementation and tests (AC: #7)
    • Emit SESSION_LOGOUT audit event via shared audit service on logout completion
  • Cover acceptance criteria #8 in implementation and tests (AC: #8)
    • Handle Keycloak end-session unreachable: clear client tokens, redirect to login, log failure
  • Validate and document completion evidence
    • Verify build/tests for touched modules
    • Capture changed files and any migration/config implications

Review Findings

  • [Review][Patch] Login completion bypasses server-side audit and role routing [campaign-tracker-client/src/auth/useOidcSession.ts:49]
  • [Review][Patch] Refresh-token expiry leaves stale tokens and does not redirect to Keycloak login [campaign-tracker-client/src/auth/useOidcSession.ts:97]
  • [Review][Patch] OIDC callback sends state but never validates returned state before exchanging the code [campaign-tracker-client/src/auth/useOidcSession.ts:43]
  • [Review][Patch] Logout does not reliably clear the browser Keycloak SSO session [Campaign_Tracker.Server/Authentication/KeycloakTokenClient.cs:69]
  • [Review][Patch] Logout audit actor is derived from an unvalidated anonymous id_token_hint [Campaign_Tracker.Server/Controllers/AuthLogoutController.cs:8]
  • [Review][Patch] Refresh can drop idToken and make required Keycloak logout/audit fail [campaign-tracker-client/src/App.tsx:17]
  • [Review][Patch] OIDC/TLS defaults allow HTTP metadata, token exchange, and redirect URLs outside a local-only guard [Campaign_Tracker.Server/appsettings.json:11]
  • [Review][Patch] Keycloak-unreachable logout test depends on a real configured endpoint instead of a throwing stub [Campaign_Tracker.Server.Tests/AuthEndpointTests.cs:150]
  • [Review][Patch] In-memory audit entries are enqueued before durable audit persistence succeeds [Campaign_Tracker.Server/Authentication/InMemoryAuthenticationAuditStore.cs:22]

Dev Notes

  • Follow Epic 1 architecture constraints: ASP.NET Core + React separation, RBAC-aware patterns, and immutable legacy tables.
  • Reuse shared component and workflow patterns defined in UX and architecture docs; avoid parallel custom implementations.
  • Keep changes scoped to this story; do not pull forward Epic 2+ features.

Project Structure Notes

  • Backend: Campaign_Tracker.Server/
  • Frontend: campaign-tracker-client/
  • Story artifacts: _bmad-output/implementation-artifacts/

References

  • Story source: _bmad-output/planning-artifacts/epics.md (Epic 1 / Story 1.3)
  • Architecture constraints: _bmad-output/planning-artifacts/architecture.md
  • UX patterns: _bmad-output/planning-artifacts/ux-design-specification.md

Dev Agent Record

Agent Model Used

GPT-5 Codex

Debug Log References

  • Story generated from epic source and architecture/UX planning artifacts.
  • 2026-05-05: dotnet test .\Campaign_Tracker.Server.Tests\Campaign_Tracker.Server.Tests.csproj passed (4 tests).
  • 2026-05-05: dotnet build .\campaign-tracker.sln passed with 0 warnings and 0 errors.
  • 2026-05-05: npm test passed (2 files, 10 tests).
  • 2026-05-05: npm run build passed; Vite reported a large chunk warning for the existing Ant Design bundle.
  • 2026-05-05: npm run lint passed.
  • 2026-05-05: dotnet build .\campaign-tracker.sln /p:UseAppHost=false passed with 0 warnings and 0 errors after Canopy Keycloak config alignment.
  • 2026-05-05: dotnet test .\Campaign_Tracker.Server.Tests\Campaign_Tracker.Server.Tests.csproj /p:UseAppHost=false passed (4 tests) after Canopy Keycloak config alignment.
  • 2026-05-05: dotnet test .\Campaign_Tracker.Server.Tests\Campaign_Tracker.Server.Tests.csproj /p:UseAppHost=false passed (5 tests) after adding .env ClientSecret override support.
  • 2026-05-05: dotnet build .\campaign-tracker.sln /p:UseAppHost=false passed with 0 warnings and 0 errors after adding .env ClientSecret override support.
  • 2026-05-05: dotnet test .\Campaign_Tracker.Server.Tests\Campaign_Tracker.Server.Tests.csproj /p:UseAppHost=false passed (6 tests) after moving authorization-code and refresh-token exchange behind server endpoints.
  • 2026-05-05: npm test passed (2 files, 12 tests) after moving frontend token exchange to backend endpoints.
  • 2026-05-05: dotnet build .\campaign-tracker.sln /p:UseAppHost=false passed with 0 warnings and 0 errors after token exchange fix.
  • 2026-05-05: npm run build passed after token exchange fix; Vite reported the existing Ant Design large chunk warning.

Completion Notes List

  • Story context created and marked ready-for-dev.
  • Implemented API JWT bearer authentication configured for the Keycloak realm/audience, with /api/auth/session protected by authorization and returning the authenticated user's role-specific workspace path.
  • Added authentication audit capture for successful token validation and invalid bearer-token failures without recording sensitive token values.
  • Added frontend Keycloak authorization-code redirect handling, callback code exchange, session token storage, role workspace routing, and pre-request refresh-token renewal.
  • Protected the React operations shell so unauthenticated users redirect to Keycloak and callback URLs are cleaned after code exchange to avoid exposing token material in URLs.
  • No legacy Access write path was introduced; this story only adds authentication/session behavior and configuration.
  • Aligned configuration with the deployed Canopy realm shape: allowed origins, internal metadata address, public/valid issuer, canopy-web client ID, server-only client secret placeholder, and disabled HTTPS metadata for the current HTTP Keycloak endpoint.
  • Added a server-side .env configuration loader so Keycloak__ClientSecret overrides the appsettings.Development.json placeholder at startup without exposing the secret to the React client.
  • Moved authorization-code and refresh-token exchange to backend endpoints so confidential-client authentication uses the server-side Keycloak client secret instead of failing in the browser callback.
  • Added Logout to AuthenticationAuditEventType and RecordLogout(subject, succeeded, traceId) to IAuthenticationAuditStore / InMemoryAuthenticationAuditStore.
  • Added EndSessionAsync(idTokenHint) to IKeycloakTokenClient / KeycloakTokenClient — POSTs to Keycloak {authority}/protocol/openid-connect/logout with id_token_hint, client_id, client_secret.
  • Added id_token to KeycloakTokenResponse and IdToken? to AuthTokenSetResponse so the id_token flows from Keycloak to the frontend.
  • Created AuthLogoutController at POST /api/auth/logout (AllowAnonymous): calls EndSessionAsync, records SESSION_LOGOUT audit event regardless of Keycloak availability, always returns 200 per AC #8.
  • Added idToken?: string to AuthTokenSet frontend type; updated requestTokenSet deserialization to include idToken.
  • Added logout(idTokenHint, storage?) to authContracts.ts — calls backend endpoint, clears token storage in finally so tokens are always cleared even when Keycloak is unreachable.
  • Added 3 backend integration tests (valid stub, unreachable Keycloak, empty hint) and 3 frontend unit tests (success, network error, server error).
  • All 14 backend tests and 18 frontend tests pass; lint and build clean.

File List

  • Campaign_Tracker.Server/Controllers/AuthLogoutController.cs
  • Campaign_Tracker.Server.Tests/AuthEndpointTests.cs
  • Campaign_Tracker.Server.Tests/Campaign_Tracker.Server.Tests.csproj
  • Campaign_Tracker.Server.Tests/DotEnvConfigurationTests.cs
  • Campaign_Tracker.Server.Tests/KeycloakTokenClientTests.cs
  • Campaign_Tracker.Server/Authentication/AuthenticationAuditEvent.cs
  • Campaign_Tracker.Server/Authentication/IAuthenticationAuditStore.cs
  • Campaign_Tracker.Server/Authentication/InMemoryAuthenticationAuditStore.cs
  • Campaign_Tracker.Server/Authentication/KeycloakTokenClient.cs
  • Campaign_Tracker.Server/Authentication/KeycloakOptions.cs
  • Campaign_Tracker.Server/Authentication/RoleWorkspaceResolver.cs
  • Campaign_Tracker.Server/Configuration/DotEnvConfiguration.cs
  • Campaign_Tracker.Server/Controllers/AuthSessionController.cs
  • Campaign_Tracker.Server/Controllers/AuthTokenController.cs
  • Campaign_Tracker.Server/Campaign_Tracker.Server.csproj
  • Campaign_Tracker.Server/Program.cs
  • Campaign_Tracker.Server/appsettings.Development.json
  • Campaign_Tracker.Server/appsettings.json
  • _bmad-output/implementation-artifacts/1-3-keycloak-realm-configuration-oidc-integration.md
  • _bmad-output/implementation-artifacts/sprint-status.yaml
  • campaign-tracker-client/src/App.css
  • campaign-tracker-client/src/App.tsx
  • campaign-tracker-client/src/auth/authContracts.test.ts
  • campaign-tracker-client/src/auth/authContracts.ts
  • campaign-tracker-client/src/auth/useOidcSession.ts
  • campaign-tracker-client/src/workspace/WorkspaceShell.tsx

Change Log

Date Version Description Author
2026-05-05 1.0 Implemented Keycloak OIDC integration, JWT-protected API session endpoint, auth audit capture, frontend protected route/callback/refresh handling, and validation tests. GPT-5 Codex
2026-05-05 1.1 Aligned Keycloak and CORS configuration with Canopy deployment values. GPT-5 Codex
2026-05-05 1.2 Added server-side .env loading so Keycloak client secret overrides development placeholder at startup. GPT-5 Codex
2026-05-05 1.3 Moved Keycloak token exchange and refresh behind backend endpoints for confidential-client login. GPT-5 Codex
2026-05-05 1.4 Implemented logout ACs (6, 7, 8): AuthLogoutController, EndSessionAsync, SESSION_LOGOUT audit, idToken in token response, frontend logout() contract with injectable storage. 14/14 backend tests + 18/18 frontend tests passing. Amelia (Dev)

Powered by TurnKey Linux.