1
- import type { AuthenticatedMachineObject , SignedOutAuthObject } from '@clerk/backend/internal' ;
2
- import { constants , verifyMachineAuthToken } from '@clerk/backend/internal' ;
1
+ import type { MachineAuthObject } from '@clerk/backend' ;
2
+ import type { AuthenticatedMachineObject , MachineTokenType , SignedOutAuthObject } from '@clerk/backend/internal' ;
3
+ import { constants } from '@clerk/backend/internal' ;
3
4
import { NextRequest } from 'next/server' ;
4
5
import { beforeEach , describe , expect , it , vi } from 'vitest' ;
5
6
6
- import { getAuthDataFromRequestAsync , getAuthDataFromRequestSync } from '../data/getAuthDataFromRequest' ;
7
+ import { getAuthDataFromRequest } from '../data/getAuthDataFromRequest' ;
8
+ import { encryptClerkRequestData } from '../utils' ;
7
9
8
- vi . mock ( '@clerk/backend/internal' , async ( ) => {
9
- const actual = await vi . importActual ( '@clerk/backend/internal' ) ;
10
+ vi . mock ( import ( '../constants.js' ) , async importOriginal => {
11
+ const actual = await importOriginal ( ) ;
10
12
return {
11
13
...actual ,
12
- verifyMachineAuthToken : vi . fn ( ) ,
14
+ ENCRYPTION_KEY : 'encryption-key' ,
15
+ PUBLISHABLE_KEY : 'pk_test_Y2xlcmsuaW5jbHVkZWQua2F0eWRpZC05Mi5sY2wuZGV2JA' ,
16
+ SECRET_KEY : 'sk_test_xxxxxxxxxxxxxxxxxx' ,
13
17
} ;
14
18
} ) ;
15
19
16
20
type MockRequestParams = {
17
21
url : string ;
18
22
appendDevBrowserCookie ?: boolean ;
19
23
method ?: string ;
20
- headers ?: any ;
24
+ headers ?: Headers ;
25
+ machineAuthObject ?: Partial < MachineAuthObject < MachineTokenType > > ;
21
26
} ;
22
27
23
28
const mockRequest = ( params : MockRequestParams ) => {
24
- const { url, appendDevBrowserCookie = false , method = 'GET' , headers = new Headers ( ) } = params ;
29
+ const { url, appendDevBrowserCookie = false , method = 'GET' , headers = new Headers ( ) , machineAuthObject } = params ;
25
30
const headersWithCookie = new Headers ( headers ) ;
31
+
26
32
if ( appendDevBrowserCookie ) {
27
33
headersWithCookie . append ( 'cookie' , '__clerk_db_jwt=test_jwt' ) ;
28
34
}
35
+
36
+ // Add encrypted auth object header if provided
37
+ if ( machineAuthObject ) {
38
+ const encryptedData = encryptClerkRequestData (
39
+ { } , // requestData
40
+ { } , // keylessModeKeys
41
+ // @ts -expect-error - mock machine auth object
42
+ machineAuthObject ,
43
+ ) ;
44
+ if ( encryptedData ) {
45
+ headersWithCookie . set ( constants . Headers . ClerkRequestData , encryptedData ) ;
46
+ }
47
+ }
48
+
29
49
return new NextRequest ( new URL ( url , 'https://www.clerk.com' ) . toString ( ) , { method, headers : headersWithCookie } ) ;
30
50
} ;
31
51
32
- const machineTokenErrorMock = [
33
- {
34
- message : 'Token type mismatch' ,
35
- code : 'token-invalid' ,
36
- status : 401 ,
37
- name : 'MachineTokenVerificationError' ,
38
- getFullMessage : ( ) => 'Token type mismatch' ,
39
- } ,
40
- ] ;
41
-
42
- describe ( 'getAuthDataFromRequestAsync' , ( ) => {
52
+ // Helper function to create mock machine auth objects
53
+ const createMockMachineAuthObject = < T extends MachineTokenType > ( data : Partial < MachineAuthObject < T > > ) => data ;
54
+
55
+ describe ( 'getAuthDataFromRequest' , ( ) => {
43
56
beforeEach ( ( ) => {
44
57
vi . clearAllMocks ( ) ;
45
58
} ) ;
46
59
47
- it ( 'returns invalid token auth object when token type does not match any in acceptsToken array' , async ( ) => {
60
+ it ( 'returns invalid token auth object when token type does not match any in acceptsToken array' , ( ) => {
61
+ const machineAuthObject = createMockMachineAuthObject ( {
62
+ tokenType : 'api_key' ,
63
+ isAuthenticated : true ,
64
+ } ) ;
65
+
48
66
const req = mockRequest ( {
49
67
url : '/api/protected' ,
50
68
headers : new Headers ( {
51
69
[ constants . Headers . Authorization ] : 'Bearer ak_xxx' ,
52
70
} ) ,
71
+ machineAuthObject,
53
72
} ) ;
54
73
55
- const auth = await getAuthDataFromRequestAsync ( req , {
74
+ const auth = getAuthDataFromRequest ( req , {
56
75
acceptsToken : [ 'machine_token' , 'oauth_token' , 'session_token' ] ,
57
76
} ) ;
58
77
59
78
expect ( auth . tokenType ) . toBeNull ( ) ;
60
79
expect ( auth . isAuthenticated ) . toBe ( false ) ;
61
80
} ) ;
62
81
63
- it ( 'returns unauthenticated auth object when token type does not match single acceptsToken' , async ( ) => {
82
+ it ( 'handles mixed token types in acceptsToken array' , ( ) => {
83
+ const machineAuthObject = createMockMachineAuthObject ( {
84
+ tokenType : 'api_key' ,
85
+ isAuthenticated : true ,
86
+ id : 'ak_id123' ,
87
+ } ) ;
88
+
89
+ const req = mockRequest ( {
90
+ url : '/api/protected' ,
91
+ headers : new Headers ( {
92
+ [ constants . Headers . Authorization ] : 'Bearer ak_xxx' ,
93
+ } ) ,
94
+ machineAuthObject,
95
+ } ) ;
96
+
97
+ const auth = getAuthDataFromRequest ( req , {
98
+ acceptsToken : [ 'api_key' , 'session_token' ] ,
99
+ } ) ;
100
+
101
+ expect ( auth . tokenType ) . toBe ( 'api_key' ) ;
102
+ expect ( auth . isAuthenticated ) . toBe ( true ) ;
103
+ } ) ;
104
+
105
+ it ( 'falls back to session logic when machine token is not accepted' , ( ) => {
106
+ const machineAuthObject = createMockMachineAuthObject ( {
107
+ tokenType : 'api_key' ,
108
+ isAuthenticated : true ,
109
+ } ) ;
110
+
111
+ const req = mockRequest ( {
112
+ url : '/api/protected' ,
113
+ headers : new Headers ( {
114
+ [ constants . Headers . Authorization ] : 'Bearer ak_xxx' ,
115
+ } ) ,
116
+ machineAuthObject,
117
+ } ) ;
118
+
119
+ const auth = getAuthDataFromRequest ( req , {
120
+ acceptsToken : 'session_token' ,
121
+ } ) ;
122
+
123
+ expect ( auth . tokenType ) . toBe ( 'session_token' ) ;
124
+ expect ( auth . isAuthenticated ) . toBe ( false ) ;
125
+ } ) ;
126
+
127
+ it ( 'returns unauthenticated auth object when token type does not match single acceptsToken' , ( ) => {
128
+ const machineAuthObject = createMockMachineAuthObject ( {
129
+ tokenType : 'api_key' ,
130
+ isAuthenticated : true ,
131
+ } ) ;
132
+
64
133
const req = mockRequest ( {
65
134
url : '/api/protected' ,
66
135
headers : new Headers ( {
67
136
[ constants . Headers . Authorization ] : 'Bearer ak_xxx' ,
68
137
} ) ,
138
+ machineAuthObject,
69
139
} ) ;
70
140
71
- const auth = await getAuthDataFromRequestAsync ( req , { acceptsToken : 'oauth_token' } ) ;
141
+ const auth = getAuthDataFromRequest ( req , { acceptsToken : 'oauth_token' } ) ;
72
142
73
143
expect ( auth . tokenType ) . toBe ( 'oauth_token' ) ;
74
144
expect ( auth . isAuthenticated ) . toBe ( false ) ;
75
145
} ) ;
76
146
77
- it ( 'returns authenticated auth object for any valid token type' , async ( ) => {
78
- vi . mocked ( verifyMachineAuthToken ) . mockResolvedValueOnce ( {
79
- data : { id : 'ak_id123' , subject : 'user_12345' } as any ,
147
+ it ( 'returns authenticated auth object for any valid token type' , ( ) => {
148
+ const machineAuthObject = createMockMachineAuthObject ( {
80
149
tokenType : 'api_key' ,
81
- errors : undefined ,
150
+ id : 'ak_id123' ,
151
+ isAuthenticated : true ,
82
152
} ) ;
83
153
84
154
const req = mockRequest ( {
85
155
url : '/api/protected' ,
86
156
headers : new Headers ( {
87
157
[ constants . Headers . Authorization ] : 'Bearer ak_xxx' ,
88
158
} ) ,
159
+ machineAuthObject,
89
160
} ) ;
90
161
91
- const auth = await getAuthDataFromRequestAsync ( req , { acceptsToken : 'any' } ) ;
162
+ const auth = getAuthDataFromRequest ( req , { acceptsToken : 'any' } ) ;
92
163
93
164
expect ( auth . tokenType ) . toBe ( 'api_key' ) ;
94
165
expect ( ( auth as AuthenticatedMachineObject < 'api_key' > ) . id ) . toBe ( 'ak_id123' ) ;
95
166
expect ( auth . isAuthenticated ) . toBe ( true ) ;
96
167
} ) ;
97
168
98
- it ( 'returns authenticated object when token type exists in acceptsToken array' , async ( ) => {
99
- vi . mocked ( verifyMachineAuthToken ) . mockResolvedValueOnce ( {
100
- data : { id : 'ak_id123' , subject : 'user_12345' } as any ,
169
+ it ( 'returns authenticated object when token type exists in acceptsToken array' , ( ) => {
170
+ const machineAuthObject = createMockMachineAuthObject ( {
101
171
tokenType : 'api_key' ,
102
- errors : undefined ,
172
+ id : 'ak_id123' ,
173
+ subject : 'user_12345' ,
174
+ isAuthenticated : true ,
103
175
} ) ;
104
176
105
177
const req = mockRequest ( {
106
178
url : '/api/protected' ,
107
179
headers : new Headers ( {
108
- [ constants . Headers . Authorization ] : 'Bearer ak_secret123 ' ,
180
+ [ constants . Headers . Authorization ] : 'Bearer ak_xxx ' ,
109
181
} ) ,
182
+ machineAuthObject,
110
183
} ) ;
111
184
112
- const auth = await getAuthDataFromRequestAsync ( req , {
185
+ const auth = getAuthDataFromRequest ( req , {
113
186
acceptsToken : [ 'api_key' , 'machine_token' ] ,
114
187
} ) ;
115
188
@@ -136,21 +209,22 @@ describe('getAuthDataFromRequestAsync', () => {
136
209
} ,
137
210
] ) (
138
211
'returns authenticated $tokenType object when token is valid and acceptsToken is $tokenType' ,
139
- async ( { tokenType, token, data } ) => {
140
- vi . mocked ( verifyMachineAuthToken ) . mockResolvedValueOnce ( {
141
- data : data as any ,
212
+ ( { tokenType, token, data } ) => {
213
+ const machineAuthObject = createMockMachineAuthObject ( {
142
214
tokenType,
143
- errors : undefined ,
215
+ isAuthenticated : true ,
216
+ ...data ,
144
217
} ) ;
145
218
146
219
const req = mockRequest ( {
147
220
url : '/api/protected' ,
148
221
headers : new Headers ( {
149
222
[ constants . Headers . Authorization ] : `Bearer ${ token } ` ,
150
223
} ) ,
224
+ machineAuthObject,
151
225
} ) ;
152
226
153
- const auth = await getAuthDataFromRequestAsync ( req , { acceptsToken : tokenType } ) ;
227
+ const auth = getAuthDataFromRequest ( req , { acceptsToken : tokenType } ) ;
154
228
155
229
expect ( auth . tokenType ) . toBe ( tokenType ) ;
156
230
expect ( auth . isAuthenticated ) . toBe ( true ) ;
@@ -173,56 +247,55 @@ describe('getAuthDataFromRequestAsync', () => {
173
247
token : 'mt_123' ,
174
248
data : undefined ,
175
249
} ,
176
- ] ) ( 'returns unauthenticated $tokenType object when token is invalid' , async ( { tokenType, token, data } ) => {
177
- vi . mocked ( verifyMachineAuthToken ) . mockResolvedValueOnce ( {
178
- data : data as any ,
250
+ ] ) ( 'returns unauthenticated $tokenType object when token is invalid' , ( { tokenType, token } ) => {
251
+ const machineAuthObject = createMockMachineAuthObject ( {
179
252
tokenType,
180
- errors : machineTokenErrorMock as any ,
253
+ isAuthenticated : false ,
181
254
} ) ;
182
255
256
+ // For invalid tokens, we don't include encrypted auth object
183
257
const req = mockRequest ( {
184
258
url : '/api/protected' ,
185
259
headers : new Headers ( {
186
260
[ constants . Headers . Authorization ] : `Bearer ${ token } ` ,
187
261
} ) ,
262
+ machineAuthObject,
188
263
} ) ;
189
264
190
- const auth = await getAuthDataFromRequestAsync ( req , { acceptsToken : tokenType } ) ;
265
+ const auth = getAuthDataFromRequest ( req , { acceptsToken : tokenType } ) ;
191
266
192
267
expect ( auth . tokenType ) . toBe ( tokenType ) ;
193
268
expect ( auth . isAuthenticated ) . toBe ( false ) ;
194
269
} ) ;
195
270
196
- it ( 'falls back to session token handling' , async ( ) => {
271
+ it ( 'falls back to session token handling when no encrypted auth object is present' , ( ) => {
197
272
const req = mockRequest ( {
198
273
url : '/api/protected' ,
199
274
headers : new Headers ( {
200
275
[ constants . Headers . Authorization ] : 'Bearer session_xxx' ,
201
276
} ) ,
202
277
} ) ;
203
278
204
- const auth = await getAuthDataFromRequestAsync ( req ) ;
279
+ const auth = getAuthDataFromRequest ( req ) ;
205
280
expect ( auth . tokenType ) . toBe ( 'session_token' ) ;
206
281
expect ( ( auth as SignedOutAuthObject ) . userId ) . toBeNull ( ) ;
207
282
expect ( auth . isAuthenticated ) . toBe ( false ) ;
208
283
} ) ;
209
- } ) ;
210
284
211
- describe ( 'getAuthDataFromRequestSync' , ( ) => {
212
- it ( 'only accepts session tokens' , ( ) => {
285
+ it ( 'only accepts session tokens when encrypted auth object is not present' , ( ) => {
213
286
const req = mockRequest ( {
214
287
url : '/api/protected' ,
215
288
headers : new Headers ( {
216
289
[ constants . Headers . Authorization ] : 'Bearer ak_123' ,
217
290
} ) ,
218
291
} ) ;
219
292
220
- const auth = getAuthDataFromRequestSync ( req , {
293
+ const auth = getAuthDataFromRequest ( req , {
221
294
acceptsToken : 'api_key' ,
222
295
} ) ;
223
296
224
297
expect ( auth . tokenType ) . toBe ( 'session_token' ) ;
225
- expect ( auth . userId ) . toBeNull ( ) ;
298
+ expect ( ( auth as SignedOutAuthObject ) . userId ) . toBeNull ( ) ;
226
299
expect ( auth . isAuthenticated ) . toBe ( false ) ;
227
300
} ) ;
228
301
} ) ;
0 commit comments