Skip to content

Auto-submit an expired consent prompt as a denial - #57

Merged
brionmario merged 2 commits into
thunder-id:mainfrom
ImalshaD:fix/4600-consent-timeout-auto-submit
Aug 6, 2026
Merged

Auto-submit an expired consent prompt as a denial#57
brionmario merged 2 commits into
thunder-id:mainfrom
ImalshaD:fix/4600-consent-timeout-auto-submit

Conversation

@ImalshaD

@ImalshaD ImalshaD commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Purpose

A timed-out consent prompt left the user stranded because its actions were no longer valid and the flow could neither proceed nor terminate. This change automatically submits the
consent prompt when it expires, carrying a timeout reason so the server can complete the flow without recording consent decisions.

Consent decisions now include a top-level approved flag alongside the existing purpose-level and element-level flags. Approval is hierarchical: denying a level denies everything
below it, while approving a level preserves its child decisions.

The change also:

  • Compiles the top-level approval flag in all React consent submission paths.
  • Handles permission purposes whose essential list is null.
  • Ensures untouched optional claims are not treated as approved during invite acceptance.

⚠️ Breaking Changes

🔧 Summary of Breaking Changes

ConsentDecisions now requires a top-level approved property.

💥 Impact

Consumers that construct ConsentDecisions directly must provide the new property.

🔄 Migration Guide

Add approved when constructing consent decisions:

  const decisions: ConsentDecisions = {
    approved: true,
    purposes: [
      // Existing purpose decisions
    ],
  };

Set it to false when the entire consent request is denied.

Approach

  • Locate the consent prompt’s non-primary submit action when its configured deadline expires.
  • Submit the action once with an internal timeout marker.
  • Compile the marker into consent_decisions.reason and remove it from normal flow inputs.
  • Force timed-out consent payloads to be denied regardless of which action carries the submission.
  • Add the top-level approved value to consent payloads built by SignIn, Accept Invite, and the component factory.
  • Make consent construction tolerate null essential and optional element lists.
  • Align untouched optional invite claims with opt-in behavior.
  • Add unit coverage for hierarchical approval and permission-purpose payload construction.

Related Issues

  • #4600
  • #4413

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.

  • Manual test round performed and verified.

  • Documentation provided. Not applicable.

  • Tests provided.

    • Unit Tests
    • Integration Tests
  • Breaking changes.

    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • New Features
    • Consent decisions now include an overall approval status and an optional explanation.
    • Expired consent prompts can be submitted automatically when appropriate, with timeout details included in the decision.
  • Bug Fixes
    • Optional consent items are approved only when explicitly selected.
    • Consent flows now handle missing permission categories without errors.
    • Expired prompts are correctly denied when they cannot be submitted automatically.

A timed out consent prompt left the user stranded: its actions no longer led
anywhere, so the flow could not proceed or terminate. Submit on expiry instead,
carrying a timeout reason the server uses to discard the decisions and record
nothing.

Consent decisions now carry a top level approved flag alongside the existing
per purpose and per element ones. Approval is hierarchical, so a denial at any
level denies everything below it while an approval leaves the decisions below
it intact.

Also compile the top level flag in the two other places consent decisions are
built, guard the permission purposes the server sends with a null essential
list, and stop treating an untouched optional claim as approved when accepting
an invite.

Fixes #4600
Refs #4413
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ImalshaD, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a99aaf9-2c77-43e7-823f-0387b2d5b671

📥 Commits

Reviewing files that changed from the base of the PR and between 542540c and a44c1dd.

📒 Files selected for processing (8)
  • packages/javascript/src/constants/ConsentConstants.ts
  • packages/javascript/src/index.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.tsx
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/SignIn/SignIn.tsx
  • packages/react/src/components/presentation/auth/__tests__/AuthOptionFactory.test.tsx
  • packages/vue/src/components/auth/sign-in/AuthOptionFactoryCore.ts
  • packages/vue/src/components/auth/sign-in/SignIn.ts
📝 Walkthrough

