fix(client)!: request magic links over POST#97
Merged
Conversation
Bccorb
force-pushed
the
fix/magic-link-post
branch
2 times, most recently
from
July 20, 2026 14:21
711987c to
cd2d334
Compare
GET /auth/magic-link was a state-changing route reachable as a simple cross-site request, so an img tag on any page could trigger unbounded magic-link emails to a signed-in user. The Express adapter removed the GET route in favor of POST. The request carries an empty JSON body. The adapter ignores it, but the resulting JSON content type forces a CORS preflight, which is what keeps the route from being reachable cross-site. A bodyless POST would still be a simple request and would leave the vector open. BREAKING CHANGE: requires an adapter serving POST /auth/magic-link (@seamless-auth/express 0.9.0 or later). Against an older adapter, requestMagicLink gets a 404. Callers need no code change. Refs #93
Bccorb
force-pushed
the
fix/magic-link-post
branch
from
July 20, 2026 14:23
cd2d334 to
a5bf59c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #93
Follow-on to fells-code/seamless-auth-server#103, which removed
GET /auth/magic-linkfrom the Express adapter and replaced it withPOST /auth/magic-link.GET /auth/magic-linkwas a state-changing route reachable as a simple cross-site request. With theSameSite=Nonecookie default and no CSRF or Origin check, an<img src="https://api.example.com/auth/magic-link">on any page triggered unbounded magic-link emails to a signed-in user: mailbox flooding, sender-reputation damage, and phishing cover.Change
requestMagicLinkinsrc/client/createSeamlessAuthClient.tsnow issues a POST.It also sends an empty JSON body, which is worth calling out because it is not obvious.
fetchWithAuthonly declaresContent-Type: application/jsonwhen a body is actually present (src/fetchWithAuth.ts). A bodyless POST therefore sends no content type, which is still a CORS simple request: no preflight, so it stays reachable cross-site viafetch(url, { method: 'POST', credentials: 'include', mode: 'no-cors' }). Flipping the verb alone would have looked like a fix without being one. The JSON content type is what forces the preflight and actually closes the vector.The adapter ignores the body (identity comes from the pre-auth cookie), and every other POST in this client already sends a JSON body, so adopter CORS configs already permit the header.
Not changed
Audited the rest of the client:
logoutandlogoutAllSessionsalready useDELETE /logoutandDELETE /logout/all. The adapter's removal ofGET /logoutdoes not affect them.checkMagicLink(GET /magic-link/check) is unchanged in the adapter.verifyMagicLink(GET /magic-link/verify/:token) stays GET on purpose. The path token is the credential and it arrives from an email link.Tests
Added a case asserting
requestMagicLinkcalls/magic-linkwithmethod: 'POST'and the JSON body. No existing test pinned the verb, which is why the SDK could have drifted from the adapter unnoticed.Verified the test is not vacuous: reverting the client to
method: 'GET'fails it while the other 27 suites still pass.Verification
Release ordering
Breaking against older adapters:
requestMagicLinkgets a 404 unless the adapter serves the POST route. fells-code/seamless-auth-server#103 must land and publish before this releases. Changeset isminor, matching the 0.x breaking channel.Downstream consumers (
seamless-portal,seamless-review-web,seamless-templates) all callauthClient.requestMagicLink()rather than hitting the route directly, so they need only a version bump, no code change.Unrelated note
package-lock.jsononmaincarries a stale"version": "0.1.1"whilepackage.jsonis at0.4.0. Anynpm installsilently rewrites it. Kept out of this PR to stay focused; worth a separate chore commit.