Skip to content

Commit 36532fd

Browse files
committed
Add case for preconfigured assertion in RFC7523 S2.2 flow
1 parent 39758e2 commit 36532fd

File tree

1 file changed

+26
-19
lines changed

1 file changed

+26
-19
lines changed

src/mcp/client/auth.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -470,27 +470,33 @@ async def _exchange_token_client_credentials(self) -> httpx.Request:
470470
# Use JWT assertion for client authentication
471471
if not self.context.jwt_parameters:
472472
raise OAuthTokenError("Missing JWT parameters for private_key_jwt flow")
473-
if not self.context.jwt_parameters.jwt_signing_key:
474-
raise OAuthTokenError("Missing JWT signing key for private_key_jwt flow")
475-
if not self.context.jwt_parameters.jwt_signing_algorithm:
476-
raise OAuthTokenError("Missing JWT signing algorithm for private_key_jwt flow")
477473

478-
now = int(time.time())
479-
claims = {
480-
"iss": self.context.jwt_parameters.issuer,
481-
"sub": self.context.jwt_parameters.subject,
482-
"aud": self.context.jwt_parameters.audience if self.context.jwt_parameters.audience else token_url,
483-
"exp": now + self.context.jwt_parameters.jwt_lifetime_seconds,
484-
"iat": now,
485-
"jti": str(uuid4()),
486-
}
487-
claims.update(self.context.jwt_parameters.claims or {})
474+
if self.context.jwt_parameters.assertion is not None:
475+
# Prebuilt JWT (e.g. acquired out-of-band)
476+
assertion = self.context.jwt_parameters.assertion
477+
else:
478+
if not self.context.jwt_parameters.jwt_signing_key:
479+
raise OAuthTokenError("Missing JWT signing key for private_key_jwt flow")
480+
if not self.context.jwt_parameters.jwt_signing_algorithm:
481+
raise OAuthTokenError("Missing JWT signing algorithm for private_key_jwt flow")
482+
483+
now = int(time.time())
484+
claims = {
485+
"iss": self.context.jwt_parameters.issuer,
486+
"sub": self.context.jwt_parameters.subject,
487+
"aud": self.context.jwt_parameters.audience if self.context.jwt_parameters.audience else token_url,
488+
"exp": now + self.context.jwt_parameters.jwt_lifetime_seconds,
489+
"iat": now,
490+
"jti": str(uuid4()),
491+
}
492+
claims.update(self.context.jwt_parameters.claims or {})
493+
494+
assertion = jwt.encode(
495+
claims,
496+
self.context.jwt_parameters.jwt_signing_key,
497+
algorithm=self.context.jwt_parameters.jwt_signing_algorithm or "RS256",
498+
)
488499

489-
assertion = jwt.encode(
490-
claims,
491-
self.context.jwt_parameters.jwt_signing_key,
492-
algorithm=self.context.jwt_parameters.jwt_signing_algorithm or "RS256",
493-
)
494500
# When using private_key_jwt, in a client_credentials flow, we use RFC 7523 Section 2.2
495501
token_data["client_assertion"] = assertion
496502
token_data["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
@@ -510,6 +516,7 @@ async def _exchange_token_jwt_bearer(self) -> httpx.Request:
510516
token_url = self._get_token_endpoint()
511517

512518
if self.context.jwt_parameters.assertion is not None:
519+
# Prebuilt JWT (e.g. acquired out-of-band)
513520
assertion = self.context.jwt_parameters.assertion
514521
else:
515522
if not self.context.jwt_parameters.jwt_signing_key:

0 commit comments

Comments
 (0)