Skip to main content
Docs

Machine-to-Machine Requests

Machine-to-machine (M2M) authentication allows services, scripts, or devices to securely communicate with each other without the need for a user's session.

For example, you might need machine tokens for:

  • Cron jobs that update your database
  • Background workers processing queued tasks
  • Microservices communicating with each other

Creating Machine Requests

You can create a machine token using the Clerk Backend API. Then use the created token in the Authorization header of outgoing requests.

Warning

Creating machine tokens uses the Clerk Backend API, and so it is subject to the Backend API rate limits.

import { createClerkClient } from '@clerk/backend'

export default async function cronJob() {
  const clerkClient = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY })

  const { jwt } = await clerkClient.machineTokens.create({
    machineId: 'mch_cron',
    claims: {
      permissions: ['read', 'write'],
    },
    expiresInSeconds: 60,
  })

  await fetch('https://api.example.com/cron', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({
      message: 'Hello World!',
    }),
  })
}

Verifying Machine Requests

For a Machine Request to be valid, it must include a valid machine token in the Bearer Authorization header.

You can verify machine tokens in two ways:

  1. Using Clerk's Backend SDKs (recommended)
  2. Manually verifying the machine token JWT using your instance's Public Key.

Using Clerk's Backend SDKs

By default, authenticating requests will default to authenticating them as user session requests.

For machine requests, you can use the entity: 'machine' option to authenticate requests.

Warning

The entity: 'machine' option is only available in the Clerk Next.js SDK and Clerk Backend Javascript SDK during the beta.

import { NextResponse } from 'next/server'
import { runCronJob } from './cronJob'

export const POST = async () => {
  const { machineId } = await auth({ entity: 'machine' })

  if (!machineId) {
    return NextResponse.json({ message: 'Unauthorized' }, { status: 401 })
  }

  await runCronJob()

  return NextResponse.json({ message: 'Cron job ran' })
}
const isMachineRoute = createRouteMatcher(['/api/cron'])

export default clerkMiddleware(async (auth, req) => {
  if (isMachineRoute(req)) {
    const { isMachineAuthenticated } = await auth({ entity: 'machine' })
    if (!isMachineAuthenticated) {
      return new Response('Unauthorized', { status: 401 })
    }
  }
})

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/'],
}

Feedback

What did you think of this content?

Last updated on