feat: add positive tests for the Authorization Code Grant#267
feat: add positive tests for the Authorization Code Grant#267Michito-Okai wants to merge 1 commit into
Conversation
commit: |
| export const AuthorizationServerOptionsSchema = z.object({ | ||
| url: z.string().url('Invalid authorization server URL') | ||
| url: z.string().url('Invalid authorization server URL'), | ||
| clientId: z.string().min(1, 'Client id cannot be empty'), |
There was a problem hiding this comment.
The schema REQUIRES clientId and secret while CLI requires them as optional parameter, which causes contradiction.
One way for resolving is that the schema requires the parameters as optional.
There was a problem hiding this comment.
Assuming this PR will be merged, the clientId and secret were optional CLI parameters. I have made clientId and secret mandatory CLI parameters.
| - Token response MUST return a JSON response including access_token and token_type`; | ||
|
|
||
| async run( | ||
| option: any, |
There was a problem hiding this comment.
I think it might be better to be type-safe as much as we can.
How about using AuthorizationServerOptions defined by schemas.ts ?
There was a problem hiding this comment.
I changed it from any to AuthorizationServerOptions.
| import { request } from 'undici'; | ||
| import { randomBytes } from 'crypto'; | ||
|
|
||
| const STATE = randomBytes(32).toString('base64url'); |
There was a problem hiding this comment.
When only the module is loaded, a pseudo-random number is generated and assigned to STATE, which means that multiple runs share that. For now, it might not cause any issue, but in the future, it might cause the issue.
If possible, I think it might be better to generate the value and assign it to STATE per run.
There was a problem hiding this comment.
I modified it so that the value of state changes each time run is called.
| const STATE = randomBytes(32).toString('base64url'); | ||
| const REDIRECT_URI_ORIGIN = 'http://localhost'; | ||
| const REDIRECT_URI_PATH = '/callback'; | ||
| const CODE_VERIFIER = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; |
There was a problem hiding this comment.
It seems that CODE_VERIFIER and CODE_CHALLENGE values are from https://datatracker.ietf.org/doc/html/rfc7636#appendix-B as a valid combination of code_challenge and code_verifier.
How about mentioning that here?
There was a problem hiding this comment.
I added a comment about CODE_VERIFIER and CODE_CHALLENGE values.
| resolveFn = resolve; | ||
| }); | ||
|
|
||
| const server = app.listen(port, () => { |
There was a problem hiding this comment.
app.listen(port) binds to 0.0.0.0 by default, which means that it listens all IP addresses of the machine running the test. I am afraid that it may cause some security issue.
How about binding explicitly to the loopback address?
const server = app.listen(port, '127.0.0.1', () => { ... });
| } | ||
| const body = resultMetadata.body; | ||
|
|
||
| const callback = startCallbackServer(option.port); |
There was a problem hiding this comment.
If any request hits the server on a path other than /callback, express returns a default 404 but the server stays open indefinitely (until timeout).
If possible, it might be good if the server receives a request other than /callback, then it server closes and the test finishes as failure.
There was a problem hiding this comment.
The callback accepts any path, and the path check is performed during the redirect_uri validation to cause failure if it does not match.
| grant_type: 'authorization_code', | ||
| code, | ||
| redirect_uri: REDIRECT_URI_ORIGIN + ':' + option.port + REDIRECT_URI_PATH, | ||
| client_id: option.clientId, |
There was a problem hiding this comment.
It includes client_id and client_secret are included into HTTP Form body and assumes that a target authorization server supports client_secret_post as client authentication method.
I am afraid that if an authorization server does not support client_secret_post but support client_secret_basic, then this token request fails.
Therefore, it might be better to firstly look at token_endpoint_auth_methods_supported of server metadata and determine which authentication method is used.
There was a problem hiding this comment.
I fixed.
If the authorization server does not support client_secret_post and client_secret_basic, it is set to SKIPPED. The implementations of client_secret_jwt, private_key_jwt, and tls_client_auth are out of scope and have been added to the To do list of this ISSUE.
| specReferences: [ | ||
| { | ||
| id: 'Authorization-Code-Grant', | ||
| url: 'https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-15#section-4.1' |
There was a problem hiding this comment.
I think It might be better to point to MCP spec's reference that mandate https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-15#section-4.1 .
There was a problem hiding this comment.
I fixed the version of the OAuth 2.1 URL.
Also, I added OAUTH_2_1_AUTHORIZATION_CODE_GRANT to spec-refercenses.ts.
| specReferences: [ | ||
| { | ||
| id: 'Authorization-Code-Grant', | ||
| url: 'https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-15#section-4.1' |
There was a problem hiding this comment.
I think It might be better to point to MCP spec's reference that mandate https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-15#section-4.1 .
There was a problem hiding this comment.
Also, it might be good to refer OAuth 2.1 v13 instead of v15 because:
- the latest MCP revision, namely 2025-11-25, refers v13.
- other MCP client and server tests refers v13.
There was a problem hiding this comment.
Also, how about following the MCP client and server test convention for the way of referring the spec?
E.g, spec-refercenses.ts:
export const SpecReferences: { [key: string]: SpecReference } = {
RFC_PRM_DISCOVERY: {
id: 'RFC-9728',
url: '[https://www.rfc-editor.org/rfc/rfc9728.html#section-3.1'](https://www.rfc-editor.org/rfc/rfc9728.html#section-3.1%27)
},
There was a problem hiding this comment.
I fixed the version of the OAuth 2.1 URL.
Also, I added OAUTH_2_1_AUTHORIZATION_CODE_GRANT to spec-refercenses.ts.
2f8846b to
eeed17f
Compare
Motivation and Context
Described in #208
How Has This Been Tested?
Breaking Changes
Types of changes
Checklist