Skip to content

WEB-551 Fix Oauth (Keycloak)#2967

Open
JaySoni1 wants to merge 1 commit intoopenMF:devfrom
JaySoni1:WEB-551-fix-oauth-keycloak
Open

WEB-551 Fix Oauth (Keycloak)#2967
JaySoni1 wants to merge 1 commit intoopenMF:devfrom
JaySoni1:WEB-551-fix-oauth-keycloak

Conversation

@JaySoni1
Copy link
Contributor

@JaySoni1 JaySoni1 commented Jan 5, 2026

Changes Made :-

  • Added backward compatibility mapping for legacy MIFOS_OAUTH_* environment variables
  • Maps old Keycloak OAuth configuration to new environment.oauth.* format
  • Implemented smart Keycloak realm detection with configurable MIFOS_OAUTH_REALM support
  • Auto-derives OAuth2 authorize/token endpoints when not explicitly provided
  • Ensures existing Keycloak deployments work without configuration changes

WEB :- 551

Summary by CodeRabbit

  • New Features

    • Keycloak-aware OAuth2 setup with automatic endpoint and realm resolution for smoother sign-in flows
  • Improvements

    • Safer default redirect URI and OAuth scopes for better out-of-the-box behavior
    • Backward compatibility with legacy OAuth environment variables to ease configuration migration

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'pre_merge_checks'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Adds Keycloak realm-aware OAuth2/OIDC resolution and computed defaults in the auth config, and extends environment files to accept legacy MIFOS_OAUTH_* variables plus default redirect URI and default scope fallbacks.

Changes

Cohort / File(s) Summary
OAuth2 Configuration Logic
src/app/core/authentication/oauth.config.ts
Introduces Keycloak realm resolution (uses MIFOS_OAUTH_REALM, default master) and computes resolvedAuthorizeUrl, resolvedTokenUrl, resolvedRedirectUri, resolvedScope, and issuerUrl when explicit endpoints are absent. Maps computed values to public config fields (issuer, loginUrl, tokenEndpoint, redirectUri, postLogoutRedirectUri, clientId/appId, scope) and sets skipIssuerCheck: true and strictDiscoveryDocumentValidation: false.
Environment Configuration
src/environments/environment.ts, src/environments/environment.prod.ts
Adds backward-compatibility for legacy MIFOS_OAUTH_* env vars: oauth.enabled accepts MIFOS_OAUTH_SERVER_ENABLED, oauth.serverUrl/appId may come from legacy vars, redirectUri defaults to ${window.location.origin}/#/callback if missing, and scope defaults to 'openid profile email'.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • IOhacker
  • adamsaghy

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely describes the main change—fixing OAuth (Keycloak) configuration—and directly aligns with the PR's primary objectives of adding backward compatibility and Keycloak realm detection.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Fix all issues with AI Agents 🤖
In @src/app/core/authentication/oauth.config.ts:
- Line 79: The current OAuth config sets issuer to serverUrl; update the issuer
to the Keycloak realm URL by using the realm-based value (e.g.,
`${normalizedServerUrl}/auth/realms/${keycloakRealm}` or if you didn't apply
normalization, `${serverUrl}/auth/realms/${keycloakRealm}`) so the issuer symbol
points to the realm endpoint rather than the root server; keep or remove
skipIssuerCheck as desired, but ensure issuer is corrected in the object where
issuer: serverUrl is set so OAuth2 semantics match Keycloak.
- Around line 68-76: Validate and normalize environment.oauth.serverUrl before
constructing endpoints: ensure serverUrl is non-empty (throw or fallback if
missing), trim trailing slashes, and ensure it includes a scheme (e.g., add
'https://' if absent) so that resolvedAuthorizeUrl, resolvedTokenUrl and any
derived URLs are well-formed; update the logic around keycloakRealm,
resolvedAuthorizeUrl and resolvedTokenUrl to use the normalized serverUrl and
return or log a clear error when serverUrl is invalid.
🧹 Nitpick comments (1)
src/environments/environment.ts (1)

34-44: Prefer consistent property access notation across environment files.

