Skip to content

[Devportal] Integrate platform API for local authentication#2201

Open
Piumal1999 wants to merge 5 commits into
wso2:mainfrom
Piumal1999:platform-api-int
Open

[Devportal] Integrate platform API for local authentication#2201
Piumal1999 wants to merge 5 commits into
wso2:mainfrom
Piumal1999:platform-api-int

Conversation

@Piumal1999

Copy link
Copy Markdown
Contributor

Purpose

$subject

Goals

Using platform API for the local auth

Approach

Added platform API to the distribution
Reused the platfrom API's file based auth in the devportal
successful login will return the JWT, and it will be stored in the devportal backend session

Removed support for basic auth for REST APIs. Users has to use the login ednpoint to get a token and use that with the RestAPIs.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 606ab635-0651-4b2e-a14b-d214f1b9822d

📥 Commits

Reviewing files that changed from the base of the PR and between fe859c7 and 0d6875e.

📒 Files selected for processing (6)
  • portals/developer-portal/src/controllers/authController.js
  • portals/developer-portal/src/controllers/devportalController.js
  • portals/developer-portal/src/middlewares/ensureAuthenticated.js
  • portals/developer-portal/src/models/application.js
  • portals/developer-portal/src/openapi/middleware/auth.js
  • portals/developer-portal/src/utils/platformJwt.js
💤 Files with no reviewable changes (1)
  • portals/developer-portal/src/models/application.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • portals/developer-portal/src/middlewares/ensureAuthenticated.js
  • portals/developer-portal/src/controllers/authController.js
  • portals/developer-portal/src/openapi/middleware/auth.js

📝 Walkthrough

Overview

This PR integrates the platform API as a local authentication backend for the developer portal, replacing basic authentication with JWT-based authentication. The platform API serves as the credential validation service, issuing JWT tokens upon successful login that are used for subsequent API requests.

Key Changes

Authentication Architecture

  • Login Flow: Users authenticate through a new platform API endpoint that validates credentials and issues JWT tokens. These tokens are stored in the devportal backend session and returned to clients for use in Bearer token authentication headers.
  • Credential Validation: Moved from local in-memory user lists to the platform API's file-based authentication mechanism, which supports bcrypt-hashed passwords and organization-based scopes.
  • Basic Auth Removal: Eliminated basic authentication support from REST APIs. All API requests now require Bearer tokens obtained from the login endpoint.

Configuration

  • Added config-platform-api.toml.example template defining platform API settings including database path, JWT configuration, TLS certificates, file-based authentication with admin user and organization definitions.
  • Extended configLoader.js with new platformApi configuration section for baseUrl, jwtSecret, and insecure settings.

Docker Compose Updates

Both Docker Compose files now include:

  • New platform-api service running as a containerized backend (port 9243)
  • Dependency ordering so devportal waits for platform-api health checks
  • Configuration for JWT secret sharing between services via AUTH_JWT_SECRET_KEY environment variable
  • Persistent data volume for platform-api
  • Environment variables passed to devportal for platform API connectivity

Authentication Controllers

  • Login Handler: Refactored to POST credentials to the platform API, extract JWT claims, and derive user roles from scopes (admin if any scope ends with _manage). Session is regenerated upon successful login.
  • Logout Handler: Differentiated behavior between local auth users (standard logout) and IDP users (logout redirect to IDP endpoint).

Middleware & Validation

  • ensureAuthenticated.js: Removed basic auth validation. Now extracts access tokens from session or Bearer headers, validates JWT tokens against platform API issuer, and enforces scope requirements.
  • OpenAPI Auth Middleware: Added platform-jwt mode alongside OAuth2, with JWT claim extraction supporting both verified (with shared secret) and unverified token parsing.

Utilities

  • New platformJwt.js utility module exports extractPlatformJwtClaims() for decoding platform-issued JWT tokens and extracting scopes.

Documentation Updates

  • Quick Start: Updated to create both config.yaml and config-platform-api.toml, authenticate CLI commands with Bearer tokens from the platform API, and retrieve organization UUIDs for API operations.
  • Manage Organizations: Replaced basic auth examples (-u admin:admin) with Bearer token authentication across all REST API calls, updated local auth setup instructions to reference the platform API sidecar.

Database Model

  • Updated DP_APPLICATION model to replace inline uniqueness constraint on ORG_ID with a composite unique index on APP_ID + ORG_ID.

Minor Changes

  • .gitignore: Added platform-api config file exclusion.
  • seeder.js: Changed default provider URL to fixed value instead of control plane URL.

Walkthrough

