Skip to content

feat(backend): Introduce M2M endpoints authentication using machine secret keys #6479

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 78 commits into from
Aug 7, 2025

Conversation

wobsoriano
Copy link
Member

@wobsoriano wobsoriano commented Aug 6, 2025

Description

This PR adds the ability to create, revoke, and verify machine-to-machine tokens using the BAPI client. All M2M token endpoints can be authorized with a machine secret key:

const clerkClient = createClerkClient({
    machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY
})

// All of the methods below will use the machine secret key from clerkClient
clerkClient.m2mTokens.create({...})
clerkClient.m2mTokens.revoke({...})
clerkClient.m2mTokens.verifySecret({...})

// You can also use a machine secret key per method
clerkClient.m2mTokens.create({ machineSecretKey: 'ak_xxx', ...rest })
clerkClient.m2mTokens.revoke({ machineSecretKey: 'ak_xxx', ...rest }))
clerkClient.m2mTokens.verifySecret({ machineSecretKey: 'ak_xxx', ...rest }))

To verify a machine-to-machine token using authenticateRequest():

const clerkClient = createClerkClient({
    machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY
})

const authReq = await clerkClient.authenticateRequest(c.req.raw, {
  acceptsToken: 'm2m_token',
})

Additionally, this PR introduces 2 breaking changes:

  • Removes the name property from m2m tokens. This change is part of the breaking changes to M2M endpoints documented here.
  • machine_token token type is now m2m_token for better terminology. Please see this Slack convo for more info.

Since this feature is still in private beta, a minor version bump should suffice.

A demo video on what's happening in this change.

Unit tests have been added, but E2E tests will be implemented in a separate PR.

Resolves USER-2264

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced endpoints for managing machine-to-machine (M2M) tokens, including creation, revocation, and verification.
    • Added support for authenticating requests using a machine secret key and specifying acceptance of M2M tokens.
    • Enabled specifying a machine secret key when creating Clerk client instances across multiple frameworks.
  • Refactor

    • Renamed all references from "machine_token" to "m2m_token" for improved clarity and consistency across the platform.
  • Bug Fixes

    • Improved token authentication logic to support both machine secret keys and instance secret keys for M2M tokens.
  • Tests

    • Added and updated comprehensive tests to cover M2M token management and authentication scenarios.
  • Documentation

    • Updated usage examples to reflect new M2M token endpoints and authentication methods.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
packages/astro/src/server/get-safe-env.ts (1)

23-41: Add explicit return type for better type safety.

The getSafeEnv function should have an explicit return type annotation to improve type safety and developer experience, especially since it now includes the new machineSecretKey property.