The dev environment uses dot notation (loadedEnv.MIFOS_OAUTH_SERVER_ENABLED) while the production environment uses bracket notation (loadedEnv['MIFOS_OAUTH_SERVER_ENABLED']) for the same properties. While both work, consistent notation improves maintainability.

🔎 Proposed fix for consistency
   oauth: {
     // Support legacy MIFOS_OAUTH_* variable names for backward compatibility with Keycloak
-    enabled: loadedEnv.oauthServerEnabled === true || loadedEnv.MIFOS_OAUTH_SERVER_ENABLED === 'true',
-    serverUrl: loadedEnv.oauthServerUrl || loadedEnv.MIFOS_OAUTH_SERVER_URL || '',
+    enabled: loadedEnv.oauthServerEnabled === true || loadedEnv['MIFOS_OAUTH_SERVER_ENABLED'] === 'true',
+    serverUrl: loadedEnv.oauthServerUrl || loadedEnv['MIFOS_OAUTH_SERVER_URL'] || '',
     logoutUrl: loadedEnv.oauthServerLogoutUrl || '',
-    appId: loadedEnv.oauthAppId || loadedEnv.MIFOS_OAUTH_CLIENT_ID || '',
+    appId: loadedEnv.oauthAppId || loadedEnv['MIFOS_OAUTH_CLIENT_ID'] || '',
     authorizeUrl: loadedEnv.oauthAuthorizeUrl || '',
     tokenUrl: loadedEnv.oauthTokenUrl || '',
     redirectUri: loadedEnv.oauthRedirectUri || `${window.location.origin}/#/callback`,
     scope: loadedEnv.oauthScope || 'openid profile email'
   },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8531dc and 00504ea.

📒 Files selected for processing (3)
  • src/app/core/authentication/oauth.config.ts
  • src/environments/environment.prod.ts
  • src/environments/environment.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**

⚙️ CodeRabbit configuration file

src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.

Files:

  • src/app/core/authentication/oauth.config.ts
🧬 Code graph analysis (1)
src/app/core/authentication/oauth.config.ts (3)
src/environments/environment.prod.ts (1)
  • environment (7-82)
src/environments/environment.ts (1)
  • environment (12-86)
src/app/settings/settings.service.ts (1)
  • serverUrl (175-177)
🔇 Additional comments (2)
src/environments/environment.prod.ts (1)

28-38: LGTM! Backward compatibility implementation looks solid.

The legacy environment variable mapping is well-implemented with appropriate fallback chains and sensible defaults for redirect URI and scope.

src/app/core/authentication/oauth.config.ts (1)

88-96: No action needed—validation flags are appropriate for OAuth2 mode, not workarounds.

The issuer is correctly set to serverUrl (line 79), and the skipIssuerCheck: true and strictDiscoveryDocumentValidation: false flags are legitimate settings for OAuth2 (non-OIDC) mode. The angular-oauth2-oidc library is designed for OIDC but supports OAuth2 without full OIDC compliance. For Keycloak OAuth2 integration, these relaxations are necessary and appropriate. The code already documents why these flags are required.

@JaySoni1 JaySoni1 force-pushed the WEB-551-fix-oauth-keycloak branch from 00504ea to 8d5f0fa Compare January 5, 2026 05:39
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @src/environments/environment.ts:
- Around line 35-36: The enabled assignment in environment.ts mixes boolean and
string checks causing inconsistent behavior; update the enabled expression for
oauthServerEnabled to accept both boolean true and the string "true" (and do the
same for the legacy MIFOS_OAUTH_SERVER_ENABLED) following the OIDC block pattern
— e.g. coerce values to strings or explicitly check both types for
loadedEnv.oauthServerEnabled and loadedEnv.MIFOS_OAUTH_SERVER_ENABLED when
setting the enabled property so both new and legacy env vars are handled
consistently.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/core/authentication/oauth.config.ts (1)

96-98: Clarify that security-relaxed flags are in the OAuth2 (non-OIDC) path only; recommend OIDC mode for Keycloak production.

