Skip to content

Conversation

@p1gp1g
Copy link

@p1gp1g p1gp1g commented Nov 28, 2025

This PR adds web push support to send push notifications. Web Push is defined by 3 RFCs: RFC8030 (the requests), RFC8291 (encryption) and RFC8292 (server authorization).

It can then be used by the web application to receive real time notifications, even when nextcloud isn't opened in a tab, and without the notify_push app (which adds a websocket support to nextcloud). This is particularly useful for collaborative work, as nexcloud isn't always opened but we can expect to be notified anyway. This support is in the second PR

This support also allow to get push notifications on Android without the proxy:

  • FCM servers can be notified with webpush
  • UnifiedPush can be used, for user who prefer or can't use the Play Services

Fix #1225

Required for:
nextcloud/android#8684
nextcloud/talk-android#257

Which will also fix:
nextcloud/android#12151 (won't be necessary)
nextcloud/android#11898 (duplicate)
nextcloud/android#5510 (supersede, openpush was a research project, and their website now link to unifiedpush)
nextcloud/android#8800 (another duplicated ?)
nextcloud/android#3333 (UP supports allow using FCM without the proprietary lib)
(There might be other issues)

This was referenced Nov 28, 2025
@wrenix
Copy link

wrenix commented Nov 28, 2025

You do not signoff your commits, could you please check the DCO CI job?

@p1gp1g
Copy link
Author

p1gp1g commented Nov 28, 2025

@provokateurin it may interest you for Neon

@nickvergessen
Copy link
Member

We'll check the rough idea later this week, but CI seems pretty red at the moment 🙈

@p1gp1g
Copy link
Author

p1gp1g commented Dec 2, 2025

We'll check the rough idea later this week, but CI seems pretty red at the moment 🙈

Indeed, I'm fixing it. I was waiting for the CI to run

PS: I forgot to mention, this feature is part of a grant with NLnet

@p1gp1g p1gp1g force-pushed the back/webpush branch 2 times, most recently from 9afa07d to 4da7b5f Compare December 2, 2025 09:03
@nickvergessen
Copy link
Member

It's conflicting in composer.lock so CI is not starting 🙈

@p1gp1g
Copy link
Author

p1gp1g commented Dec 2, 2025

Should be ok now

@p1gp1g
Copy link
Author

p1gp1g commented Dec 3, 2025

@nickvergessen Can you run the CI again ?

@p1gp1g
Copy link
Author

p1gp1g commented Dec 3, 2025

I will need some help to fix the CI.

  • The static-psalm-analysis raises many UndefinedClass: I don't know how you manage to fix this on other existent classes?
  • The integration tests fail, I think it is because test:integration runs tests/Integration/run.sh and it misses mozart compose, used to get minishlink/web-push. I can't run the CI on my own repo, I think that because the workflows use your own runners. Do you mind if I push a commit that I think should work ? We probably can expect one or 2 tries before
    it works

@p1gp1g
Copy link
Author

p1gp1g commented Dec 4, 2025

The changes for the android-lib are ready, once this PR is done: nextcloud/android-library@master...p1gp1g:nextcloud-library:feat/webpush

@p1gp1g
Copy link
Author

p1gp1g commented Dec 5, 2025