-function getSafeEnv(context: ContextOrLocals) {
+function getSafeEnv(context: ContextOrLocals): {
+  domain: string | undefined;
+  isSatellite: boolean;
+  proxyUrl: string | undefined;
+  pk: string | undefined;
+  sk: string | undefined;
+  machineSecretKey: string | undefined;
+  signInUrl: string | undefined;
+  signUpUrl: string | undefined;
+  clerkJsUrl: string | undefined;
+  clerkJsVariant: 'headless' | '' | undefined;
+  clerkJsVersion: string | undefined;
+  apiVersion: string | undefined;
+  apiUrl: string | undefined;
+  telemetryDisabled: boolean;
+  telemetryDebug: boolean;
+} {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0297391 and dc2dc84.

📒 Files selected for processing (24)
  • .changeset/pink-countries-hunt.md (1 hunks)
  • packages/astro/src/env.d.ts (1 hunks)
  • packages/astro/src/integration/create-integration.ts (1 hunks)
  • packages/astro/src/server/clerk-client.ts (1 hunks)
  • packages/astro/src/server/get-safe-env.ts (1 hunks)
  • packages/express/src/utils.ts (1 hunks)
  • packages/fastify/src/clerkClient.ts (1 hunks)
  • packages/fastify/src/constants.ts (1 hunks)
  • packages/nextjs/src/server/constants.ts (1 hunks)
  • packages/nextjs/src/server/createClerkClient.ts (2 hunks)
  • packages/nuxt/src/global.d.ts (1 hunks)
  • packages/nuxt/src/module.ts (1 hunks)
  • packages/nuxt/src/runtime/server/clerkClient.ts (1 hunks)
  • packages/react-router/src/ssr/authenticateRequest.ts (2 hunks)
  • packages/react-router/src/ssr/loadOptions.ts (2 hunks)
  • packages/react-router/src/ssr/types.ts (1 hunks)
  • packages/remix/src/ssr/authenticateRequest.ts (2 hunks)
  • packages/remix/src/ssr/loadOptions.ts (2 hunks)
  • packages/remix/src/ssr/types.ts (1 hunks)
  • packages/tanstack-react-start/src/server/authenticateRequest.ts (1 hunks)
  • packages/tanstack-react-start/src/server/clerkClient.ts (1 hunks)
  • packages/tanstack-react-start/src/server/constants.ts (1 hunks)
  • packages/tanstack-react-start/src/server/loadOptions.ts (2 hunks)
  • packages/tanstack-react-start/src/server/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (13)
  • packages/astro/src/env.d.ts
  • packages/nuxt/src/global.d.ts
  • packages/react-router/src/ssr/types.ts
  • packages/astro/src/server/clerk-client.ts
  • packages/tanstack-react-start/src/server/clerkClient.ts
  • packages/tanstack-react-start/src/server/types.ts
  • packages/nextjs/src/server/constants.ts
  • packages/remix/src/ssr/types.ts
  • packages/tanstack-react-start/src/server/constants.ts
  • packages/astro/src/integration/create-integration.ts
  • packages/fastify/src/constants.ts
  • packages/remix/src/ssr/loadOptions.ts
  • .changeset/pink-countries-hunt.md
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use Prettier for consistent code formatting

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
packages/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

TypeScript is required for all packages

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
packages/**/*.{ts,tsx,d.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Packages should export TypeScript types alongside runtime code

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)

Use proper TypeScript error types

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Use const assertions for literal types: as const
Use satisfies operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports: import type { ... } from ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)

Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
**/*

⚙️ CodeRabbit Configuration File

If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.

Files:

  • packages/astro/src/server/get-safe-env.ts
  • packages/nextjs/src/server/createClerkClient.ts
  • packages/tanstack-react-start/src/server/authenticateRequest.ts
  • packages/express/src/utils.ts
  • packages/fastify/src/clerkClient.ts
  • packages/react-router/src/ssr/loadOptions.ts
  • packages/remix/src/ssr/authenticateRequest.ts
  • packages/nuxt/src/module.ts
  • packages/react-router/src/ssr/authenticateRequest.ts
  • packages/tanstack-react-start/src/server/loadOptions.ts
  • packages/nuxt/src/runtime/server/clerkClient.ts
🧬 Code Graph Analysis (3)
packages/nextjs/src/server/createClerkClient.ts (1)
packages/nextjs/src/server/constants.ts (1)
  • MACHINE_SECRET_KEY (8-8)
packages/fastify/src/clerkClient.ts (2)
packages/nuxt/src/runtime/server/clerkClient.ts (1)
  • clerkClient (8-32)
packages/fastify/src/constants.ts (2)
  • SECRET_KEY (5-5)
  • MACHINE_SECRET_KEY (6-6)
packages/react-router/src/ssr/loadOptions.ts (1)
packages/shared/src/getEnvVariable.ts (1)
  • getEnvVariable (18-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep/ci
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (16)
packages/nextjs/src/server/createClerkClient.ts (2)

8-8: LGTM: Import follows established pattern.

The import of MACHINE_SECRET_KEY is consistent with other constant imports in the file and supports the new M2M token functionality.


26-26: LGTM: Machine secret key configuration added correctly.

The addition of machineSecretKey to the default options follows the established pattern and enables M2M token authentication support for Next.js applications. The configuration will be passed through to the underlying createClerkClient function as intended.

packages/fastify/src/clerkClient.ts (2)

3-3: LGTM: Import statement correctly updated

The addition of MACHINE_SECRET_KEY to the import statement maintains alphabetical ordering and is consistent with the M2M token authentication feature implementation.


7-7: LGTM: Machine secret key configuration properly added

The machineSecretKey property is correctly added to the client configuration, following the established pattern used in other packages. This enables M2M token authentication functionality as intended by the PR objectives.

packages/nuxt/src/module.ts (1)

81-81: LGTM! Consistent configuration pattern for M2M support.

The machineSecretKey addition follows the established pattern for private runtime configuration keys and is appropriately placed in the server-side only section alongside other sensitive keys.

packages/nuxt/src/runtime/server/clerkClient.ts (1)

19-19: LGTM! Proper integration of machine secret key.

The machineSecretKey is correctly passed to createClerkClient following the same secure pattern as existing authentication keys, enabling M2M token functionality in Nuxt applications.

packages/tanstack-react-start/src/server/loadOptions.ts (1)

18-18: LGTM! Consistent implementation of machineSecretKey support.

The addition of machineSecretKey follows the established pattern used for other keys (secretKey, publishableKey, jwtKey), correctly prioritizing override values before falling back to environment variables.

Also applies to: 56-56

packages/tanstack-react-start/src/server/authenticateRequest.ts (1)

16-17: LGTM! Proper integration of machineSecretKey parameter.

The machineSecretKey is correctly extracted from the options and passed to createClerkClient(), maintaining consistency with the existing parameter handling pattern.

Also applies to: 23-23

packages/react-router/src/ssr/authenticateRequest.ts (2)

16-16: LGTM - Parameter extraction follows established pattern.

The machineSecretKey extraction is consistent with other configuration parameters and properly supports the M2M token authentication feature.


27-27: LGTM - Proper client configuration integration.

The machineSecretKey is correctly passed to createClerkClient, enabling M2M token functionality while maintaining consistency with the existing parameter passing pattern.

packages/react-router/src/ssr/loadOptions.ts (2)

25-25: LGTM - Consistent environment variable loading pattern.

The machineSecretKey loading follows the exact same pattern as other secret keys, with proper fallback logic from overrides to environment variables. The CLERK_MACHINE_SECRET_KEY environment variable name follows established conventions.


71-71: LGTM - Proper inclusion in returned options.

The machineSecretKey is correctly included in the returned options object with logical placement near other secret keys, ensuring proper propagation to downstream consumers.

packages/remix/src/ssr/authenticateRequest.ts (2)

16-16: LGTM: Clean destructuring of the new parameter.

The addition of machineSecretKey to the destructuring assignment follows the established pattern and integrates well with the existing options.


35-35: It looks like AuthenticateRequestOptions is imported from @clerk/backend/internal, so the local types.ts only defines LoaderFunctionArgs and related remix-specific types. The machineSecretKey?: string; in types.ts is part of LoaderFunctionArgs (or other remix helper types), not the AuthenticateRequestOptions itself.

To verify that AuthenticateRequestOptions from the backend internal library includes machineSecretKey, we need to inspect that library’s type definitions. Since it’s imported and not defined locally, please confirm in @clerk/backend/internal (in its index.d.ts or source) that:

  • AuthenticateRequestOptions includes a machineSecretKey?: string property.
  • The property is correctly typed.

If your monorepo contains a local package for @clerk/backend/internal, locate its AuthenticateRequestOptions definition; otherwise, open the installed package in node_modules/@clerk/backend/internal/dist/types (or wherever its types are published) and verify the export.

Once confirmed, you can safely approve these changes.

packages/astro/src/server/get-safe-env.ts (2)

30-30: Machine secret key addition looks good.

The addition of machineSecretKey follows the established pattern and correctly uses the CLERK_MACHINE_SECRET_KEY environment variable, which aligns with the PR's objective to support M2M token authentication.


50-58: Machine secret key intentionally excluded from client‐safe environment

Confirmed that getClientSafeEnv only returns public variables and does not expose machineSecretKey. All private keys (including the machine secret) are only included in the server‐side getSafeEnv function in packages/astro/src/server/get-safe-env.ts and never injected into client code. No action required.

@@ -18,6 +18,7 @@ export const loadClientEnv = () => {
export const loadApiEnv = () => {
return {
secretKey: process.env.CLERK_SECRET_KEY || '',
machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY || '',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

LGTM! Environment variable addition follows established patterns.

The addition of machineSecretKey follows the same pattern as other environment variables in the function and supports the M2M token authentication feature described in the PR objectives.

However, ensure that:

  1. The return type of loadApiEnv includes the new machineSecretKey property in its type definition
  2. Tests are added to verify this environment variable is properly loaded

🏁 Script executed:

#!/bin/bash
# Description: Verify the return type of loadApiEnv includes machineSecretKey and check for test coverage

# Check if there's a type definition for the return type of loadApiEnv
ast-grep --pattern 'type $_ = {
  $$$
  machineSecretKey: string;
  $$$
}'

# Check for existing tests that cover loadApiEnv
rg -A 10 -B 5 "loadApiEnv"

# Look for test files related to this utils file
fd "test" --type f --exec rg -l "loadApiEnv" {} \;

Length of output: 5303


Add tests for machineSecretKey in loadApiEnv

  • No explicit return type is declared for loadApiEnv; TypeScript will infer the new machineSecretKey property correctly, so there’s no existing type definition to update.
  • Action required: Add unit tests under packages/express (for example, packages/express/src/utils.test.ts) that mock process.env.CLERK_MACHINE_SECRET_KEY and verify that loadApiEnv().machineSecretKey reflects the environment variable.
🤖 Prompt for AI Agents
In packages/express/src/utils.ts at line 21, the loadApiEnv function now
includes machineSecretKey from process.env.CLERK_MACHINE_SECRET_KEY. To fix the
issue, add unit tests in packages/express/src/utils.test.ts that mock
process.env.CLERK_MACHINE_SECRET_KEY with different values and verify that
loadApiEnv().machineSecretKey returns the expected value reflecting the
environment variable. This ensures the new property is correctly handled and
tested.

@wobsoriano wobsoriano merged commit e82f177 into main Aug 7, 2025
127 of 132 checks passed
@wobsoriano wobsoriano deleted the rob/user-2264-m2m-auth branch August 7, 2025 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants