Skip to content

Keycloak Phase 1 Implementation & Debugging

Date: 2025-04-21 Participants: Rakesh Gangwar, AI Assistant

Context

Following the decision in 2025-04-21-keycloak-implementation-strategy.md to implement Keycloak from the start, this session focused on Phase 1: Core Authentication setup. The goal was to integrate Keycloak with the FastAPI backend and SvelteKit frontend using Docker Compose.

Implementation Steps

  1. Docker Compose Setup: Added Keycloak service definition to backend/docker-compose.yml.
  2. Backend Integration (FastAPI):
    • Added fastapi-keycloak dependency.
    • Created backend/app/core/auth.py with Keycloak configuration and initialization.
    • Added a protected route (/protected) in backend/app/main.py using Keycloak dependency injection.
    • Created backend/Dockerfile and fixed build issues (uv pip sync --system).
  3. Frontend Integration (SvelteKit):
    • Added oidc-client-ts dependency.
    • Created frontend/src/lib/auth/authService.ts with UserManager configuration, user store, and auth functions (login, logout, handleCallback, getAccessToken).
    • Created frontend/src/lib/components/auth/AuthStatus.svelte to display login status and provide login/logout buttons.
    • Created frontend/src/routes/callback/+page.svelte to handle the OIDC redirect.
    • Added AuthStatus component to the main layout (frontend/src/routes/+layout.svelte).
    • Added frontend/static/silent-renew.html and configured silent_redirect_uri in authService.ts.

Debugging Issues & Solutions

Several issues were encountered during testing:

  1. Backend Docker Build Failure: The initial docker-compose up failed because backend/Dockerfile was empty. A basic Dockerfile was created. A subsequent failure occurred because uv pip sync requires the --system flag when not in a virtual environment.

    • Solution: Added --system flag to RUN uv pip sync --system pyproject.toml in backend/Dockerfile.
  2. Frontend SSR Error (window is not defined): The oidc-client-ts library attempted to access window.localStorage during server-side rendering.

    • Solution: Modified frontend/src/lib/auth/authService.ts to use if (browser) checks, conditionally initializing the UserManager and accessing window only on the client-side. Explicitly typed keycloakSettings with UserManagerSettings.
  3. Endless Token/Userinfo Calls & "User loaded" Logs: After login, the frontend made continuous calls to Keycloak's /token and /userinfo endpoints, and the console filled with "User loaded" messages.

    • Attempt 1: Simplified getAccessToken in authService.ts to rely solely on userManager.getUser() and removed manual signinSilent() from addAccessTokenExpired event handler. (No effect)
    • Attempt 2: Disabled loadUserInfo: true in authService.ts. (Stopped /userinfo calls, but /token loop persisted)
    • Attempt 3: Refactored AuthStatus.svelte to use Svelte's derived stores. (No effect)
    • Attempt 4: Added dedicated silent_redirect_uri and silent-renew.html. (No effect)
    • Attempt 5: Disabled monitorSession: true in authService.ts. (No effect)
    • Attempt 6: Disabled automaticSilentRenew: false in authService.ts.
    • Solution: Disabling automaticSilentRenew stopped the loop. This indicates the root cause is likely related to the automatic renewal process interacting incorrectly with the current setup or Keycloak configuration (e.g., token lifespans).
  4. Callback Page Stuck: The /callback page displayed "Login successful! Redirecting..." but did not navigate away.

    • Initial Attempt: Used SvelteKit's goto('/') in handleCallback. This did not work as expected.
    • Solution (Temporary): Reverted redirection logic in handleCallback to use window.location.href = '/'. This successfully redirected the user. The reason goto failed needs further investigation, but window.location.href is acceptable for now.

Current Status & Next Steps

  • Basic login/logout flow is functional.
  • The token renewal loop is stopped by disabling automaticSilentRenew.
  • Redirection from the callback page works using window.location.href.

Immediate Next Steps:

  1. Restore goto Navigation: Revert the temporary window.location.href back to goto('/') in authService.ts and investigate why it initially failed. Ensure the goto import is present.
  2. Investigate automaticSilentRenew: Research why automaticSilentRenew: true causes a loop. Check Keycloak token lifespans (Access Token, Refresh Token, SSO Session Idle/Max) and compare oidc-client-ts settings against Keycloak best practices. Re-enable automaticSilentRenew once the root cause is found and fixed.
  3. Manual Keycloak Config: Ensure Keycloak clients (fastapi-client, svelte-client) are correctly configured with appropriate redirect URIs (including /callback and /silent-renew.html) and settings (client auth for backend, public for frontend).
  4. Update Secrets: Replace placeholder client secret in backend/app/core/auth.py.

References

  • docs/logs/devlogs/2025-04-21-keycloak-implementation-strategy.md
  • frontend/src/lib/auth/authService.ts
  • frontend/src/routes/callback/+page.svelte
  • backend/Dockerfile