From 5c72783146df8adce7292febaed4d08b60452437 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 1 Apr 2026 08:49:24 -0700 Subject: [PATCH 1/6] chore(express,hono): Add machine auth integration tests --- integration/tests/express/machine.test.ts | 142 +++++++++++++++++++++ integration/tests/hono/machine.test.ts | 149 ++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 integration/tests/express/machine.test.ts create mode 100644 integration/tests/hono/machine.test.ts diff --git a/integration/tests/express/machine.test.ts b/integration/tests/express/machine.test.ts new file mode 100644 index 00000000000..e10b2d5a3c2 --- /dev/null +++ b/integration/tests/express/machine.test.ts @@ -0,0 +1,142 @@ +import { test } from '@playwright/test'; + +import { appConfigs } from '../../presets'; +import type { MachineAuthTestAdapter } from '../../testUtils/machineAuthHelpers'; +import { + registerApiKeyAuthTests, + registerM2MAuthTests, + registerOAuthAuthTests, +} from '../../testUtils/machineAuthHelpers'; + +const createMainFile = () => ` +import 'dotenv/config'; + +import { clerkMiddleware } from '@clerk/express'; +import express from 'express'; +import ViteExpress from 'vite-express'; +import { machineRoutes } from './routes/machine'; + +const app = express(); + +app.use(express.json()); +app.use( + clerkMiddleware({ + publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, + }), +); + +app.use('/api', machineRoutes); + +const port = parseInt(process.env.PORT as string) || 3002; +ViteExpress.listen(app, port, () => console.log(\`Server is listening on port \${port}...\`)); +`; + +const adapter: MachineAuthTestAdapter = { + baseConfig: appConfigs.express.vite, + apiKey: { + path: '/api/me', + addRoutes: config => + config + .addFile( + 'src/server/routes/machine.ts', + () => ` +import { getAuth } from '@clerk/express'; +import { Router } from 'express'; + +const router = Router(); + +router.get('/me', (req: any, res: any) => { + const { userId, tokenType } = getAuth(req, { acceptsToken: 'api_key' }); + + if (!userId) { + res.status(401).send('Unauthorized'); + return; + } + + res.json({ userId, tokenType }); +}); + +router.post('/me', (req: any, res: any) => { + const authObject = getAuth(req, { acceptsToken: ['api_key', 'session_token'] }); + + if (!authObject.isAuthenticated) { + res.status(401).send('Unauthorized'); + return; + } + + res.json({ userId: authObject.userId, tokenType: authObject.tokenType }); +}); + +export const machineRoutes = router; + `, + ) + .addFile('src/server/main.ts', () => createMainFile()), + }, + m2m: { + path: '/api/m2m', + addRoutes: config => + config + .addFile( + 'src/server/routes/machine.ts', + () => ` +import { getAuth } from '@clerk/express'; +import { Router } from 'express'; + +const router = Router(); + +router.get('/m2m', (req: any, res: any) => { + const { subject, tokenType, isAuthenticated } = getAuth(req, { acceptsToken: 'm2m_token' }); + + if (!isAuthenticated) { + res.status(401).send('Unauthorized'); + return; + } + + res.json({ subject, tokenType }); +}); + +export const machineRoutes = router; + `, + ) + .addFile('src/server/main.ts', () => createMainFile()), + }, + oauth: { + verifyPath: '/api/oauth-verify', + callbackPath: '/api/oauth/callback', + addRoutes: config => + config + .addFile( + 'src/server/routes/machine.ts', + () => ` +import { getAuth } from '@clerk/express'; +import { Router } from 'express'; + +const router = Router(); + +router.get('/oauth-verify', (req: any, res: any) => { + const { userId, tokenType } = getAuth(req, { acceptsToken: 'oauth_token' }); + + if (!userId) { + res.status(401).send('Unauthorized'); + return; + } + + res.json({ userId, tokenType }); +}); + +router.get('/oauth/callback', (_req: any, res: any) => { + res.json({ message: 'OAuth callback received' }); +}); + +export const machineRoutes = router; + `, + ) + .addFile('src/server/main.ts', () => createMainFile()), + }, +}; + +test.describe('Express machine authentication @machine', () => { + registerApiKeyAuthTests(adapter); + registerM2MAuthTests(adapter); + registerOAuthAuthTests(adapter); +}); diff --git a/integration/tests/hono/machine.test.ts b/integration/tests/hono/machine.test.ts new file mode 100644 index 00000000000..b09bd85537f --- /dev/null +++ b/integration/tests/hono/machine.test.ts @@ -0,0 +1,149 @@ +import { test } from '@playwright/test'; + +import { appConfigs } from '../../presets'; +import type { MachineAuthTestAdapter } from '../../testUtils/machineAuthHelpers'; +import { + registerApiKeyAuthTests, + registerM2MAuthTests, + registerOAuthAuthTests, +} from '../../testUtils/machineAuthHelpers'; + +const createMainFile = () => ` +import 'dotenv/config'; + +import { getRequestListener } from '@hono/node-server'; +import express from 'express'; +import ViteExpress from 'vite-express'; +import { app } from './app'; + +const expressApp = express(); +const honoRequestListener = getRequestListener(app.fetch); + +expressApp.use('/api', async (req: any, res: any) => { + await honoRequestListener(req, res); +}); + +const port = parseInt(process.env.PORT as string) || 3002; +ViteExpress.listen(expressApp, port, () => console.log(\`Server is listening on port \${port}...\`)); +`; + +const adapter: MachineAuthTestAdapter = { + baseConfig: appConfigs.hono.vite, + apiKey: { + path: '/api/me', + addRoutes: config => + config + .addFile( + 'src/server/app.ts', + () => ` +import { clerkMiddleware, getAuth } from '@clerk/hono'; +import { Hono } from 'hono'; + +export const app = new Hono(); + +app.use( + '*', + clerkMiddleware({ + publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, + }), +); + +app.get('/me', c => { + const { userId, tokenType } = getAuth(c, { acceptsToken: 'api_key' }); + + if (!userId) { + return c.text('Unauthorized', 401); + } + + return c.json({ userId, tokenType }); +}); + +app.post('/me', c => { + const authObject = getAuth(c, { acceptsToken: ['api_key', 'session_token'] }); + + if (!authObject.isAuthenticated) { + return c.text('Unauthorized', 401); + } + + return c.json({ userId: authObject.userId, tokenType: authObject.tokenType }); +}); + `, + ) + .addFile('src/server/main.ts', () => createMainFile()), + }, + m2m: { + path: '/api/m2m', + addRoutes: config => + config + .addFile( + 'src/server/app.ts', + () => ` +import { clerkMiddleware, getAuth } from '@clerk/hono'; +import { Hono } from 'hono'; + +export const app = new Hono(); + +app.use( + '*', + clerkMiddleware({ + publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, + }), +); + +app.get('/m2m', c => { + const { subject, tokenType, isAuthenticated } = getAuth(c, { acceptsToken: 'm2m_token' }); + + if (!isAuthenticated) { + return c.text('Unauthorized', 401); + } + + return c.json({ subject, tokenType }); +}); + `, + ) + .addFile('src/server/main.ts', () => createMainFile()), + }, + oauth: { + verifyPath: '/api/oauth-verify', + callbackPath: '/api/oauth/callback', + addRoutes: config => + config + .addFile( + 'src/server/app.ts', + () => ` +import { clerkMiddleware, getAuth } from '@clerk/hono'; +import { Hono } from 'hono'; + +export const app = new Hono(); + +app.use( + '*', + clerkMiddleware({ + publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, + }), +); + +app.get('/oauth-verify', c => { + const { userId, tokenType } = getAuth(c, { acceptsToken: 'oauth_token' }); + + if (!userId) { + return c.text('Unauthorized', 401); + } + + return c.json({ userId, tokenType }); +}); + +app.get('/oauth/callback', c => { + return c.json({ message: 'OAuth callback received' }); +}); + `, + ) + .addFile('src/server/main.ts', () => createMainFile()), + }, +}; + +test.describe('Hono machine authentication @machine', () => { + registerApiKeyAuthTests(adapter); + registerM2MAuthTests(adapter); + registerOAuthAuthTests(adapter); +}); From a0da3f08bcdb8386285814408554224348114c36 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 1 Apr 2026 09:05:05 -0700 Subject: [PATCH 2/6] chore: remove vite-express from hono --- integration/tests/express/machine.test.ts | 4 +- integration/tests/hono/machine.test.ts | 95 +++++++---------------- 2 files changed, 32 insertions(+), 67 deletions(-) diff --git a/integration/tests/express/machine.test.ts b/integration/tests/express/machine.test.ts index e10b2d5a3c2..921fa228692 100644 --- a/integration/tests/express/machine.test.ts +++ b/integration/tests/express/machine.test.ts @@ -85,9 +85,9 @@ import { Router } from 'express'; const router = Router(); router.get('/m2m', (req: any, res: any) => { - const { subject, tokenType, isAuthenticated } = getAuth(req, { acceptsToken: 'm2m_token' }); + const { subject, tokenType, machineId } = getAuth(req, { acceptsToken: 'm2m_token' }); - if (!isAuthenticated) { + if (!machineId) { res.status(401).send('Unauthorized'); return; } diff --git a/integration/tests/hono/machine.test.ts b/integration/tests/hono/machine.test.ts index b09bd85537f..7bd04e66e37 100644 --- a/integration/tests/hono/machine.test.ts +++ b/integration/tests/hono/machine.test.ts @@ -8,23 +8,27 @@ import { registerOAuthAuthTests, } from '../../testUtils/machineAuthHelpers'; -const createMainFile = () => ` -import 'dotenv/config'; +const createAppFile = (routes: string) => ` +import { clerkMiddleware, getAuth } from '@clerk/hono'; +import { Hono } from 'hono'; -import { getRequestListener } from '@hono/node-server'; -import express from 'express'; -import ViteExpress from 'vite-express'; -import { app } from './app'; +const app = new Hono(); -const expressApp = express(); -const honoRequestListener = getRequestListener(app.fetch); +app.use('*', clerkMiddleware()); -expressApp.use('/api', async (req: any, res: any) => { - await honoRequestListener(req, res); -}); +${routes} + +export default app; +`; + +const createMainFile = () => ` +import 'dotenv/config'; + +import { serve } from '@hono/node-server'; +import app from './app'; const port = parseInt(process.env.PORT as string) || 3002; -ViteExpress.listen(expressApp, port, () => console.log(\`Server is listening on port \${port}...\`)); +serve({ fetch: app.fetch, port }, () => console.log(\`Server is listening on port \${port}...\`)); `; const adapter: MachineAuthTestAdapter = { @@ -33,22 +37,9 @@ const adapter: MachineAuthTestAdapter = { path: '/api/me', addRoutes: config => config - .addFile( - 'src/server/app.ts', - () => ` -import { clerkMiddleware, getAuth } from '@clerk/hono'; -import { Hono } from 'hono'; - -export const app = new Hono(); - -app.use( - '*', - clerkMiddleware({ - publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, - }), -); - -app.get('/me', c => { + .addFile('src/server/app.ts', () => + createAppFile(` +app.get('/api/me', c => { const { userId, tokenType } = getAuth(c, { acceptsToken: 'api_key' }); if (!userId) { @@ -58,7 +49,7 @@ app.get('/me', c => { return c.json({ userId, tokenType }); }); -app.post('/me', c => { +app.post('/api/me', c => { const authObject = getAuth(c, { acceptsToken: ['api_key', 'session_token'] }); if (!authObject.isAuthenticated) { @@ -67,7 +58,7 @@ app.post('/me', c => { return c.json({ userId: authObject.userId, tokenType: authObject.tokenType }); }); - `, +`), ) .addFile('src/server/main.ts', () => createMainFile()), }, @@ -75,22 +66,9 @@ app.post('/me', c => { path: '/api/m2m', addRoutes: config => config - .addFile( - 'src/server/app.ts', - () => ` -import { clerkMiddleware, getAuth } from '@clerk/hono'; -import { Hono } from 'hono'; - -export const app = new Hono(); - -app.use( - '*', - clerkMiddleware({ - publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, - }), -); - -app.get('/m2m', c => { + .addFile('src/server/app.ts', () => + createAppFile(` +app.get('/api/m2m', c => { const { subject, tokenType, isAuthenticated } = getAuth(c, { acceptsToken: 'm2m_token' }); if (!isAuthenticated) { @@ -99,7 +77,7 @@ app.get('/m2m', c => { return c.json({ subject, tokenType }); }); - `, +`), ) .addFile('src/server/main.ts', () => createMainFile()), }, @@ -108,22 +86,9 @@ app.get('/m2m', c => { callbackPath: '/api/oauth/callback', addRoutes: config => config - .addFile( - 'src/server/app.ts', - () => ` -import { clerkMiddleware, getAuth } from '@clerk/hono'; -import { Hono } from 'hono'; - -export const app = new Hono(); - -app.use( - '*', - clerkMiddleware({ - publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, - }), -); - -app.get('/oauth-verify', c => { + .addFile('src/server/app.ts', () => + createAppFile(` +app.get('/api/oauth-verify', c => { const { userId, tokenType } = getAuth(c, { acceptsToken: 'oauth_token' }); if (!userId) { @@ -133,10 +98,10 @@ app.get('/oauth-verify', c => { return c.json({ userId, tokenType }); }); -app.get('/oauth/callback', c => { +app.get('/api/oauth/callback', c => { return c.json({ message: 'OAuth callback received' }); }); - `, +`), ) .addFile('src/server/main.ts', () => createMainFile()), }, From 9759733e4e83573ee8532390e9f3c8a2fc2b7f69 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 1 Apr 2026 11:18:43 -0700 Subject: [PATCH 3/6] fixes --- .../templates/express-vite/src/client/main.ts | 2 + .../templates/hono-vite/src/client/main.ts | 2 + integration/tests/hono/machine.test.ts | 44 +++++++++++++------ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/integration/templates/express-vite/src/client/main.ts b/integration/templates/express-vite/src/client/main.ts index a21f68d5c23..380161f1d77 100644 --- a/integration/templates/express-vite/src/client/main.ts +++ b/integration/templates/express-vite/src/client/main.ts @@ -9,6 +9,8 @@ document.addEventListener('DOMContentLoaded', async function () { await clerk.load({ ui: { ClerkUI }, }); + // @ts-expect-error: Make waitForSession test utility work + window.Clerk = clerk; if (clerk.isSignedIn) { document.getElementById('app')!.innerHTML = ` diff --git a/integration/templates/hono-vite/src/client/main.ts b/integration/templates/hono-vite/src/client/main.ts index a21f68d5c23..380161f1d77 100644 --- a/integration/templates/hono-vite/src/client/main.ts +++ b/integration/templates/hono-vite/src/client/main.ts @@ -9,6 +9,8 @@ document.addEventListener('DOMContentLoaded', async function () { await clerk.load({ ui: { ClerkUI }, }); + // @ts-expect-error: Make waitForSession test utility work + window.Clerk = clerk; if (clerk.isSignedIn) { document.getElementById('app')!.innerHTML = ` diff --git a/integration/tests/hono/machine.test.ts b/integration/tests/hono/machine.test.ts index 7bd04e66e37..b018a672657 100644 --- a/integration/tests/hono/machine.test.ts +++ b/integration/tests/hono/machine.test.ts @@ -8,13 +8,19 @@ import { registerOAuthAuthTests, } from '../../testUtils/machineAuthHelpers'; -const createAppFile = (routes: string) => ` +const createAppFile = (routes: string, middlewareOptions = '') => ` import { clerkMiddleware, getAuth } from '@clerk/hono'; import { Hono } from 'hono'; const app = new Hono(); -app.use('*', clerkMiddleware()); +app.use( + '*', + clerkMiddleware({ + publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, + ${middlewareOptions} + }), +); ${routes} @@ -24,11 +30,20 @@ export default app; const createMainFile = () => ` import 'dotenv/config'; -import { serve } from '@hono/node-server'; +import { getRequestListener } from '@hono/node-server'; +import express from 'express'; +import ViteExpress from 'vite-express'; import app from './app'; +const expressApp = express(); +const honoRequestListener = getRequestListener(app.fetch); + +expressApp.use('/api', async (req: any, res: any) => { + await honoRequestListener(req, res); +}); + const port = parseInt(process.env.PORT as string) || 3002; -serve({ fetch: app.fetch, port }, () => console.log(\`Server is listening on port \${port}...\`)); +ViteExpress.listen(expressApp, port, () => console.log(\`Server is listening on port \${port}...\`)); `; const adapter: MachineAuthTestAdapter = { @@ -39,7 +54,7 @@ const adapter: MachineAuthTestAdapter = { config .addFile('src/server/app.ts', () => createAppFile(` -app.get('/api/me', c => { +app.get('/me', c => { const { userId, tokenType } = getAuth(c, { acceptsToken: 'api_key' }); if (!userId) { @@ -49,7 +64,7 @@ app.get('/api/me', c => { return c.json({ userId, tokenType }); }); -app.post('/api/me', c => { +app.post('/me', c => { const authObject = getAuth(c, { acceptsToken: ['api_key', 'session_token'] }); if (!authObject.isAuthenticated) { @@ -67,17 +82,20 @@ app.post('/api/me', c => { addRoutes: config => config .addFile('src/server/app.ts', () => - createAppFile(` -app.get('/api/m2m', c => { - const { subject, tokenType, isAuthenticated } = getAuth(c, { acceptsToken: 'm2m_token' }); + createAppFile( + ` +app.get('/m2m', c => { + const { subject, tokenType, machineId } = getAuth(c, { acceptsToken: 'm2m_token' }); - if (!isAuthenticated) { + if (!machineId) { return c.text('Unauthorized', 401); } return c.json({ subject, tokenType }); }); -`), +`, + `machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY || '',`, + ), ) .addFile('src/server/main.ts', () => createMainFile()), }, @@ -88,7 +106,7 @@ app.get('/api/m2m', c => { config .addFile('src/server/app.ts', () => createAppFile(` -app.get('/api/oauth-verify', c => { +app.get('/oauth-verify', c => { const { userId, tokenType } = getAuth(c, { acceptsToken: 'oauth_token' }); if (!userId) { @@ -98,7 +116,7 @@ app.get('/api/oauth-verify', c => { return c.json({ userId, tokenType }); }); -app.get('/api/oauth/callback', c => { +app.get('/oauth/callback', c => { return c.json({ message: 'OAuth callback received' }); }); `), From 13eb3ab7087813a9b3815d544f89614932633946 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 1 Apr 2026 11:39:49 -0700 Subject: [PATCH 4/6] fix(hono): Fix getAuth() acceptsToken filtering and add CLERK_MACHINE_SECRET_KEY support getAuth() was hardcoding acceptsToken to 'any' in the middleware closure, causing it to accept any token type regardless of the route-level filter. Move getAuthObjectForAcceptedToken to getAuth() to match the Express SDK pattern. Also read CLERK_MACHINE_SECRET_KEY from env vars and pass machineSecretKey to createClerkClient and authenticateRequest, enabling M2M token scope verification without explicit middleware configuration. --- .changeset/fix-hono-getauth-accepts-token.md | 7 +++++++ integration/tests/hono/machine.test.ts | 10 +++------- packages/hono/src/clerkMiddleware.ts | 12 +++++------- packages/hono/src/getAuth.ts | 5 ++++- 4 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 .changeset/fix-hono-getauth-accepts-token.md diff --git a/.changeset/fix-hono-getauth-accepts-token.md b/.changeset/fix-hono-getauth-accepts-token.md new file mode 100644 index 00000000000..e3adb0b6a62 --- /dev/null +++ b/.changeset/fix-hono-getauth-accepts-token.md @@ -0,0 +1,7 @@ +--- +'@clerk/hono': patch +--- + +Fix `getAuth()` to correctly filter by `acceptsToken` option. Previously, `getAuth(c, { acceptsToken: 'api_key' })` would accept any token type (including session tokens) because the token type filtering was hardcoded to `'any'` in the middleware. Now `getAuth()` properly rejects tokens that don't match the specified `acceptsToken`, consistent with other SDKs. + +Also add support for `CLERK_MACHINE_SECRET_KEY` environment variable. This enables M2M token scope verification without needing to pass `machineSecretKey` explicitly to `clerkMiddleware()`. diff --git a/integration/tests/hono/machine.test.ts b/integration/tests/hono/machine.test.ts index b018a672657..16d0fddd9e6 100644 --- a/integration/tests/hono/machine.test.ts +++ b/integration/tests/hono/machine.test.ts @@ -8,7 +8,7 @@ import { registerOAuthAuthTests, } from '../../testUtils/machineAuthHelpers'; -const createAppFile = (routes: string, middlewareOptions = '') => ` +const createAppFile = (routes: string) => ` import { clerkMiddleware, getAuth } from '@clerk/hono'; import { Hono } from 'hono'; @@ -18,7 +18,6 @@ app.use( '*', clerkMiddleware({ publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, - ${middlewareOptions} }), ); @@ -82,8 +81,7 @@ app.post('/me', c => { addRoutes: config => config .addFile('src/server/app.ts', () => - createAppFile( - ` + createAppFile(` app.get('/m2m', c => { const { subject, tokenType, machineId } = getAuth(c, { acceptsToken: 'm2m_token' }); @@ -93,9 +91,7 @@ app.get('/m2m', c => { return c.json({ subject, tokenType }); }); -`, - `machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY || '',`, - ), +`), ) .addFile('src/server/main.ts', () => createMainFile()), }, diff --git a/packages/hono/src/clerkMiddleware.ts b/packages/hono/src/clerkMiddleware.ts index 98b15182d6e..c105aa8bc0a 100644 --- a/packages/hono/src/clerkMiddleware.ts +++ b/packages/hono/src/clerkMiddleware.ts @@ -1,7 +1,5 @@ -import type { AuthObject } from '@clerk/backend'; import { createClerkClient } from '@clerk/backend'; import type { AuthenticateRequestOptions, AuthOptions, GetAuthFnNoRequest } from '@clerk/backend/internal'; -import { getAuthObjectForAcceptedToken } from '@clerk/backend/internal'; import { clerkFrontendApiProxy, DEFAULT_PROXY_PATH, matchProxyPath, stripTrailingSlashes } from '@clerk/backend/proxy'; import type { MiddlewareHandler } from 'hono'; import { env } from 'hono/adapter'; @@ -11,6 +9,7 @@ import type { FrontendApiProxyOptions } from './types'; type ClerkEnv = { CLERK_SECRET_KEY: string; CLERK_PUBLISHABLE_KEY: string; + CLERK_MACHINE_SECRET_KEY?: string; CLERK_API_URL?: string; CLERK_API_VERSION?: string; }; @@ -43,6 +42,7 @@ export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareHan const { secretKey = clerkEnv.CLERK_SECRET_KEY || '', publishableKey = clerkEnv.CLERK_PUBLISHABLE_KEY || '', + machineSecretKey = clerkEnv.CLERK_MACHINE_SECRET_KEY || '', apiUrl = clerkEnv.CLERK_API_URL, apiVersion = clerkEnv.CLERK_API_VERSION, frontendApiProxy, @@ -92,6 +92,7 @@ export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareHan apiVersion, secretKey, publishableKey, + machineSecretKey, userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}`, }); @@ -99,6 +100,7 @@ export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareHan ...rest, secretKey, publishableKey, + machineSecretKey, proxyUrl: derivedProxyUrl, acceptsToken: 'any', }); @@ -117,11 +119,7 @@ export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareHan } } - const authObjectFn = ((authOptions?: AuthOptions) => - getAuthObjectForAcceptedToken({ - authObject: requestState.toAuth(authOptions) as AuthObject, - acceptsToken: 'any', - })) as GetAuthFnNoRequest; + const authObjectFn = ((authOptions?: AuthOptions) => requestState.toAuth(authOptions)) as GetAuthFnNoRequest; c.set('clerkAuth', authObjectFn); c.set('clerk', clerkClient); diff --git a/packages/hono/src/getAuth.ts b/packages/hono/src/getAuth.ts index 41d8f7c423a..6b9d1499921 100644 --- a/packages/hono/src/getAuth.ts +++ b/packages/hono/src/getAuth.ts @@ -1,4 +1,5 @@ import type { AuthOptions, GetAuthFn } from '@clerk/backend/internal'; +import { getAuthObjectForAcceptedToken } from '@clerk/backend/internal'; import type { Context } from 'hono'; /** @@ -33,5 +34,7 @@ export const getAuth: GetAuthFn = ((c: Context, options?: AuthOptions) ); } - return authFn(options); + const authObject = authFn(options); + + return getAuthObjectForAcceptedToken({ authObject, acceptsToken: options?.acceptsToken }); }) as GetAuthFn; From ef66757f1068bc662dbff93543ce0c9dc353b1fb Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 1 Apr 2026 11:42:45 -0700 Subject: [PATCH 5/6] chore: revert and simplify --- packages/hono/src/clerkMiddleware.ts | 8 +++++++- packages/hono/src/getAuth.ts | 5 +---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/hono/src/clerkMiddleware.ts b/packages/hono/src/clerkMiddleware.ts index c105aa8bc0a..67bf53686c5 100644 --- a/packages/hono/src/clerkMiddleware.ts +++ b/packages/hono/src/clerkMiddleware.ts @@ -1,5 +1,7 @@ +import type { AuthObject } from '@clerk/backend'; import { createClerkClient } from '@clerk/backend'; import type { AuthenticateRequestOptions, AuthOptions, GetAuthFnNoRequest } from '@clerk/backend/internal'; +import { getAuthObjectForAcceptedToken } from '@clerk/backend/internal'; import { clerkFrontendApiProxy, DEFAULT_PROXY_PATH, matchProxyPath, stripTrailingSlashes } from '@clerk/backend/proxy'; import type { MiddlewareHandler } from 'hono'; import { env } from 'hono/adapter'; @@ -119,7 +121,11 @@ export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareHan } } - const authObjectFn = ((authOptions?: AuthOptions) => requestState.toAuth(authOptions)) as GetAuthFnNoRequest; + const authObjectFn = ((authOptions?: AuthOptions) => + getAuthObjectForAcceptedToken({ + authObject: requestState.toAuth(authOptions) as AuthObject, + acceptsToken: authOptions?.acceptsToken, + })) as GetAuthFnNoRequest; c.set('clerkAuth', authObjectFn); c.set('clerk', clerkClient); diff --git a/packages/hono/src/getAuth.ts b/packages/hono/src/getAuth.ts index 6b9d1499921..41d8f7c423a 100644 --- a/packages/hono/src/getAuth.ts +++ b/packages/hono/src/getAuth.ts @@ -1,5 +1,4 @@ import type { AuthOptions, GetAuthFn } from '@clerk/backend/internal'; -import { getAuthObjectForAcceptedToken } from '@clerk/backend/internal'; import type { Context } from 'hono'; /** @@ -34,7 +33,5 @@ export const getAuth: GetAuthFn = ((c: Context, options?: AuthOptions) ); } - const authObject = authFn(options); - - return getAuthObjectForAcceptedToken({ authObject, acceptsToken: options?.acceptsToken }); + return authFn(options); }) as GetAuthFn; From fc1ebeb45ee0fb9fc9277933fb8d0d0e8e46eec1 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 1 Apr 2026 11:48:37 -0700 Subject: [PATCH 6/6] chore: update changeset --- .changeset/fix-hono-getauth-accepts-token.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.changeset/fix-hono-getauth-accepts-token.md b/.changeset/fix-hono-getauth-accepts-token.md index e3adb0b6a62..2cdeeebd833 100644 --- a/.changeset/fix-hono-getauth-accepts-token.md +++ b/.changeset/fix-hono-getauth-accepts-token.md @@ -2,6 +2,4 @@ '@clerk/hono': patch --- -Fix `getAuth()` to correctly filter by `acceptsToken` option. Previously, `getAuth(c, { acceptsToken: 'api_key' })` would accept any token type (including session tokens) because the token type filtering was hardcoded to `'any'` in the middleware. Now `getAuth()` properly rejects tokens that don't match the specified `acceptsToken`, consistent with other SDKs. - -Also add support for `CLERK_MACHINE_SECRET_KEY` environment variable. This enables M2M token scope verification without needing to pass `machineSecretKey` explicitly to `clerkMiddleware()`. +Add support for `CLERK_MACHINE_SECRET_KEY` environment variable. This enables M2M token scope verification without needing to pass `machineSecretKey` explicitly to `clerkMiddleware()`.