Skip to content

Commit 3adb99c

Browse files
Merge branch 'main' into heat/grow-672-convert-drift-detection-to-a-server-action
2 parents 3e5e269 + 2db7431 commit 3adb99c

File tree

8 files changed

+167
-17
lines changed

8 files changed

+167
-17
lines changed

.changeset/small-adults-crash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/backend": minor
3+
---
4+
5+
Deprecates `clerkClient.m2mTokens.verifySecret({ secret: 'mt_xxx' })` in favor or `clerkClient.m2mTokens.verifyToken({ token: 'mt_xxx' })`

integration/tests/machine-auth/m2m.test.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ test.describe('machine-to-machine auth @machine', () => {
4040
const app = express();
4141
4242
app.get('/api/protected', async (req, res) => {
43+
const token = req.get('Authorization')?.split(' ')[1];
44+
45+
try {
46+
const m2mToken = await clerkClient.m2mTokens.verifyToken({ token });
47+
res.send('Protected response ' + m2mToken.id);
48+
} catch {
49+
res.status(401).send('Unauthorized');
50+
}
51+
});
52+
53+
app.get('/api/protected-deprecated', async (req, res) => {
4354
const secret = req.get('Authorization')?.split(' ')[1];
4455
4556
try {
@@ -129,7 +140,7 @@ test.describe('machine-to-machine auth @machine', () => {
129140

130141
const res = await u.page.request.get(app.serverUrl + '/api/protected', {
131142
headers: {
132-
Authorization: `Bearer ${analyticsServerM2MToken.secret}`,
143+
Authorization: `Bearer ${analyticsServerM2MToken.token}`,
133144
},
134145
});
135146
expect(res.status()).toBe(401);
@@ -145,7 +156,7 @@ test.describe('machine-to-machine auth @machine', () => {
145156
// Email server can access primary API server
146157
const res = await u.page.request.get(app.serverUrl + '/api/protected', {
147158
headers: {
148-
Authorization: `Bearer ${emailServerM2MToken.secret}`,
159+
Authorization: `Bearer ${emailServerM2MToken.token}`,
149160
},
150161
});
151162
expect(res.status()).toBe(200);
@@ -160,7 +171,7 @@ test.describe('machine-to-machine auth @machine', () => {
160171

161172
const res2 = await u.page.request.get(app.serverUrl + '/api/protected', {
162173
headers: {
163-
Authorization: `Bearer ${m2mToken.secret}`,
174+
Authorization: `Bearer ${m2mToken.token}`,
164175
},
165176
});
166177
expect(res2.status()).toBe(200);
@@ -169,4 +180,17 @@ test.describe('machine-to-machine auth @machine', () => {
169180
m2mTokenId: m2mToken.id,
170181
});
171182
});
183+
184+
test('authorizes M2M requests with deprecated verifySecret method', async ({ page, context }) => {
185+
const u = createTestUtils({ app, page, context });
186+
187+
// Email server can access primary API server
188+
const res = await u.page.request.get(app.serverUrl + '/api/protected-deprecated', {
189+
headers: {
190+
Authorization: `Bearer ${emailServerM2MToken.token}`,
191+
},
192+
});
193+
expect(res.status()).toBe(200);
194+
expect(await res.text()).toBe('Protected response ' + emailServerM2MToken.id);
195+
});
172196
});

packages/backend/src/api/__tests__/M2MTokenApi.test.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ describe('M2MToken', () => {
1414
subject: 'mch_xxxxx',
1515
scopes: ['mch_1xxxxx', 'mch_2xxxxx'],
1616
claims: { foo: 'bar' },
17+
// Deprecated in favor of `token`
1718
secret: m2mSecret,
19+
token: m2mSecret,
1820
revoked: false,
1921
revocation_reason: null,
2022
expired: false,
@@ -46,6 +48,7 @@ describe('M2MToken', () => {
4648

4749
expect(response.id).toBe(m2mId);
4850
expect(response.secret).toBe(m2mSecret);
51+
expect(response.token).toBe(m2mSecret);
4952
expect(response.scopes).toEqual(['mch_1xxxxx', 'mch_2xxxxx']);
5053
expect(response.claims).toEqual({ foo: 'bar' });
5154
});
@@ -206,7 +209,84 @@ describe('M2MToken', () => {
206209
});
207210
});
208211

209-
describe('verifySecret', () => {
212+
describe('verifyToken', () => {
213+
it('verifies a m2m token using machine secret', async () => {
214+
const apiClient = createBackendApiClient({
215+
apiUrl: 'https://api.clerk.test',
216+
machineSecretKey: 'ak_xxxxx',
217+
});
218+
219+
server.use(
220+
http.post(
221+
'https://api.clerk.test/m2m_tokens/verify',
222+
validateHeaders(({ request }) => {
223+
expect(request.headers.get('Authorization')).toBe('Bearer ak_xxxxx');
224+
return HttpResponse.json(mockM2MToken);
225+
}),
226+
),
227+
);
228+
229+
const response = await apiClient.m2mTokens.verifyToken({
230+
token: m2mSecret,
231+
});
232+
233+
expect(response.id).toBe(m2mId);
234+
expect(response.secret).toBe(m2mSecret);
235+
expect(response.scopes).toEqual(['mch_1xxxxx', 'mch_2xxxxx']);
236+
expect(response.claims).toEqual({ foo: 'bar' });
237+
});
238+
239+
it('verifies a m2m token using instance secret', async () => {
240+
const apiClient = createBackendApiClient({
241+
apiUrl: 'https://api.clerk.test',
242+
secretKey: 'sk_xxxxx',
243+
});
244+
245+
server.use(
246+
http.post(
247+
'https://api.clerk.test/m2m_tokens/verify',
248+
validateHeaders(({ request }) => {
249+
expect(request.headers.get('Authorization')).toBe('Bearer sk_xxxxx');
250+
return HttpResponse.json(mockM2MToken);
251+
}),
252+
),
253+
);
254+
255+
const response = await apiClient.m2mTokens.verifyToken({
256+
token: m2mSecret,
257+
});
258+
259+
expect(response.id).toBe(m2mId);
260+
expect(response.secret).toBe(m2mSecret);
261+
expect(response.scopes).toEqual(['mch_1xxxxx', 'mch_2xxxxx']);
262+
expect(response.claims).toEqual({ foo: 'bar' });
263+
});
264+
265+
it('requires a machine secret or instance secret to verify a m2m token', async () => {
266+
const apiClient = createBackendApiClient({
267+
apiUrl: 'https://api.clerk.test',
268+
});
269+
270+
server.use(
271+
http.post(
272+
'https://api.clerk.test/m2m_tokens/verify',
273+
validateHeaders(() => {
274+
return HttpResponse.json(mockM2MToken);
275+
}),
276+
),
277+
);
278+
279+
const errResponse = await apiClient.m2mTokens
280+
.verifyToken({
281+
token: m2mSecret,
282+
})
283+
.catch(err => err);
284+
285+
expect(errResponse.status).toBe(401);
286+
});
287+
});
288+
289+
describe('verifySecret (deprecated)', () => {
210290
it('verifies a m2m token using machine secret', async () => {
211291
const apiClient = createBackendApiClient({
212292
apiUrl: 'https://api.clerk.test',

packages/backend/src/api/__tests__/factory.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,9 @@ describe('api.client', () => {
325325
),
326326
);
327327

328-
const response = await apiClient.m2mTokens.verifySecret({
328+
const response = await apiClient.m2mTokens.verifyToken({
329329
machineSecretKey: 'ak_test_in_header_params', // this will be added to headerParams.Authorization
330-
secret: 'mt_secret_test',
330+
token: 'mt_secret_test',
331331
});
332332
expect(response.id).toBe('mt_test');
333333
});
@@ -353,8 +353,8 @@ describe('api.client', () => {
353353
),
354354
);
355355

356-
const response = await apiClient.m2mTokens.verifySecret({
357-
secret: 'mt_secret_test',
356+
const response = await apiClient.m2mTokens.verifyToken({
357+
token: 'mt_secret_test',
358358
});
359359
expect(response.id).toBe('mt_test');
360360
});
@@ -404,7 +404,7 @@ describe('api.client', () => {
404404
expect(response.id).toBe('user_cafebabe');
405405
});
406406

407-
it('prioritizes machine secret key over secret key when both are provided and useMachineSecretKey is true', async () => {
407+
it('prioritizes machine secret key over instance secret key when both are provided and useMachineSecretKey is true', async () => {
408408
const apiClient = createBackendApiClient({
409409
apiUrl: 'https://api.clerk.test',
410410
secretKey: 'sk_test_xxx',
@@ -425,8 +425,8 @@ describe('api.client', () => {
425425
),
426426
);
427427

428-
const response = await apiClient.m2mTokens.verifySecret({
429-
secret: 'mt_secret_test',
428+
const response = await apiClient.m2mTokens.verifyToken({
429+
token: 'mt_secret_test',
430430
});
431431
expect(response.id).toBe('mt_test');
432432
});

packages/backend/src/api/endpoints/M2MTokenApi.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { deprecated } from '@clerk/shared/deprecated';
2+
13
import { joinPaths } from '../../util/path';
24
import type { ClerkBackendApiRequestOptions } from '../request';
35
import type { M2MToken } from '../resources/M2MToken';
@@ -31,7 +33,7 @@ type RevokeM2MTokenParams = {
3133
revocationReason?: string | null;
3234
};
3335

34-
type VerifyM2MTokenParams = {
36+
type VerifyM2MTokenParamsDeprecated = {
3537
/**
3638
* Custom machine secret key for authentication.
3739
*/
@@ -42,6 +44,17 @@ type VerifyM2MTokenParams = {
4244
secret: string;
4345
};
4446

47+
type VerifyM2MTokenParams = {
48+
/**
49+
* Custom machine secret key for authentication.
50+
*/
51+
machineSecretKey?: string;
52+
/**
53+
* Machine-to-machine token to verify.
54+
*/
55+
token: string;
56+
};
57+
4558
export class M2MTokenApi extends AbstractAPI {
4659
#createRequestOptions(options: ClerkBackendApiRequestOptions, machineSecretKey?: string) {
4760
if (machineSecretKey) {
@@ -94,9 +107,16 @@ export class M2MTokenApi extends AbstractAPI {
94107
return this.request<M2MToken>(requestOptions);
95108
}
96109

97-
async verifySecret(params: VerifyM2MTokenParams) {
110+
/**
111+
* Verify a machine-to-machine token.
112+
*
113+
* @deprecated Use {@link verifyToken} instead.
114+
*/
115+
async verifySecret(params: VerifyM2MTokenParamsDeprecated) {
98116
const { secret, machineSecretKey } = params;
99117

118+
deprecated('verifySecret', 'Use `verifyToken({ token: mt_xxx })` instead');
119+
100120
const requestOptions = this.#createRequestOptions(
101121
{
102122
method: 'POST',
@@ -108,4 +128,19 @@ export class M2MTokenApi extends AbstractAPI {
108128

109129
return this.request<M2MToken>(requestOptions);
110130
}
131+
132+
async verifyToken(params: VerifyM2MTokenParams) {
133+
const { token, machineSecretKey } = params;
134+
135+
const requestOptions = this.#createRequestOptions(
136+
{
137+
method: 'POST',
138+
path: joinPaths(basePath, 'verify'),
139+
bodyParams: { token },
140+
},
141+
machineSecretKey,
142+
);
143+
144+
return this.request<M2MToken>(requestOptions);
145+
}
111146
}

packages/backend/src/api/resources/JSON.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,11 @@ export interface MachineSecretKeyJSON {
734734

735735
export interface M2MTokenJSON extends ClerkResourceJSON {
736736
object: typeof ObjectType.M2MToken;
737+
/**
738+
* @deprecated Use {@link token} instead.
739+
*/
737740
secret?: string;
741+
token?: string;
738742
subject: string;
739743
scopes: string[];
740744
claims: Record<string, any> | null;

packages/backend/src/api/resources/M2MToken.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export class M2MToken {
1212
readonly expiration: number | null,
1313
readonly createdAt: number,
1414
readonly updatedAt: number,
15+
readonly token?: string,
1516
readonly secret?: string,
1617
) {}
1718

@@ -27,6 +28,7 @@ export class M2MToken {
2728
data.expiration,
2829
data.created_at,
2930
data.updated_at,
31+
data.token,
3032
data.secret,
3133
);
3234
}

packages/backend/src/tokens/verify.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,13 @@ function handleClerkAPIError(
200200
};
201201
}
202202

203-
async function verifyMachineToken(
204-
secret: string,
203+
async function verifyM2MToken(
204+
token: string,
205205
options: VerifyTokenOptions & { machineSecretKey?: string },
206206
): Promise<MachineTokenReturnType<M2MToken, MachineTokenVerificationError>> {
207207
try {
208208
const client = createBackendApiClient(options);
209-
const verifiedToken = await client.m2mTokens.verifySecret({ secret });
209+
const verifiedToken = await client.m2mTokens.verifyToken({ token });
210210
return { data: verifiedToken, tokenType: TokenType.M2MToken, errors: undefined };
211211
} catch (err: any) {
212212
return handleClerkAPIError(TokenType.M2MToken, err, 'Machine token not found');
@@ -247,7 +247,7 @@ async function verifyAPIKey(
247247
*/
248248
export async function verifyMachineAuthToken(token: string, options: VerifyTokenOptions) {
249249
if (token.startsWith(M2M_TOKEN_PREFIX)) {
250-
return verifyMachineToken(token, options);
250+
return verifyM2MToken(token, options);
251251
}
252252
if (token.startsWith(OAUTH_TOKEN_PREFIX)) {
253253
return verifyOAuthToken(token, options);

0 commit comments

Comments
 (0)