# Story 1.3: Keycloak Realm Configuration & OIDC Integration Status: review ## 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 ## Tasks / Subtasks - [x] Implement story behavior in aligned backend/frontend modules (AC: #1) - [x] Add or update API/service/UI components required by the story scope - [x] Keep legacy Access entities read-only and route writes to extension-layer structures - [x] Cover acceptance criteria #2 in implementation and tests (AC: #2) - [x] Add validation/error handling and UX state updates as needed - [x] Cover acceptance criteria #3 in implementation and tests (AC: #3) - [x] Add validation/error handling and UX state updates as needed - [x] Cover acceptance criteria #4 in implementation and tests (AC: #4) - [x] Add validation/error handling and UX state updates as needed - [x] Validate and document completion evidence - [x] Verify build/tests for touched modules - [x] Capture changed files and any migration/config implications ## 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. ### File List - `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 |