Attachment download request is missing authentication header

Hey all,
we are currently updating to 2024.06 (FormEngine Core version 37.1.1) and are having trouble with how the form engine is using our custom AttachmentLoader (using the ones provided by A12 is no option for us).

When we provide the form engine with the download url to our custom endpoint (/api/attachments/<id>) during download it calls the correct url but the request is missing the authorization header with the user information. This of course makes it impossible for us in the backend to verify if the user is actually authorized to access the attachment.
In the documentation about the AttachmentLoader it is not mentioned that this request will be done without authorization or if there is some way to enable the header (which is kind of essential for a secure implementation).

Is this a bug in A12? Or can this authorization header be enabled by some config in the client?

This sounds like AuthorizationHeaderFilter is somehow not used for that endpoint. Can you please check that the attachment loader you use does integrate the instance of your uaa protected server connector.

As we are just providing the download link and the FormEngine is using it, I think it is in the responsibility of the FormEngine to attach the authorization header in the request (as we don’t have access to the request).
But the way it is implemented in @com.mgmtp.a12.formengine/formengine-core/src/client-extensions/internal/extensions/form-engine/internal/attachments/sagas/downloadSaga.ts is missing the usage of the uaa server connector:

			const url = yield* call(
				[options.attachmentLoader, "retrieveDownloadLink"],
				actualPayload.attachment,
				documentDescriptor
			);

			downloadAnchor.href = url;
			downloadAnchor.download = actualPayload.attachment.original_filename ?? "";

			downloadAnchor.click();

Our easiest workaround is probably to just call our endpoint ourselves and then providing the FormEngine with an objectUrl like @com.mgmtp.a12.formengine/formengine-core/src/client-extensions/internal/extensions/form-engine/internal/attachments/attachmentLoader/embeddedAttachmentLoader.ts is doing.
But it seems like a major security oversight by the FormEngine to only allow/expect public links from being provided by a custom AttachmentLoader.

Hi @tim-steep-bit,

any client code we provide assumes a backend provided by dataservices is used. According to their documentation, they provide an authenticated endpoint LOAD_ATTACHMENT_URL, that generates a short-lived public unprotected URL for download. See GetA12 Login. The reason for this design decision is out of my expertise, feel free to contact someone from dataservices or TPS about it. Our default attachment loader calls this endpoint in its “retrieveDownloadLink” function.

If you don’t want to mimic that behavior in your backend, you could either write a requirement to allow the download request to be customizable (or to be authenticated by default). Or you could replace the downloadSaga with your own implementation, that uses an authenticated request. The “overridePlatformSaga” property in the client createApplicationSetup can be used for that.

any client code we provide assumes a backend provided by dataservices is used

From reading the documentation I can’t come to the conclusion that using the ContentStore is mandatory for the FormEngine or that there are special limitations.
In the Setup of the AttachmentLoader its implied that using the official handlers is just one possible option next to some custom implementation:

// or use on of the two default implementations
const yourAttachmentLoaderImplementation: AttachmentLoader = /* your impl here */

And in the few words about Retrieving the Download Link no hint about such a limitation:

Note that the loader is responsible for retrieving the download link for a given attachment. The internal Form Engine saga will then trigger the actual download from it. […] This method should resolve to a string which points to a downloadable resource.

I know that ContentStore is using unprotected URLs and that its the preferred A12-way of handling attachments, but I don’t see how purposely limiting the usefulness of the AttachmentLoader by forcing the usage of public URLs (and not even mentioning this critical limitation in the documentation) is benefitial to anybody? If the backend does not need the header it can still be ignored there…

But for anybody else stumbling about this: I solved this for me by calling the endpoint myself using ConnectorLocator.getInstance().getServerConnector().fetchData(<request>), converting the file to a blob and returning URL.createObjectURL(blob) in my custom AttachmentLoader.

It is not mandatory, but we have to implement against some backend and the one provided by Dataservices is our reference implementation. We are happy to implement a option to load attachments via a protected URL (and skip the roundtrip with LOAD_ATTACHMENT_URL), but we would need a requirement ticket for that. Are you willing to write one? We can’t justify that as a bug, since security is ensured while using the designed way of downloading attachments. Thumbnails are in the same boat by the way and are maybe a bit harder to fix. We normally just render an image and the browser sends the request.