fix: normalize bridge metadata authorization

This commit is contained in:
Haitao Pan 2026-04-30 13:28:24 +08:00
parent b58b00f9d1
commit 9a8dac8c83
2 changed files with 53 additions and 2 deletions

View File

@ -27,7 +27,10 @@ Future<Map<String, dynamic>> loadBridgeMetadataForSettingsAbout({
fragment: null,
);
final authorizationHeader = await authorizationResolver(pingEndpoint);
if (authorizationHeader == null || authorizationHeader.trim().isEmpty) {
final normalizedAuthorizationHeader = _normalizeAuthorizationHeader(
authorizationHeader ?? '',
);
if (normalizedAuthorizationHeader.isEmpty) {
return const <String, dynamic>{
'status': 'unavailable',
'version': '',
@ -45,7 +48,7 @@ Future<Map<String, dynamic>> loadBridgeMetadataForSettingsAbout({
.timeout(const Duration(seconds: 4));
request.headers.set(
HttpHeaders.authorizationHeader,
'Bearer $authorizationHeader',
normalizedAuthorizationHeader,
);
request.headers.set(HttpHeaders.acceptHeader, 'application/json');
final response = await request.close().timeout(const Duration(seconds: 4));
@ -88,6 +91,21 @@ Future<Map<String, dynamic>> loadBridgeMetadataForSettingsAbout({
};
}
String _normalizeAuthorizationHeader(String raw) {
final trimmed = raw.trim();
if (trimmed.isEmpty) {
return '';
}
final separatorIndex = trimmed.indexOf(RegExp(r'\s'));
if (separatorIndex > 0 && separatorIndex < trimmed.length - 1) {
final scheme = trimmed.substring(0, separatorIndex);
if (RegExp(r"^[A-Za-z][A-Za-z0-9!#$%&'*+.^_`|~-]*$").hasMatch(scheme)) {
return trimmed;
}
}
return 'Bearer $trimmed';
}
class SettingsPage extends StatefulWidget {
const SettingsPage({
super.key,

View File

@ -50,6 +50,39 @@ void main() {
expect(metadata['buildDate'], '2026-04-21');
});
test('preserves prebuilt bearer authorization', () async {
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
addTearDown(() async {
await server.close(force: true);
});
var authorizationHeader = '';
server.listen((request) async {
authorizationHeader =
request.headers.value(HttpHeaders.authorizationHeader) ?? '';
request.response
..statusCode = HttpStatus.ok
..headers.contentType = ContentType.json
..write(
jsonEncode(<String, dynamic>{
'status': 'ok',
'version': '991ecb0',
}),
);
await request.response.close();
});
final metadata = await loadBridgeMetadataForSettingsAbout(
bridgeEndpoint: Uri.parse(
'http://${server.address.address}:${server.port}',
),
authorizationResolver: (_) async => 'Bearer bridge-token',
);
expect(authorizationHeader, 'Bearer bridge-token');
expect(metadata['status'], 'ok');
});
test('returns unavailable when bridge authorization is missing', () async {
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
addTearDown(() async {