Fix managed bridge token priority

This commit is contained in:
Haitao Pan 2026-05-25 09:13:54 +08:00
parent 88fa597c8e
commit 0201ffa676
4 changed files with 105 additions and 19 deletions

View File

@ -1075,15 +1075,8 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
normalizedHost == bridgeHost &&
(bridgePort <= 0 || endpoint.port == bridgePort);
if (matchesBridgeEndpoint) {
final envToken = runtimeEnvironmentValueInternal('BRIDGE_AUTH_TOKEN');
if (envToken != null && envToken.isNotEmpty) {
return envToken;
}
final bridgeToken = (await storeInternal.loadAccountManagedSecret(
target: kAccountManagedSecretTargetBridgeAuthToken,
))?.trim();
if (bridgeToken?.isNotEmpty == true) {
final bridgeToken = await _resolveManagedBridgeAuthTokenInternal();
if (bridgeToken != null && bridgeToken.isNotEmpty) {
return bridgeToken;
}
}
@ -1100,20 +1093,27 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
return null;
}
final envToken = runtimeEnvironmentValueInternal('BRIDGE_AUTH_TOKEN');
if (envToken != null && envToken.isNotEmpty) {
return _normalizeAuthorizationHeaderInternal(envToken);
}
final bridgeToken = (await storeInternal.loadAccountManagedSecret(
target: kAccountManagedSecretTargetBridgeAuthToken,
))?.trim();
if (bridgeToken?.isNotEmpty == true) {
return _normalizeAuthorizationHeaderInternal(bridgeToken!);
final bridgeToken = await _resolveManagedBridgeAuthTokenInternal();
if (bridgeToken != null && bridgeToken.isNotEmpty) {
return _normalizeAuthorizationHeaderInternal(bridgeToken);
}
return null;
}
Future<String?> _resolveManagedBridgeAuthTokenInternal() async {
final accountSyncState = settingsControllerInternal.accountSyncState;
if (settingsControllerInternal.accountSignedIn &&
accountSyncState?.tokenConfigured.bridge == true) {
final bridgeToken = (await storeInternal.loadAccountManagedSecret(
target: kAccountManagedSecretTargetBridgeAuthToken,
))?.trim();
return bridgeToken?.isNotEmpty == true ? bridgeToken : null;
}
final envToken = runtimeEnvironmentValueInternal('BRIDGE_AUTH_TOKEN');
return envToken?.isNotEmpty == true ? envToken : null;
}
int? gatewayProfileIndexMatchingEndpointInternal(Uri endpoint) {
final normalizedHost = endpoint.host.trim().toLowerCase();
final normalizedScheme = endpoint.scheme.trim().toLowerCase();

View File

@ -116,6 +116,16 @@ void main() {
enableSecureStorage: false,
);
await store.initialize();
await store.saveAccountSessionToken('session-token');
await store.saveAccountSessionSummary(
const AccountSessionSummary(
userId: 'user-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'reviewer',
mfaEnabled: true,
),
);
await store.saveAccountManagedSecret(
target: kAccountManagedSecretTargetBridgeAuthToken,
value: 'bridge-token',

View File

@ -1247,6 +1247,72 @@ void main() {
},
);
test(
'desktop bridge auth resolver prefers synced managed bridge token over stale environment token',
() async {
final storeRoot = await Directory.systemTemp.createTemp(
'xworkmate-acp-auth-managed-bridge-env-priority-',
);
addTearDown(() async {
if (await storeRoot.exists()) {
try {
await storeRoot.delete(recursive: true);
} on FileSystemException {
// Temp cleanup is best effort here. The controller may still be
// releasing files when teardown starts.
}
}
});
final store = SecureConfigStore(
secretRootPathResolver: () async => '${storeRoot.path}/secrets',
appDataRootPathResolver: () async => '${storeRoot.path}/app-data',
supportRootPathResolver: () async => '${storeRoot.path}/support',
enableSecureStorage: false,
);
await store.initialize();
await store.saveAccountSessionToken('session-token');
await store.saveAccountSessionSummary(
const AccountSessionSummary(
userId: 'user-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'reviewer',
mfaEnabled: true,
),
);
await store.saveAccountSyncState(
AccountSyncState.defaults().copyWith(
syncState: 'ready',
tokenConfigured: const AccountTokenConfigured(
bridge: true,
vault: false,
),
),
);
await store.saveAccountManagedSecret(
target: kAccountManagedSecretTargetBridgeAuthToken,
value: 'fresh-bridge-token',
);
final controller = AppController(
environmentOverride: const <String, String>{
'BRIDGE_AUTH_TOKEN': 'stale-env-token',
},
store: store,
);
addTearDown(controller.dispose);
await controller.settingsControllerInternal.initialize();
final header = await controller
.resolveGatewayAcpAuthorizationHeaderInternal(
Uri.parse('https://xworkmate-bridge.svc.plus/acp/rpc'),
);
expect(header, 'fresh-bridge-token');
},
);
test(
'desktop bridge auth resolver does not fallback to the remote gateway token for bridge ACP',
() async {

View File

@ -388,6 +388,16 @@ void main() {
enableSecureStorage: false,
);
await store.initialize();
await store.saveAccountSessionToken('session-token');
await store.saveAccountSessionSummary(
const AccountSessionSummary(
userId: 'user-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'reviewer',
mfaEnabled: true,
),
);
await store.saveAccountSyncState(
AccountSyncState.defaults().copyWith(
syncState: 'ready',