Skip to content

fix: take a yes for the terms at app sign-in instead of a version - #1385

Merged
mnapoli merged 1 commit into
dev-null-sh:productionfrom
simonhamp:feat/terms-accepted-boolean
Nov 3, 2026
Merged

mnapoli merged 1 commit into
dev-null-sh:productionfrom
simonhamp:feat/terms-accepted-boolean

Conversation

@simonhamp

@simonhamp simonhamp commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

The app's sign-in screen asks people to accept the terms, then tells us when it redeems its code. Until now it had to send terms_version, and anything other than the exact version in force got a 422.

That doesn't work for an app. Its version is fixed when it's built, so raising legal.terms_version would stop every installed build from signing in until people update. The 1 November terms change has already moved the version past the one in the unmerged app PR, NativePHP/dev-null-app#82. The value wasn't used either: we record terms_accepted_at as now() and compare that with the version.

POST /api/v1/devices now takes terms_accepted as a boolean instead. The app links to the live terms and privacy pages and carries no copy of them, so the person accepted whatever was in force at that moment. The recorded time already says which version that is.

What to check:

  • No released app sends terms_version, because Improve commit star #82 is the first app change to use it. If one did, the field would now be ignored rather than refused, and acceptance wouldn't be recorded.
  • PUT for accepting from a signed-in session still takes the version, for the app and the web. Those clients read it from the server just before sending, so they can't go stale and I left them alone.
  • #558 adds Apple sign-in to this same endpoint and doesn't mention terms yet. The app sends terms_accepted for Apple as well, so that path needs to record it once feat: link and sign in with Apple from the API #558 lands.

This should merge before #82. If #82 ships first, sign-in still works but acceptance isn't recorded until this is deployed.

Device, terms and current user API tests pass, along with Pint, Rector and PHPStan on the changed files. I didn't run the full suite.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Changes
    • Device registration now records acceptance of the current terms when the terms-accepted option is set to true. If omitted or false, acceptance remains unset.
    • The option must be a boolean; other values are rejected.

The app had to send the exact version in force or sign-in failed with a 422. A version in the app is fixed when it's built, so raising the version would lock out every installed build. The value also did nothing: acceptance is recorded as a timestamp and compared with the version.

The app now says terms_accepted, and the time we record stands for the version in force.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

The API device request now accepts terms_accepted instead of terms_version. A true value records acceptance of the current terms. Tests cover omitted, false, and invalid values.

Changes: API device terms acceptance

Layer / File(s) Summary
Boolean acceptance contract
app/Http/Requests/CreateApiDeviceRequest.php, app/Http/Controllers/Api/DeviceController.php, tests/Feature/Controllers/Api/DeviceControllerTest.php
The request validates optional terms_accepted as a boolean. The controller records acceptance when it is true. Tests verify acceptance, omitted and false values, and validation errors for non-boolean values.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Possibly related PRs

Suggested reviewers: shanerbaner82, nunomaduro

Merge Risk: Minimal · up to 64ca0

Device sign-in now records terms acceptance from a boolean, so installed app builds no longer fail when the terms version changes. The only edge case is a terms rollout landing between a person accepting and the device sign-in completing, which is rare and consistent with the intended design. The change is ready to merge.

Security architecture risk: Low · up to 64ca0

Device authentication and account ownership controls remain intact. The main risk is limited to acceptance records: a terms change during sign-in can cause earlier consent to be recorded against the newer terms. Compatibility with external app releases also remains unverified.

Retained concerns

  • Low · architecture · inferred: If terms change after the person accepts but before device-code redemption, the new boolean request can record acceptance of the newer revision without renewed consent. The former exact-version check rejected an older submitted revision; the unchanged timestamp comparison now lacks that safeguard over the client sign-in interval. This affects consent-record integrity, not device authentication.
Security review details

Security Blast Radius

  • inferred — The changed input affects the acceptance timestamp of the user bound to a successfully redeemed device code. It does not provide independent cross-account reachability. The record is user-level rather than device-local, so its derived acceptance status is visible to other sessions using that user.