@nickvergessen : Are you OK to run the CI if I push a potential fix for it? (cf. #2662 (comment))

@nickvergessen
Copy link
Member

You were missing some files to set up Mozart correctly, I added them. Let's see what that does to CI.

"time": "2025-12-02T00:53:42+00:00"
},
{
"name": "psr/clock",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is wrong here.
It might be that your dependency brings in psr/clock or in a different version or something

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it has moved to the packages list, same version: it may be possible another lib use it. Is it a problem since the lib is still listed ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that it should be here, but aparently is not anymore as per Psalm:

Error: lib/BackgroundJob/GenerateUserSettings.php:21:3: MissingDependency: OCP\AppFramework\Utility\ITimeFactory depends on class or interface psr\clock\clockinterface that does not exist (see https://psalm.dev/157)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok if I do composer require psr/clock --dev and update the lock, so it will be explicitly included in dev requirement ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how that fully works, but psr/clock is part of server's 3rdparty directory already and we should not duplicate it or create a conflict or something.
Maybe you can try if provide solves that?
https://github.com/ChristophWurst/nextcloud_sentry/blob/9e8385fb2c0c170edbedc3d81f3d304f50197cd6/composer.json#L27-L29

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, we have to add the "provide" to nextcloud/ocp, I can push a test using a temporary repo to override nextcloud/ocp

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested different combinations, with/without provide, in notification app, in ocp, with require/require-dev and nothing works: p1gp1g#1 any idea ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've found the issue: mozart removes the vendor directory after namespacing, and psc/clock is a transitive dependence of the webpush lib

@p1gp1g
Copy link
Author

p1gp1g commented Dec 5, 2025

Thank you. I think we need to ignore the new vendor directories too?

@nickvergessen
Copy link
Member

I think we need to ignore the new vendor directories too?

The vendor-bin/ part is correctly ignored already:

/vendor-bin/*/vendor

But since this app is shipped with the server it has to be buildable repeatingly, so we need to commit lib/Vendor/ at the end of the pull request work or in a follow up. (Please don't commit it yet, as it makes the PR even more unreadable and not loadable in the GitHub UI)

@p1gp1g
Copy link
Author

p1gp1g commented Dec 5, 2025

OK, I see, maybe excluding them from the lint checks then ?

p1gp1g and others added 7 commits January 12, 2026 09:04
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Co-authored-by: Joas Schilling <213943+nickvergessen@users.noreply.github.com>
Signed-off-by: S1m <31284753+p1gp1g@users.noreply.github.com>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
Signed-off-by: sim <git@sgougeon.fr>
@p1gp1g
Copy link
Author

p1gp1g commented Jan 17, 2026

Do you mind running the CI ? I had to fix them following changes after the first review

PS: I've finished implementing UnifiedPush support on talk :)

Signed-off-by: sim <git@sgougeon.fr>
@p1gp1g
Copy link
Author

p1gp1g commented Jan 19, 2026

Thanks, I forgot to rename the appTypes in psalm-baseline, it is fixed. The other CI error was because of the pending vendor directory

I will address the pending comments once you answer them. It looks like the Github UI sort of hide some of them, it isn't ideal.

@p1gp1g
Copy link
Author

p1gp1g commented Jan 26, 2026

@nickvergessen Any update on the review ?

@nickvergessen
Copy link
Member

We will have a call about this tomorrow, then I will afterwards do the next review.

@p1gp1g
Copy link
Author

p1gp1g commented Jan 26, 2026

OK, great! If you want or need, I am also available for a call.

Just to sell the feature: web push is what is implemented on every platforms when you receive notifications without an opened tab (eg. Facebook, Reddit, Mastodon, YouTube, etc. *). I think this can be sold as a major new feature for Nextcloud: it's often necessary to be notified when working in team. cf. MDN Push API doc

* If you're using Firefox, and allows websites to send notifications, check about:serviceworkers: they are the websites with a non-null push endpoint

$device['endpoint'],
$device['p256dh'],
$device['auth'],
json_encode($data, JSON_THROW_ON_ERROR),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see the information is not encrypted against the "service provider" (Apple, Mozilla, Google, …)?
At least I also don't see decryption code in the frontend PR.

If that is the case we consider this not acceptable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is encrypted (E2EE) following RFC8291, and the decryption is done automatically by the browser, we don't have to implement it, because it's a standard

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to see how encryption works, you can look at this page here: https://mozilla-services.github.io/WebPushDataTestPage/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the authorization is done by VAPID (RFC8292), it's based on asymmetric key signature

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, that was exactly what I was looking for and missing 🙈

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, I understand that point needs to be clear 👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, did you get your call about the feature ?

/**
* Remove a subscription from push notifications
*
* @return DataResponse<Http::STATUS_OK|Http::STATUS_ACCEPTED|Http::STATUS_UNAUTHORIZED, list<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return DataResponse<Http::STATUS_OK|Http::STATUS_ACCEPTED|Http::STATUS_UNAUTHORIZED, list<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}>
* @return DataResponse<Http::STATUS_OK|Http::STATUS_ACCEPTED|Http::STATUS_UNAUTHORIZED, list<empty>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{message: 'INVALID_SESSION_TOKEN'}, array{}>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty high, I will check if master needs a bump before, but if this is what the change is, we need some more caching in place

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it simply because I've added some tests ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it simply because I've added some tests

You didn't add any integration test, only those count.
As per conclusding comment I will check it later (after merge), but I assume most of it is because we not need to check 2 tables for devices for each user everytime a push happens.
We should cache that information if you have a push device for each type and only query then.

Comment on lines +1036 to +1037
'p256dh' => 'BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcx aOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4',
'auth' => 'BTBZMqHH6r4Tts7J_aSIgg',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHould be a const with a comment about this being testing values, like in the other test

"sort-packages": true
},
"require": {
"coenjacobs/mozart": "^0.7.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is 1.0.2 by now :)

@nickvergessen
Copy link
Member

So let's do those last refinements and then we merge it for master.
I will then follow up with some performance analysis if there are things we can cut out or resort and improve otherwise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebPush support

5 participants