The skipIssuerCheck and strictDiscoveryDocumentValidation flags appear in getOAuth2Config() (lines 96–98) with oidc: false, which is appropriate for OAuth2-only flows. However, for Keycloak in production, prefer the getOIDCConfig() path (oidc: true), which applies strict validation defaults and does not override these flags. If using pure OAuth2 mode with custom endpoints, document the specific provider constraints that justify disabling discovery validation.

♻️ Duplicate comments (1)
src/app/core/authentication/oauth.config.ts (1)

70-79: Missing validation for empty serverUrl can produce malformed OAuth endpoints.

When serverUrl is empty (which is the default in environment.ts), normalizedServerUrl becomes '', and the resolved URLs become malformed (e.g., /auth/realms/master/protocol/openid-connect/auth). This will cause OAuth flows to fail silently or redirect incorrectly.

Consider adding validation or a guard when OAuth2 is enabled but serverUrl is not configured.

🔎 Proposed fix with validation
 function getOAuth2Config(): AuthConfig {
   const frontendUrl = window.location.origin;
   const { serverUrl, authorizeUrl, tokenUrl, redirectUri, scope, appId } = environment.oauth;
-  const normalizedServerUrl = serverUrl?.replace(/\/$/, '') || '';
+  const normalizedServerUrl = serverUrl?.trim().replace(/\/$/, '') || '';
+
+  // Warn if OAuth2 is enabled but serverUrl is missing (unless custom endpoints are provided)
+  if (!normalizedServerUrl && (!authorizeUrl || !tokenUrl)) {
+    console.warn('OAuth2 is enabled but serverUrl is not configured. OAuth flows may fail.');
+  }

   // Allow custom Keycloak realm via MIFOS_OAUTH_REALM (defaults to master)
   const keycloakRealm = (window as any)['env']?.['MIFOS_OAUTH_REALM'] || 'master';
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00504ea and 8d5f0fa.

📒 Files selected for processing (3)
  • src/app/core/authentication/oauth.config.ts
  • src/environments/environment.prod.ts
  • src/environments/environment.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/environments/environment.prod.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**

⚙️ CodeRabbit configuration file

src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.

Files:

  • src/app/core/authentication/oauth.config.ts
🧬 Code graph analysis (1)
src/app/core/authentication/oauth.config.ts (3)
src/environments/environment.prod.ts (1)
  • environment (7-82)
src/environments/environment.ts (1)
  • environment (12-86)
src/app/settings/settings.service.ts (1)
  • serverUrl (175-177)
🔇 Additional comments (2)
src/environments/environment.ts (1)

37-43: Backward compatibility and defaults look good.

The fallback chain for serverUrl, appId, redirectUri, and scope provides sensible defaults and maintains backward compatibility with legacy MIFOS_OAUTH_* environment variables. The default scope 'openid profile email' is appropriate for standard OIDC/OAuth2 flows.

src/app/core/authentication/oauth.config.ts (1)

81-92: Issuer URL logic correctly handles Keycloak realm semantics.

The conditional issuer logic appropriately uses the realm-based URL (${normalizedServerUrl}/auth/realms/${keycloakRealm}) for standard Keycloak deployments while falling back to normalizedServerUrl when custom authorizeUrl is provided. This addresses proper OAuth2 semantics for Keycloak.

@JaySoni1 JaySoni1 force-pushed the WEB-551-fix-oauth-keycloak branch from 8d5f0fa to fc9a888 Compare January 5, 2026 05:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/app/core/authentication/oauth.config.ts (1)

70-70: Validate serverUrl to prevent malformed OAuth endpoint URLs.

The normalization on Line 70 removes trailing slashes but doesn't validate that serverUrl is non-empty or includes a scheme. If serverUrl is empty, the constructed OAuth endpoints will be relative URLs (e.g., /auth/realms/master/protocol/openid-connect/auth) without a host, causing OAuth flow failures.

🔎 Proposed fix with validation and normalization
 function getOAuth2Config(): AuthConfig {
   const frontendUrl = window.location.origin;
   const { serverUrl, authorizeUrl, tokenUrl, redirectUri, scope, appId } = environment.oauth;
-  const normalizedServerUrl = serverUrl?.replace(/\/$/, '') || '';
+  
+  // Validate and normalize serverUrl
+  const trimmedServerUrl = serverUrl?.trim() || '';
+  if (!trimmedServerUrl) {
+    throw new Error('OAuth2 serverUrl is required but was not provided. Please configure environment.oauth.serverUrl.');
+  }
+  const normalizedServerUrl = trimmedServerUrl.replace(/\/$/, '');

   // Allow custom Keycloak realm via MIFOS_OAUTH_REALM (defaults to master)
   const keycloakRealm = (window as any)['env']?.['MIFOS_OAUTH_REALM'] || 'master';
   const resolvedAuthorizeUrl =
     authorizeUrl || `${normalizedServerUrl}/auth/realms/${keycloakRealm}/protocol/openid-connect/auth`;
   const resolvedTokenUrl =
     tokenUrl || `${normalizedServerUrl}/auth/realms/${keycloakRealm}/protocol/openid-connect/token`;

Based on learnings from past review comments.

Also applies to: 74-77

🧹 Nitpick comments (1)
src/app/core/authentication/oauth.config.ts (1)

81-82: Consider clarifying the issuer logic for better maintainability.

The issuer logic is functionally correct and addresses the past review comment about setting the correct Keycloak realm URL. However, the conditional logic could be clearer:

  • When authorizeUrl is provided (custom OAuth provider): issuer = normalizedServerUrl
  • When authorizeUrl is NOT provided (Keycloak default): issuer = realm URL
🔎 Suggested refactor for improved clarity
-  // For Keycloak, issuer should be the realm URL for correct OAuth2 semantics
-  const issuerUrl = authorizeUrl ? normalizedServerUrl : `${normalizedServerUrl}/auth/realms/${keycloakRealm}`;
+  // Determine issuer based on OAuth provider type:
+  // - Custom OAuth provider (custom authorizeUrl provided): use serverUrl as issuer
+  // - Keycloak (default endpoints): use realm URL as issuer for correct OAuth2 semantics
+  const issuerUrl = authorizeUrl
+    ? normalizedServerUrl
+    : `${normalizedServerUrl}/auth/realms/${keycloakRealm}`;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d5f0fa and fc9a888.

📒 Files selected for processing (3)
  • src/app/core/authentication/oauth.config.ts
  • src/environments/environment.prod.ts
  • src/environments/environment.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/environments/environment.prod.ts
  • src/environments/environment.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**

⚙️ CodeRabbit configuration file

src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.

Files:

  • src/app/core/authentication/oauth.config.ts
🧬 Code graph analysis (1)
src/app/core/authentication/oauth.config.ts (3)
src/environments/environment.prod.ts (1)
  • environment (7-85)
src/environments/environment.ts (1)
  • environment (12-89)
src/app/settings/settings.service.ts (1)
  • serverUrl (175-177)
🔇 Additional comments (3)
src/app/core/authentication/oauth.config.ts (3)

64-66: LGTM!

The updated comment accurately reflects the function's support for both Fineract and Keycloak OAuth2 providers.


85-92: LGTM!

The return statement correctly uses all the resolved values with appropriate defaults. The configuration structure is correct for the angular-oauth2-oidc library, and the OAuth2-specific settings (skipIssuerCheck: true, strictDiscoveryDocumentValidation: false, oidc: false) are appropriate for non-OIDC OAuth2 flows.


72-73: Code correctly implements Keycloak realm detection with consistent usage.

The keycloakRealm variable is properly used in all three required OAuth endpoint constructions:

  • authorize endpoint (line 75)
  • token endpoint (line 77)
  • issuer URL (line 82)

The optional chaining and 'master' fallback are appropriate for this runtime configuration pattern. Type assertion is justified for the non-TypeScript window.env pattern.

redirectUri: loadedEnv.oauthRedirectUri || '',
scope: loadedEnv.oauthScope || ''
redirectUri: loadedEnv.oauthRedirectUri || `${window.location.origin}/#/callback`,
scope: loadedEnv.oauthScope || 'openid profile email'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please dont mix oauth and openid together!

redirectUri: loadedEnv.oauthRedirectUri || '',
scope: loadedEnv.oauthScope || ''
redirectUri: loadedEnv.oauthRedirectUri || `${window.location.origin}/#/callback`,
scope: loadedEnv.oauthScope || 'openid profile email'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please dont mix oauth and openid together!

const normalizedServerUrl = serverUrl?.replace(/\/$/, '') || '';

// Allow custom Keycloak realm via MIFOS_OAUTH_REALM (defaults to master)
const keycloakRealm = (window as any)['env']?.['MIFOS_OAUTH_REALM'] || 'master';
Copy link
Collaborator

Choose a reason for hiding this comment

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

I dont think we should have hardcoded dependencies for authentication implementation. Keep it generic and avoid any hardcoding!

// Allow custom Keycloak realm via MIFOS_OAUTH_REALM (defaults to master)
const keycloakRealm = (window as any)['env']?.['MIFOS_OAUTH_REALM'] || 'master';
const resolvedAuthorizeUrl =
authorizeUrl || `${normalizedServerUrl}/auth/realms/${keycloakRealm}/protocol/openid-connect/auth`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do not mix oauth and openid and hardcode any URI!

const resolvedAuthorizeUrl =
authorizeUrl || `${normalizedServerUrl}/auth/realms/${keycloakRealm}/protocol/openid-connect/auth`;
const resolvedTokenUrl =
tokenUrl || `${normalizedServerUrl}/auth/realms/${keycloakRealm}/protocol/openid-connect/token`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as above

const resolvedScope = scope || 'openid profile email';

// For Keycloak, issuer should be the realm URL for correct OAuth2 semantics
const issuerUrl = authorizeUrl ? normalizedServerUrl : `${normalizedServerUrl}/auth/realms/${keycloakRealm}`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Dont hardcode anything for keycloak please. Keycloak is one of many implementation!

Copy link
Collaborator

@adamsaghy adamsaghy left a comment

Choose a reason for hiding this comment

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

I think the UI project should NOT:
mix OAuth and OpenId!
Hardcode any URI and anything that is for a particular implementation (like keycloak)
use "keycloak" and "openId" specific configuration

@adamsaghy adamsaghy requested a review from IOhacker January 5, 2026 13:18
Copy link
Collaborator

@alberto-art3ch alberto-art3ch left a comment

Choose a reason for hiding this comment

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

Let's discuss this solution for the different options and scenarios, like with some env variable present one LoginForm or other one

tshumal added a commit to vuka-africa/web-app that referenced this pull request Jan 19, 2026
- Auto-construct Keycloak authorize/token endpoints from serverUrl + realm
- Default realm to 'fineract' if not specified
- Add sensible defaults for redirectUri and scope
- Fix oauth.enabled to handle string 'true' from environment variables
- Add realm property to oauth configuration

Based on upstream PR openMF#2967 (WEB-551 Fix OAuth Keycloak)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Feb 5, 2026

This pull request seems to be stale. Are you still planning to work on it? We will automatically close it in 30 days.

@github-actions github-actions bot added the Stale label Feb 5, 2026
@gkbishnoi07
Copy link
Collaborator

@IOhacker @adamsaghy @alberto-art3ch any updates? can we close this PR?

@github-actions github-actions bot removed the Stale label Feb 16, 2026
@IOhacker
Copy link
Contributor

IOhacker commented Feb 16, 2026 via email

@adamsaghy
Copy link
Collaborator

is there any Apache Fineract updated docs about how to do the setup for using the OAUTH?

I dont recall, but probably we should have...

@adamsaghy
Copy link
Collaborator

@IOhacker I dont see any changes on this PR...

@IOhacker
Copy link
Contributor

@adamsaghy is there any updated doc for setting up a Keycloak instance + Apache Fineract ? so then we can verify

@adamsaghy
Copy link
Collaborator

@adamsaghy is there any updated doc for setting up a Keycloak instance + Apache Fineract ? so then we can verify

We dont have documentation for setting up Keycloak, since that's just one auth server implementation from the many.

Also we are lacking proper documentation for Apache Fineract acting as a resource server only. We are using spring-boot-starter-oauth2-resource-server. The only "extra" that is needed is to add the tenantId into the JWT token:
org.apache.fineract.infrastructure.security.filter.TenantAwareAuthenticationFilter -> claims.getClaim("tenant")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants