binwiederhier / ntfy

Send push notifications to your phone or desktop using PUT/POST
https://ntfy.sh
Apache License 2.0
17.77k stars 691 forks source link

Propagate authorization credentials to matrix client #1034

Open aerusso opened 6 months ago

aerusso commented 6 months ago

Problem With a (self-hosted) ntfy setup with auth-default-access set to deny, it is "impossible" to use the matrix gateway.

:bulb: Idea It's not actually impossible, though. If you are also self-hosting a synapse matrix server, you can (in the matrix database)

update pushers set data '{"url":"https://$ntfy_server/_matrix/push/v1/notify?auth=$auth_token","format":"event_id_only"}' where id = $the_push_id

Be sure properly base64 encode $auth_token twice, as described in the docs! (Also, you must stop the matrix server, do the modification, and then start the matrix server up again). Do select * from pushers ; to get an idea of what is going on.

The "idea" here is to have the ntfy app provide this url, instead of the unauthenticated one, to element. This would be the easiest, I think, because it only requires a few bits of changes in the mobile apps.

Another option is to put the token in pushkey. This is a little harder, because authentication currently occurs in server.handle, which calls handleInternal, which in turn calls into the matrix-specific code. This is only abstractly very slightly better, because the clients will automatically replace the url with a generic gateway url if there is no functioning matrix gateway. Realistically, I don't see the benefit: this proposed change would only apply to ntfy versions that already support the matrix gateway.

To protect the passwords of users, the android app could automatically get a token, and pass that as the auth_token.

:computer: Target components I propose adding to only the mobile apps.

wunter8 commented 6 months ago

The docs recommend that people create an access rule that allows anonymous writes to "up*" topics, so that you can use the matrix gateway and other UnifiedPush services, while still having auth-default-access: deny-all

aerusso commented 6 months ago

Now that I'm thinking more, it would probably be best to create some token that is unique to the UP topic and subscribing user, and pass that as the authentication token (instead of one specific to only the user). Then, at topic write-time, the service can check to see if the user is still subscribed to the UP topic, and deny/allow based on that. This would naturally expire stale authorizations.

This would require changes to both the authorization pathway in the go server, and proper calculation of those credentials:

  1. A new service provided to authenticated users that returns an encoding of (a) their username and (b) the hash of (their username, a server-wide secret string, and the UP topic name).
  2. The mobile apps should, at UP subscribe time, query the server for this token, appropriately embed it in the UP subscriber URL, and then pass it to the client app.
  3. The authentication chain in the server should check for this kind of token in URLs, and (a) confirm the hashes match, and (b) confirm the user is actually subscribed to the named topic.

Then, anyone with that secret token will be able to post to that topic name as long as the user is subscribed to it. Only constant additional state is required to be tracked by any parties. Similarly, only constant time is required to validate this. Unsubscribing and resubscribing the service will generate a new UP topic name, making the old token worthless (unless the up topic name happens to be reused by the same username, which is unlikely). As long as the server is updated to provide (1) and (3) first (which can be done without breaking backwards-compatibility), clients can gradually roll out support for (2), gracefully improving service. We could simultaneously change the up prefix at this point, and keep the unconditional write-access to that for a while until everyone has transitioned.

Would patches that implement this be welcome? (Unfortunately, it would be a while before I could get to this. In particular I am probably going to try to debug #1035 first)