K
Kinde3mo ago
Neil E.

Can't get Kinde nextjs working in Cloudflare worker

I'm trying to integrate Kinde into my opennextjs app. In local development everything is great. I can authenticate, and use permissions to selectively grant access to pages. It's super cool. In my dev deployment using opennextjs to a Cloudflare worker, however, it doesn't work at all. After logging in I either get one of these two errors: Error: State not found. Attempting to commit invalid access_token token I've gone through the guide at https://docs.kinde.com/developer-tools/sdks/backend/nextjs-sdk/#state-not-found-error several times, and triple checked that my environment variables at build time in GitHub and deploy time in Cloudflare match and look correct. Anyone have tips on how to troubleshoot what is wrong here? I assume it is a config/environment variable/mismatch somewhere, but I can't for the life of me see it.
13 Replies
Neil E.
Neil E.OP3mo ago
I did see this issue in GitHub, which sounded similar: https://github.com/kinde-oss/kinde-auth-nextjs/issues/333. However the underlying fix was released in opennextjs 1.0.0-beta.3, and I'm using 1.3.1. I gave this a whirl with a prod deployment too, just to see if it was some sort of dumb dev-only mistake, and I get the same behaviour. Immediately after login I get Attempting to commit invalid access_token token.
Roshan
Roshan3mo ago
Hi Neil. Sorry you've been having this issue. I'll pass this onto one of our support devs who will try to replicate and solve. Can you let us know any additional info relevant for that. Also, have you searching our docs. We have this topic about cloudflare workers as well. In case it helps while you wait. https://docs.kinde.com/integrate/third-party-tools/kinde-edge-workers/#example-cloudflare-workers-integration
Neil E.
Neil E.OP3mo ago
Thanks Claire! I looked at those docs and they didn't really say much beyond "init a project and install Kinde", which I've done. Hopefully a support dev will have an idea for what I can look at, or logging I can enable, to chase down what's going on
Roshan
Roshan3mo ago
Hi Neil, Thank you for reaching out. We really appreciate the troubleshooting you've done so far. Let’s Focus on These 3 Areas Cookie Persistence in Cloudflare Workers The state value used for login verification is stored in a cookie before redirecting to Kinde. If that cookie is not preserved across the redirect (or not returned to the /callback endpoint), you’ll see the state not found error. - Can you confirm if cookies are present in the request to /callback? A quick log of req.headers.cookie at that point might reveal if kinde_state is missing - Ensure that Cloudflare isn’t stripping headers or altering paths during redirects,  especially if you're using custom routes, rewrites, or Cloudflare functions. Runtime Environment Variables in Cloudflare Even though your .env looks correct locally and at build time, Cloudflare requires runtime secrets to be added explicitly in the dashboard or via wrangler. Please double-check these are set via:
npx wrangler secrets list
npx wrangler secrets list

