Skip to content

Commit c6eccd7

Browse files
committed
refactor: streamline authentication classes and remove unused components
1 parent d2a93c7 commit c6eccd7

17 files changed

+166
-218
lines changed

src/Authentication/AuthInfo.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Mcp\Server\Authentication;
66

7+
use Mcp\Server\Authentication\Dto\UserProfile;
8+
79
/**
810
* Information about a validated access token, provided to request handlers.
911
*/
@@ -38,10 +40,7 @@ public function getExpiresAt(): ?int;
3840
public function getResource(): ?string;
3941

4042
/**
41-
* Additional data associated with the token.
42-
* This field should be used for any additional data that needs to be attached to the auth info.
43-
*
44-
* @return array<string, mixed>
43+
* The user profile associated with this token, if available.
4544
*/
46-
public function getExtra(): array;
45+
public function getUserProfile(): ?UserProfile;
4746
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mcp\Server\Authentication\Contract;
6+
7+
use Mcp\Server\Authentication\Dto\UserProfile;
8+
9+
interface UserProviderInterface
10+
{
11+
public function getUser(): ?UserProfile;
12+
}

src/Authentication/DefaultAuthInfo.php

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

55
namespace Mcp\Server\Authentication;
66

7+
use Mcp\Server\Authentication\Dto\UserProfile;
8+
79
/**
810
* Default implementation of AuthInfo.
911
*/
1012
final readonly class DefaultAuthInfo implements AuthInfo
1113
{
1214
/**
1315
* @param string[] $scopes
14-
* @param array<string, mixed> $extra
1516
*/
1617
public function __construct(
1718
private string $token,
1819
private string $clientId,
1920
private array $scopes,
21+
private ?UserProfile $profile = null,
2022
private ?int $expiresAt = null,
2123
private ?string $resource = null,
22-
private array $extra = [],
2324
) {}
2425

2526
public function getToken(): string
@@ -47,8 +48,8 @@ public function getResource(): ?string
4748
return $this->resource;
4849
}
4950

50-
public function getExtra(): array
51+
public function getUserProfile(): ?UserProfile
5152
{
52-
return $this->extra;
53+
return $this->profile;
5354
}
5455
}

