-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathIETF-RFC.xml
1057 lines (921 loc) · 72.7 KB
/
IETF-RFC.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.29 (Ruby 3.2.3) -->
<!DOCTYPE rfc [
<!ENTITY nbsp " ">
<!ENTITY zwsp "​">
<!ENTITY nbhy "‑">
<!ENTITY wj "⁠">
]>
<rfc ipr="trust200902" docName="draft-lopresti-open-cloud-mesh-01" category="std" consensus="true">
<front>
<title>Open Cloud Mesh</title>
<author initials="G." surname="Lo Presti" fullname="Giuseppe Lo Presti">
<organization>CERN</organization>
<address>
<email>[email protected]</email>
<uri>http://cern.ch/lopresti</uri>
</address>
</author>
<author initials="M. B." surname="de Jong" fullname="Michiel de Jong">
<organization>Ponder Source</organization>
<address>
<email>[email protected]</email>
<uri>https://pondersource.com</uri>
</address>
</author>
<author initials="M." surname="Baghbani" fullname="Mahdi Baghbani">
<organization>Ponder Source</organization>
<address>
<email>[email protected]</email>
<uri>https://pondersource.com</uri>
</address>
</author>
<author initials="M." surname="Nordin" fullname="Micke Nordin">
<organization>SUNET</organization>
<address>
<email>[email protected]</email>
<uri>https://code.smolnet.org/micke</uri>
</address>
</author>
<date year="2025" month="April" day="22"/>
<area>Security</area>
<keyword>Internet-Draft</keyword>
<abstract>
<?line 37?>
<t>Open Cloud Mesh is a server federation protocol that is used to notify a Receiving Party that they have
been granted access to some Resource. It has similarities with authorization flows such as OAuth, as well as with social internet protocols such as ActivityPub and email.</t>
<t>Open Cloud Mesh only handles the necessary interactions up to the point where the Receiving Party is informed that they were granted access to the Resource. The actual resource access is then left to protocols such as WebDAV and others.</t>
</abstract>
</front>
<middle>
<?line 44?>
<section anchor="terms"><name>Terms</name>
<t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
RFC 2119.</t>
<t>We define the following concepts (with some non-normative references to related concepts from OAuth and elsewhere):</t>
<t><list style="symbols">
<t><strong>Resource</strong> - the piece of data or interaction to which access is being granted, e.g. a file, folder, video call, or printer queue</t>
<t><strong>Share</strong> - a policy rule stating that certain actors are allowed access to a Resource. Also: a record in a database representing this rule</t>
<t><strong>Sending Party</strong> - a person or party who is authorized to create Shares (similar to "Resource Owner" in OAuth)</t>
<t><strong>Receiving Party</strong> - a person, group or party who is granted access to the Resource through the Share (similar to "Requesting Party / RqP" in OAuth-UMA)</t>
<t><strong>Sending Server</strong> - the server that:
<list style="symbols">
<t>holds the Resource ("file server" or "Entreprise File Sync and Share (EFSS) server" role),</t>
<t>provides access to it (by exposing at least one "API"),</t>
<t>takes the decision to create the Share based on user interface gestures from the Sending Party (the "Authorization Server" role in OAuth)</t>
<t>takes the decision about authorizing attempts to access the Resource (the "Resource Server" role in OAuth)</t>
<t>sends out Share Creation Notifications when appropriate (see below)</t>
</list></t>
<t><strong>Receiving Server</strong> - the server that:
<list style="symbols">
<t>receives Share Creation Notifications (see below)</t>
<t>actively or passively notifies the receiving user or group of any incoming Share Creation Notification</t>
<t>acts as an API client, allowing the receiving user to access the Resource through an API (e.g. WebDAV) of the sending server</t>
</list></t>
<t><strong>Sending Gesture</strong> - a user interface interaction from the Sending Party to the Sending Server, conveying the intention to create a Share</t>
<t><strong>Share Creation</strong> - the addition of a Share to the database state of the Sending Server, in response to a successful Sending Gesture or for another reason</t>
<t><strong>Share Creation Notification</strong> - a server-to-server request from the sending server to the receiving server, notifying the receiving server that a Share has been created</t>
<t><strong>FQDN</strong> - Fully Qualified Domain Name, such as <spanx style="verb">"cloud.example.com"</spanx></t>
<t><strong>OCM Server</strong> - a server that supports OCM.</t>
<t><strong>Discovering Server</strong> - a server that tries to obtain information in OCM API discovery</t>
<t><strong>Discoverable Server</strong> - a server that tries to supply information in OCM API discovery</t>
<t><strong>OCM Address</strong> - a string of the form <spanx style="verb"><Receiving Party's identifier>@<fqdn></spanx> which can be used to uniquely identify a user or group "at" an OCM Server. <spanx style="verb"><Receiving Party's identifier></spanx> is an opaque string,
unique at the server. <spanx style="verb"><fqdn></spanx> is the Fully Qualified Domain Name by which the server is identified. This MUST be the domain at which the <spanx style="verb">/.well-known/ocm</spanx> endpoint of that server is hosted.</t>
<t><strong>OCM Notification</strong> - a message from the Receiving Server to the Sending Server or vice versa, using the OCM Notifications endpoint.</t>
<t><strong>Invite Message</strong> - out-of-band message used to establish contact between parties and servers in the Invite Flow, containing an Invite Token (see below) and the Invite Sender's OCM Address</t>
<t><strong>Invite Sender</strong> - the party sending an Invite</t>
<t><strong>Invite Receiver</strong> - the party receiving an Invite</t>
<t><strong>Invite Sender OCM Server</strong> - the server holding an address book used by the Invite Sender, to which details of the Invite Receiver are to be added</t>
<t><strong>Invite Receiver OCM Server</strong> - the server holding an address book used by the Invite Receiver, to which details of the Invite Sender are to be added</t>
<t><strong>Invite Token</strong> - a hard-to-guess string used in the Invite Flow, generated by the Invite Sender OCM Server and linked uniquely to the Invite Sender's OCM Address</t>
<t><strong>Invite Creation Gesture</strong> - gesture from the Invite Sender to the Invite Sender OCM Server, resulting in the creation of an Invite Token.</t>
<t><strong>Invite Acceptance Gesture</strong> - gesture from the Invite Receiver to the Invite Receiver OCM Server, supplying the Invite Token as well as the OCM Address of the Invite Sender, effectively allowlisting the Invite Sender OCM Server for sending Share Creation Notifications to the Invite Receiver OCM Server.</t>
<t><strong>Invite Acceptance Request</strong> - API call from the Invite Receiver OCM Server to the Invite Sender OCM Server, supplying the Invite Token as well as the OCM Address of the Invite Receiver, effectively allowlisting the Invite Sender OCM Server for sending Share Creation Notifications to the Invite Receiver OCM Server.</t>
<t><strong>Invite Acceptance Response</strong> - HTTP response to the Invite Acceptance Request</t>
<t><strong>Share Name</strong> - a human-readable string, provided by the Sending Party or the Sending Server, to help the Receiving Party understand which Resource the Share grants access to</t>
<t><strong>Share Permissions</strong> - protocol-specific allowances granted to the Receiving Party on the modes of accessing the Resource</t>
<t><strong>Share Requirements</strong> - protocol-specific restrictions on the modes of accessing the Resource</t>
</list></t>
</section>
<section anchor="general-flow"><name>General Flow</name>
<t>The lifecycle of an Open Cloud Mesh Share starts with prerequisites such as
establishing trust, establishing contact, and OCM API discovery.</t>
<t>Then the share creation involves the Sending Party making a Sending Gesture to the Sending Server,
the Sending Server carrying out the actual Share Creation,
and the Sending Server sending a Share Creation Notification to the Receiving Server.</t>
<t>After this, the Receiving Server MAY notify the Receiving Party and/or the Sending Server, and will act as an API client
through which the Receiving Party can access the Resource. After that, the Share may be updated, deleted, and/or reshared.</t>
</section>
<section anchor="establishing-contact"><name>Establishing Contact</name>
<t>Before the Sending Server can send a Share Creation Notification to the Receiving Server, it needs to establish the Receiving Party's OCM Address (containing the Receiving Server's FQDN, and the Receiving Party's identifier), among other things.
Some steps may preceed the Sending Gesture, allowing the Sending Party to establish (with some level of trust) the OCM Address of the Receiving Party. In other cases, establishing the OCM Address
of the Receiving Party happens as part of the Sending Gesture.</t>
<section anchor="direct-entry"><name>Direct Entry</name>
<t>The simplest way for this is if the Receiving Party shares their OCM Address with the Sending Party through some out-of-band means, and the Sending Party enters this string into the user interface of the Sending Server, by means of typing or pasting into an HTML form, or clicking a link to a URL that includes the string in some form.</t>
</section>
<section anchor="address-books"><name>Address books</name>
<t>The Sending Server MAY offer the Sending Party an address book tool, where OCM Addresses can be stored over time in a labeled and/or searchable way. This decouples the act by which the OCM Address string is passed into the Sending Server's database from the selection of the Receiving Party in preparation for Share Creation.</t>
</section>
<section anchor="public-link-flow"><name>Public Link Flow</name>
<t>An interface for anonymously viewing a Resource on the Sending Server MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture. This means that the Sending Party and the Receiving Party could be the same person, so contact between them does not need to be explicitly established.</t>
</section>
<section anchor="public-invite-flow"><name>Public Invite Flow</name>
<t>Similarly, an interface on the Sending Server MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture for a given Resource, without itself providing a way to access that particular Resource. A link to this interface could then for instance be shared on a mailing list, allowing all subscribers to effectively request access to the Resource by making a Sending Gesture to the Sending Server with their own OCM Address.</t>
</section>
<section anchor="invite-flow"><name>Invite Flow</name>
<t>### Rationale
Many methods for establishing contact allow unsolicited contact with the prospective Receiving Party whenever that party's OCM Address is known. The Invite Flow requires the Receiving Party to explicitly accept it before it can be used, which establishes bidirectional trust between the two parties involved.</t>
<t>OCM Servers MAY enforce a policy to only accept Shares between such trusted contacts, or MAY display a warning to the Receiving Party when a Share Creation Notification from an unknown Sending Party is received</t>
<section anchor="steps"><name>Steps</name>
<t><list style="symbols">
<t>the Invite Sender OCM Server generates a unique Invite Token and helps the Invite Sender to create the Invite Message</t>
<t>the Invite Sender uses some out-of-band communication to send the Invite Message, containing the Invite Token and the Invite Sender OCM Server FQDN, to the Invite Receiver</t>
<t>the Invite Receiver navigates to the Invite Receiver OCM Server (possibly using a Where-Are-You-From page provided as part of the Invite Message) and makes the Invite Acceptance Gesture</t>
<t>the Invite Receiver OCM Server discovers the OCM API of the Invite Sender OCM Server using generic OCM API Discovery (see section below)</t>
<t>the Invite Receiver OCM Server sends the Invite Acceptance Request to the Invite Sender OCM Server</t>
</list></t>
</section>
<section anchor="invite-acceptance-request-details"><name>Invite Acceptance Request Details</name>
<t>Whereas the precise syntax of the Invite Message and the Invite Acceptance Gesture will differ between implementations, the Invite Acceptance Request SHOULD be a HTTP POST request:</t>
<t><list style="symbols">
<t>to the <spanx style="verb">/invited-accepted</spanx> path in the Invite Sender OCM Server's OCM API</t>
<t>using <spanx style="verb">application/json</spanx> as the <spanx style="verb">Content-Type</spanx> HTTP request header</t>
<t>its request body containing a JSON document representing an object with the following string fields:
<list style="symbols">
<t>REQUIRED: <spanx style="verb">recipientProvider</spanx> - FQDN of the Invite Receiver OCM Server</t>
<t>REQUIRED: <spanx style="verb">token</spanx> - the Invite Token. The Invite Sender OCM Server SHOULD recall which Invite Sender OCM Address this token was linked to</t>
<t>REQUIRED: <spanx style="verb">userID</spanx> - the Invite Receiver's identifier at their OCM Server</t>
<t>REQUIRED: <spanx style="verb">email</spanx> - non-normative / informational; an email address for the Invite Receiver. Not necessarily at the same FQDN as their OCM Server</t>
<t>REQUIRED: <spanx style="verb">name</spanx> - human-readable name of the Invite Receiver, as a suggestion for display in the Invite Sender's address book</t>
</list></t>
<t>using TLS</t>
<t>using <eref target="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12">httpsig</eref></t>
</list></t>
<t>The Invite Receiver OCM Server SHOULD apply its own policies for trusting the Invite Sender OCM Server before making the Invite Acceptance Request.</t>
<t>Since the Invite Flow does not require either Party to type or remember the <spanx style="verb">userID</spanx>, this string does not need to be human-memorable. Even if the Invite Receiver has a memorable username at the Invite Receiver OCM Server, this <spanx style="verb">userID</spanx> that forms part of their OCM Address does not need to match it.</t>
<t>Also, a different <spanx style="verb">userID</spanx> could be given out to each contact, to avoid correlation of identities.</t>
</section>
<section anchor="invite-acceptance-response-details"><name>Invite Acceptance Response Details</name>
<t>The Invite Acceptance Response SHOULD be a HTTP response:</t>
<t><list style="symbols">
<t>in response to the Invite Acceptance Request</t>
<t>using <spanx style="verb">application/json</spanx> as the <spanx style="verb">Content-Type</spanx> HTTP response header</t>
<t>its response body containing a JSON document representing an object with the following string fields:
<list style="symbols">
<t>REQUIRED: <spanx style="verb">userID</spanx> - the Invite Sender's identifier at their OCM Server</t>
<t>REQUIRED: <spanx style="verb">email</spanx> - non-normative / informational; an email address for the Invite Sender. Not necessarily at the same FQDN as their OCM Server</t>
<t>REQUIRED: <spanx style="verb">name</spanx> - human-readable name of the Invite Sender, as a suggestion for display in the Invite Receiver's address book</t>
</list></t>
</list></t>
<t>A 200 response status means the Invite Acceptance Request was successful.
A 400 response status means the Invite Token is invalid or does not exist.
A 403 response status means the Invite Receiver OCM Server is not trusted to accept this Invite.
A 409 response status means the Invite was already accepted.</t>
<t>The Invite Sender OCM Server SHOULD verify the HTTP Signature on the Invite Acceptance Request and apply its own policies for trusting the Invite Receiver OCM Server before processing the Invite Acceptance Request and sending the Invite Acceptance Response.</t>
<t>As with the <spanx style="verb">userID</spanx> in the Invite Acceptance Request, the one in the Response also doesn't need to be human-memorable, doesn't need to match the Invite Sender's username at their OCM Server.</t>
</section>
<section anchor="addition-into-address-books"><name>Addition into address books</name>
<t>Following these step, both servers MAY display the <spanx style="verb">name</spanx> of the other party as a trusted or allowlisted contact, and enable selecting them as a Receiving Party. OCM Servers MAY enforce a policy to only accept Share Creation Notifications from such trusted contacts, or MAY display a warning to users when a Share Creation Notification from an unknown party is received.</t>
<t>Both servers MAY also allowlist each other as a server with which at least one of their users wishes to interact.</t>
<t>Note that Invites act symmetrically, so once contact has been established, both the Invite Sender and the Invite Receiver may take on either the Sending Party or the Receiving Party role in subsequent Share Creation events.</t>
<t>Both parties may delete the other party from their address book at any time without notifying them.</t>
</section>
<section anchor="security-advantages"><name>Security Advantages</name>
<t>It is important to underscore the value of the Invite in this scenario, as it provides four important security advantages. First of all, if the Receiving Server blocks Share Creation Notifications from Sending Parties who are not in the address book of the Receiving Party, then this protects the Receiving Party from receiving unsolicited Shares. An attacker could still send the Receiving Party an unsolicited Share, but they would first need to convince the Receiving Party through an out-of-band communication channel to accept their invite. In many use cases, the Receiving Party has had other forms of contact with the Sending Party (e.g. in-person or email back-and-forth). The out-of-band Invite Message thus leverages the filters and context which the Receiving Party may already benefit from in that out-of-band communication. For instance, a careful Receiving Party may choose to only accept Invites that reach them via a private or moderated messaging platform.</t>
<t>Second, when the Receiving Party accepts the Invite, the Receiving Server knows that the Sending Server they are about to interact with is trusted by the Sending Party, which in turn is trusted by the Receiving Party, which in turn is trusted by them. In other words, one of their users is requesting the allowlisting of a server they wish to interact with, in order to interact with a party they know out-of-band. This gives the Receiving Server reason to put more trust in the Sending Server than it would put into an arbitrary internet-hosted server.</t>
<t>Third, equivalently, the Sending Server knows it is essentially registering the Receiving Server as an API client at the request of the Receiving Party, to whom the right to request this has been traceably delegated by the Sending Party, which is one of its registered users.</t>
<t>Fourth, related to the second one, it removes the partial 'open relay' problem that exists when the Sending Server is allowed to include any Receiving Server FQDN in the Sending Gesture. Without the use of Invites, a Distributed Denial of Service attack could be organised if many internet users collude to flood a given OCM Server with Share Creation Notifications which will be hard to distinguish from legitimate requests without human interaction. An unsolicited (invalid) Invite Acceptance Request is much easier to filter out than an unsolicited (possibly valid, possibly invalid) Share Creation Notification Request, since the Invite Acceptance Request needs to contain an Invite Token that was previously uniquely generated at the Invite Sender OCM server.</t>
</section>
</section>
</section>
<section anchor="ocm-api-discovery"><name>OCM API Discovery</name>
<t>## Introduction
After establishing contact as discussed in the previous section, the Sharing User can send the Share Creation Gesture to the Sending Server, providing the Sending Server with the following information:</t>
<t><list style="symbols">
<t>Resource to be shared</t>
<t>Protocol to be offered for access</t>
<t>Sending Party's identifier</t>
<t>Receiving Party's identifier</t>
<t>Receiving Server FQDN</t>
<t>OPTIONAL: Share Requirements</t>
<t>OPTIONAL: Share Name</t>
<t>OPTIONAL: Share Permissions</t>
</list></t>
<t>The next step is for the Sending Server to additionally discover:</t>
<t><list style="symbols">
<t>if the Receiving Server is trusted</t>
<t>if the Receiving Server supports OCM</t>
<t>if so, which version and with which optional functionality</t>
<t>at which URL</t>
<t>the public key the Receiving Server will use for HTTP Signatures (if any)</t>
</list></t>
<t>The Sending Server MAY first perform denylist and allowlist checks on the FQDN.</t>
<t>If a finite allowlist of Receiving Servers exists on the Sending Server side, then this list may already contain all necessary information.</t>
<t>If the FQDN passes the denylist and/or allowlist checks, but no details about its OCM API are known, the Sending Server can use the following process to try to fetch this information from the Receiving Server.</t>
<t>This process MAY be influenced by a VPN connection and/or IP allowlisting.</t>
<t>When OCM API discovery can occur in preparation of a Share Creation Notification, the Sending Server takes on the 'Discovering Server' role and the Receiving Server plays the role of 'Discoverable Server'.</t>
<section anchor="process"><name>Process</name>
<t>At the start of the process, the Discovering Server has either an OCM Address, or just an FQDN from for instance the <spanx style="verb">recipientProvider</spanx> field of an Invite Acceptance Request.</t>
<t>Step 1: In case it has an OCM Address, it should first extract <spanx style="verb"><fqdn></spanx> from it (the part after the last <spanx style="verb">@</spanx> sign).
Step 2: The Discovering Server SHOULD attempt OCM API discovery a HTTP GET request to <spanx style="verb">https://<fqdn>/.well-known/ocm</spanx>.
Step 3: If that results in a valid HTTP response with a valid JSON response body within reasonable time, go to step 7.
Step 4: If not, try a HTTP GET with <spanx style="verb">https://<fqdn>/ocm-provider</spanx> as the URL instead.
Step 5: If that results in a valid HTTP response with a valid JSON response body within reasonable time, go to step 7.
Step 6: If not, fail.
Step 7: The JSON response body is the data that was discovered.</t>
</section>
<section anchor="fields"><name>Fields</name>
<t>The JSON response body offered by the Discoverable Server SHOULD contain the following information about its OCM API:</t>
<t><list style="symbols">
<t>REQUIRED: enabled (boolean) - Whether the OCM service is enabled at this endpoint</t>
<t>REQUIRED: apiVersion (string) - The OCM API version this endpoint supports. Example: <spanx style="verb">"1.2.0"</spanx></t>
<t>REQUIRED: endPoint (string) - The URI of the OCM API available at this endpoint. Example: <spanx style="verb">"https://my-cloud-storage.org/ocm"</spanx></t>
<t>OPTIONAL: provider (string) - A friendly branding name of this endpoint. Example: <spanx style="verb">"MyCloudStorage"</spanx></t>
<t>REQUIRED: resourceTypes (array) - A list of all resource types this server supports in both the Sending Server role and the Receiving Server role, with their access protocols. Each item in this list should
itself be an object containing the following fields:
<list style="symbols">
<t>name (string) - A supported resource type (file, folder, calendar, contact, ...).
Implementations MUST support <spanx style="verb">file</spanx> at a minimum. Each resource type is identified by its <spanx style="verb">name</spanx>: the list MUST NOT
contain more than one resource type object per given <spanx style="verb">name</spanx>.</t>
<t>shareTypes (array of string) -
The supported recipient share types.
MUST contain <spanx style="verb">"user"</spanx> at a minimum, plus optionally <spanx style="verb">"group"</spanx> and <spanx style="verb">"federation"</spanx>.
Example: <spanx style="verb">["user"]</spanx></t>
<t>protocols (object) - The supported protocols for accessing shared resources of this type.
Implementations MUST support at least <spanx style="verb">webdav</spanx> for <spanx style="verb">file</spanx> resources,
any other combination of resources and protocols is optional. Example:
<spanx style="verb">json
{
"webdav": "/remote/dav/ocm/",
"webapp": "/app/ocm/",
"talk": "/apps/spreed/api/"
}
</spanx>
Fields:
<list style="symbols">
<t>webdav (string) - The top-level WebDAV path at this endpoint. In order to access
a remote shared resource, implementations MAY use this path
as a prefix, or as the full path (see sharing examples).</t>
<t>webapp (string) - The top-level path for web apps at this endpoint. This value
is provided for documentation purposes, and it SHALL NOT
be intended as a prefix for share requests.</t>
<t>datatx (string) - The top-level path to be used for data transfers. This
value is provided for documentation purposes, and it SHALL
NOT be intended as a prefix. In addition, implementations
are expected to execute the transfer using WebDAV as
the wire protocol.</t>
<t>Any additional protocol supported for this resource type MAY
be advertised here, where the value MAY correspond to a top-level
URI to be used for that protocol.</t>
</list></t>
</list></t>
<t>OPTIONAL: capabilities (array of string) - The optional capabilities supported by this OCM Server.
As implementations MUST accept Share Creation Notifications to be compliant,
it is not necessary to expose that as a capability.
Example: <spanx style="verb">["receive-code", "webdav-uri"]</spanx>. The array MAY include for instance:
<list style="symbols">
<t><spanx style="verb">"enforce-mfa"</spanx> - to indicate that this OCM server can apply a Sending Server's MFA requirements for a Share on their behalf.</t>
<t><spanx style="verb">"webdav-uri"</spanx> - to indicate that this OCM server can append a relative URI to the path listed for WebDAV in the appropriate <spanx style="verb">resourceTypes</spanx> entry</t>
<t><spanx style="verb">"protocol-object"</spanx> - to indicate that this OCM server can receive a Share Creation Notification whose <spanx style="verb">protocol</spanx> object contains one property per supported protocol instead of containing the standard <spanx style="verb">name</spanx> and <spanx style="verb">options</spanx> properties.</t>
<t><spanx style="verb">"invites"</spanx> - to indicate the server would support acting as an Invite Sender or Invite Receiver OCM Server. This might be useful for suggesting to a user that existing contacts might be upgraded to the more secure (and possibly required) invite flow.</t>
<t><spanx style="verb">"receive-code"</spanx> - to indicate that this OCM server can receive a <spanx style="verb">code</spanx> as part of a Share Creation Notification, and exchange it for a bearer token at the Sending Server's <spanx style="verb">/token</spanx> API endpoint.</t>
</list></t>
<t>OPTIONAL: criteria (array of string) - The criteria for accepting a Share Creation Notification.
As all Receiving Servers should require the use of TLS in API calls,
it is not necessary to expose that as a criterium.
Example: <spanx style="verb">["http-request-signatures", "code"]</spanx>. The array MAY include for instance:
<list style="symbols">
<t><spanx style="verb">"http-request-signatures"</spanx> - to indicate that API requests without http signatures will be rejected.</t>
<t><spanx style="verb">"code"</spanx> - to indicate that API requests without code will be rejected (i.e. the <spanx style="verb">sharedSecret</spanx> in the protocol details will be ignored).</t>
<t><spanx style="verb">"denylist"</spanx> - some servers may be blocked based on their IP address</t>
<t><spanx style="verb">"allowlist"</spanx> - unknown servers may be blocked based on their IP address</t>
<t><spanx style="verb">"invite"</spanx> - an invite must have been exchanged between the sender and the receiver before a Share Creation Notification can be sent</t>
</list></t>
<t>OPTIONAL: publicKey (object) - The signatory used to sign outgoing request to confirm its origin. The
signatory is optional, but if present, it MUST contain two string fields, <spanx style="verb">id</spanx> and <spanx style="verb">publicKeyPem</spanx>.
properties:
<list style="symbols">
<t>REQUIRED id (string) unique id of the key in URI format. The hostname set the origin of the
request and MUST be identical to the current discovery endpoint.
Example: https://my-cloud-storage.org/ocm#signature</t>
<t>REQUIRED publicKeyPem (string) - PEM-encoded version of the public key.
Example: "-----BEGIN PUBLIC KEY-----\nMII...QDD\n-----END PUBLIC KEY-----\n"</t>
</list></t>
<t>OPTIONAL: inviteAcceptDialog (string) - URL path of a web page where a user can accept an invite, when query parameters <spanx style="verb">"token"</spanx> and <spanx style="verb">"providerDomain"</spanx> are provided. Implementations that offer the <spanx style="verb">"invites"</spanx> capability SHOULD provide this URL as well in order to enhance the UX of the Invite Flow. If for example <spanx style="verb">"/index.php/apps/sciencemesh/accept"</spanx> is specified here then a Where-Are-You-From page could redirect the end-user to <spanx style="verb">/index.php/apps/sciencemesh/accept?token=zi5kooKu3ivohr9a&providerDomain=example.com</spanx>.</t>
</list></t>
</section>
</section>
<section anchor="share-creation-notification"><name>Share Creation Notification</name>
<t>To create a share, the sending server SHOULD make a HTTP POST request</t>
<t><list style="symbols">
<t>to the <spanx style="verb">/shares</spanx> path in the Receiving Server's OCM API</t>
<t>using <spanx style="verb">application/json</spanx> as the <spanx style="verb">Content-Type</spanx> HTTP request header</t>
<t>its request body containing a JSON document representing an object with the fields as described below</t>
<t>using TLS</t>
<t>using <eref target="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12">httpsig</eref></t>
</list></t>
<section anchor="fields-1"><name>Fields</name>
<t><list style="symbols">
<t>REQUIRED shareWith (string)
Consumer specific identifier of the user, group or federation the provider
wants to share the resource with. This is known in advance.
Please note that the consumer service endpoint is known in advance
as well, so this is no part of the request body.
Example: "[email protected]"</t>
<t>REQUIRED name (string)
Name of the resource (file or folder).
Example: "resource.txt"</t>
<t>OPTIONAL description (string)
Optional description of the resource (file or folder).
Example: "This is the Open API Specification file (in YAML format) of the Open
Cloud Mesh API."</t>
<t>REQUIRED providerId (string)
Identifier to identify the shared resource at the provider side. This is
unique per provider such that if the same resource is shared twice,
this providerId will not be repeated.
Example: 7c084226-d9a1-11e6-bf26-cec0c932ce01</t>
<t>REQUIRED owner (string) -
Provider specific identifier of the user who owns the resource.
Example: "[email protected]"</t>
<t>REQUIRED sender (string) -
Provider specific identifier of the user that wants to share the
resource. Please note that the requesting provider is being
identified on a higher level, so the former <spanx style="verb">remote</spanx> property
is not part of the request body.
Example: "[email protected]"</t>
<t>OPTIONAL ownerDisplayName (string)
Display name of the owner of the resource
Example: "Dimitri"</t>
<t>OPTIONAL senderDisplayName (string)
Display name of the user that wants to share the resource
Example: "John Doe"</t>
<t>REQUIRED shareType (string)
SHOULD have a value of "user", "group", or "federation", to indicated that the first part
of the <spanx style="verb">shareWith</spanx> OCM Address refers to a Receiving Party who is a single user of the Receiving Server,
a group of users at the Receiving Servers, or a group of users that is spread out over various servers,
including at least one user at the Receiving Server.</t>
<t>REQUIRED resourceType (string)
Resource type (file, folder, calendar, contact, ...)</t>
<t>OPTIONAL expiration (integer)
The expiration time for the OCM share, in seconds
of UTC time since Unix epoch. If omitted, it is assumed
that the share does not expire.</t>
<t>OPTIONAL code (string)
A nonce to be exchanged for a (potentially short-lived) bearer token at the Sending Server's /token endpoint.</t>
<t>REQUIRED protocol (object)
JSON object with specific options for each protocol.
The supported protocols are:
- <spanx style="verb">webdav</spanx>, to access the data
- <spanx style="verb">webapp</spanx>, to access remote web applications
- <spanx style="verb">datatx</spanx>, to transfer the data to the remote endpoint <vspace blankLines='1'/>
<figure><artwork><![CDATA[
Other custom protocols might be added in the future.
In case a single protocol is offered, there are three ways to specify this object:
Option 1: Set the `name` field to the name of the protocol, and put the protocol
details in a field named `options`.
Option 2: Set the `name` field to the name of the protocol, and put the protocol
details in a field carrying the name of the protocol.
Option 3: Set the `name` field to `multi`, and put the protocol
details in a field carrying the name of the protocol.
Option 1 using the `options` field is now deprecated. Implementations are encouraged to
transition to the new optional properties defined below, such that
this field may be removed in a future major version of the spec.
When specifying more than one protocol as different ways to access the Share, the `name`
field needs to be set to `multi`.
]]></artwork></figure>
</t>
</list></t>
<t>If <spanx style="verb">multi</spanx> is given, one or more protocol
endpoints are expected to be defined according to the optional
properties specified below.
Otherwise, at least <spanx style="verb">webdav</spanx> is expected to be supported, and
its options MAY be given in the opaque <spanx style="verb">options</spanx> payload for
compatibility with v1.0 implementations (see examples). Note
though that this format is deprecated.
Warning: client implementers should be aware that v1.1 servers
MAY support both <spanx style="verb">webdav</spanx> and <spanx style="verb">multi</spanx>, but v1.0 servers MAY
only support <spanx style="verb">webdav</spanx>.</t>
<t><list style="symbols">
<t>Protocol details for <spanx style="verb">webdav</spanx> MAY contain:
<list style="symbols">
<t>REQUIRED uri (string)
An URI to access the remote resource. The URI SHOULD be relative,
in which case the prefix exposed by the <spanx style="verb">/.well-known/ocm</spanx> endpoint MUST
be used. Absolute URIs are deprecated.</t>
<t>OPTIONAL sharedSecret (string) - required if no <spanx style="verb">code</spanx> field is given for the Share as a whole (see above).
An optional secret to be used to access the resource,
such as a bearer token.
To prevent leaking it in logs it MUST NOT appear in any URI.</t>
<t>OPTIONAL permissions (array of strings) -
The permissions granted to the sharee. A subset of:
- <spanx style="verb">read</spanx> allows read-only access including download of a copy.
- <spanx style="verb">write</spanx> allows create, update, and delete rights on the resource.
- <spanx style="verb">share</spanx> allows re-share rights on the resource.</t>
<t>OPTIONAL requirements (array of strings) -
The requirements that the sharee MUST fulfill to access the resource. A subset of:
- <spanx style="verb">mfa-enforced</spanx> requires the consumer to be MFA-authenticated. This
MAY be used if the recipient provider exposes the <spanx style="verb">enforce-mfa</spanx> capability.
- <spanx style="verb">use-code</spanx> requires the consumer to exchange the given <spanx style="verb">code</spanx> via a
signed HTTPS request. This MAY be used if the recipient provider exposes
the <spanx style="verb">receive-code</spanx> capability.</t>
</list></t>
<t>Protocol details for <spanx style="verb">webapp</spanx> MAY contain:
<list style="symbols">
<t>REQUIRED uri (string)
An URI to a client-browsable view of the shared resource, such that
users may use the web applications available at the site. The URI SHOULD
be relative, in which case the prefix exposed by the <spanx style="verb">/.well-known/ocm</spanx>
endpoint MUST be used. Absolute URIs are deprecated.</t>
<t>REQUIRED viewMode (string)
The permissions granted to the sharee. A subset of:
- <spanx style="verb">view</spanx> allows access to the web app in view-only mode.
- <spanx style="verb">read</spanx> allows read and download access via the web app.
- <spanx style="verb">write</spanx> allows full editing rights via the web app.</t>
<t>OPTIONAL sharedSecret (string)
An optional secret to be used to access the remote web app,
for example in the form of a bearer token.</t>
</list></t>
<t>Protocol details for <spanx style="verb">datatx</spanx> MAY contain:
<list style="symbols">
<t>REQUIRED srcUri (string)
An URI to access the remote resource. The URI SHOULD be relative,
in which case the prefix exposed by the <spanx style="verb">/.well-known/ocm</spanx> endpoint MUST
be used. Absolute URIs are deprecated.</t>
<t>OPTIONAL sharedSecret (string)
An optional secret to be used to access the resource,
for example in the form of a bearer token.
To prevent leaking it in logs it MUST NOT appear in any URI.</t>
<t>OPTIONAL size (integer)
The size of the file to be transferred from the sending server.</t>
</list></t>
</list></t>
</section>
<section anchor="decision-to-discard"><name>Decision to Discard</name>
<t>The Receiving Server MAY discard the notification if any of the following hold true:</t>
<t><list style="symbols">
<t>the HTTP Signature is missing but the Sending Server does expose a keypair discoverable from the FQDN part of the <spanx style="verb">sender</spanx> field in the request body</t>
<t>the HTTP Signature is missing</t>
<t>the HTTP Signature is not valid</t>
<t>no keypair is trusted or discoverable from the FQDN part of the <spanx style="verb">sender</spanx> field in the request body</t>
<t>the keypair used to generate the HTTP Signature doesn't match the one trusted or discoverable from the FQDN part of the <spanx style="verb">sender</spanx> field in the request body</t>
<t>the Sending Server is denylisted</t>
<t>the Sending Server is not allowlisted</t>
<t>the Sending Party is not trusted by the Receiving Party (e.g. no Invite was exchanged and/or the Sending Party's OCM Address does not appear in the Receiving Party's address book)</t>
<t>the Receiving Server is unable to act as an API client for (any of) the protocol(s) listed for accessing the resource</t>
<t>an initial check shows that the resource cannot successfully be accessed through (any of) the protocol(s) listed</t>
</list></t>
</section>
</section>
<section anchor="receiving-party-notification"><name>Receiving Party Notification</name>
<t>If the Share Creation Notification is not discarded by the Receiving Server, they MAY notify the Receiving Party passively by adding the Share to some inbox list, and MAY also notify them actively through for instance a push notification or an email message.</t>
<t>They could give the Receiving Party the option to accept or reject the share, or add the share automatically and only send an informational notification that this happened.</t>
</section>
<section anchor="share-acceptance-notification"><name>Share Acceptance Notification</name>
<t>In response to a Share Creation Notification, the Receiving Server MAY discover the OCM API of the Sending Server,
starting from the <spanx style="verb"><fqdn></spanx> part of the <spanx style="verb">sender</spanx> field in the Share Creation Notification.</t>
<t>If the OCM API of the Sending Server is successfully discovered, the Receiving Server MAY
make a HTTP POST request</t>
<t><list style="symbols">
<t>to the <spanx style="verb">/notifications</spanx> path in the Sending Server's OCM API</t>
<t>using <spanx style="verb">application/json</spanx> as the <spanx style="verb">Content-Type</spanx> HTTP request header</t>
<t>its request body containing a JSON document representing an object with the fields as described below</t>
<t>using TLS</t>
<t>using <eref target="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12">httpsig</eref></t>
</list></t>
<section anchor="fields-2"><name>Fields</name>
<t><list style="symbols">
<t>REQUIRED notificationType (string) - in a Share Acceptance Notification it SHOULD be one of:
<list style="symbols">
<t>'SHARE_ACCEPTED'</t>
<t>'SHARE_DECLINED'</t>
</list></t>
<t>REQUIRED providerId (string) - copied from the Share Creation Notification for the Share this notification is about</t>
<t>OPTIONAL resourceType (string) - copied from the Share Creation Notification for the Share this notification is about</t>
<t>OPTIONAL notification (object) - optional additional parameters, depending on the notification and the resource type</t>
</list></t>
<section anchor="receiving-party-notification-1"><name>Receiving Party Notification</name>
<t>If the Share Creation Notification is not discarded by the Receiving Server, they MAY notify the Receiving Party passively by adding the Share to some inbox list, and MAY also notify them actively through for instance a push notification or an email message.</t>
<t>They could give the Receiving Party the option to accept or reject the Share, or add the Share automatically and only send an informational notification that this happened.</t>
</section>
</section>
</section>
<section anchor="resource-access"><name>Resource Access</name>
<t>To access the Resource, the Receiving Server MAY use multiple ways, depending on the body of the Share Creation Notification. The procedure is as follows:
1. The receiver MUST extract the OCM Server FQDN from the <spanx style="verb">sender</spanx> field of the received share, and MUST query the <xref target="ocm-api-discovery">Discovery</xref> endpoint at that address: the <spanx style="verb">resourceTypes[0].protocols.webdav</spanx> value is the <spanx style="verb"><sender-ocm-path></spanx> to be used in step 3.
2. If <spanx style="verb">code</spanx> is not empty, the receiver SHOULD make a signed POST request to the <spanx style="verb">/token</spanx> path inside the Sending Server's OCM API, to exchange the code for a short-lived bearer token, and then use that bearer token to access the Resource.
3. If <spanx style="verb">protocol.name</spanx> = <spanx style="verb">webdav</spanx>, the receiver SHOULD inspect the <spanx style="verb">protocol.options</spanx> property. If it contains a <spanx style="verb">sharedSecret</spanx>, as in the <eref target="https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post">legacy example</eref>, then the receiver SHOULD make a HTTP PROPFIND request to <spanx style="verb">https://<sharedSecret>:@<sender-host><sender-ocm-path></spanx>. Note that this access method, based on Basic Auth, is <em>deprecated</em> and may be removed in a future release of the Protocol.
4. Otherwise, if <spanx style="verb">protocol.name</spanx> = <spanx style="verb">multi</spanx>, the receiver MUST inspect the <spanx style="verb">protocol.webdav.uri</spanx> property: if it's a complete URI, the receiver MUST make a HTTP PROPFIND request against it to access the remote resource. If it only contains an identifier <spanx style="verb"><key></spanx>, the receiver MUST make a HTTP PROPFIND request to <spanx style="verb">https://<sender-host><sender-ocm-path>/<key></spanx> in order to access the remote resource. Additionally, the receiver MUST pass an <spanx style="verb">Authorization: bearer</spanx> header with either the short-lived bearer token obtained in step 2, if applicable, or the <spanx style="verb">protocol.webdav.sharedSecret</spanx> value.</t>
<t>In all cases, in case the Shared Resource is a folder and the Receiving Server accesses a resource within that shared folder, it SHOULD append its relative path to that URL. In other words, the Sending Server SHOULD support requests to URLs such as <spanx style="verb">https://<sender-host><sender-ocm-path>/path/to/resource.txt</spanx>.</t>
<t>Additionally, if <spanx style="verb">protocol.<protocolname>.requirements</spanx> includes <spanx style="verb">mfa-enforced</spanx>, the Receiving Server MUST ensure that the Receiving Party has been authenticated with MFA, or prompt the consmer in order to elevate their session, if applicable.</t>
</section>
<section anchor="share-deletion"><name>Share Deletion</name>
<t>A <spanx style="verb">"SHARE_ACCEPTED"</spanx> notification followed by a <spanx style="verb">"SHARE_UNSHARED"</spanx> notification is
equivalent to a <spanx style="verb">"SHARE_DECLINED"</spanx> notification.</t>
<t>Note that the Sending Server MAY at any time revoke access to a Resource (effectively undoing or deleting the Share) without notifying the Receiving Server.</t>
</section>
<section anchor="share-updating"><name>Share Updating</name>
<t>Some implementations have experimented with a <spanx style="verb">"RESHARE_CHANGE_PERMISSION"</spanx>notification, but the payload and side effects such a notification may have are out of scope of this version of this specification.
The Receiving Party sending such a notification has no way of knowing if the Sending Party understood and processed the reshare request or not.</t>
</section>
<section anchor="resharing"><name>Resharing</name>
<t>The <spanx style="verb">"REQUEST_RESHARE"</spanx> and <spanx style="verb">"RESHARE_UNDO"</spanx> notification types MAY be used by the
Receiving Server to persuade the Sending Server to share the same Resource with another Receiving Party.
The details of the payload and side effects such a notification may have are out of scope of this version of this specification.
Note that the Receiving Party sending such a notification has no way of knowing if the Sending Party understood and processed the reshare request or not.</t>
</section>
<section anchor="appendix-a-multi-factor-authentication"><name>Appendix A: Multi Factor Authentication</name>
<t>If a Receiving Server exposes the capability <spanx style="verb">enforce-mfa</spanx>, it indicates that it will try and comply with a MFA requirement set on a Share. If the Sending Server trusts the Receiving Server, the Sending Server MAY set the requirement <spanx style="verb">mfa-enforced</spanx> on a Share, which the Receiving Server MUST honor. A compliant Receiving Server that signals that it is MFA-capable MUST not allow access to a resource protected with the <spanx style="verb">mfa-enforced</spanx> requirement, if the Receiving Party has not provided a second factor to establish their identity with greater confidence.</t>
<t>Since there is no way to guarantee that the Receiving Server will actually enforce the MFA requirement, it is up to the Sending Server to establish a trust with the Receiving Server such that it is reasonable to assume that the Receiving Server will honor the MFA requirement. This establishment of trust will inevitably be implementation dependent, and can be done for example using a pre approved allow list of trusted Receiving Servers. The procedure of establishing trust is out of scope for this specification: a mechanism similar to the <eref target="https://sciencemesh.io">ScienceMesh</eref> integration for the <xref target="invite-flow">Invite</xref> capability may be envisaged.</t>
</section>
<section anchor="appendix-b-request-signing"><name>Appendix B: Request Signing</name>
<t>A request is signed by adding the signature in the headers. The sender also needs to expose the public key used to generate the signature. The receiver can then validate the signature and therefore the origin of the request.
To help debugging, it is recommended to also add all properties used in the signature as headers, even if they can easily be re-generated from the payload.</t>
<t>Note: Signed requests prove the identity of the sender but do not encrypt nor affect its payload.</t>
<t>Here is an example of headers needed to sign a request.</t>
<figure><artwork><![CDATA[
{
"(request-target)": "post /path",
"content-length": 380,
"date": "Mon, 08 Jul 2024 14:16:20 GMT",
"digest": "SHA-256=U7gNVUQiixe5BRbp4Tg0xCZMTcSWXXUZI2\\/xtHM40S0=",
"host": "hostname.of.the.recipient",
"Signature": "keyId=\"https://author.hostname/key\",algorithm=\"rsa-sha256\",headers=\"content-length date digest host\",signature=\"DzN12OCS1rsA[...]o0VmxjQooRo6HHabg==\""
}
]]></artwork></figure>
<t><list style="symbols">
<t>'(request-target)' contains the reached endpoint and the used method,</t>
<t>'content-length' is the total length of the payload of the request,</t>
<t>'date' is the date and time when the request has been sent,</t>
<t>'digest' is a checksum of the payload of the request,</t>
<t>'host' is the hostname of the recipient of the request (remote when signing outgoing request, local on incoming request),</t>
<t>'Signature' contains the signature generated using the private key and details on its generation:
<list style="symbols">
<t>'keyId' is a unique id, formatted as an url. hostname is used to retrieve the public key via custom discovery</t>
<t>'algorithm' specify the algorithm used to generate signature</t>
<t>'headers' specify the properties used when generating the signature</t>
<t>'signature' the signature of an array containing the properties listed in 'headers'. Some properties like content-length, date, digest, and host are mandatory to protect against authenticity override.</t>
</list></t>
</list></t>
<section anchor="how-to-generate-the-signature-for-outgoing-request"><name>How to generate the Signature for outgoing request</name>
<t>After properties are set in the headers, the Signature is generated and added to the list.</t>
<t>This is a pseudo-code example for generating the <spanx style="verb">Signature</spanx> header for outgoing requests:</t>
<figure><artwork><![CDATA[
headers = {
'(request-target)': 'post /path',
'content-length': length_of(payload),
'date': current_gmt_datetime(), # Use a function to get the current GMT date as 'D, d M Y H:i:s T'
'digest': 'SHA-256=' + base64_encode(hash('sha256', utf8_encode(payload))),
'host': 'recipient-fqdn',
}
signed = ssl_sign(concatenate_with_newlines(headers), private_key, 'sha256')
signature = {
'keyId': 'sender-fqdn', # The sending server's FQDN; find its public key through OCM API discovery
'algorithm': 'rsa-sha256',
'headers': 'content-length date digest host',
'signature': signed,
}
headers['Signature'] = format_signature(signature)
]]></artwork></figure>
<section anchor="how-to-confirm-signature-on-incoming-request"><name>How to confirm Signature on incoming request</name>
<t>The first step would be to confirm the validity of each properties:</t>
<t><list style="symbols">
<t><spanx style="verb">(request-target)</spanx> and <spanx style="verb">host</spanx> are immutable to the type of the request and the local/current host,</t>
<t><spanx style="verb">content-length</spanx> and <spanx style="verb">digest</spanx> can be re-generated and compared from the payload of the request,</t>
<t>a maximum TTL must be applied to <spanx style="verb">date</spanx> and current timestamp,</t>
<t>regarding data contained in the <spanx style="verb">Signature</spanx> header:
<list style="symbols">
<t>using <spanx style="verb">keyId</spanx> to get the public key from remote signatory,</t>
<t><spanx style="verb">headers</spanx> is used to generate the clear version of the signature and must contain at least <spanx style="verb">content-length</spanx>, <spanx style="verb">date</spanx>, <spanx style="verb">digest</spanx> and <spanx style="verb">host</spanx>,</t>
<t><spanx style="verb">signature</spanx> is the encrypted version of the signature.</t>
</list></t>
</list></t>
<t>Here is an example of how to verify the signature using the headers, the signature and the public key:</t>
<figure><artwork><![CDATA[
clear = {
'(request-target)': 'post /path',
'content-length': length_of(payload),
'date': 'Mon, 08 Jul 2024 14:16:20 GMT', # The date used in the verification process
'digest': 'SHA-256=' + base64_encode(hash('sha256', utf8_encode(payload))), # Recompute the digest for verification
'host': 'sender-fqdn',
}
signed = headers['Signature']
verification_result = ssl_verify(concatenate_with_newlines(clear), signed, public_key, 'sha256')
if not verification_result then
raise InvalidSignatureException
]]></artwork></figure>
</section>
</section>
<section anchor="validating-the-payload"><name>Validating the payload</name>
<t>Following the validation of the signature, the host should also confirm the validity of the payload, that is ensuring that the actions implied in the payload actually initiated on behalf of the source of the request.</t>
<t>As an example, if the payload is about initiating a new share the file owner has to be an account from the instance at the origin of the request.</t>
</section>
</section>
</middle>
<back>
</back>
<!-- ##markdown-source:
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