Skip to content

fix(authz): bind eval cache key to user token so re-login forces re-evaluation#549

Merged
kaviththiranga merged 1 commit into
openchoreo:mainfrom
kaviththiranga:fix-perm-bugs
May 15, 2026
Merged

fix(authz): bind eval cache key to user token so re-login forces re-evaluation#549
kaviththiranga merged 1 commit into
openchoreo:mainfrom
kaviththiranga:fix-perm-bugs

Conversation

@kaviththiranga
Copy link
Copy Markdown
Contributor

@kaviththiranga kaviththiranga commented May 15, 2026

Cache key was (userEntityRef, action, resourcePath, environment)
stable across sign-outs. A stale ABAC decision from before a binding
change survived re-login for the full JWT TTL (up to 24h), and the only
recovery was restarting the backend or waiting it out.

Fold a token-derived hash into the key (mirroring AuthzProfileCache's
existing buildKey pattern) so a new sign-in produces a new key and
re-evaluates against the current binding state.

Summary by CodeRabbit

  • Bug Fixes
    • Authorization evaluation cache entries are now scoped to individual user sessions, preventing cached decisions from persisting across sign-out and sign-in events.

Review Change Stack

…valuation

  Cache key was `(userEntityRef, action, resourcePath, environment)` —
  stable across sign-outs. A stale ABAC decision from before a binding
  change survived re-login for the full JWT TTL (up to 24h), and the only
  recovery was restarting the backend or waiting it out.

  Fold a token-derived hash into the key (mirroring AuthzProfileCache's
  existing buildKey pattern) so a new sign-in produces a new key and
  re-evaluates against the current binding state.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

📝 Walkthrough

Walkthrough

Authorization evaluation cache entries are now isolated per user token session. AuthzProfileCache exposes tokenHash as a required cache key component via updated getEvaluation() and setEvaluation() signatures, with a new static hashToken() helper for hashing. AuthzProfileService.evaluate() computes the token hash and supplies it to all cache operations, preventing stale denials from persisting across re-login within the JWT TTL.

Changes

Token-scoped evaluation cache keying

Layer / File(s) Summary
Cache API contract with token hash keying
plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileCache.ts
buildEvaluationKey() now accepts and embeds tokenHash; new static hashToken() helper provides SHA-256-based token hashing; getEvaluation() and setEvaluation() methods updated to accept tokenHash and pass it to the key builder.
Service token hashing and cache integration
plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileService.ts
evaluate() computes tokenHash from userToken and supplies it to cache.getEvaluation() for cache reads and cache.setEvaluation() for cache writes, ensuring cached ABAC decisions are scoped to the token session.
Token-scoped caching validation tests
plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileService.test.ts
Extended test asserts cache.setEvaluation receives token-derived hash; new test verifies different JWT tokens trigger backend re-evaluation instead of cache reuse.

Sequence Diagram(s)

sequenceDiagram
    participant Service as AuthzProfileService
    participant Cache as AuthzProfileCache
    participant Backend as ABAC Backend
    participant Token as User Token
    Service->>Token: extract token from context
    Service->>Service: compute tokenHash from userToken
    Service->>Cache: getEvaluation(..., tokenHash, ...)
    alt cache hit
        Cache-->>Service: return cached allowed boolean
    else cache miss
        Service->>Backend: POST evaluate decision
        Backend-->>Service: return allowed
        Service->>Cache: setEvaluation(..., tokenHash, ..., allowed)
        Cache-->>Service: cache stored
    end
    Service-->>Service: return allowed
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • openchoreo/backstage-plugins#538: Both PRs modify ABAC authorization evaluation caching in the OpenChoreo policy backend module; this PR adds token-scoping to extend the cache isolation work.

Suggested reviewers

  • sameerajayasoma
  • stefinie123

Poem

🐰 A token arrives with a hash in its hand,

No stale cache decisions will cross token land,

Each re-login brings fresh evaluation sight,

The permission cache now bounces just right! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem (stale cache across sign-outs) and solution (token-derived hash in key), but is missing most required template sections like Goals, Approach, User stories, Release note, Documentation, etc. Fill in the missing template sections (Goals, Approach, User stories, Release note, Documentation, Training, Certification, Marketing, Automation tests, Security checks, Samples, Related PRs, Test environment, Learning) to meet repository standards.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: binding the evaluation cache key to user token to force re-evaluation on re-login.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