The Developer Portal's local authentication system has been replaced with a Platform API sidecar model. A new config-platform-api.toml.example configuration template provides server, database, JWT auth, file-based auth, and TLS settings. The CONFIG_DEFAULTS object adds a new platformApi section and removes disableScopeValidation from advanced config. Both Docker Compose files introduce a platform-api service on port 9243 with a healthcheck, and devportal now depends on platform-api health with new environment variables for JWT validation (DP_PLATFORMAPI_BASEURL, DP_PLATFORMAPI_JWTSECRET, DP_PLATFORMAPI_INSECURE).

Login controllers (authController.js, devportalController.js) now call the Platform API's /api/portal/v1/auth/login endpoint via axios over HTTPS with a configurable agent, decode the returned JWT to derive roles from dp:* scopes, and store the token in the session. The logout handler now distinguishes local-auth sessions from IDP flows with dedicated branches.

The OpenAPI auth middleware introduces a new extractPlatformJwtClaims helper and a platform-jwt mode. authResolver sets this mode for local sessions and extracts scopes from the session token. OAuth2Security now enforces scopes for both oauth2 and platform-jwt modes without the deprecated disableScopeValidation bypass. Basic-auth handling is removed throughout.

The ensureAuthenticated middleware now validates platform JWT scopes for local sessions via token parsing, removing the previous bypass. The DP_APPLICATION model gains a composite unique index on APP_ID + ORG_ID. The default org seeder hardcodes the WSO2 provider URL. Documentation is updated to reflect the new Platform API login flow, Bearer token authentication for API calls, and local development setup with the sidecar.

Suggested reviewers

  • VirajSalaka
  • AnuGayan
  • renuka-fernando
  • malinthaprasan
  • RakhithaRR
  • chamilaadhi
  • Tharsanan1
  • dushaniw
  • tharindu1st
  • tgtshanika
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers Purpose, Goals, and Approach sections but is missing critical sections from the template including User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, and Test environment. Complete the remaining required sections from the template: User stories, Documentation (with links), Automation tests (unit and integration), Security checks (secure coding, FindSecurityBugs, secret verification), Samples, Related PRs, and Test environment details.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective of the changeset: integrating the platform API for local authentication in the devportal.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

@Piumal1999 Piumal1999 changed the title Integrate platform API for local authentication [Devportal] Integrate platform API for local authentication Jun 15, 2026

@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: 13

Caution

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

⚠️ Outside diff range comments (2)
portals/developer-portal/src/services/seeder.js (1)

111-121: ⚠️ Potential issue | 🟠 Major

Seeder does not update existing provider records during upgrades.

The seeder creates the WSO2 provider only on initial setup. When a provider already exists, createProvider raises UniqueConstraintError (lines 114–120), which is caught and silently ignored—no update occurs. This creates an inconsistency:

  • Fresh installations: WSO2 provider created with providerURL: 'https://wso2.com'
  • Existing installations (upgrades): Provider retains its previous value unchanged

Other seeded data (identity providers, labels, views) use an upsert pattern with explicit checks or findOrCreate. Consider applying a similar pattern here or implementing a separate database migration to ensure providers are updated consistently across all installations.

🤖 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 `@portals/developer-portal/src/services/seeder.js` around lines 111 - 121, The
seeder's createProvider call in the try block only creates a new provider but
does not update existing ones. When UniqueConstraintError is caught (indicating
the provider already exists), the code returns without updating the existing
record, causing inconsistency between fresh installations and upgrades. Modify
the catch block handling the UniqueConstraintError for the WSO2 provider to
update the existing provider record with the current providerURL value instead
of silently returning. Apply an upsert pattern similar to other seeded data in
the codebase by either calling an update method on the existing provider or
using a findOrCreate/updateOrCreate pattern with adminDao to ensure the provider
is consistently configured across all installations.
portals/developer-portal/src/openapi/middleware/auth.js (1)

220-226: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Hydrate a platform-jwt principal for bearer-token REST calls.

After validating a Platform API bearer token, this path only sets req.auth and req[constants.USER_ID]; downstream handlers in this file read req.user[...]. Populate req.user from the token’s user/org claims and set mode: 'platform-jwt' for local tokens.

🤖 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 `@portals/developer-portal/src/openapi/middleware/auth.js` around lines 220 -
226, In the Platform API bearer token validation path (where jwt.decode extracts
the decoded token), the code currently only sets req.auth and
req[constants.USER_ID] but does not populate req.user. After decoding the token,
extract the user and org claims from the decoded token object and assign them to
req.user (e.g., req.user[constants.USER_ID], req.user[constants.ORG_ID], etc.).
Additionally, update the mode property in the req.auth object from 'oauth2' to
'platform-jwt' to correctly identify this as a local token authentication path
for downstream handlers that read from req.user.
🤖 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 `@portals/developer-portal/distribution/docker-compose.yaml`:
- Around line 102-110: The commented PostgreSQL dependency block creates a
duplicate depends_on key which would override the existing platform-api
dependency when uncommented. Instead of having a separate commented depends_on
section, move the postgres service entry to be nested as an additional service
within the existing depends_on block alongside platform-api. This way, when
users uncomment the postgres section, it adds to the existing dependencies
rather than replacing them. Update the comment to reflect that users should
uncomment the postgres entry within the active depends_on block.

