Skip to content

Commit 73d69b4

Browse files
committed
refactor: remove ClientAuthMiddleware and restructure OAuth handlers
1 parent 7f17b38 commit 73d69b4

File tree

7 files changed

+195
-211
lines changed

7 files changed

+195
-211
lines changed

composer.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
"type": "library",
66
"license": "MIT",
77
"authors": [
8-
{
9-
"name": "Kyrian Obikwelu",
10-
"email": "koshnawaza@gmail.com"
11-
},
128
{
139
"name": "Pavel Buchnev",
1410
"email": "butschster@gmail.com"

src/Authentication/Handler/MetadataHandler.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Mcp\Server\Authentication\Dto\OAuthMetadata;
88
use Mcp\Server\Authentication\Dto\OAuthProtectedResourceMetadata;
9+
use Mcp\Server\Authentication\Router\AuthRouterOptions;
910
use Psr\Http\Message\ResponseFactoryInterface;
1011
use Psr\Http\Message\ResponseInterface;
1112
use Psr\Http\Message\ServerRequestInterface;
@@ -23,6 +24,33 @@ public function __construct(
2324
private StreamFactoryInterface $streamFactory,
2425
) {}
2526

27+
public static function create(
28+
AuthRouterOptions $options,
29+
ResponseFactoryInterface $responseFactory,
30+
StreamFactoryInterface $streamFactory,
31+
): self {
32+
$oauthMetadata = self::createOAuthMetadata($options);
33+
34+
// Create protected resource metadata
35+
$protectedResourceMetadata = new OAuthProtectedResourceMetadata(
36+
resource: $options->baseUrl ?? $oauthMetadata->getIssuer(),
37+
authorizationServers: [$oauthMetadata->getIssuer()],
38+
jwksUri: null,
39+
scopesSupported: empty($options->scopesSupported) ? null : $options->scopesSupported,
40+
bearerMethodsSupported: null,
41+
resourceSigningAlgValuesSupported: null,
42+
resourceName: $options->resourceName,
43+
resourceDocumentation: $options->serviceDocumentationUrl,
44+
);
45+
46+
return new self(
47+
oauthMetadata: $oauthMetadata,
48+
protectedResourceMetadata: $protectedResourceMetadata,
49+
responseFactory: $responseFactory,
50+
streamFactory: $streamFactory,
51+
);
52+
}
53+
2654
public function handleOAuthMetadata(ServerRequestInterface $request): ResponseInterface
2755
{
2856
$json = \json_encode($this->oauthMetadata->jsonSerialize(), JSON_THROW_ON_ERROR);
@@ -46,4 +74,58 @@ public function handleProtectedResourceMetadata(ServerRequestInterface $request)
4674
->withHeader('Cache-Control', 'public, max-age=3600')
4775
->withBody($body);
4876
}
77+
78+
private static function createOAuthMetadata(AuthRouterOptions $options): OAuthMetadata
79+
{
80+
self::checkIssuerUrl($options->issuerUrl);
81+
82+
$baseUrl = $options->baseUrl ?? $options->issuerUrl;
83+
84+
return new OAuthMetadata(
85+
issuer: $options->issuerUrl,
86+
authorizationEndpoint: self::buildUrl('/oauth2/authorize', $baseUrl),
87+
tokenEndpoint: self::buildUrl('/oauth2/token', $baseUrl),
88+
responseTypesSupported: ['code'],
89+
registrationEndpoint: self::buildUrl('/oauth2/register', $baseUrl),
90+
scopesSupported: empty($options->scopesSupported) ? null : $options->scopesSupported,
91+
responseModesSupported: null,
92+
grantTypesSupported: ['authorization_code', 'refresh_token'],
93+
tokenEndpointAuthMethodsSupported: ['client_secret_post'],
94+
tokenEndpointAuthSigningAlgValuesSupported: null,
95+
serviceDocumentation: $options->serviceDocumentationUrl,
96+
revocationEndpoint: self::buildUrl('/oauth2/revoke', $baseUrl),
97+
revocationEndpointAuthMethodsSupported: ['client_secret_post'],
98+
revocationEndpointAuthSigningAlgValuesSupported: null,
99+
introspectionEndpoint: null,
100+
introspectionEndpointAuthMethodsSupported: null,
101+
introspectionEndpointAuthSigningAlgValuesSupported: null,
102+
codeChallengeMethodsSupported: ['S256'],
103+
);
104+
}
105+
106+
private static function checkIssuerUrl(string $issuer): void
107+
{
108+
$parsed = \parse_url($issuer);
109+
110+
// Technically RFC 8414 does not permit a localhost HTTPS exemption, but this is necessary for testing
111+
if (
112+
$parsed['scheme'] !== 'https' &&
113+
!\in_array($parsed['host'] ?? '', ['localhost', '127.0.0.1'], true)
114+
) {
115+
throw new \InvalidArgumentException('Issuer URL must be HTTPS');
116+
}
117+
118+
if (isset($parsed['fragment'])) {
119+
throw new \InvalidArgumentException("Issuer URL must not have a fragment: {$issuer}");
120+
}
121+
122+
if (isset($parsed['query'])) {
123+
throw new \InvalidArgumentException("Issuer URL must not have a query string: {$issuer}");
124+
}
125+
}
126+
127+
private static function buildUrl(string $path, string $baseUrl): string
128+
{
129+
return \rtrim($baseUrl, '/') . $path;
130+
}
49131
}

src/Authentication/Handler/TokenHandler.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Mcp\Server\Authentication\Handler;
66

77
use Mcp\Server\Authentication\Contract\OAuthServerProviderInterface;
8+
use Mcp\Server\Authentication\Dto\OAuthClientInformation;
89
use Mcp\Server\Authentication\Error\InvalidClientError;
910
use Mcp\Server\Authentication\Error\InvalidGrantError;
1011
use Mcp\Server\Authentication\Error\InvalidRequestError;
@@ -54,7 +55,9 @@ public function handle(ServerRequestInterface $request): ResponseInterface
5455
return match ($params['grant_type']) {
5556
'authorization_code' => $this->handleAuthorizationCodeGrant($client, $params, $response),
5657
'refresh_token' => $this->handleRefreshTokenGrant($client, $params, $response),
57-
default => throw new UnsupportedGrantTypeError('The grant type is not supported by this authorization server'),
58+
default => throw new UnsupportedGrantTypeError(
59+
'The grant type is not supported by this authorization server',
60+
),
5861
};
5962
} catch (OAuthError $e) {
6063
return $this->createErrorResponse($e);
@@ -66,7 +69,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface
6669
}
6770

