Skip to content

feat: exchange cloud ID#4417

Open
redblom wants to merge 2 commits into
nextcloud:mainfrom
sara-nl:invite-for-cloudid-exchange
Open

feat: exchange cloud ID#4417
redblom wants to merge 2 commits into
nextcloud:mainfrom
sara-nl:invite-for-cloudid-exchange

Conversation

@redblom
Copy link
Copy Markdown

@redblom redblom commented Apr 8, 2025

This is a work in progress
This PR implements #4416

About

The goal of this feature is to allow for easy remote contact information exchange (cloud ID, email, name) between 2 users from different instances/organizations based on an invitation request.
The feature is an implementation of the invitation workflow from the OCM specification (https://datatracker.ietf.org/doc/draft-ietf-ocm-open-cloud-mesh/)

A user (sender) creates the invitation. This invitation consists of an invite link and (encoded) invite code. The invitation is sent to another user (receiver) via email or through another channel, eg. chat.
After receiving the invitation, the receiver can accept the invitation by following the invite link or by pasting the (encoded) invite code into a manual accept form.
After accepting, the exchange of user information will take place (OCM /invite-accepted endpoint) resulting in the creation of both users in each other's contacts list.
Following the invite link in the email is the most convenient way to accept. It will redirect the receiver via a WAYF (Where Are You From) page immediately to an accept dialog.
Note: Manual acceptance of the invitation works with both the invite link and (encoded) invite code.

Screenshots

1_contacts-app-main-page-Mike Contacts page with new buttons 'Invite contact', 'Accept invite' and 'All invites'.

2_fill-in-invite-form Click 'Invite contact' and fill in form fields:
  • Invite label for reference in the Invites list (optional)
  • Recipient email (required)
  • Personal message that will be sent with the email (optional)
  • Check to send copy of the invite email to yourself (optional)
3_invite-sent-list 'All invites' button displays the invites sent.

3a_alt-way-to-share-invite It's also possible to share the invite by sending the invite link or code through an alternative channel (eg. chat).

4_invite-email-to-Pete This what the email to the receiver of the invite looks like.

5_copy-of-invite-email-to-Pete This is what the copy of the email to the sender of the invite looks like.

6_wayf-page Clicking on the invite link will direct the receiver to a WAYF (Where Are You From) page.
Here you choose your institute. You can also fill in your institute's provider address if you are not on the list. A discovery process will run to check whether your provider supports OCM.

7_login-after-wayf-page-select-Pete Login after selecting your institute.

8_accept-invite You will be redirected to an invite accept dialog.

9_invite-accepted-Pete On acceptance the contact information exchange will take place via the OCM `/invite-accepted` endpoint. This will lead to the creation of a new contact (the sender) on the receiver's end.

10_invite-accepted-Mike And on the sender end the receiver contact info will also be added.

11_alt-invite-without-email-Jimmie When the config setting `ocm_invites_optional_mail` is set to false, creating an invite will give you by default the option to share the invite through an alternative channel (eg. chat).

12_alt-invite-no-email-copy-code Copy the invite link or code and send it via your channel of choice.

12a_alt-invite-with-email You may still decide to send the invite via email by clicking the 'Send via email' button.

13_alt-manual-accept-invite-Dan The receiver of the invite can click 'Accept invite' and manually paste the invite link, code, or encoded invite code into the accept form.
And this will lead to the exchange of contact information via the OCM /invite-accepted endpoint.

14_alt-manual-invite-accepted-Dan After contact info exchange the sender will be added as a new contact on the receiver's end.

15_alt-manual-invite-accepted-Jimmie And the receiver will be added as a new contact on the sender's end.

OCM invites admin settings

ocm-invites-admin-settings

@hamza221 hamza221 added enhancement New feature or request 2. developing Work in progress labels Apr 8, 2025
@hamza221 hamza221 marked this pull request as draft April 8, 2025 09:14
@redblom
Copy link
Copy Markdown
Author

redblom commented Apr 8, 2025

General layout plan:

  • dedicated button to send an invite to a remote user - below 'New contact' button
  • dedicated button on cloud ID component for existing remote contacts that have no cloud ID set
  • new sent invites will be displayed in the left panel - with a revoke option

Status:

  • invite accept dialog route implemented

@hamza221 hamza221 added this to the v7.1.0 milestone Apr 10, 2025
@redblom redblom force-pushed the invite-for-cloudid-exchange branch from ea81812 to 7c0eaa0 Compare April 10, 2025 20:45
@github-actions
Copy link
Copy Markdown

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

@ChristophWurst
Copy link
Copy Markdown
Member

Thanks for the contribution, @redblom!

The features relies on an implementation of the Invitation Workflow (see https://github.com/sara-nl/nc-collaboration).

Is there any way this could work without introducing an app dependency?

@hamza221 hamza221 modified the milestones: v7.1.0, v7.2.0 Apr 28, 2025
@redblom
Copy link
Copy Markdown
Author

redblom commented Apr 29, 2025

Thanks for the contribution, @redblom!

The features relies on an implementation of the Invitation Workflow (see https://github.com/sara-nl/nc-collaboration).

Is there any way this could work without introducing an app dependency?

Short answer: No, that is to say, the actual cloud ID exchange is not a responsibility of the contacts app but the contacts app (the cloud ID field) is a logical place where to have this option (as per earlier discussion with the Design Team).
The feature actually depends on the implementation of OCM /invite-accepted. This is currently in the process of being implemented for nextcloud server core (nextcloud/server#51113) so cloud ID exchange can become an integral part of nextcloud. Up until then there should be an app present implementing this.
The idea is to make the option not visible to the user when the platform can not do the actual cloud ID exchange (by configuration).
(so the best answer is: yes, when cloud ID exchange has become an integral part of nextcloud :)

@redblom redblom force-pushed the invite-for-cloudid-exchange branch 2 times, most recently from 0112a78 to e7c2604 Compare April 30, 2025 14:32
@ChristophWurst
Copy link
Copy Markdown
Member

Thanks for the clarification

@ChristophWurst ChristophWurst requested review from ArtificialOwl and removed request for GVodyanov, SebastianKrupinski and hamza221 May 15, 2025 12:09
@redblom redblom force-pushed the invite-for-cloudid-exchange branch 5 times, most recently from 76d6df3 to ca42433 Compare June 18, 2025 14:33
@redblom redblom force-pushed the invite-for-cloudid-exchange branch from ca42433 to 62ce0c2 Compare June 26, 2025 08:21
@redblom redblom marked this pull request as ready for review February 3, 2026 14:41
@redblom redblom force-pushed the invite-for-cloudid-exchange branch from aed6433 to 299aea5 Compare February 18, 2026 11:24
@redblom
Copy link
Copy Markdown
Author

redblom commented Feb 18, 2026

  • Ping me and I'll enable the workflows

@miaulalala - ping !

@redblom redblom force-pushed the invite-for-cloudid-exchange branch from 299aea5 to 3984fd6 Compare April 7, 2026 14:25
@redblom redblom closed this Apr 8, 2026
@redblom redblom force-pushed the invite-for-cloudid-exchange branch from 3984fd6 to a4ce368 Compare April 8, 2026 14:56
@redblom redblom reopened this Apr 8, 2026
@redblom
Copy link
Copy Markdown
Author

redblom commented Apr 9, 2026

@miaulalala - What's wrong with the sign-off, maybe co-author is not allowed ?

@miaulalala
Copy link
Copy Markdown
Contributor

@miaulalala - What's wrong with the sign-off, maybe co-author is not allowed ?

No that's not the issue, one setting has Anton P., the other Anton P (no dot) 😅

@redblom redblom force-pushed the invite-for-cloudid-exchange branch from 4e4bdca to e78bd65 Compare April 9, 2026 11:08
@redblom
Copy link
Copy Markdown
Author

redblom commented Apr 9, 2026

@miaulalala - What's wrong with the sign-off, maybe co-author is not allowed ?

No that's not the issue, one setting has Anton P., the other Anton P (no dot) 😅

Pfff 😳

Comment thread appinfo/routes.php Outdated
Comment thread lib/Controller/FederatedInvitesController.php
Comment thread lib/Controller/FederatedInvitesController.php
Comment thread lib/Command/DisableOcmInvites.php Outdated
Comment thread lib/Command/DisableOcmInvites.php Outdated
Comment thread lib/Listener/OcmDiscoveryListener.php Outdated
Comment thread lib/Service/FederatedInvitesService.php Outdated
Comment thread lib/Service/SocialApiService.php Outdated
Comment thread lib/WayfProvider.php Outdated
Comment thread lib/WayfProvider.php Outdated
@miaulalala
Copy link
Copy Markdown
Contributor

@redblom - can you check on artonge's review comments please?

@redblom
Copy link
Copy Markdown
Author

redblom commented May 4, 2026

@redblom - can you check on artonge's review comments please?

👍 working on it.

@redblom redblom force-pushed the invite-for-cloudid-exchange branch 2 times, most recently from 55dafdf to e05f396 Compare May 8, 2026 18:44
@redblom
Copy link
Copy Markdown
Author

redblom commented May 12, 2026

@redblom - can you check on artonge's review comments please?

@miaulalala We've now covered them all we think.

…ion workflow

see https://github.com/cs3org/OCM-API/blob/v1.2.1/IETF-RFC.md

Features:
 - Button to invite remote users to exchange cloudIDs.
 - Button to manually accept invite to exchange cloudIDs.
 - WAYF page allowing the receiver of the invite to open and accept the invitation.
 - Listing of open invitations.
 - Option to resend, revoke open invitations.
 - Administer invitation settings

Signed-off-by: Antoon P. <antoon.prins@surf.nl>
Co-authored-by: Micke Nordin <kano@sunet.se>
Co-authored-by: Mahdi Baghbani <mahdi-baghbani@azadehafzar.io>
@redblom
Copy link
Copy Markdown
Author

redblom commented May 15, 2026

Rebased and some cleanup.

@miaulalala
Copy link
Copy Markdown
Contributor

Hi @redblom! CI is currently failing on two fronts:

1. Missing #[Override] attributes (Psalm)
Multiple methods in Cron/UpdateOcmProviders.php, the two new Listener classes, and the Migration files are missing #[Override] attributes. You can fix most of these automatically by running composer psalm:update-baseline and committing the result.

2. NPM lint error in src/wayf.js
Line 13 uses a top-level arrow function — change it to a regular function declaration to satisfy the antfu/top-level-function rule, then run npm run lint:fix.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we really want a command for that? Why is the already existing command to set config values not enough?

If the reason is for documentation, then I think we should drop the custom command and simply extend the documentation.

Copy link
Copy Markdown
Author

@redblom redblom May 18, 2026

Choose a reason for hiding this comment

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

@artonge - Hmm maybe we misunderstood your suggestion here. A dedicated command (with documentation) seemed useful to us.

But is extending the docs the preferred way then?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But is extending the docs the preferred way then?

I would say yes, as in this case this is doing nothing else than setting the config value.


class UpdateOcmProviders extends TimedJob {
// Run every five minutes
private int $expire_time = 5 * 60;

This comment was marked as duplicate.

@artonge
Copy link
Copy Markdown
Contributor

artonge commented May 18, 2026

@redblom there are still some unresolved conversations from the last time I reviewed, can you address them?

Fix NPM lint errror

Signed-off-by: Antoon P. <antoon.prins@surf.nl>
@redblom
Copy link
Copy Markdown
Author

redblom commented May 18, 2026

@artonge

Is this because the operation is slow?

Yes, it can be slow-ish, so definitely for a responsive wayf page.

You can also manually register a QueuedJob for this kind of use case.

Will have a look at that.

@redblom
Copy link
Copy Markdown
Author

redblom commented May 21, 2026

@redblom there are still some unresolved conversations from the last time I reviewed, can you address them?

Hi @artonge - I have these 3 questions left:
#4417 (comment)
#4417 (comment)
#4417 (comment)

* On success the user is redirected to the new contact url.
*
* @param string $token the token of the invite
* @param string $provider the provider of the sender of the invite
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The argument is not part of the method signature

* @param string $recipientAddress the original recipient's email address
* @param string $message the optional message included in the invite
*/
private function sendCcEmail(string $token, string $senderProvider, string $senderAddress, string $recipientAddress, string $message): void {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't the logic really close to the sendMail method?
Can't we add a $cc argument to sendMail to benefit from the work that was already done there?

Comment thread lib/WayfProvider.php
try {
$res = $this->httpClient->newClient()->get($url);
$code = $res->getStatusCode();
if (!($code >= 200 && $code < 400)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!($code >= 200 && $code < 400)) {
if (!($code >= Http::STATUS_OK && $code < ...)) {

Comment thread lib/WayfProvider.php
if (!($code >= 200 && $code < 400)) {
continue;
}
$data = json_decode($res->getBody(), true);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Where is the format of the answer defined?

/**
* Returns all open (not yet accepted) invites.
*
* @return JSONResponse
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Phpdoc are not necessary if the native php types are used, you can remove them.

Comment on lines +152 to +158
public function inviteAcceptDialog(string $token = '', string $providerDomain = ''): TemplateResponse {
$this->initialState->provideInitialState('inviteToken', $token);
$this->initialState->provideInitialState('inviteProvider', $providerDomain);
$this->initialState->provideInitialState('acceptInviteDialogUrl', FederatedInvitesService::OCM_INVITE_ACCEPT_DIALOG_ROUTE);

return $this->index();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, and looking at the variable that you provide:

  • inviteToken seems to be accessible from the frontend already
  • same for inviteProvider

So maybe you only need to provide acceptInviteDialogUrl?

@nimishavijay
Copy link
Copy Markdown
Member

nimishavijay commented May 22, 2026

This seems very comprehensive, nice! :) From what I understand this is a way to exchange contacts with people from other organizations directly from the contacts app. Design wise I have a few questions and some feedback:

Thinking about the design and flow itself, some questions come to mind:

  • There seem to be a lot of ways of sending invitations: via email, via invite link, via invite code. Is there a necessity to have an invite code? Could we have just one way: invite link and have options like "copy link" and "share via email" for that one link? It would simplify a lot of decision making for the user. They would only have to click on a link to exchange contacts in every case. Does that make sense?
  • Since the goal is to NOT look at the invites page (there shouldn't be invitations that are sent and pending acceptance, and once you have exchanged contacts there's no reason to look at the invitation again) I am not sure if having an entire view in the main content is the way to go. What do you think about moving it into a modal?
  • Similar reasoning for the buttons too: the combined visual weight of the invitations is even more than of the CTA of the whole app! So what do you think about collapsing all invites into one item called "External invites" and that opens up a modal with the list of invitations and an option to create?
  • instead of having an option to send a copy of the invite to the sender's own address, why not have the invite message shown in the UI? It can be collapsed by default to save space of course

Visual design feedback to be more consistent with Nextcloud design:

  • We want to be accessible, so the headings for the modals/dialogs must be h2. I see that some of them are different like h5. If you want them to look smaller you can override the styles, but it should follow the heading order
  • The buttons in a dialog a usually positioned as: primary action button on the right end and the cancel button on the left end for left-to-right languages. Your buttons seem to be the other way around, which could lead to accidentally clicking on cancel. The primary action button should also always be the primary variant so that the color stands out in the dialog
  • Also try to use existing variables or multiples of existing variables for padding, margin and gaps as much as possible. Instead of 4px, use var(--default-grid-baseline)
  • In the "where are you from" page, consider using the NcFormbox component, it would automatically adjust colours, spacing, etc

But either way this is really cool. Interested to know what you think of the design feedback! :)

@redblom
Copy link
Copy Markdown
Author

redblom commented May 22, 2026

But either way this is really cool. Interested to know what you think of the design feedback! :)

@nimishavijay - Thank you for this valuable feedback! We're on it.

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

Labels

3. to review Waiting for reviews enhancement New feature or request feedback-requested

Projects

Status: 🏗️ At engineering

Development

Successfully merging this pull request may close these issues.

9 participants