In `@portals/developer-portal/docs/administer/manage-organizations.md`:
- Line 260: The text on line 260 contains "devportal" written in lowercase,
which is inconsistent with the standard product naming convention. Replace
"devportal" with "Developer Portal" to maintain consistent product terminology
throughout the documentation and improve readability.
- Around line 193-205: The documentation now describes token-based
authentication for local development, but organization and IdP command examples
throughout the document still use basic credentials instead of Bearer tokens.
Update all curl examples at the referenced line numbers (42, 70, 90, 98, 133,
178, 186) to replace basic authentication (such as `-u username:password` or
`Authorization: Basic` headers) with Bearer token authentication using the `-H
"Authorization: Bearer <token>"` pattern, ensuring consistency with the newly
documented token-based auth flow described in the updated section.

In `@portals/developer-portal/src/controllers/authController.js`:
- Around line 311-314: The scope suffix check in both authController.js and
devportalController.js is using `:manage` but the platform API template grants
scopes with `_manage` suffix (like `dp:api_manage`), causing manage-scoped users
to be incorrectly classified as subscribers. In
portals/developer-portal/src/controllers/authController.js at lines 311-314,
update the isAdmin check in the scope filtering logic to use `_manage` suffix
instead of `:manage`. In
portals/developer-portal/src/controllers/devportalController.js at lines
442-445, apply the same fix to ensure consistent admin classification between UI
and REST login paths. Consider extracting this scope checking logic into a
shared helper function to prevent future inconsistencies between the two
controllers.
- Around line 291-293: The logger.warn call in the Platform API login failure
handler is currently logging the username parameter, which should not be exposed
in logs for security reasons. Remove the username parameter from the logger.warn
call that logs 'Platform API login failed: invalid credentials', keeping only
the orgName in the logged context to maintain org/request traceability without
exposing user credentials.
- Around line 326-345: The profile object is being populated with orgName
sourced directly from the route parameter, but this does not validate that the
platformToken actually belongs to that organization. Extract the organization
claim from the platformToken (similar to how claims are extracted from other
tokens), and either reject the request if the token's org claim does not match
the route orgName, or populate the session profile using the token's org claim
instead. Specifically, update the assignments to
[constants.ROLES.ORGANIZATION_CLAIM], userOrg, and authorizedOrgs in the profile
object to use the validated org claim from the platformToken rather than the
unvalidated route parameter.
- Around line 280-286: Both the axios.post call in authController.js (lines
280-286) and the equivalent HTTPS client configuration in devportalController.js
(lines 419-425) disable certificate verification by setting rejectUnauthorized:
false in the httpsAgent, which creates a security vulnerability. Instead of
disabling certificate verification, configure the HTTPS agents in both locations
to use a properly validated CA certificate by loading the Platform API
certificate from your configuration or environment and passing it via the ca
option in the https.Agent constructor, or alternatively gate the relaxed
validation behind an explicit development-only flag to ensure it is never used
in production.

In `@portals/developer-portal/src/controllers/devportalController.js`:
- Around line 437-459: The current implementation silently catches token parsing
errors with an empty catch block and continues to create a user profile even
with missing or malformed tokens, leaving org fields empty. Modify the exception
handling so that after attempting to parse the platformToken, you validate that
claims were successfully extracted and that required fields like the org_handle
claim are present; if either the token parsing fails or required claims are
missing, return an auth-service error instead of proceeding with profile
creation. This ensures that only valid tokens with complete organization
information can establish a session.

In `@portals/developer-portal/src/middlewares/ensureAuthenticated.js`:
- Around line 45-57: The ensureAuthenticated middleware is incorrectly skipping
authorization checks when a local-auth user has no platform token. Currently,
when the condition `!platformToken` is true, the code calls `return next()`
which bypasses the scope validation. Instead, return an authentication error
(using util.handleError with a 401 status code or appropriate authentication
error) to ensure that local-auth sessions without a valid platform token are
properly rejected rather than allowed through.
- Around line 330-332: The `scopes` variable on Line 331 may be undefined or
null when the scope is absent from the validated token payload, causing a
runtime error when calling `.split(' ')` on it. Normalize `scopes` by coercing
it to a string before splitting (e.g., using a logical OR operator or similar
coercion technique) to ensure safe evaluation, preventing the application from
crashing when processing tokens with missing scope information in the
ensureAuthenticated middleware.

