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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
05d6c8e
chore(backend): Introduce machine token secrets as authorization header
wobsoriano Jul 1, 2025
ca7a8be
chore: clean up
wobsoriano Jul 1, 2025
af6a27b
chore: use a more readable option for bapi proxy methods
wobsoriano Jul 1, 2025
fa94227
chore: add initial changeset
wobsoriano Jul 1, 2025
8dcd607
chore: add machine_secret_key type to api keys api
wobsoriano Jul 1, 2025
5d78030
Merge remote-tracking branch 'origin/main' into rob/user-2264-m2m
wobsoriano Jul 1, 2025
7bb3eb8
chore: reuse header consts
wobsoriano Jul 1, 2025
424a5a4
chore: rename to machine secret
wobsoriano Jul 1, 2025
1dbd41b
chore: clean up
wobsoriano Jul 1, 2025
7c3063c
chore: add secret property to create method
wobsoriano Jul 1, 2025
9dab708
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 1, 2025
db38ca5
chore: remove machine secret type from api key creation
wobsoriano Jul 1, 2025
5ce88ee
chore: make secret property optional
wobsoriano Jul 2, 2025
c33e3fd
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 2, 2025
f9526af
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 3, 2025
cb6c822
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 7, 2025
68bcb7e
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 8, 2025
e900e13
chore: add machines BAPI endpoints
wobsoriano Jul 8, 2025
6c0fc64
chore: trigger rebuild
wobsoriano Jul 8, 2025
c1d1ae2
chore: remove unnecessary params
wobsoriano Jul 8, 2025
d53115d
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 19, 2025
d87f937
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 25, 2025
7ff0538
chore: remove unused properties
wobsoriano Jul 25, 2025
e844565
chore: improve machine secret check
wobsoriano Jul 25, 2025
017bb4b
fix required secrets
wobsoriano Jul 25, 2025
e26660e
fix required secrets
wobsoriano Jul 25, 2025
0f7387d
fix required secrets
wobsoriano Jul 25, 2025
f78ddcc
chore: remove removed properties
wobsoriano Jul 25, 2025
1492a1e
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Jul 28, 2025
a8c66e1
chore: remove name and claims from m2m tokens
wobsoriano Jul 28, 2025
201cb23
fix tests
wobsoriano Jul 28, 2025
64afde6
fix incorrect method in tests
wobsoriano Jul 28, 2025
b38465b
chore: update tests
wobsoriano Jul 29, 2025
18b76da
chore: update test descriptions
wobsoriano Jul 29, 2025
d91404c
chore: improve tests
wobsoriano Jul 29, 2025
37a3d65
chore: update changeset
wobsoriano Jul 29, 2025
1d69db8
chore: skip pub key init for machine tokens
wobsoriano Jul 29, 2025
b26bd76
chore: skip pub and secret key check for authenticate request with ma…
wobsoriano Jul 29, 2025
d609285
fix error handling
wobsoriano Jul 29, 2025
2e080db
chore: allow machine secrets in authenticateRequest
wobsoriano Jul 29, 2025
7055b8a
chore: remove unused export keyword
wobsoriano Jul 29, 2025
051dd85
chore: more tests
wobsoriano Jul 29, 2025
7371a32
chore: add missing secret key or machine secret error test
wobsoriano Jul 29, 2025
d96d436
Merge branch 'main' into rob/user-2264-m2m
wobsoriano Aug 4, 2025
6642321
chore: run dedupe
wobsoriano Aug 4, 2025
239b6ba
chore: add secret key
wobsoriano Aug 4, 2025
a8d310f
chore: do not destructure body params in m2m endpoints
wobsoriano Aug 4, 2025
2a74cf9
chore: do not destructure body params in machine endpoints
wobsoriano Aug 4, 2025
60b139a
chore: update tests
wobsoriano Aug 4, 2025
ad9a0ec
chore: Use machine secret key from created Clerk client
wobsoriano Aug 4, 2025
8ca8009
chore: update missing clerk instance key or machine secret key error
wobsoriano Aug 4, 2025
7569d95
formatting
wobsoriano Aug 4, 2025
ad5a0f9
fix authenticate request option types
wobsoriano Aug 4, 2025
b72e891
fix assertion
wobsoriano Aug 4, 2025
2b2ce8e
Add machine secret key to merged options
wobsoriano Aug 4, 2025
afb5923
allow custom machine secret key per method
wobsoriano Aug 5, 2025
01653ae
add jsdoc
wobsoriano Aug 5, 2025
e05d414
chore: separate backend api client machine secret key and options sec…
wobsoriano Aug 5, 2025
85e47f1
chore: clean up authorizationHeader
wobsoriano Aug 5, 2025
e2ede5f
clean up authenticate context
wobsoriano Aug 6, 2025
c98d1ed
clean up authenticate context
wobsoriano Aug 6, 2025
97679bb
Merge branch 'main' into rob/user-2264-m2m-auth
wobsoriano Aug 6, 2025
234f1f0
chore: apply coderabbit suggestions
wobsoriano Aug 6, 2025
03d72d9
chore: update changeset and improve tests
wobsoriano Aug 6, 2025
e241dc7
chore: more tests
wobsoriano Aug 6, 2025
9c9c78f
revert
wobsoriano Aug 6, 2025
28ba427
refactor(backend): Use m2m tokens instead of machine tokens (#6484)
wobsoriano Aug 6, 2025
e7167c7
chore: update changeset
wobsoriano Aug 7, 2025
38303eb
chore: update changeset
wobsoriano Aug 7, 2025
0e18a99
chore: fix nextjs tests with updated terminology
wobsoriano Aug 7, 2025
43b8c2c
chore: fix nuxt tests with updated terminology
wobsoriano Aug 7, 2025
7d69807
Merge branch 'main' into rob/user-2264-m2m-auth
wobsoriano Aug 7, 2025
e643612
chore: better m2m token param type names
wobsoriano Aug 7, 2025
0297391
chore: remove internal keyword from public prop type
wobsoriano Aug 7, 2025
dc2dc84
chore(nextjs): Add machine secret key environment variable to BAPI cl…
wobsoriano Aug 7, 2025
8e483ca
fix snapshot tests
wobsoriano Aug 7, 2025
6a7ac7e
Merge branch 'main' into rob/user-2264-m2m-auth
wobsoriano Aug 7, 2025
fb5ea0c
chore: fix method name in changeset
wobsoriano Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .changeset/hot-tables-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
"@clerk/backend": minor
---

Adds machine-to-machine endpoints to the Backend SDK:

**Note:** Renamed from "machine_tokens" to "m2m_tokens" for clarity and consistency with canonical terminology. This avoids confusion with other machine-related concepts like machine secrets.

### Create M2M Tokens

A machine secret is required when creating M2M tokens.

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

clerkClient.m2mTokens.create({
// or pass as an option here
// machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY
secondsUntilExpiration: 3600,
})
```

### Revoke M2M Tokens

You can revoke tokens using either a machine secret or instance secret:

```ts
// Using machine secret
const clerkClient = createClerkClient({ machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY })
clerkClient.m2mTokens.revoke({
// or pass as an option here
// machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY
m2mTokenId: 'mt_xxxxx',
revocationReason: 'Revoked by user',
})

// Using instance secret (default)
const clerkClient = createClerkClient({ secretKey: 'sk_xxxx' })
clerkClient.m2mTokens.revoke({
m2mTokenId: 'mt_xxxxx',
revocationReason: 'Revoked by user',
})
```

### Verify M2M Tokens

You can verify tokens using either a machine secret or instance secret:

```ts
// Using machine secret
const clerkClient = createClerkClient({ machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY })
clerkClient.m2mTokens.verifySecret({
// or pass as an option here
// machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY
secret: 'mt_secret_xxxxx',
})

// Using instance secret (default)
const clerkClient = createClerkClient({ secretKey: 'sk_xxxx' })
clerkClient.m2mTokens.verifySecret({
secret: 'mt_secret_xxxxx',
})
```

To verify machine-to-machine tokens using when using `authenticateRequest()` with a machine secret, use the `machineSecret` option:

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

const authReq = await clerkClient.authenticateRequest(c.req.raw, {
// or pass as an option here
// machineSecretKey: process.env.CLERK_MACHINE_SECRET_KEY
acceptsToken: 'm2m_token', // previously machine_token
})

if (authReq.isAuthenticated) {
// ... do something
}
```
18 changes: 18 additions & 0 deletions .changeset/pink-countries-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@clerk/astro": patch
"@clerk/express": patch
"@clerk/fastify": patch
"@clerk/nextjs": patch
"@clerk/nuxt": patch
"@clerk/react-router": patch
"@clerk/remix": patch
"@clerk/tanstack-react-start": patch
---

Add ability to define a machine secret key to Clerk BAPI client function

```ts
const clerkClient = createClerkClient({ machineSecretKey: 'ak_xxxxx' })

clerkClient.m2mTokens.create({...})
```
1 change: 1 addition & 0 deletions packages/astro/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface InternalEnv {
readonly CLERK_API_VERSION?: string;
readonly CLERK_JWT_KEY?: string;
readonly CLERK_SECRET_KEY?: string;
readonly CLERK_MACHINE_SECRET_KEY?: string;
readonly PUBLIC_CLERK_DOMAIN?: string;
readonly PUBLIC_CLERK_IS_SATELLITE?: string;
readonly PUBLIC_CLERK_PROXY_URL?: string;
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/integration/create-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ function createClerkEnvSchema() {
PUBLIC_CLERK_TELEMETRY_DISABLED: envField.boolean({ context: 'client', access: 'public', optional: true }),
PUBLIC_CLERK_TELEMETRY_DEBUG: envField.boolean({ context: 'client', access: 'public', optional: true }),
CLERK_SECRET_KEY: envField.string({ context: 'server', access: 'secret' }),
CLERK_MACHINE_SECRET_KEY: envField.string({ context: 'server', access: 'secret', optional: true }),
CLERK_JWT_KEY: envField.string({ context: 'server', access: 'secret', optional: true }),
};
}
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/server/clerk-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type CreateClerkClientWithOptions = (context: APIContext, options?: ClerkOptions
const createClerkClientWithOptions: CreateClerkClientWithOptions = (context, options) =>
createClerkClient({
secretKey: getSafeEnv(context).sk,
machineSecretKey: getSafeEnv(context).machineSecretKey,
publishableKey: getSafeEnv(context).pk,
apiUrl: getSafeEnv(context).apiUrl,
apiVersion: getSafeEnv(context).apiVersion,
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/server/get-safe-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function getSafeEnv(context: ContextOrLocals) {
proxyUrl: getContextEnvVar('PUBLIC_CLERK_PROXY_URL', context),
pk: getContextEnvVar('PUBLIC_CLERK_PUBLISHABLE_KEY', context),
sk: getContextEnvVar('CLERK_SECRET_KEY', context),
machineSecretKey: getContextEnvVar('CLERK_MACHINE_SECRET_KEY', context),
signInUrl: getContextEnvVar('PUBLIC_CLERK_SIGN_IN_URL', context),
signUpUrl: getContextEnvVar('PUBLIC_CLERK_SIGN_UP_URL', context),
clerkJsUrl: getContextEnvVar('PUBLIC_CLERK_JS_URL', context),
Expand Down
Loading