Skip to content

Limit OAuth to 37signals staff while it's piloted - #3249

Merged
jeremy merged 3 commits into
mainfrom
oauth-staff-gate-before-consent
Oct 27, 2026
Merged

jeremy merged 3 commits into
mainfrom
oauth-staff-gate-before-consent

Conversation

@jeremy

@jeremy jeremy commented Oct 27, 2026

Copy link
Copy Markdown
Member

Supersedes #3246 for ordering: the staff gate now lands before the consent design (#3212), so main never carries #3212 without the gate. Same change as #3246's final head (c32b732), applied to #3229's head; #3212 now merges this branch and sits on top of it.

Stacked on #3214.

OAUTH_STAFF_ONLY=true limits OAuth to staff (Identity#staff?), whichever client asks. Oauth::Availability's acceptance, issuance and pilot switches choose clients; this one chooses people, and composes with them: a request has to pass both.

This works like Basecamp's, where only staff can authorize an agent for now, and anyone else is told so, not shown a 404.

What's gated

Where Staff Anyone else
Consent screen (GET and approve) as today 403 page: "Only 37signals staff can connect apps to Fizzy for now." No code.
Declining at consent as today still declines, so the app hears no
Code redemption as today invalid_grant, even for a code issued while they were staff
Refresh as today invalid_grant. The grant stays, for its owner to revoke.
An OAuth token at the API, delegated tokens included as today invalid_token, via Identity::AccessToken#honored?
Delegation exchange as today subject refused, via the same honored?
Personal access tokens untouched untouched
Connected Apps, revocation open open

Gating honored? means someone who stops being staff loses what they connected on their next request. That's the only part that runs per request, and it reads the identity, which is already loaded to authenticate.

Turning it on

OAUTH_STAFF_ONLY: "true" is set in the clear env of the production, staging and beta deploy configs, so OAuth lands staff-gated everywhere it might be lit. Opening it up means removing those lines. Anything but exactly "true" is off, and OSS installs never set it.

Tests

test/integration/oauth_staff_only_test.rb covers:

  • staff connect;
  • anyone else is refused at consent, both showing and approving, but can still decline;
  • a de-staffed code doesn't redeem;
  • a de-staffed grant stops authenticating and refreshing;
  • personal access tokens work for anyone.

The exchange test covers a de-staffed subject and its delegated token. Oauth::AvailabilityTest covers admits? and exact-true parsing.

I removed each of the four gates in turn, and each removal fails at least one of these tests.

Stacking note

The personal-access-token exemption reads oauth_client_id.nil? here, as C does. #3205 moves PATs onto a first-party client and adds personal?. When the two meet, this check becomes personal?, or every PAT would be gated.

The staff gate from #3246, reordered to land before the consent design (#3212).
It carries #3246's net change against #3212 as one commit on #3229's head,
with no dependency on #3212.
Copilot AI balanced review requested due to automatic review settings October 27, 2026 03:53
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T04:42:16.735844Z e28fd3b Manual request
🔒 Security Review ✅ Completed 2026-10-27T04:43:43.329953Z e28fd3b Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@jeremy

jeremy commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Security-sensitive OAuth admission and replay-revocation changes need human validation because runtime behavior was not verified during review.

No open issues 🎉

What changed in this PR

Adds a staff-only OAuth pilot gate before the consent-screen rollout. The gate restricts people independently of existing client controls while preserving personal access tokens and grant management.

Changes:

  • Enforces staff admission for consent, code redemption, refresh, OAuth token use, delegation, and app push delivery.
  • Preserves replay-triggered revocation and adds coverage for loss of staff status and personal-token exemptions.
  • Enables the gate in production, staging, and beta deployments.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
test/​test_helpers/​oauth_availability_test_helper.rb Adds staff-only configuration isolation for tests.
test/​models/​oauth/​availability_test.rb Tests identity admission and strict boolean configuration.
test/​integration/​oauth_token_exchange_test.rb Tests delegation refusal after staff status is removed.
test/​integration/​oauth_staff_only_test.rb Covers consent, redemption, refresh, replay revocation, and personal-token exemptions.
test/​integration/​oauth_delegate_resources_test.rb Tests push-delivery gating and avoids identity loading when disabled.
saas/​config/​deploy.staging.yml Enables staff-only OAuth in staging.
saas/​config/​deploy.production.yml Enables staff-only OAuth in production.
saas/​config/​deploy.beta.yml Enables staff-only OAuth in beta.
config/​environments/​production.rb Parses the staff-only environment switch.
app/​views/​oauth/​authorizations/​staff_only.html.erb Adds the staff-only consent refusal page.
app/​models/​push/​subscription.rb Stops app push delivery for nonstaff while gated.
app/​models/​oauth/​availability.rb Defines and documents staff admission checks.
app/​models/​identity/​access_token.rb Gates OAuth token use and extracts code-replay handling.
app/​controllers/​oauth/​tokens_controller.rb Gates issuance while preserving replay-triggered revocation.
app/​controllers/​oauth/​authorizations_controller.rb Refuses nonstaff consent requests while allowing denial.

Review effort: Balanced

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 14fbb6d7ab

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14fbb6d7ab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/models/identity/access_token.rb Outdated
@jeremy

jeremy commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 5ed10fae85

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 5ed10fae85

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Security-sensitive admission and replay-locking changes need human review with MySQL-backed validation, particularly because the replay-lock assertion remains too broad.

1 open issue

Review effort: Balanced

Comment thread test/integration/oauth_audit_test.rb Outdated
@jeremy

jeremy commented Oct 27, 2026

Copy link
Copy Markdown
Member Author

🤖 @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: e28fd3b959

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Security-sensitive replay transaction and locking changes need human review and database-backed concurrency verification.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: e28fd3b959

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Base automatically changed from oauth-dcr-allowlist to main October 27, 2026 06:30
@jeremy
jeremy merged commit 4df6538 into main Oct 27, 2026
15 of 22 checks passed
@jeremy
jeremy deleted the oauth-staff-gate-before-consent branch October 27, 2026 06:30
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