Skip to content

Commit ba2d057

Browse files
committed
chore(release): 1.0.9
Changelog entries for the three PRs merged into main: the per-recipient bound on verification emails (#39), the client-IP resolution fix (#40), and mailbox-keyed rate limits (#41). Both security items are listed under Security rather than folded into Fixed, since each was exploitable: replayed credentials from an unverified account could drive unlimited mail at the registered address, and the activity log recorded whatever address a caller claimed. The removal of the change_email_rate_limit_* options is called out explicitly, because sites that set them lose them. Version bumped in all five places that carry it, including src/WorkOS/Plugin.php, which 1.0.8 missed. That property backs WORKOS_VERSION, so leaving it stale makes the runtime disagree with the plugin header and the .org listing. Note that AGENTS.md documents four locations and omits package.json, and .puprc cannot help here because it targets a workos.php that does not exist.
1 parent e33a876 commit ba2d057

6 files changed

Lines changed: 46 additions & 5 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
Enterprise identity management for WordPress powered by WorkOS. SSO, directory sync, MFA, and user management.
66

7-
- **Version:** 1.0.8
7+
- **Version:** 1.0.9
88
- **Namespace:** `WorkOS\`
99
- **PHP Requirement:** 7.4+
1010
- **WordPress Requirement:** 5.9+

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Changelog
22

3+
## [1.0.9] - 2026-08-05
4+
5+
### Security
6+
7+
- **Unlimited verification emails from the sign-in endpoint** (#39) — an account that signed up and abandoned email verification left a working credential behind, and replaying it against `POST /wp-json/workos/v1/auth/password/authenticate` made WorkOS mail a fresh verification code every time. The only bound was 5 per minute against the requester, which is sustainable indefinitely, and the transient counter behind it was non-atomic so a concurrent burst passed straight through. Email sends are now charged against the recipient across layered windows: 3 per 5 minutes, 5 per hour, and 10 per 8 hours. The sign-in path reserves a slot before calling WorkOS and refunds it when the response shows nothing was sent, so a failed password or a completed login costs the recipient nothing. Magic-code send and password-reset start share one recipient bucket, so rotating between endpoints buys no extra mail.
8+
- **Activity log recorded forgeable IP addresses** (#40) — the log read forwarded-for headers without checking that the request had actually passed through a proxy, and took the left-most entry, which is the portion a client writes. Any caller could attribute their own actions to an address of their choosing, which made the log unusable as evidence for the thing it exists to record. The address the webserver saw is now the default, and it cannot be forged.
9+
10+
### Added
11+
12+
- **`WORKOS_CLIENT_IP_HEADER` constant** (#40) — names the proxy header to trust on sites behind a CDN. Setting it is a deliberate assertion that the origin accepts nothing but proxied traffic, because an origin reachable directly makes the header attacker-controlled again.
13+
14+
### Changed
15+
16+
- **Rate limits count the mailbox, not the string as typed** (#41) — `alice@gmail.com`, `alice+1@gmail.com`, and `a.l.i.c.e@gmail.com` each earned a separate allowance against a single inbox, which was enough to type past the cap. Anything whose effect is mail now counts the mailbox an address lands in. Rewrites apply per provider rather than universally: merging two real mailboxes would let either starve the other, and `business+brian@corp.com` is routinely its own mailbox with its own owner. The address sent to WorkOS is untouched; the reduced form is only ever a counter key, never a destination.
17+
- **Guessing limits still count the address exactly as typed** (#41) — WorkOS keys accounts on the exact string, so `alice@` and `alice+work@` can both exist with separate passwords. Merging them would let an attacker on one lock out the other, so password and code attempts keep the literal address as their subject.
18+
- **One answer to "who is calling"** (#40) — rate limiting, the activity log, and the audit log each had their own IP resolution and could name three different callers for one request. They now share one implementation.
19+
- **Rate limiting moved into its own domain** (#39) — a single-window interface with transient and object-cache implementations, plus a wrapper that stacks windows into a policy. Previously this lived inside the AuthKit package while being used across the plugin.
20+
21+
### Removed
22+
23+
- **`change_email_rate_limit_*` options** (#39) — the change-email flow now uses the shared tiered limiter. Sites that set these will lose them; the layered send policy replaces them.
24+
25+
### Fixed
26+
27+
- **Concurrent-burst bypass on rate limits** (#39) — sites with a persistent object cache now get atomic counters. Sites without one keep the read-modify-write path, which the longer window tiers bound.
28+
- **Magic-code verify accepted malformed addresses** (#41) — it took any non-empty string; it now rejects addresses that are not valid.
29+
330
## [1.0.8] - 2026-06-30
431

532
### Added

integration-workos.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Plugin Name: Integration with WorkOS
44
* Plugin URI: https://github.com/bordoni/integration-workos
55
* Description: Enterprise identity management for WordPress powered by WorkOS. SSO, directory sync, MFA, and user management.
6-
* Version: 1.0.8
6+
* Version: 1.0.9
77
* Author: Gustavo Bordoni
88
* Author URI: https://github.com/bordoni
99
* License: GPL-2.0-or-later

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "integration-workos",
3-
"version": "1.0.8",
3+
"version": "1.0.9",
44
"description": "Enterprise identity management for WordPress powered by WorkOS.",
55
"private": true,
66
"scripts": {

readme.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Tags: sso, identity, workos, authentication, directory-sync
55
Requires at least: 6.2
66
Tested up to: 6.9
77
Requires PHP: 7.4
8-
Stable tag: 1.0.8
8+
Stable tag: 1.0.9
99
License: GPL-2.0-or-later
1010
License URI: https://www.gnu.org/licenses/gpl-2.0.html
1111

@@ -175,6 +175,17 @@ WorkOS is provided by WorkOS, Inc.
175175

176176
== Changelog ==
177177

178+
= 1.0.9 - 2026-08-05 =
179+
180+
* Fix: Unlimited verification emails from the sign-in endpoint. Credentials from an account that never finished email verification could be replayed to make WorkOS send a fresh code every time, bounded only at 5 per minute against the caller. Email sends are now counted against the recipient across 5 minute, 1 hour, and 8 hour windows, and a reserved slot is refunded when the response shows nothing was sent. (#39)
181+
* Fix: The activity log no longer records forgeable IP addresses. Forwarded-for headers were trusted without a proxy in front, and the left-most entry was used, so any caller could attribute their actions to an address of their choosing. The address the webserver saw is now used by default. (#40)
182+
* New: `WORKOS_CLIENT_IP_HEADER` constant names a trusted proxy header for sites behind a CDN. Only set it when the origin accepts nothing but proxied traffic. (#40)
183+
* Fix: Rate limits now count the mailbox an address lands in, so plus-tags and dots on providers that ignore them no longer multiply a single inbox's allowance. The address sent to WorkOS is unchanged. Sign-in and code guessing still count the address exactly as typed, since WorkOS treats those as separate accounts. (#41)
184+
* Fix: Rate limiting, the activity log, and the audit log now agree on the caller's IP address instead of each resolving it separately. (#40)
185+
* Fix: Sites with a persistent object cache now get atomic rate-limit counters, closing a bypass where concurrent requests passed the same check. (#39)
186+
* Fix: Magic-code verify rejects malformed email addresses instead of accepting any non-empty string. (#41)
187+
* Fix: Removed the `change_email_rate_limit_*` options. The change-email flow now uses the shared tiered rate limiter. (#39)
188+
178189
= 1.0.8 - 2026-06-30 =
179190

180191
* New: Change a user's email from the WorkOS → Users admin page, alongside "Open in WorkOS" and "Send password reset". (#33)
@@ -272,6 +283,9 @@ Base platform:
272283

273284
== Upgrade Notice ==
274285

286+
= 1.0.9 =
287+
Security release. Closes an unlimited-verification-email path on the sign-in endpoint and stops the activity log trusting spoofable IP headers. Rate limits now count the mailbox an address lands in, so plus-tags no longer multiply an inbox's allowance. Removes the `change_email_rate_limit_*` options in favour of the shared tiered limiter; sites behind a CDN should set `WORKOS_CLIENT_IP_HEADER`.
288+
275289
= 1.0.8 =
276290
Adds a "Change email" action to the WorkOS → Users admin page and makes admin-of-other email changes commit immediately, while self-service keeps the verified token flow. Also fixes a 500 in the admin password-reset endpoint when the profile field is empty.
277291

src/WorkOS/Plugin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class Plugin {
5757
*
5858
* @var string
5959
*/
60-
private string $version = '1.0.8';
60+
private string $version = '1.0.9';
6161

6262
/**
6363
* Container instance.

0 commit comments

Comments
 (0)