Build1 publisher3 min readPublished
A Cloudflare Worker generates the code_challenge IFTTT's redirect leaves out
IFTTT's authorization redirect arrives with no code_challenge, and an authorization server that mandates PKCE answers invalid_request. Publora's developer kept the requirement and put a Cloudflare Worker in front of it.
The Engineer · Build desk

What happened
- The same request with a code_challenge attached reached the login screen normally, isolating the failure to that one absent PKCE field.
- The developer kept PKCE mandatory and inserted a Cloudflare Worker: plain OAuth2 and a non-expiring token facing IFTTT, a full PKCE exchange facing Publora's authorization server.
- IFTTT's automated run has 535 checks, among them a required test/setup endpoint that the developer could not find documented anywhere.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Any team whose authorization server mandates PKCE faces the same fork when IFTTT knocks: write a per-client exemption into the server, or run the PKCE half somewhere IFTTT never sees.
- exposure The proxy is now the credential holder. It hands IFTTT a token with no expiry, so revoking IFTTT's access is work done at the Worker and not at the authorization server.
- cost The real adoption cost sits in IFTTT's harness, where failure detail comes back only through a session POST and a chunked results GET.
- constraint A service with sparse activity cannot certify a trigger as it stands, because IFTTT will not pass one that returns fewer than three events during testing.
PKCE binds two messages. The client generates a verifier, sends its hash as code_challenge on the authorization request, then presents the verifier when it trades the code for a token [6]. Publora's authorization server requires that first parameter [2]. IFTTT's redirect does not carry it, so validation kills the request before the login screen: a 302 back with error=invalid_request and "expected string, received undefined" on code_challenge [3]. Resend the same URL with a challenge attached and the login screen renders [4].
The shim runs both sides of that exchange. Facing IFTTT it is a plain OAuth2 provider with no PKCE and a token that never expires. Facing Publora it generates the verifier and challenge itself before calling the authorization server [6].
So the Worker now owns the part of the flow that used to run end to end. The verifier lives inside it, and the credential IFTTT stores is one the Worker minted, with no expiry [6]. Cutting IFTTT off is therefore an operation on the Worker.
Making PKCE optional for IFTTT was the other option on the table [5]. In my view the proxy is the better call, for one reason: an exemption inside the authorization server is a branch that every request walks past, and it applies to anything presenting that client registration. A proxy leaves the server with one rule for all clients.
This becomes your problem if your authorization server rejects an authorization request with no code_challenge. That is one config line to check. Publora's developer, Eugeniya Ivanova, describes one integration against one server, and the post does not say whether IFTTT can be configured to send a challenge.
IFTTT's automated run has 535 checks [17], and what they test is IFTTT's contract. A test/setup endpoint is required, the documentation omits it, and the automated suite hits it second; the scaffold JSON in the dashboard needs a queries section, and media_url cannot be an empty string [12]. Requests with triggerFields missing have to come back as a 400 with an errors array, and supplying defaults instead made those checks fail [15]. A truncated feed needs a cursor [16].
The trigger contract is a feed of up to fifty events, newest first, each with its own meta.id and a timestamp in seconds [8]. One Publora post group has to fan out to one event per channel, or meta.id repeats [9]. Three channels means three events per group, so fifty slots hold sixteen groups [20]. IFTTT also tests that a trigger returns at least three events, so an empty or short response fails [10].
Ivanova wrote of this platform-side work: "This is where I spent most of the time." [19] The slug generator doubled ingredient names into values like contentcontent without showing it in the form, and the corrupted slug was visible only in the service export under Tools, Export [13]. Failed-check detail took two calls, a POST to open a session and a GET for chunked results ending at suite_end; the failed row in the UI expands to an empty panel [14]. Review itself, estimated at one to two weeks, passed in under a day [18].
Hourly polling went out with the same change. A Publora webhook is created when the connection is set up, and when an event arrives the Worker calls IFTTT's Realtime API without the event data, leaving IFTTT to fetch the feed [11].
What to watch
- Whether IFTTT adds a PKCE option to service authorization, which would make the proxy deletable.
- Whether the Worker-issued non-expiring token gets a revocation path, since nothing in the flow expires it.
- Whether IFTTT documents test/setup and surfaces failed-check detail in the dashboard instead of only through its results API.