In `@portals/developer-portal/src/models/application.js`:
- Around line 57-64: Remove the composite unique index on ['APP_ID', 'ORG_ID']
from the Application model since APP_ID is already the primary key and makes
this composite index redundant for uniqueness enforcement. If this index serves
a specific purpose beyond uniqueness (such as being a covering index for
particular queries), clarify that intent and document it. Additionally, create a
database migration file to drop the global UNIQUE constraint on ORG_ID from the
Postgres schema to prevent constraint conflicts. Ensure that both the model
definition and the actual database schema remain synchronized after these
changes.

In `@portals/developer-portal/src/openapi/middleware/auth.js`:
- Around line 153-158: Consolidate Platform API JWT validation across multiple
files and add production safety guards. At
portals/developer-portal/src/openapi/middleware/auth.js anchor site (lines
153-158), remove the unverified fallback that returns valid claims when
jwtSecret is null—instead, require the secret to be configured or implement
explicit server-side token validation for production deployments. At the sibling
location in auth.js (lines 187-195), replace the manual base64url decoding logic
with a call to extractPlatformJwtClaims() to ensure consistent verification
across all JWT validation points. In
portals/developer-portal/src/middlewares/ensureAuthenticated.js (lines 48-54),
portals/developer-portal/src/controllers/devportalController.js (lines 437-442),
and portals/developer-portal/src/controllers/authController.js (lines 299-304),
replace all duplicate unverified decode logic with calls to
extractPlatformJwtClaims() using the configured jwtSecret. In
portals/developer-portal/configs/config-platform-api.toml.example (lines 22-25),
portals/developer-portal/docker-compose.yaml (lines 32-34 and 109-113), and
portals/developer-portal/distribution/docker-compose.yaml (lines 30-32 and
149-153), add documentation comments explicitly marking AUTH_JWT_SECRET_KEY and
DP_PLATFORMAPI_JWTSECRET as development-only settings, and ensure the
application fails startup in production if local auth is enabled without a
configured shared secret or proper OIDC identity provider.

In `@portals/developer-portal/src/services/seeder.js`:
- Line 112: The hardcoded provider URL `'https://wso2.com'` in the
adminDao.createProvider call should be replaced with the existing
constants.WSO2_DEFAULT_URL constant to maintain consistency with how the same
operation is handled elsewhere in the codebase. Replace the string literal with
the constant reference to ensure uniform configuration across the seeder and
other services.

---

Outside diff comments:
In `@portals/developer-portal/src/openapi/middleware/auth.js`:
- Around line 220-226: In the Platform API bearer token validation path (where
jwt.decode extracts the decoded token), the code currently only sets req.auth
and req[constants.USER_ID] but does not populate req.user. After decoding the
token, extract the user and org claims from the decoded token object and assign
them to req.user (e.g., req.user[constants.USER_ID], req.user[constants.ORG_ID],
etc.). Additionally, update the mode property in the req.auth object from
'oauth2' to 'platform-jwt' to correctly identify this as a local token
authentication path for downstream handlers that read from req.user.

In `@portals/developer-portal/src/services/seeder.js`:
- Around line 111-121: The seeder's createProvider call in the try block only
creates a new provider but does not update existing ones. When
UniqueConstraintError is caught (indicating the provider already exists), the
code returns without updating the existing record, causing inconsistency between
fresh installations and upgrades. Modify the catch block handling the
UniqueConstraintError for the WSO2 provider to update the existing provider
record with the current providerURL value instead of silently returning. Apply
an upsert pattern similar to other seeded data in the codebase by either calling
an update method on the existing provider or using a findOrCreate/updateOrCreate
pattern with adminDao to ensure the provider is consistently configured across
all installations.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fa96d254-9669-4f37-9170-3ba57df65778

📥 Commits

Reviewing files that changed from the base of the PR and between f237073 and c3487b2.

📒 Files selected for processing (13)
  • portals/developer-portal/.gitignore
  • portals/developer-portal/configs/config-platform-api.toml.example
  • portals/developer-portal/distribution/docker-compose.yaml
  • portals/developer-portal/docker-compose.yaml
  • portals/developer-portal/docs/administer/manage-organizations.md
  • portals/developer-portal/docs/introduction/quick-start.md
  • portals/developer-portal/src/config/configLoader.js
  • portals/developer-portal/src/controllers/authController.js
  • portals/developer-portal/src/controllers/devportalController.js
  • portals/developer-portal/src/middlewares/ensureAuthenticated.js
  • portals/developer-portal/src/models/application.js
  • portals/developer-portal/src/openapi/middleware/auth.js
  • portals/developer-portal/src/services/seeder.js

