Skip to content

Role Based Access Control (RBAC) for the Consent Portal - #81

Merged
anjuchamantha merged 7 commits into
wso2:feature/portalfrom
rashmithachamikara:feature/portal-RBAC
Aug 4, 2026
Merged

anjuchamantha merged 7 commits into
wso2:feature/portalfrom
rashmithachamikara:feature/portal-RBAC

Conversation

@rashmithachamikara

@rashmithachamikara rashmithachamikara commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Introduce scope-based RBAC for the Consent Portal, expose the authenticated portal session through the BFF, and provide an administrative consent-management view by referring to APIM portals.

Resolves #73, Resolves #77, Resolves #80

Goals

  • Use JWKS-validated portal scopes for frontend authorization.
  • Keep the frontend independent of identity-provider roles and token introspection.
  • Expose the authenticated user, organization, and effective scopes through GET /me.
  • Hide unauthorized routes, navigation items, and actions.
  • Add Administration → Consents for users with system-wide consent permissions.
  • Remove the frontend VITE_ORG_ID dependency.
  • Support a fixed backend organization-ID override when required.

Approach

The approach is similar to the RBAC in APIM portals. UI elements are hidden depending on the scopes granted to the user. Backend authorization remains authoritative.

Backend

  • Added authenticated GET /me, returning:
    • userId
    • organizationId
    • canonical portal scopes
  • Filters out non-portal OIDC scopes and returns scopes in registry order.
  • Uses existing split-token and OIDC/JWKS validation without token introspection.
  • Added configurable placeholder scopes for local authentication-disabled mode.
  • Added BFF_AUTH__ORG_ID_OVERRIDE:
    • A non-empty value takes precedence over the token organization claim.
    • An empty value retains BFF_AUTH__ORG_ID_CLAIM behavior.
  • Updated the backend OpenAPI contract and documentation.

Frontend

  • Added a centralized typed portal scope registry.
  • Added an OpenAPI drift test for frontend scope constants.
  • Added a typed /me API, TanStack Query hook, authorization context, route guards, and element guards.
  • Uses /me as the portal authentication and authorization authority.
  • Uses the ID token only for display information such as name, email, and avatar.
  • Removed VITE_ORG_ID; API requests use the organization resolved by /me.
  • Filters sidebar categories, routes, and actions based on effective scopes.
  • Added localized loading, failure, redirect, and no-access behavior.
  • Updated frontend CI to run when the backend portal OpenAPI document changes.

Administrative consents

  • Added Administration → Consents for portal:consents:read:any.
  • Added revoke support for portal:consents:write:any.
  • Added filters for:
    • Consent ID
    • Status
    • User IDs
    • Multiple group IDs
    • Purpose name and version
    • Element name, namespace, and version
    • Start and end dates
  • Consent ID searches use GET /api/consents/{id} and display the result as a single registry row.
  • Status and advanced filters are disabled while a Consent ID filter is active.
  • Uses catalog-backed element selection when portal:elements:read is available and free-text fields otherwise.
  • Reuses the existing consent details UI without changing self-service consent behavior.

UI screenshots:

image

User stories

  • As an authenticated user, I see only portal pages and actions allowed by my scopes.
  • As a user without portal permissions, I see a clear no-access state.
  • As a consent administrator, I can search and inspect consents across users and groups.
  • As a consent administrator with write permission, I can revoke eligible consents.
  • As a deployment administrator, I can resolve the organization from a token claim or configure a fixed override.
  • As a frontend developer, I can use typed centralized scope constants without hardcoding scope strings.

Release note

Added scope-based portal access control, an authenticated /me session endpoint, and an administrative consent registry with system-wide search and revoke capabilities.

Documentation

Updated:

  • portal/backend/README.md
  • portal/backend/docs/README.md
  • portal/backend/.env.example
  • portal/frontend/README.md
  • portal/frontend/.env.example
  • portal/backend/openapi/portal-backend.yaml

Automation tests

  • Unit tests
    • Backend /me responses, scope filtering, placeholder scopes, authentication failures, configuration loading, and organization override behavior.
    • Frontend scope helpers, OpenAPI drift detection, session behavior, route authorization, sidebar filtering, action visibility, administrative filters, Consent ID lookup, and API requests.
  • Integration tests
    • Authenticated /me behavior.
    • Split-token authentication and invalid-token handling.
    • Placeholder-mode behavior.
    • Backend proxy authorization and OpenAPI synchronization.

Verification performed:

Backend:
- gofmt
- go test ./...
- go vet ./...
- go build ./...
- golangci-lint

Frontend:
- pnpm lint
- pnpm test
- pnpm build
- production CSP and source/sink security verification

Security checks

Backend scope enforcement remains the security boundary. Frontend guards provide visibility and navigation control only.

Migrations (if applicable)

  • Remove VITE_ORG_ID from frontend deployment configuration.
  • Ensure the identity provider supplies the organization claim configured by BFF_AUTH__ORG_ID_CLAIM, or configure BFF_AUTH__ORG_ID_OVERRIDE.
  • Leave BFF_AUTH__ORG_ID_OVERRIDE empty to retain claim-based organization resolution.
  • Configure BFF_PROXY__PLACEHOLDER_SCOPES when using local placeholder mode.
  • Reinstall frontend dependencies because @wso2/oxygen-ui-icons-react was updated.

No data or database migration is required.

Test environment

  • OS: Windows, amd64
  • Go: 1.26.1
  • Node.js: 24.13.0
  • pnpm: 10.32.1
  • Database: N/A
  • Browser: Chrome Version 150.0.7871.187 (Official Build) (64-bit)

Learning

The implementation follows a BFF-based authorization model:

  • The frontend does not communicate directly with the identity provider for authorization.
  • Access tokens are validated locally through OIDC discovery and JWKS.
  • The BFF exposes a minimal current-user session through /me.
  • Frontend capabilities are derived from typed scopes rather than role names.
  • Backend authorization remains authoritative even when UI elements are hidden.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 90751625-7bd9-4623-8f09-a1aa1ef9c59e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@rashmithachamikara

Copy link
Copy Markdown
Contributor Author

This removes the ORG-ID from the frontend config (therefore resolves #73). Also makes the ORG-ID configurable (therefore resolves #80)

@anjuchamantha

@rashmithachamikara

rashmithachamikara commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Tested with ThunderID (v0.47.0), Asgardeo, Identity Server (implicit), and Auth0 (by Okta)

@anjuchamantha @hasithakn

Note: You might need to set BFF_AUTH__ORG_ID_OVERRIDE in env for the Auth0 to work. Make sure you set the correct BFF_AUTH__RESOURCE_AUDIENCE as well

@rashmithachamikara

rashmithachamikara commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor Author

One concern: users with consent:write:any can access consent write endpoints (PUT, DELETE) as in [scopes.go]. For acts like revocation, the portal FE sends the user ID (from the ID token) for ACTION_BY in the request body. The BFF proxies admin routes without transformations.

This may allow users to submit any value as ACTION_BY. Adding authorization validation to the BFF would require maintaining many /admin endpoints and auth logic. This complicates things.

Suggested solution: Make ACTION_BY a request header and have the BFF inject the authenticated user ID from the access token. This is secure and simpler because it avoids handling different request-body formats.

There's already an issue for this #51. Will update issues too

@hasithakn

@anjuchamantha
anjuchamantha merged commit a5556a5 into wso2:feature/portal Aug 4, 2026
4 checks passed
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.

2 participants