Skip to content

Commit 996822f

Browse files
committed
Test if code verifier is passed through to upstream proxy
1 parent 9e32fd5 commit 996822f

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

src/server/auth/handlers/token.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import supertest from 'supertest';
77
import * as pkceChallenge from 'pkce-challenge';
88
import { InvalidGrantError, InvalidTokenError } from '../errors.js';
99
import { AuthInfo } from '../types.js';
10+
import { ProxyOAuthServerProvider } from '../proxyProvider.js';
1011

1112
// Mock pkce-challenge
1213
jest.mock('pkce-challenge', () => ({
@@ -280,6 +281,66 @@ describe('Token Handler', () => {
280281
expect(response.body.expires_in).toBe(3600);
281282
expect(response.body.refresh_token).toBe('mock_refresh_token');
282283
});
284+
285+
it('passes through PKCE verification for proxy providers', async () => {
286+
const originalFetch = global.fetch;
287+
288+
try {
289+
global.fetch = jest.fn().mockResolvedValue({
290+
ok: true,
291+
json: () => Promise.resolve({
292+
access_token: 'mock_access_token',
293+
token_type: 'bearer',
294+
expires_in: 3600,
295+
refresh_token: 'mock_refresh_token'
296+
})
297+
});
298+
299+
const proxyProvider = new ProxyOAuthServerProvider({
300+
endpoints: {
301+
tokenUrl: 'https://example.com/token'
302+
},
303+
verifyToken: async (token) => ({
304+
token,
305+
clientId: 'valid-client',
306+
scopes: ['read', 'write'],
307+
expiresAt: Date.now() / 1000 + 3600
308+
}),
309+
getClient: async (clientId) => clientId === 'valid-client' ? validClient : undefined
310+
});
311+
312+
const proxyApp = express();
313+
const options: TokenHandlerOptions = { provider: proxyProvider };
314+
proxyApp.use('/token', tokenHandler(options));
315+
316+
const response = await supertest(proxyApp)
317+
.post('/token')
318+
.type('form')
319+
.send({
320+
client_id: 'valid-client',
321+
client_secret: 'valid-secret',
322+
grant_type: 'authorization_code',
323+
code: 'valid_code',
324+
code_verifier: 'any_verifier'
325+
});
326+
327+
expect(response.status).toBe(200);
328+
expect(response.body.access_token).toBe('mock_access_token');
329+
330+
expect(global.fetch).toHaveBeenCalledWith(
331+
'https://example.com/token',
332+
expect.objectContaining({
333+
method: 'POST',
334+
headers: {
335+
'Content-Type': 'application/x-www-form-urlencoded'
336+
},
337+
body: expect.stringContaining('code_verifier=any_verifier')
338+
})
339+
);
340+
} finally {
341+
global.fetch = originalFetch;
342+
}
343+
});
283344
});
284345

285346
describe('Refresh token grant', () => {

0 commit comments

Comments
 (0)