Comment thread portals/developer-portal/distribution/docker-compose.yaml Outdated
Comment thread portals/developer-portal/docs/administer/manage-organizations.md
Comment thread portals/developer-portal/docs/administer/manage-organizations.md Outdated
Comment on lines +280 to +286
const response = await axios.post(
`${platformApiUrl}/api/portal/v1/auth/login`,
new URLSearchParams({ username, password }).toString(),
{
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
timeout: 10000,

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all login clients that disable HTTPS certificate validation.
rg -n -C3 'rejectUnauthorized:\s*false|httpsAgent' --type js

Repository: wso2/api-platform

Length of output: 3400


Configure the HTTPS client with a trusted Platform API certificate instead of disabling certificate validation. Both login endpoints currently disable HTTPS certificate verification, creating a security risk. Wire the Platform API CA/certificate into the devportal configuration or gate relaxed validation behind an explicit development-only flag.

  • portals/developer-portal/src/controllers/authController.js#L285: Replace rejectUnauthorized: false with a CA-backed HTTPS agent.
  • portals/developer-portal/src/controllers/devportalController.js#L424: Apply the same HTTPS client configuration for consistency across both login paths.
📍 Affects 2 files
  • portals/developer-portal/src/controllers/authController.js#L280-L286 (this comment)
  • portals/developer-portal/src/controllers/devportalController.js#L419-L425
🤖 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 `@portals/developer-portal/src/controllers/authController.js` around lines 280
- 286, Both the axios.post call in authController.js (lines 280-286) and the
equivalent HTTPS client configuration in devportalController.js (lines 419-425)
disable certificate verification by setting rejectUnauthorized: false in the
httpsAgent, which creates a security vulnerability. Instead of disabling
certificate verification, configure the HTTPS agents in both locations to use a
properly validated CA certificate by loading the Platform API certificate from
your configuration or environment and passing it via the ca option in the
https.Agent constructor, or alternatively gate the relaxed validation behind an
explicit development-only flag to ensure it is never used in production.

Comment thread portals/developer-portal/src/controllers/authController.js
Comment thread portals/developer-portal/src/middlewares/ensureAuthenticated.js
Comment thread portals/developer-portal/src/middlewares/ensureAuthenticated.js
Comment on lines +57 to +64
returning: true,
indexes: [
{
unique: true,
fields: ['APP_ID', 'ORG_ID'],
},
],
});

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there's a migration file addressing the ORG_ID constraint change
fd -e js -e sql migration | xargs rg -l -i "ORG_ID" 2>/dev/null || echo "No migration files found referencing ORG_ID"

# Check current schema definition for DP_APPLICATION
rg -A 5 "DP_APPLICATION" --glob "*.sql"

Repository: wso2/api-platform

Length of output: 7115


🏁 Script executed:

# Read the application.js file around the changed lines
cat -n portals/developer-portal/src/models/application.js | sed -n '30,75p'

Repository: wso2/api-platform

Length of output: 1350


🏁 Script executed:

# Search for migration files that might address schema changes
fd -type f -name "*migration*" -o -name "*migrate*" | head -20
rg -i "composite.*unique\|unique.*composite" --glob "*.sql" --glob "*.js" -B 2 -A 2

Repository: wso2/api-platform

Length of output: 232


🏁 Script executed:

# Check for any DP_APPLICATION related changes in recent commits or other model definitions
rg -A 10 "DP_APPLICATION" portals/developer-portal/src/models/ --glob "*.js"

Repository: wso2/api-platform

Length of output: 1692


🏁 Script executed:

# Check for migration files in the database directory
fd migration portals/developer-portal/database/

# Look for any sequelize migration or sync patterns
rg -i "sequelize.*sync|sequelize.*migrate" portals/developer-portal/src/ --glob "*.js" -A 3 -B 3

Repository: wso2/api-platform

Length of output: 8270


🏁 Script executed:

# Check if there's any configuration or setup that syncs models to database
rg -i "sync.*true|alter.*true" portals/developer-portal/src/ --glob "*.js"

Repository: wso2/api-platform

Length of output: 1245


🏁 Script executed:

# Check if there are any migration files or setup scripts in the database directory
ls -la portals/developer-portal/database/

# Look for any references to migrations or alter table statements
rg -i "alter.*table.*dp_application\|drop.*constraint.*org_id" portals/developer-portal/database/ --glob "*.sql"

Repository: wso2/api-platform

Length of output: 307


🏁 Script executed:

# Check if sequelize sync is called with alter: true in any environment
rg "sequelize.sync.*alter" portals/developer-portal/src/ --glob "*.js"

# Look for any environment-specific database initialization
rg -i "process\.env\|config\.db" portals/developer-portal/src/server.js -A 10 -B 10 | head -50

Repository: wso2/api-platform

Length of output: 43


🏁 Script executed:

# Check git history for changes to this file and related schema changes
git log --oneline portals/developer-portal/src/models/application.js | head -10
git log --oneline portals/developer-portal/database/schema.postgres.sql | head -10