Security Findings and Attack Paths

  • inferred — The supported concern is privacy-consent attribution across a terms revision change, not a demonstrated privilege-escalation path. A legitimate consent given before a revision change can be submitted afterward as true and receive a current server timestamp. The five-minute authorization-code lifetime does not identify when consent occurred.

Trust Boundaries and Controls

  • observed — Acceptance remains a client assertion, and it was optional at the base endpoint as well. Successful sign-in without acceptance is therefore not a newly introduced bypass of a mandatory terms gate. Downstream signed API authority continues to derive from the device signature and fingerprint, not the terms boolean.
Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the main change: sign-in now accepts a terms-acceptance response instead of a terms version.
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 3 files.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0b7f650c-05ff-42c2-9f96-2a6527595baa
Commits

Reviewing files that changed from the base of the PR and between d53d807 and 64ca0db.

Files selected for processing: 3
  • app/Http/Controllers/Api/DeviceController.php
  • app/Http/Requests/CreateApiDeviceRequest.php
  • tests/Feature/Controllers/Api/DeviceControllerTest.php

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

simonhamp added a commit to NativePHP/dev-null-app that referenced this pull request Nov 2, 2026
The phone sent a hard-coded terms version and the server refuses anything but the one in force, so the 1 November terms update would have made sign-in fail with a 422, and every later update would do the same to builds already installed. The server now takes a yes (dev-null-sh/dev-null.sh#1385) and works out the version from when it hears.

Both Client methods always send it. A sign-in can't start without the tick, and reading the tick when the sign-in finishes lost it whenever the browser's answer landed on a new onboarding screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simonhamp
simonhamp marked this pull request as ready for review November 3, 2026 12:38
@simonhamp

Copy link
Copy Markdown
Contributor Author

@mnapoli @xHeaven could you guys review? we'll need this in the app in time for launch

@mnapoli
mnapoli merged commit 3c79473 into dev-null-sh:production Nov 3, 2026
7 checks passed
simonhamp added a commit to NativePHP/dev-null-app that referenced this pull request Nov 3, 2026
… terms change (#82)

* Ask people to agree to the terms and privacy policy before they sign in

Neither Sign in with GitHub nor Sign in with Apple works until the box is ticked, and links open the terms and privacy policy in the app's browser. The phone tells devnull which version it showed as it signs in (terms_version), which devnull records.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Say the terms were accepted instead of naming a version

The phone sent a hard-coded terms version and the server refuses anything but the one in force, so the 1 November terms update would have made sign-in fail with a 422, and every later update would do the same to builds already installed. The server now takes a yes (dev-null-sh/dev-null.sh#1385) and works out the version from when it hears.

Both Client methods always send it. A sign-in can't start without the tick, and reading the tick when the sign-in finishes lost it whenever the browser's answer landed on a new onboarding screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Ask for terms that changed before anything else, and show them in Settings

When devnull says the terms in force haven't been accepted, the app now opens on
a screen that asks for them, like the web's terms dialog. Launch goes straight
there when the last answer said so, and any /me answer that says so sends you
there from whatever screen you're on. Accepting sends the version /me named,
so terms that change again while the screen is open get a 422 and are asked
for again. Sign out and Delete account are the other ways out.

Account settings gets a Terms and privacy section just before Delete account:
links to both pages in the in-app browser, and the version in force beside the
one you agreed to and when. devnull only keeps when you accepted, so an older
version shows as "an older version" rather than a date.

Sign out and delete account move into a LeavesDevnull concern so the new
screen can share them with Settings, and the two page links into
OpensLegalPages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Say which terms you accepted in one sentence in Settings

The current, agreed to and accepted rows put two version names and one
acceptance day side by side, all as dates, and agreed to always matched
current because the terms screen takes over when they differ. One
sentence says it plainly: "You accepted the terms of Nov 1, 2026 on
Nov 3, 2026."

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Simon <simon.hamp@me.com>
@simonhamp
simonhamp deleted the feat/terms-accepted-boolean branch November 3, 2026 19:29
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