@binoyPeries
Copy link
Copy Markdown
Contributor

so the updated conditions will only take effect from the backstage side only after a relogin?

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileCache.ts (1)

186-191: ⚡ Quick win

Reuse the shared token-hash helper in buildKey() to avoid hash drift.

buildKey() still inlines hashing while hashToken() now centralizes the same logic. Using one helper prevents future divergence between capabilities and evaluation key partitioning.

♻️ Proposed refactor
   private buildKey(
     userToken: string,
     org: string,
     project?: string,
     component?: string,
   ): string {
-    const tokenHash = crypto
-      .createHash('sha256')
-      .update(userToken)
-      .digest('hex')
-      .substring(0, 16);
+    const tokenHash = AuthzProfileCache.hashToken(userToken);
 
     const parts = ['openchoreo', 'capabilities', tokenHash, org];
     if (project) parts.push(project);
     if (component) parts.push(component);
🤖 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
`@plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileCache.ts`
around lines 186 - 191, buildKey() currently duplicates the token hashing logic
while hashToken() centralizes it; change buildKey() to call
AuthzProfileCache.hashToken(userToken) (or the class/static method hashToken)
instead of re-implementing the sha256 + substring logic so both key construction
and token hashing share the same helper and avoid future drift between
capabilities and evaluation key partitioning.
🤖 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
`@plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileService.ts`:
- Around line 347-355: The evaluate() method's docstring is out of date: update
the comment on AuthzProfileService.evaluate (and any related doc for
AuthzProfileCache) to include tokenHash (derived via
AuthzProfileCache.hashToken(userToken)) as part of the cache key in addition to
(userEntityRef, action, resourcePath, environment) so the documentation matches
the implementation that includes the token/session component in cache key
generation.

---

Nitpick comments:
In
`@plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileCache.ts`:
- Around line 186-191: buildKey() currently duplicates the token hashing logic
while hashToken() centralizes it; change buildKey() to call
AuthzProfileCache.hashToken(userToken) (or the class/static method hashToken)
instead of re-implementing the sha256 + substring logic so both key construction
and token hashing share the same helper and avoid future drift between
capabilities and evaluation key partitioning.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9c3ce9f7-ae3f-4cdc-9f8c-4c238e5a0d74

📥 Commits

Reviewing files that changed from the base of the PR and between 980c226 and 5616689.

📒 Files selected for processing (3)
  • plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileCache.ts
  • plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileService.test.ts
  • plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileService.ts

Comment on lines +347 to +355
// Include a token-derived component in the cache key so that signing out
// and back in produces a new key and forces a re-evaluation. Without this,
// a stale `false` from before an authz binding change would survive
// re-login for the full JWT TTL (up to 24h on our setup), making the only
// recovery paths "wait" or "restart the backend".
const tokenHash = userToken
? AuthzProfileCache.hashToken(userToken)
: 'no-token';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the evaluate() cache-key doc to include tokenHash.

The method comment still states caching is per (userEntityRef, action, resourcePath, environment), but the implementation now keys by token session too. Please align the docstring.

📝 Suggested doc fix
- * per `(userEntityRef, action, resourcePath, environment)` for the lifetime
+ * per `(userEntityRef, tokenHash, action, resourcePath, environment)` for the lifetime
🤖 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
`@plugins/permission-backend-module-openchoreo-policy/src/services/AuthzProfileService.ts`
around lines 347 - 355, The evaluate() method's docstring is out of date: update
the comment on AuthzProfileService.evaluate (and any related doc for
AuthzProfileCache) to include tokenHash (derived via
AuthzProfileCache.hashToken(userToken)) as part of the cache key in addition to
(userEntityRef, action, resourcePath, environment) so the documentation matches
the implementation that includes the token/session component in cache key
generation.

@kaviththiranga
Copy link
Copy Markdown
Contributor Author

so the updated conditions will only take effect from the backstage side only after a relogin?

@binoyPeries yes - or after 24hours TTL

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...nchoreo-policy/src/services/AuthzProfileService.ts 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kaviththiranga kaviththiranga merged commit 1ebce57 into openchoreo:main May 15, 2026
8 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