Repository: wso2/api-platform

Length of output: 199


Composite unique index conflicts with database schema and lacks migration.

The model now defines a composite unique index on ['APP_ID', 'ORG_ID'], but the Postgres schema (portals/developer-portal/database/schema.postgres.sql) enforces a global UNIQUE constraint on ORG_ID alone. These constraints are semantically different:

  • Global UNIQUE on ORG_ID → one application per organization
  • Composite index on (APP_ID, ORG_ID) → multiple applications per organization allowed

Since Postgres does not auto-sync models to the actual schema, a migration must explicitly drop the global uniqueness constraint and apply the composite index. No migration file currently exists for this change.

Additionally, because APP_ID is the primary key (globally unique), any composite unique index including it is redundant for uniqueness enforcement—each APP_ID already appears at most once. If the index serves other purposes (e.g., covering index for specific queries), clarify the intent.

🤖 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 `@portals/developer-portal/src/models/application.js` around lines 57 - 64,
Remove the composite unique index on ['APP_ID', 'ORG_ID'] from the Application
model since APP_ID is already the primary key and makes this composite index
redundant for uniqueness enforcement. If this index serves a specific purpose
beyond uniqueness (such as being a covering index for particular queries),
clarify that intent and document it. Additionally, create a database migration
file to drop the global UNIQUE constraint on ORG_ID from the Postgres schema to
prevent constraint conflicts. Ensure that both the model definition and the
actual database schema remain synchronized after these changes.

