Skip to content

feat(contract): statement-acceptance readback + step-up problem type (Taiga #199) - #13

Open
t0kubetsu wants to merge 4 commits into
mainfrom
feat/199-statement-readback-stepup-problem
Open

feat(contract): statement-acceptance readback + step-up problem type (Taiga #199)#13
t0kubetsu wants to merge 4 commits into
mainfrom
feat/199-statement-readback-stepup-problem

Conversation

@t0kubetsu

@t0kubetsu t0kubetsu commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Two additive contract changes carried from the PR #1 review closure (Taiga task #199, "contract additions" group). Both shapes were decided in today's follow-up session; nothing here changes an existing operation's behavior.

1. Statement-acceptance readback (review comment A22)

POST /statement-responses was write-only: no operation returned the caller's acceptance state, so the UI could not decide whether to show the ToS/AUP prompt, nor learn which version was answered.

  • New GET /statement-responses (authenticated): the caller's receipts, account-level and context-bound alike.
  • StatementResponseReceipt gains statement_key and version — restated on the receipt so the readback needs no join against retired statement versions (GET /statements only returns what is currently in force). The POST response is enriched the same way.
  • GET /statements stays fully anonymous and untouched. Client rule: a statement in force whose current version has no receipt is one the caller has yet to answer.

Considered and rejected: accepted_version per item on GET /statements (mixes caller state into an anonymous resource) and an acceptance projection on GET /account (grows the account resource with legal-domain state).

2. Step-up MFA problem type (A20 companion; two CodeRabbit review threads asked for the same)

§5.1 defines the assurance signal (amr within max_age), but a client could not distinguish step-up required from a plain 403 — the two have different remedies.

  • The platform's first minted RFC 9457 type URI: https://testing.nc3.lu/problems/step-up-required, documented in §1 and §5.1 and carried by the 403 of the three operations gated on current MFA assurance (POST /api-keys, POST /api-keys/{key_id}/revoke, POST /assets/{asset_id}/verification).
  • The 403 response example in the contract shows the typed body; role-based 403s elsewhere keep about:blank.
  • Wording is IdP-agnostic throughout: assurance is read from the identity provider's token, whichever provider that is.
  • Mechanical enabler: the OpenAPI relabel pass now merges instead of clobbering keys pre-declared under application/problem+json, so the example survives export. Behavior for every existing problem_responses() call is unchanged (empty declared dict).

Checks

make export-openapi committed in sync · make lint clean · make typecheck 0 errors · make test 104/104 (smoke case added for the new operation; test_cases_cover_every_operation green).

Note

Trivial overlap with #12 in statements/router.py (that PR drops the GET /statements 500) — whichever merges second rebases in seconds.

Refs: Taiga #199 (v4.0.1 contract follow-ups from PR #1 review closure).

Summary by CodeRabbit

  • New Features

    • Added an authenticated endpoint to retrieve recorded statement-response receipts with cursor pagination.
    • Receipts now include the statement key and exact version answered.
    • Statement-response submissions now clearly document supported authentication methods.
  • Bug Fixes

    • Clarified API error responses for operations requiring MFA step-up authentication.
    • Improved documentation for general errors and MFA-related 403 responses.
    • Added clearer handling when context-bound statements cannot be submitted.
  • Tests

    • Added coverage for the statement-response receipts endpoint.

…e (Taiga #199)

Two additive contract changes from the PR #1 review closure:

- GET /statement-responses: the caller's receipts, each restating the
  statement_key and exact version answered, so the UI can decide whether
  to show the ToS/AUP prompt without joining against retired statement
  versions (review comment A22). StatementResponseReceipt gains
  statement_key and version, which also enriches the POST response.
- Step-up problem type: the platform's first minted RFC 9457 type URI,
  https://testing.nc3.lu/problems/step-up-required, carried by the 403 of
  the three operations gated on current MFA assurance (API-key create and
  revoke, verification start). Distinguishes step-up — remedied by fresh
  authentication with an MFA method — from a plain forbidden (A20
  companion). The OpenAPI relabel pass now preserves keys pre-declared
  under problem+json so the example survives export.

api-design §1/§5.1/§14 updated; openapi.json re-exported; smoke case added.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1b828448-53d0-4243-9752-a06d39cf4598

📥 Commits

Reviewing files that changed from the base of the PR and between 56c686b and 6a96b00.

📒 Files selected for processing (4)
  • api/openapi.json
  • docs/reference/api-design-v4_0_1.md
  • src/nc3_testing_platform/core/security.py
  • src/nc3_testing_platform/domains/statements/router.py

📝 Walkthrough

Walkthrough

The API adds an authenticated, cursor-paginated statement-response readback endpoint. Receipts now include statement identity and version fields. Protected operations now document structured MFA step-up failures through a shared 403 response contract.

Changes

Statement response receipts

Layer / File(s) Summary
Receipt data contract
src/nc3_testing_platform/domains/statements/schemas.py, src/nc3_testing_platform/domains/statements/router.py, api/openapi.json
StatementResponseReceipt now requires statement_key and version. Recorded and sample receipts include both fields. Context-bound keys return HTTP 409.
Authenticated receipt readback
src/nc3_testing_platform/domains/statements/router.py, api/openapi.json, docs/reference/api-design-v4_0_1.md, tests/test_smoke_surface.py
The Statements API adds authenticated, cursor-paginated GET /api/v1/statement-responses, returns receipt pages, documents the endpoint, and adds smoke-test coverage. The statement-response POST operation declares OpenID Connect or API-key security.

MFA step-up error responses

Layer / File(s) Summary
Step-up error contract
src/nc3_testing_platform/core/errors.py, src/nc3_testing_platform/core/security.py
Adds the STEP_UP_PROBLEM_TYPE constant and step_up_forbidden() helper. MFA assurance failures now use a structured 403 response. Problem-detail media handling preserves declared application/problem+json content.
Protected route response wiring
src/nc3_testing_platform/domains/api_keys/router.py, src/nc3_testing_platform/domains/assets/router.py
API-key creation, API-key revocation, and verification challenge routes use the structured step-up response.
Published error documentation
api/openapi.json, docs/reference/api-design-v4_0_1.md
Documents step-up-required 403 responses, updates response ordering, and specifies about:blank for general errors.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant StatementsAPI
  participant Authenticator
  Client->>StatementsAPI: GET /api/v1/statement-responses with cursor and limit
  StatementsAPI->>Authenticator: Validate caller authentication
  Authenticator-->>StatementsAPI: Authentication result
  StatementsAPI-->>Client: Page_StatementResponseReceipt with next cursor
Loading

Suggested reviewers: nicky-mezzina-lhc

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two primary contract changes: statement-acceptance readback and the step-up problem type.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/199-statement-readback-stepup-problem

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 `@src/nc3_testing_platform/core/errors.py`:
- Around line 75-109: Align step-up handling with
require_current_mfa_assurance() by changing step_up_forbidden() to document the
contracted 401 response and RFC 9470 insufficient_user_authentication challenge,
replacing the current 403 and STEP_UP_PROBLEM_TYPE problem-details response
while preserving the existing step-up remedy semantics.

In `@src/nc3_testing_platform/domains/statements/router.py`:
- Around line 24-32: Update the statement submission and retrieval flow around
_sample_receipt and the POST/GET handlers to persist each accepted response
together with its authenticated principal, construct the POST receipt from the
stored response so the submitted version is preserved, and filter GET results to
the requesting principal. Add coverage for submitted-version accuracy and
isolation between authenticated callers.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4d74fd58-42fa-44fc-aac1-e172c1d7e827

📥 Commits

Reviewing files that changed from the base of the PR and between fe4c97f and 660b152.

📒 Files selected for processing (8)
  • api/openapi.json
  • docs/reference/api-design-v4_0_1.md
  • src/nc3_testing_platform/core/errors.py
  • src/nc3_testing_platform/domains/api_keys/router.py
  • src/nc3_testing_platform/domains/assets/router.py
  • src/nc3_testing_platform/domains/statements/router.py
  • src/nc3_testing_platform/domains/statements/schemas.py
  • tests/test_smoke_surface.py

Comment thread src/nc3_testing_platform/core/errors.py
Comment thread src/nc3_testing_platform/domains/statements/router.py
…eipt echoes the submission

CodeRabbit round 1:
- require_current_mfa_assurance() docstring promised 401 + RFC 9470
  challenge, contradicting the 403s the three gated operations already
  declare and the minted step-up problem type; the seam now states the
  decided contract and why RFC 9470 (acr-based) is not used.
- POST /statement-responses builds its receipt from the submitted
  statement_key and version instead of a fixed sample, so the mock echoes
  what was answered. Stateless-mock behavior otherwise unchanged:
  credential verification is an unwired seam, so caller-scoped storage
  belongs to the implementation phase.
@t0kubetsu

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

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.

@t0kubetsu

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
✅ 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

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 `@src/nc3_testing_platform/core/security.py`:
- Around line 107-109: Update the explanatory comment around
STEP_UP_PROBLEM_TYPE to remove the incorrect RFC 9470 §5.1 and amr attribution.
Either cite the specification that defines amr, or state that the service
evaluates provider-specific amr values and therefore does not emit RFC 9470’s
acr_values challenge.

In `@src/nc3_testing_platform/domains/statements/router.py`:
- Around line 85-91: Update record_statement_response to reject submissions
whose required_context_type is non-null before constructing
StatementResponseReceipt, including context-bound statements identified through
StatementResponseSubmission.scan_target_permission. Preserve the existing
receipt construction for account-level statements and leave context_type and
context_id unset there.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7643cd59-2b89-4c65-8224-8b7b48590a62

📥 Commits

Reviewing files that changed from the base of the PR and between 660b152 and 56c686b.

📒 Files selected for processing (2)
  • src/nc3_testing_platform/core/security.py
  • src/nc3_testing_platform/domains/statements/router.py

Comment thread src/nc3_testing_platform/core/security.py Outdated
Comment thread src/nc3_testing_platform/domains/statements/router.py

@t0kubetsu t0kubetsu left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ECC review (/code-review, PR mode): clean — no findings above LOW. Validation green on the branch (ruff, pyright, 104 tests, OpenAPI regenerated in sync). The step-up 403 problem type is the right remedy discriminator and the RFC 9470 rejection is well-argued; the readback restating statement_key/version avoids joins against retired versions. Advisory only: merge still waits on Nicky's review (fresh CodeRabbit pass also triggered today). Artifact: .claude/reviews/pr-13-review.md.

…ording

The handler now does what its docstring and the API design already
promised: a statement whose key carries a required context answers 409,
because a per-launch declaration travels in the launch payload. The
assurance seam's docstring also disambiguates its section reference —
§5.1 is the API design's, reading RFC 8176 amr values, not a section of
RFC 9470.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 10, 2026
Account-level receipts stay countable on one hand, but context-bound
receipts arrive per launch from v4.1, and turning a bare array into the
Page envelope later would be a breaking contract change. Same CursorPage
dependency and Page shape as every other collection.
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