6871
private function handleAuthorizationCodeGrant(
69-
$client,
72+
OAuthClientInformation $client,
7073
array $params,
7174
ResponseInterface $response,
7275
): ResponseInterface {
@@ -111,8 +114,11 @@ private function handleAuthorizationCodeGrant(
111114
->withBody($body);
112115
}
113116

114-
private function handleRefreshTokenGrant($client, array $params, ResponseInterface $response): ResponseInterface
115-
{
117+
private function handleRefreshTokenGrant(
118+
OAuthClientInformation $client,
119+
array $params,
120+
ResponseInterface $response,
121+
): ResponseInterface {
116122
if (empty($params['refresh_token'])) {
117123
throw new InvalidRequestError('Missing refresh_token parameter');
118124
}

src/Authentication/Middleware/ClientAuthMiddleware.php

Lines changed: 0 additions & 110 deletions
This file was deleted.

src/Authentication/Router/AuthRouterOptions.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@
44

55
namespace Mcp\Server\Authentication\Router;
66

7-
use Mcp\Server\Authentication\Contract\OAuthServerProviderInterface;
8-
97
/**
108
* Configuration for OAuth router.
119
*/
1210
final readonly class AuthRouterOptions
1311
{
1412
/**
1513
* @param string[] $scopesSupported
14+
* @param string[] $requiredScopes
1615
*/
1716
public function __construct(
18-
public OAuthServerProviderInterface $provider,
1917
public string $issuerUrl,
2018
public ?string $baseUrl = null,
2119
public ?string $serviceDocumentationUrl = null,
2220
public array $scopesSupported = [],
2321
public ?string $resourceName = null,
22+
public array $requiredScopes = [],
23+
public ?string $resourceMetadataUrl = null,
2424
) {}
2525
}

0 commit comments

Comments
 (0)