- Issue created by @znerol
- 🇨🇭Switzerland znerol
The Webhook Encryption Service > read endpoint is a bit different than other resources on this API:
- It does not require authentication.
- It does not take any
spaceId
parameter.
It follows that the
keyId
field in thex-signature
request header in the webook call is neither bound to an account nor to a space. At the moment, webhook calls from all of my test accounts are signed with the samekeyID=15a411ae-5d1b-465e-aaa0-65347e4c9f0e
.The public key can be retrieved using
WebhookEncryptionService::read($id)
- or any other HTTP client. Calling the endpoint withcurl
reveals that there are proper caching headers (cache-control: public, max-age=31536000
) on the response.% curl -ifs 'https://checkout.postfinance.ch/api/webhook-encryption/read?id=15a411ae-5d1b-465e-aaa0-65347e4c9f0e' HTTP/2 200 date: Fri, 06 Dec 2024 13:16:33 GMT content-type: application/json;charset=utf-8 vary: Accept-Encoding x-svid: 0e518d183291a1bcc set-cookie: _csrf_token_443=79hlv52gv6h8dnu7f4gq373r4s; Path=/; Expires=Sat, 7 Dec 2024 13:16:33 GMT; Max-Age=86400; Secure; HttpOnly set-cookie: language=en-US; Path=/; Secure; HttpOnly set-cookie: language=en-US; Path=/; Secure; HttpOnly expires: Sat, 06 Dec 2025 13:16:33 GMT reporting-endpoints: csp-endpoint="/csp-reports" report-to: { "group": "csp-endpoint", "max_age": 10886400, "endpoints": [ { "url": "/csp-reports" } ] } content-security-policy: default-src 'self'; child-src 'self'; connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self'; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src 'self' 'nonce-W/InUZhxHt2YcndKLu8Utw=='; style-src 'self' 'nonce-W/InUZhxHt2YcndKLu8Utw=='; worker-src 'self'; report-to csp-endpoint; report-uri /csp-reports; x-xss-protection: 1 cache-control: public, max-age=31536000 cf-cache-status: DYNAMIC strict-transport-security: max-age=0; includeSubDomains; preload x-content-type-options: nosniff server: cloudflare cf-ray: 8edc8d165e9627bd-LYS {"id":"15a411ae-5d1b-465e-aaa0-65347e4c9f0e","publicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcP72Buvf1myOG9qqpcx4wkdKXdYC+FTCqstYH+NzX0o4vg6X0PY80nXmcbHlmu7TBMEc55MCv4z/n4Xy4HqC3A=="}
- 🇨🇭Switzerland znerol
Regrettably the ApiClient constructor requires an user id and a secret.
Hence, we basically have three options to retrieve the public key when a webhook signature needs checking:
- Stick with
ApiClient
and correct credentials. In order to do that we'd need a way to configure some sort of global or default credentials which are not bound to a payment method. - Stick with
ApiClient
and supply fake credentials. Easy to implement, but doesn't feel like a proper solution. - Use guzzle without credentials. This additionally opens up the option to cache the public key and avoid repeated lookups.
- Stick with
- 🇨🇭Switzerland znerol
There WebhookEncryptionService::isContentValid which does all the parsing, but also the
read()
call. Since I'm not keen to reimplement that, option 3 is out.In order to get things rolling here, I'll implement for option 2 for the start.
- Merge request !15Draft: Issue #3490804: Implement signature checking as a drupal access check → (Open) created by znerol
- 🇨🇭Switzerland znerol
Implement this as an access check. This needs tests and the check needs to be configurable in some way in order to provide a smooth transition.
- 🇨🇭Switzerland znerol
With the test coverage I'm pretty confident that everything is working as expected now. Need to think about the docs, it is somewhat important that people first enable payload signing at the PSP side and only afterwards the webhook access check.