You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We use NextJS and Keycloak in docker containers, and for the most part it is working well. However, sometimes when a user attempts to login, it throws a CallbackRouteError with error description "PKCE verification failed: Code mismatch":
[auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror
[auth][cause]: ResponseBodyError: server responded with an error in the response body
at processGenericAccessTokenResponse (webpack-internal:///(rsc)/./node_modules/oauth4webapi/build/index.js:1208:19)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async processAuthorizationCodeOpenIDResponse (webpack-internal:///(rsc)/./node_modules/oauth4webapi/build/index.js:1419:20)
at async handleOAuth (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/oauth/callback.js:164:35)
at async Module.callback (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/index.js:47:41)
at async AuthInternal (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/index.js:43:24)
at async Auth (webpack-internal:///(rsc)/./node_modules/@auth/core/index.js:130:34)
at async /workspace/client/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:53446
at async e_.execute (/workspace/client/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:44747)
at async e_.handle (/workspace/client/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:54700)
at async doRender (/workspace/client/node_modules/next/dist/server/base-server.js:1377:42)
at async cacheEntry.responseCache.get.routeKind (/workspace/client/node_modules/next/dist/server/base-server.js:1587:40)
at async DevServer.renderToResponseWithComponentsImpl (/workspace/client/node_modules/next/dist/server/base-server.js:1507:28)
at async DevServer.renderPageComponent (/workspace/client/node_modules/next/dist/server/base-server.js:1924:24)
at async DevServer.renderToResponseImpl (/workspace/client/node_modules/next/dist/server/base-server.js:1962:32)
at async DevServer.pipeImpl (/workspace/client/node_modules/next/dist/server/base-server.js:920:25)
at async NextNodeServer.handleCatchallRenderRequest (/workspace/client/node_modules/next/dist/server/next-server.js:272:17)
at async DevServer.handleRequestImpl (/workspace/client/node_modules/next/dist/server/base-server.js:816:17)
at async /workspace/client/node_modules/next/dist/server/dev/next-dev-server.js:339:20
at async Span.traceAsyncFn (/workspace/client/node_modules/next/dist/trace/trace.js:154:20)
at async DevServer.handleRequest (/workspace/client/node_modules/next/dist/server/dev/next-dev-server.js:336:24)
at async invokeRender (/workspace/client/node_modules/next/dist/server/lib/router-server.js:174:21)
at async handleRequest (/workspace/client/node_modules/next/dist/server/lib/router-server.js:353:24)
at async requestHandlerImpl (/workspace/client/node_modules/next/dist/server/lib/router-server.js:377:13)
at async Server.requestListener (/workspace/client/node_modules/next/dist/server/lib/start-server.js:141:13)
[auth][details]: {
"error": "invalid_grant",
"error_description": "PKCE verification failed: Code mismatch",
"provider": "keycloak"
}
This is our auth.ts config:
import NextAuth, { NextAuthConfig } from "next-auth"
import { JWT } from 'next-auth/jwt';
import Keycloak from "next-auth/providers/keycloak"
import dayjs from 'dayjs';
declare module 'next-auth/jwt' {
interface JWT {
access_token?: string;
id_token?: string;
expires_at: number;
refresh_token?: string;
error?: string;
}
}
const AUTH_SECRET = process.env.AUTH_SECRET;
const KEYCLOAK_CONTAINER_ENDPOINT = process.env.KEYCLOAK_CONTAINER_ENDPOINT;
const KEYCLOAK_PUBLIC_URL = process.env.NEXT_PUBLIC_KEYCLOAK_URL;
const DEBUGGING_ENABLED = process.env.DEBUG_AUTH === "true";
const COOKIES_LIFE_TIME = 24 * 60 * 60; // Lifetime of authjs state cookies in seconds (does not affect session expiration)
const COOKIE_PREFIX = process.env.NODE_ENV === 'production' ? '__Secure-' : '';
// Attempt to refresh the Keycloak session
const refreshAccessToken = async (token: JWT) => {
const url = `${KEYCLOAK_CONTAINER_ENDPOINT}/protocol/openid-connect/token`;
const resp = await fetch(url, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
client_secret: process.env.KEYCLOAK_CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: <string>token.refresh_token,
}),
method: 'POST',
});
const refreshToken = await resp.json();
if (!resp.ok) throw refreshToken;
return {
...token,
access_token: refreshToken.access_token,
id_token: refreshToken.id_token,
expires_at: Math.floor(Date.now() / 1000) + refreshToken.expires_in,
refresh_token: refreshToken.refresh_token
};
};
export const authOptions: NextAuthConfig = {
secret: AUTH_SECRET,
trustHost: true,
debug: DEBUGGING_ENABLED,
providers: [
Keycloak({
jwks_endpoint: `${KEYCLOAK_CONTAINER_ENDPOINT}/protocol/openid-connect/certs`,
wellKnown: undefined,
clientId: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
issuer: `${KEYCLOAK_PUBLIC_URL}`,
authorization: {
params: {
scope: "openid",
},
url: `${KEYCLOAK_PUBLIC_URL}/protocol/openid-connect/auth`,
},
token: `${KEYCLOAK_CONTAINER_ENDPOINT}/protocol/openid-connect/token`,
userinfo: `${KEYCLOAK_CONTAINER_ENDPOINT}/protocol/openid-connect/userinfo`,
})
],
callbacks: {
async jwt({ token, account, profile, trigger }) {
if(account) {
token.access_token = account.access_token;
token.id_token = account.id_token;
token.refresh_token = account.refresh_token;
token.expires_at = account.expires_at || 0;
}
if(profile) {
token.groups = profile.groups;
}
const nowTimeStamp = dayjs();
try {
// we refresh token on update action
if (trigger === 'update') {
return await refreshAccessToken(token);
}
// we get intial token on sign in
if (
account &&
account.access_token &&
account.id_token &&
account.expires_at &&
account.refresh_token
) {
token.access_token = account.access_token;
token.id_token = account.id_token;
token.expires_at = account.expires_at;
token.refresh_token = account.refresh_token;
return token;
// if time of expires is more then current date then we return existing token.
} else if (
token &&
nowTimeStamp.isBefore(dayjs.unix(token.expires_at))
) {
return token;
// this happens after user session is expired, so in this case we try to update user session
} else {
return await refreshAccessToken(token);
}
// if session update is failed we return error and on client we are doing logout(TokenExpireController).
} catch (error) {
return { ...token, error: 'RefreshAccessTokenError' };
}
},
session({ session, token }) {
session.user.groups = token.groups as string[];
session.accessToken = token.access_token as string;
session.idToken = token.id_token as string;
return session;
}
},
cookies: {
sessionToken: {
name: `${COOKIE_PREFIX}next-auth.session-token`,
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: true,
},
},
callbackUrl: {
name: `${COOKIE_PREFIX}next-auth.callback-url`,
options: {
sameSite: 'lax',
path: '/',
secure: true,
},
},
csrfToken: {
name: `${COOKIE_PREFIX}next-auth.csrf-token`,
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: true,
},
},
pkceCodeVerifier: {
name: `${COOKIE_PREFIX}next-auth.pkce.code_verifier`,
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: true,
maxAge: COOKIES_LIFE_TIME,
},
},
state: {
name: `${COOKIE_PREFIX}next-auth.state`,
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: true,
maxAge: COOKIES_LIFE_TIME,
},
},
nonce: {
name: `${COOKIE_PREFIX}next-auth.nonce`,
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: true,
},
}
}
}
export const { handlers, signIn, signOut, auth } = NextAuth(authOptions);
I've tried a few different things but just can't seem to figure out why this is happening, any help would be appreciated.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi all,
We use NextJS and Keycloak in docker containers, and for the most part it is working well. However, sometimes when a user attempts to login, it throws a CallbackRouteError with error description "PKCE verification failed: Code mismatch":
This is our auth.ts config:
I've tried a few different things but just can't seem to figure out why this is happening, any help would be appreciated.
Beta Was this translation helpful? Give feedback.
All reactions