Ensure values like: - KINDE_SITE_URL - KINDE_CLIENT_ID - KINDE_CLIENT_SECRET - KINDE_ISSUER_URL
…are available at runtime, otherwise the SDK will silently fail during token validation, leading to the invalid access_token error.
Routing and Middleware Alignment Since you're on OpenNextJS 1.3.1, that includes the fix for #333, which is great. Just to confirm: - Is your next.config.js using appDir: true and the proper withKindeConfig middleware wrapper? - Are you overriding any Edge runtime behaviors or applying custom middlewares that might conflict?
If you're open to it, please try: 1. Logging req.headers.cookie inside /api/auth/callback 2. Confirming your environment variables via wrangler secrets Thanks again for your patience.
We're also flagging this with our dev team to replicate in a test OpenNext + Cloudflare Worker setup.If we’re able to reproduce the behaviour, we’ll follow up with you in the loop as soon as we know more.
Neil E.
Neil E.OP3mo ago
Thanks for the detailed reply Krish! One thing jumped out at me from your list:
Is your next.config.js using appDir: true and the proper withKindeConfig middleware wrapper?
I don't have any withKindeConfig anywhere. What is that? I don't see it in the docs, and it doesn't seem to exist in the nextjs SDK github repo. I don't have appDir set, but it doesn't matter, that's a setting for migrating old sites. Mine is fresh new and already using app router everywhere.
Neil E.
Neil E.OP3mo ago
My site is super simple at this point. I don't have any custom routes, rewrites, cloudflare functions, additional middleware (beyond the basic stuff for Kinde). I'm using the nextjs SDK so I don't have a /callback endpoint to add logging to for the cookies. I'm using handleAuth() in api/auth/[kindeAuth]/route.js. wrangler secrets list isn't a valid command in my wrangler, but I can look on the cloudflare site and see the secrets and environment variables.
No description
Neil E.
Neil E.OP3mo ago
for what it's worth, the jwt decodes fine and the issuer matches my KINDE_ISSUER_URL environment variable I did a quick search and it looks like I'm not the only person with this problem: https://discord.com/channels/1070212618549219328/1362305610770812978
Roshan
Roshan3mo ago
Thanks Neil for bringing this to our attention. We’ll try to reproduce the behavior on our side and will get back to you with our findings.
Krish - Kinde
Krish - Kinde3mo ago
Thanks again for sticking with this. I’ve gone ahead and tested the full flow using the Kinde Next.js App Router starter kit, deploying it via @opennextjs/cloudflare to Cloudflare Workers, and I’ve now got a working setup.Please check following code change and let us know whether this helps.
 * Added OpenNext Cloudflare package: @opennextjs/cloudflare in package.json * Cloudflare configuration: open-next.config.ts with Cloudflare-specific settings * Wrangler configuration: wrangler.jsonc for Cloudflare Workers deployment package.json
{
"name": "kinde-nextjs-app-router-starter-kit",
"version": "0.1.0",
"scripts": {
"dev": "next dev",
"start": "next start",
"lint": "next lint",
"build": "next build",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
"upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
},
"dependencies": {
"@kinde-oss/kinde-auth-nextjs": "^2.5.0",
"@opennextjs/cloudflare": "^1.3.1",
"@types/node": "20.11.5",
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"eslint": "8.56.0",
"eslint-config-next": "^15.1.2",
"next": "^15.1.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "5.3.3"
},
"devDependencies": {
"wrangler": "^4.20.5"
}
}
{
"name": "kinde-nextjs-app-router-starter-kit",
"version": "0.1.0",
"scripts": {
"dev": "next dev",
"start": "next start",
"lint": "next lint",
"build": "next build",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
"upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
},
"dependencies": {
"@kinde-oss/kinde-auth-nextjs": "^2.5.0",
"@opennextjs/cloudflare": "^1.3.1",
"@types/node": "20.11.5",
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"eslint": "8.56.0",
"eslint-config-next": "^15.1.2",
"next": "^15.1.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "5.3.3"
},
"devDependencies": {
"wrangler": "^4.20.5"
}
}
open-next.config.ts
const { defineCloudflareConfig } = require("@opennextjs/cloudflare");

module.exports = defineCloudflareConfig({
});
const { defineCloudflareConfig } = require("@opennextjs/cloudflare");

module.exports = defineCloudflareConfig({
});
wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"main": ".open-next/worker.js",
"name": "my-app",
"compatibility_date": "2024-12-30",
"compatibility_flags": [
// Enable Node.js API
// see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag
"nodejs_compat",
// Allow to fetch URLs in your app
// see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#global-fetch-strictly-public
"global_fetch_strictly_public",
],
"assets": {
"directory": ".open-next/assets",
"binding": "ASSETS",
},
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
// The service should match the "name" of your worker
"service": "my-app",
},
],
"r2_buckets": [
// Create a R2 binding with the binding name "NEXT_INC_CACHE_R2_BUCKET"
// {
// "binding": "NEXT_INC_CACHE_R2_BUCKET",
// "bucket_name": "<BUCKET_NAME>",
// },
],
}
{
"$schema": "node_modules/wrangler/config-schema.json",
"main": ".open-next/worker.js",
"name": "my-app",
"compatibility_date": "2024-12-30",
"compatibility_flags": [
// Enable Node.js API
// see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag
"nodejs_compat",
// Allow to fetch URLs in your app
// see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#global-fetch-strictly-public
"global_fetch_strictly_public",
],
"assets": {
"directory": ".open-next/assets",
"binding": "ASSETS",
},
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
// The service should match the "name" of your worker
"service": "my-app",
},
],
"r2_buckets": [
// Create a R2 binding with the binding name "NEXT_INC_CACHE_R2_BUCKET"
// {
// "binding": "NEXT_INC_CACHE_R2_BUCKET",
// "bucket_name": "<BUCKET_NAME>",
// },
],
}
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {};

module.exports = nextConfig;
/** @type {import('next').NextConfig} */
const nextConfig = {};

module.exports = nextConfig;
.dev.vars


