Skip to content

Commit 0ca9add

Browse files
committed
tests cleanup
1 parent a2d077c commit 0ca9add

File tree

1 file changed

+168
-168
lines changed

1 file changed

+168
-168
lines changed

__tests__/oauth-provider.test.ts

Lines changed: 168 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,135 @@ describe('OAuthProvider', () => {
198198
mockEnv.OAUTH_KV.clear();
199199
});
200200

201+
describe('API Route Configuration', () => {
202+
it('should support multi-handler configuration with apiHandlers', async () => {
203+
// Create handler classes for different API routes
204+
class UsersApiHandler extends WorkerEntrypoint {
205+
fetch(request: Request) {
206+
return new Response('Users API response', { status: 200 });
207+
}
208+
}
209+
210+
class DocumentsApiHandler extends WorkerEntrypoint {
211+
fetch(request: Request) {
212+
return new Response('Documents API response', { status: 200 });
213+
}
214+
}
215+
216+
// Create provider with multi-handler configuration
217+
const providerWithMultiHandler = new OAuthProvider({
218+
apiHandlers: {
219+
'/api/users/': UsersApiHandler,
220+
'/api/documents/': DocumentsApiHandler,
221+
},
222+
defaultHandler: testDefaultHandler,
223+
authorizeEndpoint: '/authorize',
224+
tokenEndpoint: '/oauth/token',
225+
clientRegistrationEndpoint: '/oauth/register', // Important for registering clients in the test
226+
scopesSupported: ['read', 'write'],
227+
});
228+
229+
// Create a client and get an access token
230+
const clientData = {
231+
redirect_uris: ['https://client.example.com/callback'],
232+
client_name: 'Test Client',
233+
token_endpoint_auth_method: 'client_secret_basic',
234+
};
235+
236+
const registerRequest = createMockRequest(
237+
'https://example.com/oauth/register',
238+
'POST',
239+
{ 'Content-Type': 'application/json' },
240+
JSON.stringify(clientData)
241+
);
242+
243+
const registerResponse = await providerWithMultiHandler.fetch(registerRequest, mockEnv, mockCtx);
244+
const client = await registerResponse.json();
245+
const clientId = client.client_id;
246+
const clientSecret = client.client_secret;
247+
const redirectUri = 'https://client.example.com/callback';
248+
249+
// Get an auth code
250+
const authRequest = createMockRequest(
251+
`https://example.com/authorize?response_type=code&client_id=${clientId}` +
252+
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
253+
`&scope=read%20write&state=xyz123`
254+
);
255+
256+
const authResponse = await providerWithMultiHandler.fetch(authRequest, mockEnv, mockCtx);
257+
const location = authResponse.headers.get('Location')!;
258+
const code = new URL(location).searchParams.get('code')!;
259+
260+
// Exchange for tokens
261+
const params = new URLSearchParams();
262+
params.append('grant_type', 'authorization_code');
263+
params.append('code', code);
264+
params.append('redirect_uri', redirectUri);
265+
params.append('client_id', clientId);
266+
params.append('client_secret', clientSecret);
267+
268+
const tokenRequest = createMockRequest(
269+
'https://example.com/oauth/token',
270+
'POST',
271+
{ 'Content-Type': 'application/x-www-form-urlencoded' },
272+
params.toString()
273+
);
274+
275+
const tokenResponse = await providerWithMultiHandler.fetch(tokenRequest, mockEnv, mockCtx);
276+
const tokens = await tokenResponse.json();
277+
const accessToken = tokens.access_token;
278+
279+
// Make requests to different API routes
280+
const usersApiRequest = createMockRequest('https://example.com/api/users/profile', 'GET', {
281+
Authorization: `Bearer ${accessToken}`,
282+
});
283+
284+
const documentsApiRequest = createMockRequest('https://example.com/api/documents/list', 'GET', {
285+
Authorization: `Bearer ${accessToken}`,
286+
});
287+
288+
// Request to Users API should be handled by UsersApiHandler
289+
const usersResponse = await providerWithMultiHandler.fetch(usersApiRequest, mockEnv, mockCtx);
290+
expect(usersResponse.status).toBe(200);
291+
expect(await usersResponse.text()).toBe('Users API response');
292+
293+
// Request to Documents API should be handled by DocumentsApiHandler
294+
const documentsResponse = await providerWithMultiHandler.fetch(documentsApiRequest, mockEnv, mockCtx);
295+
expect(documentsResponse.status).toBe(200);
296+
expect(await documentsResponse.text()).toBe('Documents API response');
297+
});
298+
299+
it('should throw an error when both single-handler and multi-handler configs are provided', () => {
300+
expect(() => {
301+
new OAuthProvider({
302+
apiRoute: '/api/',
303+
apiHandler: {
304+
fetch: () => Promise.resolve(new Response()),
305+
},
306+
apiHandlers: {
307+
'/api/users/': {
308+
fetch: () => Promise.resolve(new Response()),
309+
},
310+
},
311+
defaultHandler: testDefaultHandler,
312+
authorizeEndpoint: '/authorize',
313+
tokenEndpoint: '/oauth/token',
314+
});
315+
}).toThrow('Cannot use both apiRoute/apiHandler and apiHandlers');
316+
});
317+
318+
it('should throw an error when neither single-handler nor multi-handler config is provided', () => {
319+
expect(() => {
320+
new OAuthProvider({
321+
// Intentionally omitting apiRoute and apiHandler and apiHandlers
322+
defaultHandler: testDefaultHandler,
323+
authorizeEndpoint: '/authorize',
324+
tokenEndpoint: '/oauth/token',
325+
});
326+
}).toThrow('Must provide either apiRoute + apiHandler OR apiHandlers');
327+
});
328+
});
329+
201330
describe('OAuth Metadata Discovery', () => {
202331
it('should return correct metadata at .well-known/oauth-authorization-server', async () => {
203332
const request = createMockRequest('https://example.com/.well-known/oauth-authorization-server');
@@ -644,6 +773,44 @@ describe('OAuthProvider', () => {
644773
expect(error.error_description).toBe('redirect_uri is required when not using PKCE');
645774
});
646775

776+
it('should reject token exchange with code_verifier when PKCE was not used in authorization', async () => {
777+
// First get an auth code WITHOUT using PKCE
778+
const authRequest = createMockRequest(
779+
`https://example.com/authorize?response_type=code&client_id=${clientId}` +
780+
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
781+
`&scope=read%20write&state=xyz123`
782+
);
783+
784+
const authResponse = await oauthProvider.fetch(authRequest, mockEnv, mockCtx);
785+
const location = authResponse.headers.get('Location')!;
786+
const url = new URL(location);
787+
const code = url.searchParams.get('code')!;
788+
789+
// Now exchange the code and incorrectly provide a code_verifier
790+
const params = new URLSearchParams();
791+
params.append('grant_type', 'authorization_code');
792+
params.append('code', code);
793+
params.append('redirect_uri', redirectUri);
794+
params.append('client_id', clientId);
795+
params.append('client_secret', clientSecret);
796+
params.append('code_verifier', 'some_random_verifier_that_wasnt_used_in_auth');
797+
798+
const tokenRequest = createMockRequest(
799+
'https://example.com/oauth/token',
800+
'POST',
801+
{ 'Content-Type': 'application/x-www-form-urlencoded' },
802+
params.toString()
803+
);
804+
805+
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
806+
807+
// Should fail because code_verifier is provided but PKCE wasn't used in authorization
808+
expect(tokenResponse.status).toBe(400);
809+
const error = await tokenResponse.json();
810+
expect(error.error).toBe('invalid_request');
811+
expect(error.error_description).toBe('code_verifier provided for a flow that did not use PKCE');
812+
});
813+
647814
// Helper function for PKCE tests
648815
function generateRandomString(length: number): string {
649816
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@@ -711,44 +878,6 @@ describe('OAuthProvider', () => {
711878
expect(tokens.expires_in).toBe(3600);
712879
});
713880

714-
it('should reject token exchange with code_verifier when PKCE was not used in authorization', async () => {
715-
// First get an auth code WITHOUT using PKCE
716-
const authRequest = createMockRequest(
717-
`https://example.com/authorize?response_type=code&client_id=${clientId}` +
718-
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
719-
`&scope=read%20write&state=xyz123`
720-
);
721-
722-
const authResponse = await oauthProvider.fetch(authRequest, mockEnv, mockCtx);
723-
const location = authResponse.headers.get('Location')!;
724-
const url = new URL(location);
725-
const code = url.searchParams.get('code')!;
726-
727-
// Now exchange the code and incorrectly provide a code_verifier
728-
const params = new URLSearchParams();
729-
params.append('grant_type', 'authorization_code');
730-
params.append('code', code);
731-
params.append('redirect_uri', redirectUri);
732-
params.append('client_id', clientId);
733-
params.append('client_secret', clientSecret);
734-
params.append('code_verifier', 'some_random_verifier_that_wasnt_used_in_auth');
735-
736-
const tokenRequest = createMockRequest(
737-
'https://example.com/oauth/token',
738-
'POST',
739-
{ 'Content-Type': 'application/x-www-form-urlencoded' },
740-
params.toString()
741-
);
742-
743-
const tokenResponse = await oauthProvider.fetch(tokenRequest, mockEnv, mockCtx);
744-
745-
// Should fail because code_verifier is provided but PKCE wasn't used in authorization
746-
expect(tokenResponse.status).toBe(400);
747-
const error = await tokenResponse.json();
748-
expect(error.error).toBe('invalid_request');
749-
expect(error.error_description).toBe('code_verifier provided for a flow that did not use PKCE');
750-
});
751-
752881
it('should accept the access token for API requests', async () => {
753882
// Get an auth code
754883
const authRequest = createMockRequest(
@@ -2102,135 +2231,6 @@ describe('OAuthProvider', () => {
21022231
});
21032232
});
21042233

2105-
describe('API Route Configuration', () => {
2106-
it('should support multi-handler configuration with apiHandlers', async () => {
2107-
// Create handler classes for different API routes
2108-
class UsersApiHandler extends WorkerEntrypoint {
2109-
fetch(request: Request) {
2110-
return new Response('Users API response', { status: 200 });
2111-
}
2112-
}
2113-
2114-
class DocumentsApiHandler extends WorkerEntrypoint {
2115-
fetch(request: Request) {
2116-
return new Response('Documents API response', { status: 200 });
2117-
}
2118-
}
2119-
2120-
// Create provider with multi-handler configuration
2121-
const providerWithMultiHandler = new OAuthProvider({
2122-
apiHandlers: {
2123-
'/api/users/': UsersApiHandler,
2124-
'/api/documents/': DocumentsApiHandler,
2125-
},
2126-
defaultHandler: testDefaultHandler,
2127-
authorizeEndpoint: '/authorize',
2128-
tokenEndpoint: '/oauth/token',
2129-
clientRegistrationEndpoint: '/oauth/register', // Important for registering clients in the test
2130-
scopesSupported: ['read', 'write'],
2131-
});
2132-
2133-
// Create a client and get an access token
2134-
const clientData = {
2135-
redirect_uris: ['https://client.example.com/callback'],
2136-
client_name: 'Test Client',
2137-
token_endpoint_auth_method: 'client_secret_basic',
2138-
};
2139-
2140-
const registerRequest = createMockRequest(
2141-
'https://example.com/oauth/register',
2142-
'POST',
2143-
{ 'Content-Type': 'application/json' },
2144-
JSON.stringify(clientData)
2145-
);
2146-
2147-
const registerResponse = await providerWithMultiHandler.fetch(registerRequest, mockEnv, mockCtx);
2148-
const client = await registerResponse.json();
2149-
const clientId = client.client_id;
2150-
const clientSecret = client.client_secret;
2151-
const redirectUri = 'https://client.example.com/callback';
2152-
2153-
// Get an auth code
2154-
const authRequest = createMockRequest(
2155-
`https://example.com/authorize?response_type=code&client_id=${clientId}` +
2156-
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
2157-
`&scope=read%20write&state=xyz123`
2158-
);
2159-
2160-
const authResponse = await providerWithMultiHandler.fetch(authRequest, mockEnv, mockCtx);
2161-
const location = authResponse.headers.get('Location')!;
2162-
const code = new URL(location).searchParams.get('code')!;
2163-
2164-
// Exchange for tokens
2165-
const params = new URLSearchParams();
2166-
params.append('grant_type', 'authorization_code');
2167-
params.append('code', code);
2168-
params.append('redirect_uri', redirectUri);
2169-
params.append('client_id', clientId);
2170-
params.append('client_secret', clientSecret);
2171-
2172-
const tokenRequest = createMockRequest(
2173-
'https://example.com/oauth/token',
2174-
'POST',
2175-
{ 'Content-Type': 'application/x-www-form-urlencoded' },
2176-
params.toString()
2177-
);
2178-
2179-
const tokenResponse = await providerWithMultiHandler.fetch(tokenRequest, mockEnv, mockCtx);
2180-
const tokens = await tokenResponse.json();
2181-
const accessToken = tokens.access_token;
2182-
2183-
// Make requests to different API routes
2184-
const usersApiRequest = createMockRequest('https://example.com/api/users/profile', 'GET', {
2185-
Authorization: `Bearer ${accessToken}`,
2186-
});
2187-
2188-
const documentsApiRequest = createMockRequest('https://example.com/api/documents/list', 'GET', {
2189-
Authorization: `Bearer ${accessToken}`,
2190-
});
2191-
2192-
// Request to Users API should be handled by UsersApiHandler
2193-
const usersResponse = await providerWithMultiHandler.fetch(usersApiRequest, mockEnv, mockCtx);
2194-
expect(usersResponse.status).toBe(200);
2195-
expect(await usersResponse.text()).toBe('Users API response');
2196-
2197-
// Request to Documents API should be handled by DocumentsApiHandler
2198-
const documentsResponse = await providerWithMultiHandler.fetch(documentsApiRequest, mockEnv, mockCtx);
2199-
expect(documentsResponse.status).toBe(200);
2200-
expect(await documentsResponse.text()).toBe('Documents API response');
2201-
});
2202-
2203-
it('should throw an error when both single-handler and multi-handler configs are provided', () => {
2204-
expect(() => {
2205-
new OAuthProvider({
2206-
apiRoute: '/api/',
2207-
apiHandler: {
2208-
fetch: () => Promise.resolve(new Response()),
2209-
},
2210-
apiHandlers: {
2211-
'/api/users/': {
2212-
fetch: () => Promise.resolve(new Response()),
2213-
},
2214-
},
2215-
defaultHandler: testDefaultHandler,
2216-
authorizeEndpoint: '/authorize',
2217-
tokenEndpoint: '/oauth/token',
2218-
});
2219-
}).toThrow('Cannot use both apiRoute/apiHandler and apiHandlers');
2220-
});
2221-
2222-
it('should throw an error when neither single-handler nor multi-handler config is provided', () => {
2223-
expect(() => {
2224-
new OAuthProvider({
2225-
// Intentionally omitting apiRoute and apiHandler and apiHandlers
2226-
defaultHandler: testDefaultHandler,
2227-
authorizeEndpoint: '/authorize',
2228-
tokenEndpoint: '/oauth/token',
2229-
});
2230-
}).toThrow('Must provide either apiRoute + apiHandler OR apiHandlers');
2231-
});
2232-
});
2233-
22342234
describe('Token Revocation', () => {
22352235
let clientId: string;
22362236
let clientSecret: string;
@@ -2311,4 +2311,4 @@ describe('OAuthProvider', () => {
23112311
expect(apiResponse.status).toBe(401); // Token should no longer work
23122312
});
23132313
});
2314-
});
2314+
});

0 commit comments

Comments
 (0)