Upgrade example app to Next.js 16, React 19, and AuthKit v4 (AUTH-6473) - #75
Upgrade example app to Next.js 16, React 19, and AuthKit v4 (AUTH-6473)#75Rmole57 wants to merge 3 commits into
Conversation
Greptile SummaryThis PR upgrades the example application to Next.js 16, React 19, and current WorkOS SDK APIs.
Confidence Score: 4/5The PR is not yet safe to merge because password-reset bearer tokens are written to application logs and can be used to take over accounts. The prior client-side token disclosure has been removed, but sendReset now logs the complete reset URL containing the token that resetPassword accepts with a caller-selected password. Files Needing Attention: src/app/using-your-own-ui/reset-password/reset-password.ts
|
| Filename | Overview |
|---|---|
| src/app/using-your-own-ui/reset-password/reset-password.ts | Replaces browser disclosure with server logging of the password-reset bearer token, leaving account takeover possible for log readers. |
| src/app/using-your-own-ui/reset-password/page.tsx | Stops rendering reset tokens or distinguishable account-existence responses in the client. |
| package.json | Upgrades Next.js, React, WorkOS SDKs, ESLint, and associated type packages. |
| src/proxy.ts | Migrates the AuthKit middleware convention to the Next.js 16 proxy API. |
Sequence Diagram
sequenceDiagram
participant U as Requester
participant A as sendReset
participant W as WorkOS
participant L as Application logs
participant R as resetPassword
U->>A: Submit account email
A->>W: createPasswordReset(email)
W-->>A: passwordResetToken
A->>L: Log complete reset URL and token
L-->>U: Token visible to a log reader
U->>R: Submit token and chosen password
R->>W: resetPassword(token, newPassword)
Prompt To Fix All With AI
### Issue 1
src/app/using-your-own-ui/reset-password/reset-password.ts:33-38
**Reset token leaks through logs**
When a reset is created for an existing account, this code writes the bearer token and complete reset URL to application logs, allowing anyone with log access to submit the token with a chosen password and take over the account.
**How this was verified:** The token returned by `createPasswordReset` is interpolated into `console.log`, and `resetPassword` accepts that token with a caller-selected password.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (2): Last reviewed commit: "Stop returning the password reset token ..." | Re-trigger Greptile
|
@greptileai update |
| console.log( | ||
| `[example] Password reset for ${reset.email} — in a real app, email this link:\n` + | ||
| ` http://localhost:3000/using-your-own-ui/reset-password` + | ||
| `?token=${encodeURIComponent(reset.passwordResetToken)}` + | ||
| `&email=${encodeURIComponent(reset.email)}` | ||
| ); |
There was a problem hiding this comment.
Reset token leaks through logs
When a reset is created for an existing account, this code writes the bearer token and complete reset URL to application logs, allowing anyone with log access to submit the token with a chosen password and take over the account.
How this was verified: The token returned by createPasswordReset is interpolated into console.log, and resetPassword accepts that token with a caller-selected password.
Rule Used: Do not log sensitive fields like access_token, ref... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/using-your-own-ui/reset-password/reset-password.ts
Line: 33-38
Comment:
**Reset token leaks through logs**
When a reset is created for an existing account, this code writes the bearer token and complete reset URL to application logs, allowing anyone with log access to submit the token with a chosen password and take over the account.
**How this was verified:** The token returned by `createPasswordReset` is interpolated into `console.log`, and `resetPassword` accepts that token with a caller-selected password.
**Rule Used:** Do not log sensitive fields like access_token, ref... ([source](https://app.greptile.com/workos/-/custom-context?memory=395edb09-f95b-4759-834f-94cb847721f5))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
yeah can you add a
if (process.env.NODE_ENV == 'development') { console.log(...) }
gate here, just in case?
There was a problem hiding this comment.
Good call. Here's the suggestion:
| console.log( | |
| `[example] Password reset for ${reset.email} — in a real app, email this link:\n` + | |
| ` http://localhost:3000/using-your-own-ui/reset-password` + | |
| `?token=${encodeURIComponent(reset.passwordResetToken)}` + | |
| `&email=${encodeURIComponent(reset.email)}` | |
| ); | |
| if (process.env.NODE_ENV === 'development') { | |
| console.log( | |
| `[example] Password reset for ${reset.email} — in a real app, email this link:\n` + | |
| ` http://localhost:3000/using-your-own-ui/reset-password` + | |
| `?token=${encodeURIComponent(reset.passwordResetToken)}` + | |
| `&email=${encodeURIComponent(reset.email)}` | |
| ); | |
| } |
This keeps the convenience for local dev while ensuring the token is never logged in staging or production environments where log access is less controlled.
Summary
Upgrades the example app from Next.js 14.1.4 to 16.2.12, which pulls React 19 and
majors of both WorkOS SDKs along with it. All required migrations are applied here.
Dependencies
nextreact/react-dom@workos-inc/authkit-nextjs@workos-inc/nodeeslinteslint-config-next@types/react/@types/react-domBreaking changes handled
Next.js 16
src/middleware.ts→src/proxy.ts,authkitMiddleware→authkitProxy. Next 16 errors (E900) if both files exist.searchParamsis nowPromise<…>in 7 pages, andcookies()is awaited inwith-session/auth.ts.reset-password/page.tsxis a Client Component, so it unwraps viause(searchParams).next lintwas removed:.eslintrc.json→eslint.config.mjs(flat config),lintscript is now plaineslint.tsconfig:targetes5→ES2017; Next's build also requiresjsx: react-jsxand adds.next/dev/types.React 19
useFormState(react-dom) →useActionState(react) across 8 components.authkit-nextjs v4
getUser()→withAuth(); added the now-requiredAuthKitProviderto the root layout.WORKOS_REDIRECT_URI→NEXT_PUBLIC_WORKOS_REDIRECT_URIin.env.local.exampleand inusing-hosted-authkit/README.md. v4 reads only the prefixed name.getSignInUrl()writes a PKCE cookie, so Next 16 forbids calling it during render. Thewith-nextjsexample now signs in via a POST Server Action instead of an<a href>.@workos-inc/node v10
workos.mfa→workos.multiFactorAuth;userManagement.enrollAuthFactor→multiFactorAuth.createUserAuthFactor;sendMagicAuthCode→createMagicAuth.Behavior change worth a look
sendPasswordResetEmailis gone. Its replacement,createPasswordReset, dropped thepasswordResetUrlinput and no longer sends an email — it mints the token and returns it, so delivery is now the application's responsibility. The example renders a labelled link to keep the flow clickable, and comments on why a real app must email the token instead. This is the one place where the example demonstrates something different than before, so it may warrant a product call rather than just a mechanical migration.Also in this PR
yarn.lockremoved,package-lock.jsonadded, README install step updated. The committedyarn.lockwas in Yarn Berry format while Yarn 1 is what most environments resolve, so installs had been rewriting it inconsistently.JWT_SECRET_KEYto.env.local.example— thewith-sessionexample already required it but it was undocumented.Upgrading an existing local checkout
Only one step isn't covered by this PR, because the file is gitignored: rename
WORKOS_REDIRECT_URItoNEXT_PUBLIC_WORKOS_REDIRECT_URIin your own.env.local(the value is unchanged). The old name is silently ignored, so auth fails with no clear error.Testing
tsc --noEmitcleaneslintcleannext buildgreen — 29/29 routes,proxy.tsdetectedCloses AUTH-6473