Comment on lines 153 to +158
if (!idp || !idp.clientId) {
// No IdP configured — accept bearer presence (legacy parity), no scopes.
return { valid: true, scopes: '' };
// Local auth mode: verify Platform API JWT with shared secret when configured.
const jwtSecret = config.platformApi?.jwtSecret;
const claims = extractPlatformJwtClaims(token, jwtSecret || null);
if (jwtSecret && !claims) return { valid: false, scopes: '' };
return { valid: true, scopes: claims?.scopes?.join(' ') ?? '' };

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all Platform API JWT-secret wiring and decode/verify paths.
rg -n -C3 'AUTH_JWT_SECRET_KEY|DP_PLATFORMAPI_JWTSECRET|platformApi\.jwtSecret|extractPlatformJwtClaims|base64url' --iglob '*.{js,yaml,toml,md}'

Repository: wso2/api-platform

Length of output: 20900


🏁 Script executed:

#!/bin/bash
# Check config schema and how platformApi.jwtSecret is validated/set
rg -n 'platformApi|jwtSecret' portals/developer-portal/src --type js -A 3 -B 1 | head -60

# Search for config loading and validation
fd -e js . portals/developer-portal/src -exec rg -l 'config\.platformApi|loadConfig|validateConfig' {} \;

Repository: wso2/api-platform

Length of output: 20061


🏁 Script executed:

#!/bin/bash
# Check if there's any startup validation or environment-based behavior
rg -n 'DEV_MODE|NODE_ENV|production|startup|warn' portals/developer-portal/src/openapi/middleware/auth.js -C 2

Repository: wso2/api-platform

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Look for documentation about local auth and when verification is required
rg -n -i 'local auth|dev.?only|unverified|fallback' portals/developer-portal --type md --type js --type toml

Repository: wso2/api-platform

Length of output: 3316


🏁 Script executed:

#!/bin/bash
# Check app startup and config loading for any validation logic
cat -n portals/developer-portal/src/config/configLoader.js | head -150

Repository: wso2/api-platform

Length of output: 5632


🏁 Script executed:

#!/bin/bash
# Look for any startup checks or warnings about missing JWT secret
rg -n 'warn|error|throw|require.*jwtSecret|NODE_ENV.*production' portals/developer-portal/src/config portals/developer-portal/src/server.js -A 2

Repository: wso2/api-platform

Length of output: 4197


🏁 Script executed:

#!/bin/bash
# Check if there's documentation about production safety for local auth
rg -n -i 'production|never|must|require' portals/developer-portal/docs/administer/manage-organizations.md | grep -i 'auth\|secret\|jwt'

Repository: wso2/api-platform

Length of output: 257


Consolidate Platform API JWT validation and document local auth as development-only. The auth layer supports optional JWT verification for development, falling back to unverified payload decoding when DP_PLATFORMAPI_JWTSECRET is unset. This intentional behavior allows local development without shared secrets, but multiple decode paths bypass verification inconsistently, and production deployments using this mode lack startup warnings.

  • portals/developer-portal/src/openapi/middleware/auth.js#L156-L158: Replace unverified fallback with either a required secret or explicit server-side token validation for production deployments.
  • portals/developer-portal/src/openapi/middleware/auth.js#L187-L195: Use extractPlatformJwtClaims() instead of manual base64url decoding to ensure consistent verification logic across all validation points.
  • portals/developer-portal/src/middlewares/ensureAuthenticated.js#L48-L54, portals/developer-portal/src/controllers/devportalController.js#L437-L442, portals/developer-portal/src/controllers/authController.js#L299-L304: Apply the same verification helper to replace duplicate unverified decode logic.
  • portals/developer-portal/configs/config-platform-api.toml.example, portals/developer-portal/docker-compose.yaml, portals/developer-portal/distribution/docker-compose.yaml: Document AUTH_JWT_SECRET_KEY and DP_PLATFORMAPI_JWTSECRET as development-only settings; for production, require OIDC identity provider configuration and fail startup if local auth is enabled without a configured shared secret.
📍 Affects 4 files
  • portals/developer-portal/src/openapi/middleware/auth.js#L153-L158 (this comment)
  • portals/developer-portal/src/openapi/middleware/auth.js#L187-L195
  • portals/developer-portal/configs/config-platform-api.toml.example#L22-L25
  • portals/developer-portal/docker-compose.yaml#L32-L34
  • portals/developer-portal/docker-compose.yaml#L109-L113
  • portals/developer-portal/distribution/docker-compose.yaml#L30-L32
  • portals/developer-portal/distribution/docker-compose.yaml#L149-L153
🤖 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 `@portals/developer-portal/src/openapi/middleware/auth.js` around lines 153 -
158, Consolidate Platform API JWT validation across multiple files and add
production safety guards. At
portals/developer-portal/src/openapi/middleware/auth.js anchor site (lines
153-158), remove the unverified fallback that returns valid claims when
jwtSecret is null—instead, require the secret to be configured or implement
explicit server-side token validation for production deployments. At the sibling
location in auth.js (lines 187-195), replace the manual base64url decoding logic
with a call to extractPlatformJwtClaims() to ensure consistent verification
across all JWT validation points. In
portals/developer-portal/src/middlewares/ensureAuthenticated.js (lines 48-54),
portals/developer-portal/src/controllers/devportalController.js (lines 437-442),
and portals/developer-portal/src/controllers/authController.js (lines 299-304),
replace all duplicate unverified decode logic with calls to
extractPlatformJwtClaims() using the configured jwtSecret. In
portals/developer-portal/configs/config-platform-api.toml.example (lines 22-25),
portals/developer-portal/docker-compose.yaml (lines 32-34 and 109-113), and
portals/developer-portal/distribution/docker-compose.yaml (lines 30-32 and
149-153), add documentation comments explicitly marking AUTH_JWT_SECRET_KEY and
DP_PLATFORMAPI_JWTSECRET as development-only settings, and ensure the
application fails startup in production if local auth is enabled without a
configured shared secret or proper OIDC identity provider.


try {
await adminDao.createProvider(orgId, { name: 'WSO2', providerURL: config.controlPlane.url });
await adminDao.createProvider(orgId, { name: 'WSO2', providerURL: 'https://wso2.com' });

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify config.controlPlane usage and provider URL references.

# Check if config.controlPlane is still defined or used elsewhere
rg -n 'config\.controlPlane|controlPlane\s*:' --type js --type toml

# Check how WSO2 provider URL is used downstream
rg -n 'providerURL.*WSO2|provider.*WSO2.*providerURL' --type js -C3

Repository: wso2/api-platform

Length of output: 816


🏁 Script executed:

# Find constants file and check WSO2_DEFAULT_URL definition
rg -n 'WSO2_DEFAULT_URL\s*=' --type js -A2

# Check what constants are imported in seeder.js
head -50 portals/developer-portal/src/services/seeder.js | grep -n "import\|require\|constants"

# Check full import section of seeder.js
sed -n '1,30p' portals/developer-portal/src/services/seeder.js

Repository: wso2/api-platform

Length of output: 1667


🏁 Script executed:

# Find the constants file location and check WSO2_DEFAULT_URL
find . -name "constants.js" -type f | head -5

# Once found, display the WSO2_DEFAULT_URL definition
rg -n 'WSO2_DEFAULT_URL' --type js -B2 -A2

Repository: wso2/api-platform

Length of output: 1155


🏁 Script executed:

# Display line 112 and surrounding context in seeder.js to confirm the hardcoded URL
sed -n '105,120p' portals/developer-portal/src/services/seeder.js

Repository: wso2/api-platform

Length of output: 538


Use constants.WSO2_DEFAULT_URL instead of hardcoding the provider URL.

The provider URL is hardcoded to 'https://wso2.com', but the codebase defines constants.WSO2_DEFAULT_URL as 'https://localhost:9443' and uses this constant for the same operation in adminService.js line 209. Replace the hardcoded string with the constant to maintain consistency across the codebase.

🤖 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 `@portals/developer-portal/src/services/seeder.js` at line 112, The hardcoded
provider URL `'https://wso2.com'` in the adminDao.createProvider call should be
replaced with the existing constants.WSO2_DEFAULT_URL constant to maintain
consistency with how the same operation is handled elsewhere in the codebase.
Replace the string literal with the constant reference to ensure uniform
configuration across the seeder and other services.

@Piumal1999

Piumal1999 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Caution

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

⚠️ Outside diff range comments (2)
portals/developer-portal/src/controllers/devportalController.js (2)

474-479: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle req.session.save errors before returning login success.

req.logIn can succeed while the session store write fails; returning 200 in that case leaves the client with a non-persisted login.

Proposed fix
-            req.session.save(() => res.status(200).json({ message: 'Login successful' }));
+            req.session.save((saveErr) => {
+                if (saveErr) {
+                    logger.error('Session save failed', { error: saveErr.message });
+                    return util.handleError(res, saveErr);
+                }
+                return res.status(200).json({ message: 'Login successful' });
+            });
🤖 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 `@portals/developer-portal/src/controllers/devportalController.js` around lines
474 - 479, The req.session.save() callback does not handle potential errors from
the session store write operation. Currently, if the session save fails, the
code still returns a 200 success response to the client even though the session
was not persisted. Add error handling to the req.session.save() callback by
accepting an error parameter, checking if an error occurred, and if so, log the
error and return an appropriate error response using util.handleError before
attempting to send the success response.

409-415: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mirror the local-auth guards before calling Platform API.

The view login path disables local login when an IdP is configured and rejects missing credentials first; this API login path should do the same to keep authentication behavior consistent.

Proposed fix
 const login = async (req, res) => {
-    const { username, password } = req.body;
+    const { username, password } = req.body || {};
+
+    if (config.identityProvider?.clientId) {
+        return res.status(404).json({ message: 'Not found' });
+    }
+
+    if (!username || !password) {
+        return res.status(400).json({ message: 'Username and password are required' });
+    }
 
     const platformApiUrl = config.platformApi?.baseUrl;
🤖 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 `@portals/developer-portal/src/controllers/devportalController.js` around lines
409 - 415, The login function in devportalController.js needs to implement the
same authentication guards that exist in the view login path for consistency.
Before attempting to call the Platform API, add validation to check if an IdP is
configured (and reject local login if it is) and verify that username and
password credentials are provided in the request. These checks should happen
after extracting the credentials from req.body but before checking the
platformApiUrl configuration, ensuring the API endpoint enforces the same
authentication behavior as the view login path.
🤖 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.

Outside diff comments:
In `@portals/developer-portal/src/controllers/devportalController.js`:
- Around line 474-479: The req.session.save() callback does not handle potential
errors from the session store write operation. Currently, if the session save
fails, the code still returns a 200 success response to the client even though
the session was not persisted. Add error handling to the req.session.save()
callback by accepting an error parameter, checking if an error occurred, and if
so, log the error and return an appropriate error response using
util.handleError before attempting to send the success response.
- Around line 409-415: The login function in devportalController.js needs to
implement the same authentication guards that exist in the view login path for
consistency. Before attempting to call the Platform API, add validation to check
if an IdP is configured (and reject local login if it is) and verify that
username and password credentials are provided in the request. These checks
should happen after extracting the credentials from req.body but before checking
the platformApiUrl configuration, ensuring the API endpoint enforces the same
authentication behavior as the view login path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6470a5b5-5586-4876-ac51-05e02ad218e9

📥 Commits

Reviewing files that changed from the base of the PR and between c3487b2 and fe859c7.

📒 Files selected for processing (8)
  • portals/developer-portal/distribution/docker-compose.yaml
  • portals/developer-portal/docker-compose.yaml
  • portals/developer-portal/docs/administer/manage-organizations.md
  • portals/developer-portal/src/config/configLoader.js
  • portals/developer-portal/src/controllers/authController.js
  • portals/developer-portal/src/controllers/devportalController.js
  • portals/developer-portal/src/middlewares/ensureAuthenticated.js
  • portals/developer-portal/src/openapi/middleware/auth.js
🚧 Files skipped from review as they are similar to previous changes (7)
  • portals/developer-portal/src/config/configLoader.js
  • portals/developer-portal/distribution/docker-compose.yaml
  • portals/developer-portal/docs/administer/manage-organizations.md
  • portals/developer-portal/docker-compose.yaml
  • portals/developer-portal/src/controllers/authController.js
  • portals/developer-portal/src/middlewares/ensureAuthenticated.js
  • portals/developer-portal/src/openapi/middleware/auth.js

@Piumal1999

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

1 participant