Skip to content

Commit 4892b7c

Browse files
committed
add support for additional auth scenarios in conformance tests and refine JWT assertion audience handling
1 parent 7c1a1cf commit 4892b7c

3 files changed

Lines changed: 25 additions & 6 deletions

File tree

conformance-test/conformance-baseline.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,3 @@ server:
1212
client:
1313
- elicitation-sep1034-client-defaults
1414
- sse-retry
15-
- auth/client-credentials-jwt

conformance-test/run-conformance.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,24 @@ run_client_auth_suite() {
109109
echo "=========================================="
110110
echo " Running CLIENT (auth) conformance tests"
111111
echo "=========================================="
112+
local rc=0
112113
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" client \
113114
--command "$CLIENT_DIST" \
114115
--suite auth \
115116
--output-dir "$output_dir" \
116117
--expected-failures "$SCRIPT_DIR/conformance-baseline.yml" \
117-
"$@" || return 1
118+
"$@" || rc=$?
119+
120+
local extra_scenarios=("auth/client-credentials-jwt" "auth/client-credentials-basic" "auth/cross-app-access-complete-flow")
121+
for scenario in "${extra_scenarios[@]}"; do
122+
npx "@modelcontextprotocol/conformance@$CONFORMANCE_VERSION" client \
123+
--command "$CLIENT_DIST" \
124+
--scenario "$scenario" \
125+
--output-dir "$output_dir" \
126+
--expected-failures "$SCRIPT_DIR/conformance-baseline.yml" \
127+
"$@" || rc=$?
128+
done
129+
return $rc
118130
}
119131

120132
# ============================================================================

conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/auth/JWTScenario.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import io.modelcontextprotocol.kotlin.sdk.client.StreamableHttpClientTransport
1111
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
1212
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
1313
import kotlinx.serialization.json.buildJsonObject
14+
import kotlinx.serialization.json.jsonArray
1415
import kotlinx.serialization.json.jsonPrimitive
1516
import kotlinx.serialization.json.put
1617
import java.security.KeyFactory
@@ -33,10 +34,17 @@ internal suspend fun runClientCredentialsJwt(serverUrl: String) {
3334
}
3435

3536
httpClient.use { client ->
36-
val tokenEndpoint = discoverTokenEndpoint(client, serverUrl)
37+
val resourceMetadata = discoverResourceMetadata(client, serverUrl)
38+
val authServer = resourceMetadata["authorization_servers"]?.jsonArray?.firstOrNull()?.jsonPrimitive?.content
39+
?: error("No authorization_servers in resource metadata")
40+
val oauthMetadata = fetchOAuthMetadata(client, authServer)
41+
val tokenEndpoint = oauthMetadata["token_endpoint"]?.jsonPrimitive?.content
42+
?: error("No token_endpoint in AS metadata")
43+
val issuer = oauthMetadata["issuer"]?.jsonPrimitive?.content
44+
?: error("No issuer in AS metadata")
3745

3846
// Create JWT client assertion
39-
val assertion = createJwtAssertion(clientId, tokenEndpoint, privateKeyPem, signingAlgorithm)
47+
val assertion = createJwtAssertion(clientId, issuer, privateKeyPem, signingAlgorithm)
4048

4149
// Exchange for token
4250
val tokenResponse = client.submitForm(
@@ -67,7 +75,7 @@ internal suspend fun runClientCredentialsJwt(serverUrl: String) {
6775
@OptIn(ExperimentalUuidApi::class)
6876
private fun createJwtAssertion(
6977
clientId: String,
70-
tokenEndpoint: String,
78+
audience: String,
7179
privateKeyPem: String,
7280
algorithm: String,
7381
): String {
@@ -80,7 +88,7 @@ private fun createJwtAssertion(
8088
val payload = buildJsonObject {
8189
put("iss", clientId)
8290
put("sub", clientId)
83-
put("aud", tokenEndpoint)
91+
put("aud", audience)
8492
put("iat", now)
8593
put("exp", now + 300)
8694
put("jti", Uuid.random().toString())

0 commit comments

Comments
 (0)