src/Authentication/Dto/OAuthMetadata.php

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@
2525
public function __construct(
2626
public string $issuer,
2727
public string $authorizationEndpoint,
28+
public string $registrationEndpoint,
29+
public string $revocationEndpoint,
2830
public string $tokenEndpoint,
2931
public array $responseTypesSupported,
30-
public ?string $registrationEndpoint = null,
3132
public ?array $scopesSupported = null,
3233
public ?array $responseModesSupported = null,
3334
public ?array $grantTypesSupported = null,
3435
public ?array $tokenEndpointAuthMethodsSupported = null,
3536
public ?array $tokenEndpointAuthSigningAlgValuesSupported = null,
3637
public ?string $serviceDocumentation = null,
37-
public ?string $revocationEndpoint = null,
3838
public ?array $revocationEndpointAuthMethodsSupported = null,
3939
public ?array $revocationEndpointAuthSigningAlgValuesSupported = null,
4040
public ?string $introspectionEndpoint = null,
@@ -43,9 +43,35 @@ public function __construct(
4343
public ?array $codeChallengeMethodsSupported = null,
4444
) {}
4545

46-
public function getIssuer(): string
46+
public static function forProxy(string $issuer): self
4747
{
48-
return $this->issuer;
48+
return new self(
49+
tokenEndpoint: \rtrim($issuer, '/') . '/oauth2/token',
50+
authorizationEndpoint: \rtrim($issuer, '/') . '/oauth2/authorize',
51+
registrationEndpoint: \rtrim($issuer, '/') . '/oauth2/register',
52+
revocationEndpoint: \rtrim($issuer, '/') . '/oauth2/revoke',
53+
responseTypesSupported: ['code'],
54+
scopesSupported: ['read', 'write'],
55+
grantTypesSupported: ['authorization_code', 'refresh_token'],
56+
tokenEndpointAuthMethodsSupported: ['client_secret_post', 'client_secret_basic'],
57+
codeChallengeMethodsSupported: ['S256'],
58+
);
59+
}
60+
61+
public static function forGithub(string $issuer): self
62+
{
63+
return new self(
64+
issuer: $issuer,
65+
authorizationEndpoint: 'https://github.com/login/oauth/authorize',
66+
tokenEndpoint: 'https://github.com/login/oauth/access_token',
67+
revocationEndpoint: 'https://github.com/login/oauth/revoke',
68+
registrationEndpoint: 'https://github.com/login/oauth/register',
69+
responseTypesSupported: ['code'],
70+
scopesSupported: ['read', 'write'],
71+
grantTypesSupported: ['authorization_code', 'refresh_token'],
72+
tokenEndpointAuthMethodsSupported: ['client_secret_post', 'client_secret_basic'],
73+
codeChallengeMethodsSupported: ['S256'],
74+
);
4975
}
5076

5177
public function jsonSerialize(): array

src/Authentication/Handler/MetadataHandler.php

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

55
namespace Mcp\Server\Authentication\Handler;
66

7-
use JsonException;
87
use Mcp\Server\Authentication\Dto\OAuthMetadata;
98
use Mcp\Server\Authentication\Dto\OAuthProtectedResourceMetadata;
109
use Psr\Http\Message\ResponseFactoryInterface;
@@ -25,7 +24,7 @@ public function __construct(
2524
) {}
2625

2726
/**
28-
* @throws JsonException
27+
* @throws \JsonException
2928
*/
3029
public function handleOAuthMetadata(ServerRequestInterface $request): ResponseInterface
3130
{
@@ -40,7 +39,7 @@ public function handleOAuthMetadata(ServerRequestInterface $request): ResponseIn
4039
}
4140

4241
/**
43-
* @throws JsonException
42+
* @throws \JsonException
4443
*/
4544
public function handleProtectedResourceMetadata(ServerRequestInterface $request): ResponseInterface
4645
{

src/Authentication/Provider/GenericTokenVerifier.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,19 +159,13 @@ private function createAuthInfo(string $token, array $tokenData, UserProfile $us
159159
// Extract resource/audience
160160
$resource = $tokenData['aud'] ?? $tokenData['resource'] ?? null;
161161

162-
// Combine all extra data
163-
$extra = \array_merge(
164-
['user_profile' => $userProfile->jsonSerialize()],
165-
$tokenData,
166-
);
167-
168162
return new DefaultAuthInfo(
169163
token: $token,
170164
clientId: (string)$clientId,
171165
scopes: $scopes,
166+
profile: $userProfile,
172167
expiresAt: $expiresAt,
173168
resource: $resource,
174-
extra: $extra,
175169
);
176170
}
177171
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mcp\Server\Authentication\Provider;
6+
7+
use Mcp\Server\Authentication\AuthInfo;
8+
use Mcp\Server\Authentication\Contract\UserProviderInterface;
9+
use Mcp\Server\Authentication\Dto\UserProfile;
10+
11+
final readonly class InMemoryUserProvider implements UserProviderInterface
12+
{
13+
public function __construct(
14+
private ?AuthInfo $authInfo = null,
15+
) {}
16+
17+
public function getUser(): ?UserProfile
18+
{
19+
return $this->authInfo?->getUserProfile();
20+
}
21+
}

src/Authentication/Router/McpAuthMetadataRouter.php

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

src/Authentication/Router/McpAuthRouter.php

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,62 +46,70 @@ public function process(
4646
$method = $request->getMethod();
4747

4848
// Handle OAuth endpoints (no authentication required for these)
49-
$oauthResponse = match ([$path, $method]) {
50-
['/.well-known/mcp-oauth-metadata', 'GET'] => $this->metadataHandler->handleOAuthMetadata($request),
49+
$oauthResponse = match (true) {
50+
[$path, $method] === [
51+
'/.well-known/mcp-oauth-metadata',
52+
'GET',
53+
] => $this->metadataHandler->handleOAuthMetadata($request),
5154
\str_starts_with(
5255
$path,
5356
'/.well-known/oauth-protected-resource',
5457
) && $method === 'GET' => $this->metadataHandler->handleProtectedResourceMetadata(
5558
$request,
5659
),
57-
['/.well-known/oauth-authorization-server', 'GET'] => $this->metadataHandler->handleOAuthMetadata(
60+
\str_starts_with(
61+
$path,
62+
'/.well-known/oauth-authorization-server',
63+
) && $method === 'GET' => $this->metadataHandler->handleOAuthMetadata(
5864
$request,
5965
),
60-
['/oauth2/authorize', 'GET'] => $this->authorizeHandler->handle($request),
61-
['/oauth2/token', 'POST'] => $this->tokenHandler->handle($request),
62-
['/oauth2/revoke', 'POST'] => $this->revokeHandler->handle($request),
63-
['/oauth2/register', 'POST'] => $this->registerHandler->handle($request),
66+
[$path, $method] == ['/oauth2/authorize', 'GET'] => $this->authorizeHandler->handle($request),
67+
[$path, $method] == ['/oauth2/token', 'POST'] => $this->tokenHandler->handle($request),
68+
[$path, $method] == ['/oauth2/revoke', 'POST'] => $this->revokeHandler->handle($request),
69+
[$path, $method] == ['/oauth2/register', 'POST'] => $this->registerHandler->handle($request),
6470
default => null,
6571
};
6672

6773
if ($oauthResponse !== null) {
6874
return $oauthResponse;
6975
}
7076

71-
// For all other requests, require authentication if token verifier is configured
72-
if ($this->tokenVerifier !== null) {
73-
try {
77+
try {
78+
// For all other requests, require authentication if token verifier is configured
79+
if ($this->tokenVerifier !== null) {
7480
$authInfo = $this->extractAndVerifyToken($request);
7581

7682
// Check required scopes
77-
if (!empty($this->requiredScopes)) {
78-
$this->validateScopes($authInfo->getScopes(), $this->requiredScopes);
79-
}
83+
if ($authInfo !== null) {
84+
if (!empty($this->requiredScopes)) {
85+
$this->validateScopes($authInfo->getScopes(), $this->requiredScopes);
86+
}
8087

81-
// Add authenticated user to request attributes
82-
$request = $request->withAttribute('auth', $authInfo);
83-
} catch (InvalidTokenError) {
84-
return $this->createAuthErrorResponse(401, 'invalid_token', 'Authentication required');
85-
} catch (InsufficientScopeError $e) {
86-
return $this->createAuthErrorResponse(403, 'insufficient_scope', $e->getMessage());
88+
// Add authenticated user to request attributes
89+
$request = $request->withAttribute('auth', $authInfo);
90+
}
8791
}
88-
}
8992

90-
return $handler->handle($request);
93+
return $handler->handle($request);
94+
} catch (InvalidTokenError $e) {
95+
return $this->createAuthErrorResponse(401, 'invalid_token', 'Authentication required');
96+
} catch (InsufficientScopeError $e) {
97+
return $this->createAuthErrorResponse(403, 'insufficient_scope', $e->getMessage());
98+
}
9199
}
92100

93-
private function extractAndVerifyToken(ServerRequestInterface $request): AuthInfo
101+
private function extractAndVerifyToken(ServerRequestInterface $request): ?AuthInfo
94102
{
95103
// Extract Bearer token from Authorization header
96104
$authHeader = $request->getHeaderLine('Authorization');
97105
if (!$authHeader || !\str_starts_with(\strtolower($authHeader), 'bearer ')) {
98-
throw new InvalidTokenError('Missing or invalid Authorization header');
106+
return null;
99107
}
100108

101109
$token = \substr($authHeader, 7); // Remove "Bearer " prefix
102110

103111
if (empty($token)) {
104-
throw new InvalidTokenError('Empty bearer token');
112+
return null;
105113
}
106114

107115
// Verify token

0 commit comments

Comments
 (0)