discoverMcpAuthorizationServerMetadata function
Future<McpAuthorizationServerDiscovery>
discoverMcpAuthorizationServerMetadata(
- Uri issuer, {
- HttpClient? httpClient,
- Map<
String, String> headers = const <String, String>{}, - bool closeHttpClient = false,
- int maxMetadataBytes = _defaultMaxMetadataBytes,
- Duration timeout = _defaultDiscoveryTimeout,
- void onRequestOpened(
- HttpClientRequest request
Discovers MCP-compatible OAuth authorization-server metadata for issuer.
The RFC 8414 and OpenID Connect endpoints are attempted in MCP priority
order. Requests intentionally omit authorization and MCP session state.
timeout is one total deadline across every fallback. onRequestOpened
observes each request before it is sent so an owning client can bind it to a
larger lifecycle.
Implementation
Future<McpAuthorizationServerDiscovery> discoverMcpAuthorizationServerMetadata(
Uri issuer, {
HttpClient? httpClient,
Map<String, String> headers = const <String, String>{},
bool closeHttpClient = false,
int maxMetadataBytes = _defaultMaxMetadataBytes,
Duration timeout = _defaultDiscoveryTimeout,
void Function(HttpClientRequest request)? onRequestOpened,
}) async {
_validateAuthorizationServerIssuer(issuer, 'issuer');
if (maxMetadataBytes <= 0) {
throw ArgumentError.value(
maxMetadataBytes,
'maxMetadataBytes',
'must be greater than zero',
);
}
if (timeout <= Duration.zero) {
throw ArgumentError.value(timeout, 'timeout', 'must be greater than zero');
}
_validateDiscoveryHeaders(headers);
final client = httpClient ?? HttpClient();
final ownsClient = httpClient == null || closeHttpClient;
final attempted = <Uri>[];
final stopwatch = Stopwatch()..start();
McpAuthorizationDiscoveryException? lastFailure;
try {
for (final metadataUri in _authorizationServerMetadataUris(issuer)) {
attempted.add(metadataUri);
try {
final response = await _getDiscoveryDocument(
client,
metadataUri,
headers: headers,
maxMetadataBytes: maxMetadataBytes,
stopwatch: stopwatch,
timeout: timeout,
documentLabel: 'Authorization Server Metadata',
onRequestOpened: onRequestOpened,
);
if (response.statusCode != HttpStatus.ok) {
throw McpAuthorizationDiscoveryException(
'Authorization Server Metadata request failed.',
uri: metadataUri,
statusCode: response.statusCode,
);
}
final metadata = _authorizationServerMetadataFromResponse(
response,
metadataUri,
issuer,
);
return McpAuthorizationServerDiscovery(
metadataUri: metadataUri,
metadata: metadata,
);
} on McpAuthorizationDiscoveryException catch (error) {
if (stopwatch.elapsed >= timeout) {
rethrow;
}
lastFailure = error;
}
}
final detail = lastFailure == null ? '' : ' ${lastFailure.message}';
throw McpAuthorizationDiscoveryException(
'Authorization Server Metadata discovery failed after attempting '
'${attempted.join(', ')}.$detail',
uri: lastFailure?.uri ?? issuer,
statusCode: lastFailure?.statusCode,
);
} finally {
stopwatch.stop();
if (ownsClient) {
client.close(force: true);
}
}
}