Summary
When the WOPISrc URL registered in storage.wopi.alias_groups already contains a query string (e.g. an opaque fp parameter used by the WOPI host to resolve the file), the outbound CheckFileInfo request built by coolwsd appends access_token inside the existing query parameter’s value instead of adding it as its own top-level query parameter joined with &.
As a result the WOPI host never receives a real, separate access_token parameter — it only sees its own parameter corrupted with an embedded, percent-encoded ?access_token=... string — and the document fails to open.
Environment
- coolwsd version:
26.04.3.1(Docker imagecollabora/code:26.04.3.1) - Deployment: Kubernetes (self-hosted), coolwsd behind an nginx Ingress with TLS termination
storage.wopi.alias_groupsmode:groups, explicit bare-hostname allow-list (unrelated to this issue)
Steps to reproduce
-
Configure a WOPI host whose file URLs use a query string for an internal opaque parameter, e.g.:
https://wopihost.example.com/wopi/files/<FILE_ID>?fp=<OPAQUE_PARAM>==(note the base64-style
==padding at the end of the parameter value — this seems to matter, see “Additional context” below.) -
Register this pattern as an allowed WOPI host in
coolwsd.xml. -
Open a document served through this WOPI host from the Collabora editor (
cool.html). -
Observe the outgoing
CheckFileInforequest in the wsd logs.
Expected result
coolwsd should call:
https://wopihost.example.com/wopi/files/<FILE_ID>?fp=<OPAQUE_PARAM>==&access_token=<ACCESS_TOKEN>&access_token_ttl=0
i.e. access_token and access_token_ttl both added as distinct, top-level query parameters (joined with &, since a query string already exists).
Actual result
The wsd log shows (anonymized, real hostname/tokens replaced with placeholders):
ERR #43: WOPI::CheckFileInfo returned 500 (Internal Server Error) Internal Server Error for URI
[https://wopihost.example.com/wopi/files/<FILE_ID>?fp=<OPAQUE_PARAM>%3D%3D%3Faccess_token%3D<ACCESS_TOKEN>&access_token_ttl=0].
...
Body: [Internal server error]
|.../wsd/wopi/CheckFileInfo.cpp:98
ERR #43: Failed or timed-out CheckFileInfo [https://wopihost.example.com/wopi/files/<FILE_ID>?fp=<OPAQUE_PARAM>%3D%3D%3Faccess_token%3D<ACCESS_TOKEN>&access_token_ttl=0]
|.../wsd/wopi/CheckFileInfo.cpp:112
ERR #43: CheckFileInfo failed for [https%3A%2F%2Fwopihost.example.com%3A443%2Fwopi%2Ffiles%2F<FILE_ID>], State::Fail
|.../wsd/RequestVettingStation.cpp:349
Decoding the query string of the outgoing request URI shows there is in fact only one top-level parameter, fp, whose decoded value is:
fp = <OPAQUE_PARAM>==?access_token=<ACCESS_TOKEN>
access_token_ttl = 0
access_token is not a parameter at all — it has been concatenated onto the end of the fp value using a literal ? (later percent-encoded to %3F because it now lives inside another parameter’s value), while access_token_ttl is correctly added afterwards as its own parameter using &. This inconsistency (one parameter appended correctly, the very next one appended incorrectly) strongly suggests two different code paths are used to attach these two values to the WOPISrc URL, and only one of them checks for/handles a pre-existing query string correctly.
The backend WOPI host, receiving no usable access_token, cannot authenticate the request and returns its own HTTP 500, which is then surfaced by coolwsd as WOPI::CheckFileInfo returned 500.
Additional context
- This only became visible after fixing an unrelated, correctly-behaving
storage.wopi.alias_groupsconfiguration issue (host allow-list now matches bare hostnames in 26.04, without scheme/port — which is intentional/expected behavior change from 25.04 and not part of this report). Before that fix, requests never reachedCheckFileInfo, so this URL-construction issue may have been present for longer without being observed. - The relevant code paths appear to live in the new
wsd/wopi/subfolder introduced as part of the WOPI handling refactor in 26.04 (wsd/wopi/CheckFileInfo.cpp,wsd/RequestVettingStation.cpp). - As a workaround on our side, we are considering moving the WOPI host’s opaque
fpparameter out of the query string (e.g. into the URL path) so the WOPISrc has no pre-existing query string when coolwsd appendsaccess_token/access_token_ttl— this should avoid the bug entirely if confirmed correct, since it would makeaccess_tokenthe first parameter added rather than the second one merged into an existing value.
Request
Could someone confirm whether this is a known regression in the 26.04 WOPI request-building code (vs. the pre-26.04 implementation), and if a fix already exists or is planned? Happy to provide further (anonymized) logs or test a patched build if useful.