Walkthrough

The PR extends consent decisions with overall approval and reason fields, tightens optional approval checks, handles absent purpose arrays, adds consent decision tests, and auto-submits expired consent prompts with a denial reason.

Changes

Consent flow

Layer / File(s) Summary
Consent decision contracts and approval handling
packages/javascript/src/models/embedded-flow.ts, packages/react/src/components/presentation/auth/AuthOptionFactory.tsx, packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.tsx, packages/react/src/components/presentation/auth/__tests__/AuthOptionFactory.test.tsx
ConsentDecisions now includes required approved and optional reason fields. Consent generation treats missing purpose arrays as empty and approves optional elements only when their value is 'true'. Tests cover approval, denial, optional selections, and missing essential elements.
Timeout decision preparation
packages/react/src/components/presentation/auth/SignIn/SignIn.tsx
Consent submission removes the internal reason sentinel. Timed-out consent decisions are denied and include the timeout reason.
Expired consent auto-submission
packages/react/src/components/presentation/auth/SignIn/SignIn.tsx
Expired consent prompts select a routable submit action and submit once with a timeout reason. Other expiry cases continue to report errors.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SignIn
  participant SubmitAction
  participant ConsentDecision
  participant Server
  SignIn->>SubmitAction: select routable timeout action
  SignIn->>ConsentDecision: mark timed-out consent denied
  SignIn->>ConsentDecision: add timeout reason
  ConsentDecision->>Server: submit compiled consent decision
Loading

Possibly related PRs

Suggested reviewers: senthalan, brionmario, zesu22

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes the primary change: automatically submitting expired consent prompts as denials.
Description check ✅ Passed The description covers the purpose, approach, breaking change, related issues, checklist, security checks, and test coverage.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@packages/react/src/components/presentation/auth/AuthOptionFactory.tsx`:
- Around line 378-385: Centralize consent-decision compilation in
`@thunderid/javascript` by adding the shared compiler, consent optional-key
helper, and exported ConsentDecisions type. Replace the independent
decision-building logic in AuthOptionFactory.tsx (lines 378-385),
BaseAcceptInvite.tsx (lines 555-563), and SignIn.tsx (lines 767-786) with calls
to the shared compiler, preserving the established approval rules and optional
reason handling at all three sites.

In `@packages/react/src/components/presentation/auth/SignIn/SignIn.tsx`:
- Around line 906-938: The timeout effect around handleTimeout must use the
current flow context when the step changes without changing stepTimeout. Refresh
the callback’s access to components, additionalData consentPrompt, and
handleSubmit by updating the effect dependencies with stable references or
synchronizing the current context through refs, while preserving the existing
timeout and deduplication behavior. Add a regression test covering a step update
that retains the same deadline and verifies the timer submits using the updated
context.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3cfcca98-15db-46a2-8e98-8f0b6d9af0ee

📥 Commits

Reviewing files that changed from the base of the PR and between 30c4c81 and 542540c.

📒 Files selected for processing (5)
  • packages/javascript/src/models/embedded-flow.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/BaseAcceptInvite.tsx
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/SignIn/SignIn.tsx
  • packages/react/src/components/presentation/auth/__tests__/AuthOptionFactory.test.tsx

Comment thread packages/react/src/components/presentation/auth/SignIn/SignIn.tsx
The Vue consent paths built decisions without the new top level approved flag,
which broke the build, and one of them still treated an untouched optional claim
as approved. Bring both in line with the React paths and guard the permission
purposes the server sends with a null essential list.

Record a reason for an explicit denial too, so a denial and a timeout are told
apart by the same field rather than by inference. Both values now live in
ConsentConstants in the core package, since five call sites across two framework
packages need them.

Read the timeout callback's flow context through a ref. The effect is keyed on
the deadline alone, so a step arriving with the same deadline would otherwise
leave the pending timer holding the previous step's components and submit
handler.
@brionmario
brionmario merged commit 011ce04 into thunder-id:main Aug 6, 2026
3 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