NEXTJS_ENV=development
NEXTJS_ENV=development
.env
# Server-side (for Kinde SDK)
KINDE_CLIENT_ID=<>
KINDE_CLIENT_SECRET=<>
KINDE_ISSUER_URL=https://<>.kinde.com
KINDE_SITE_URL=https://<>-cloudfare.workers.dev
KINDE_POST_LOGOUT_REDIRECT_URL=https://<>-cloudfare.workers.dev
KINDE_POST_LOGIN_REDIRECT_URL=https://<>-cloudfare.workers.dev/dashboard
# Server-side (for Kinde SDK)
KINDE_CLIENT_ID=<>
KINDE_CLIENT_SECRET=<>
KINDE_ISSUER_URL=https://<>.kinde.com
KINDE_SITE_URL=https://<>-cloudfare.workers.dev
KINDE_POST_LOGOUT_REDIRECT_URL=https://<>-cloudfare.workers.dev
KINDE_POST_LOGIN_REDIRECT_URL=https://<>-cloudfare.workers.dev/dashboard
The core Kinde authentication remains largely unchanged from the standard implementation: * Auth route: src/app/api/auth/[...kindeAuth]/route.ts uses standard handleAuth() * Middleware: src/middleware.ts uses standard withAuth() with public paths configuration route.ts

import { handleAuth } from "@kinde-oss/kinde-auth-nextjs/server";

export const GET = handleAuth();

import { handleAuth } from "@kinde-oss/kinde-auth-nextjs/server";

export const GET = handleAuth();
middleware.ts
import {
withAuth,
} from "@kinde-oss/kinde-auth-nextjs/middleware";

export default withAuth(
async function middleware() {
},
{
publicPaths: ["/", "/api/public"],
}
);

export const config = {
matcher: [
// Run on everything but Next internals and static files
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)"
],
};
import {
withAuth,
} from "@kinde-oss/kinde-auth-nextjs/middleware";

export default withAuth(
async function middleware() {
},
{
publicPaths: ["/", "/api/public"],
}
);

export const config = {
matcher: [
// Run on everything but Next internals and static files
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)"
],
};
Neil E.
Neil E.OP3mo ago
I went through all of what you shared there and it matches the way I have mine set up. It is, essentially, a stock opennextjs/nextjs site with kinde added. You're saying this works for you? You can authenticate on the deployed site without issues? The middleware is different, mine is restricted to a specific path. Other than that, the two setups look the same.
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";

export default function middleware(req: Request) {
return withAuth(req);
}

export const config = {
matcher: ["/guides"],
};
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";

export default function middleware(req: Request) {
return withAuth(req);
}

export const config = {
matcher: ["/guides"],
};
One other difference: I'm using a custom domain, not cloudflare.workers.dev
Krish - Kinde
Krish - Kinde3mo ago
Thank you for troubleshooting. let’s try one last adjustment , Your current middleware setup might be interfering with Kinde's authentication flow, particularly in a Cloudflare Workers environment
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";

export default withAuth(
async function middleware() {
// Optional: insert logic here (e.g. logging, role checks)
},
{
publicPaths: ["/", "/api/public"], // Add any non-protected paths here
}
);

export const config = {
matcher: [
// Match everything except static files and Next internals
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)"
],
};
import { withAuth } from "@kinde-oss/kinde-auth-nextjs/middleware";

export default withAuth(
async function middleware() {
// Optional: insert logic here (e.g. logging, role checks)
},
{
publicPaths: ["/", "/api/public"], // Add any non-protected paths here
}
);

export const config = {
matcher: [
// Match everything except static files and Next internals
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)"
],
};
This ensures: Kinde’s internal routes like /api/auth/* are included in middleware Protected routes are enforced Middleware behaves predictably on Cloudflare’s edge runtime Please let me know how it goes.
Neil E.
Neil E.OP3mo ago
I gave that a try, still doesn't work. Not sure why adding /api/public would make a difference since the route is /api/auth. I tried making /api/auth a public path as well and still have the same error. @Marek Urbanowicz did you ever solve this for your project? This is fixed. Marek replied in another discord, the issue was this: https://github.com/kinde-oss/kinde-typescript-sdk/pull/70 However, I didn't have that fix in my Kinde installation because I was using an older version to work around this issue: https://github.com/kinde-oss/kinde-auth-nextjs/issues/347 That bug was fixed a few days ago though, so I updated to 2.8.1 and it worked!
Roshan
Roshan3mo ago
That’s great to hear Neil, thanks so much for following up and sharing the exact resolution.

Did you find this page helpful?