idnits 2.17.1 draft-dejong-remotestorage-04.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- No issues found here. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year -- The document date (15 December 2014) is 3417 days in the past. Is this intentional? Checking references for intended status: Proposed Standard ---------------------------------------------------------------------------- (See RFCs 3967 and 4897 for information about using normative references to lower-maturity documents in RFCs) -- Obsolete informational reference (is this intentional?): RFC 2818 (ref. 'HTTPS') (Obsoleted by RFC 9110) -- Obsolete informational reference (is this intentional?): RFC 7231 (ref. 'HTTP') (Obsoleted by RFC 9110) -- Obsolete informational reference (is this intentional?): RFC 7232 (ref. 'COND') (Obsoleted by RFC 9110) -- Obsolete informational reference (is this intentional?): RFC 7233 (ref. 'RANGE') (Obsoleted by RFC 9110) Summary: 0 errors (**), 0 flaws (~~), 1 warning (==), 5 comments (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 1 INTERNET DRAFT Michiel B. de Jong 2 Document: draft-dejong-remotestorage-04 IndieHosters 3 F. Kooman 4 Intended Status: Proposed Standard (independent) 5 Expires: 18 June 2015 15 December 2014 7 remoteStorage 9 Abstract 11 This draft describes a protocol by which client-side applications, 12 running inside a web browser, can communicate with a data storage 13 server that is hosted on a different domain name. This way, the 14 provider of a web application need not also play the role of data 15 storage provider. The protocol supports storing, retrieving, and 16 removing individual documents, as well as listing the contents of an 17 individual folder, and access control is based on bearer tokens. 19 Status of this Memo 21 This Internet-Draft is submitted in full conformance with the 22 provisions of BCP 78 and BCP 79. 24 Internet-Drafts are working documents of the Internet Engineering 25 Task Force (IETF). Note that other groups may also distribute 26 working documents as Internet-Drafts. The list of current Internet- 27 Drafts is at http://datatracker.ietf.org/drafts/current/. 29 Internet-Drafts are draft documents valid for a maximum of six months 30 and may be updated, replaced, or obsoleted by other documents at any 31 time. It is inappropriate to use Internet-Drafts as reference 32 material or to cite them other than as "work in progress." 34 This Internet-Draft will expire on 15 December 2014. 36 Copyright Notice 38 Copyright (c) 2014 IETF Trust and the persons identified as the 39 document authors. All rights reserved. 41 This document is subject to BCP 78 and the IETF Trust's Legal 42 Provisions Relating to IETF Documents 43 (http://trustee.ietf.org/license-info) in effect on the date of 44 publication of this document. Please review these documents 45 carefully, as they describe your rights and restrictions with respect 46 to this document. Code Components extracted from this document must 47 include Simplified BSD License text as described in Section 4.e of 48 the Trust Legal Provisions and are provided without warranty as 49 described in the Simplified BSD License. 51 Table of Contents 53 1. Introduction...................................................2 54 2. Terminology....................................................3 55 3. Storage model..................................................3 56 4. Requests.......................................................4 57 5. Response codes.................................................7 58 6. Versioning.....................................................7 59 7. CORS headers...................................................8 60 8. Session description............................................8 61 9. Bearer tokens and access control...............................9 62 10. Application-first bearer token issuance.......................10 63 11. Storage-first bearer token issuance...........................11 64 12. Example wire transcripts......................................12 65 12.1. WebFinger................................................12 66 12.2. OAuth dialog form........................................13 67 12.3. OAuth dialog form submission.............................14 68 12.4. OPTIONS preflight........................................15 69 12.5. Initial PUT..............................................15 70 12.6. Subsequent PUT...........................................16 71 12.7. GET......................................................16 72 12.8. DELETE...................................................17 73 13. Distributed versioning........................................17 74 14. Security Considerations.......................................19 75 15. IANA Considerations...........................................20 76 16. Acknowledgments...............................................20 77 17. References....................................................21 78 17.1. Normative References.....................................21 79 17.2. Informative References...................................21 80 18. Authors' addresses............................................22 82 1. Introduction 84 Many services for data storage are available over the internet. This 85 specification describes a vendor-independent interface for such 86 services. It is based on https, CORS and bearer tokens. The 87 metaphor for addressing data on the storage is that of folders 88 containing documents and subfolders. The actions the interface 89 exposes are: 91 * GET a folder: retrieve the names and current versions of the 92 documents and subfolders currently contained by the folder 94 * GET a document: retrieve its content type, current version, 95 and contents 97 * PUT a document: store a new version, its content type, and 98 contents, conditional on the current version 100 * DELETE a document: remove it from the storage, conditional on 101 the current version 103 * HEAD a folder or document: like GET, but omitting the response 104 body 106 The exact details of these four actions are described in this 107 specification. 109 2. Terminology 111 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 112 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 113 document are to be interpreted as described in RFC 2119 [WORDS]. 115 "SHOULD" and "SHOULD NOT" are appropriate when valid exceptions to a 116 general requirement are known to exist or appear to exist, and it is 117 infeasible or impractical to enumerate all of them. However, they 118 should not be interpreted as permitting implementors to fail to 119 implement the general requirement when such failure would result in 120 interoperability failure. 122 3. Storage model 124 The server stores data in nodes that form a tree structure. 125 Internal nodes are called 'folders' and leaf nodes are called 126 'documents'. For a folder, the server stores references to nodes 127 contained in the folder, and it should be able to produce a list of 128 them, with for each contained item: 130 * item name 131 * item type (folder or document) 132 * current version 133 * content type 134 * content length 136 For a document, the server stores, and should be able to produce: 138 * current version 139 * content type 140 * content length 141 * content 143 4. Requests 145 Client-to-server requests SHOULD be made over https [HTTPS], and 146 servers MUST comply with HTTP/1.1 [HTTP]. Specifically, they 147 MUST support chunked transfer coding on PUT requests. Servers MAY 148 also offer an optional switch from https to SPDY [SPDY]. 150 A request is considered successful if the HTTP response code is in 151 the 2xx range (e.g. 200 OK, 201 Created), and unsuccessful if an 152 error occurred or a condition was not met (response code e.g. 404 153 Not Found, 304 Not Modified). 155 The root folder of the storage tree is represented by the following 156 URL: 158 URI_ENCODE( '/' ) 160 Subsequently, if is the URL of a folder, then the 161 URL of an item contained in it is: 163 URI_ENCODE( ) 165 for a document, or: 167 URI_ENCODE( '/' ) 169 for a folder. Item names MAY contain all characters except '/' and 170 the null character, and MUST NOT have zero length. 172 A document description is a map containing one string-valued 'ETag' 173 field, one string-valued 'Content-Type' and one integer-valued 174 'Content-Length' field. They represent the document's current 175 version, its content type, and its content length respectively. Note 176 that content length is measured in octets (bytes), not in 177 characters. 179 A folder description is a map containing a string-valued 'ETag' 180 field, representing the folder's current version. 182 A successful GET request to a folder MUST be responded to with a 183 JSON-LD [JSON-LD] document (content type 'application/ld+json'), 184 containing as its 'items' field a map in which contained documents 185 appear as entries to a document description, and 186 contained non-empty folders appear as entries '/' to a 187 folder description. It MUST also contain an '@context' field with 188 the value 'http://remotestorage.io/spec/folder-description'. For 189 instance: 191 { 192 "@context": "http://remotestorage.io/spec/folder-description", 193 "items": { 194 "abc": { 195 "ETag": "DEADBEEFDEADBEEFDEADBEEF", 196 "Content-Type": "image/jpeg", 197 "Content-Length": 82352 198 }, 199 "def/": { 200 "ETag": "1337ABCD1337ABCD1337ABCD" 201 } 202 } 203 } 205 All folders are treated as existing, and therefore GET requests to 206 untouched folders SHOULD be responded to with a folder description 207 with no items (the items field set to '{}'). However, an empty 208 folder MUST NOT be listed as an item in its parent folder. 210 Also, since folders exist automatically, PUT and DELETE requests 211 only need to be made to documents, and never to folders. A document 212 PUT will make all ancestor folders along its path become non-empty; 213 deleting the last document from a subtree will make that whole 214 subtree become empty. Folders will therefore show up in their parent 215 folder descriptions if and only if their subtree contains at least 216 one document. 218 A successful GET request to a document SHOULD be responded to with 219 the full document contents in the body, the document's content type 220 in a 'Content-Type' header, its content length in octets (not in 221 characters) in a 'Content-Length' header, and the document's current 222 version as a strong ETag in an 'ETag' header. 224 Note that the use of strong ETags prohibits changing the response 225 body based on request headers; in particular, the server will not be 226 able to serve the same document uncompressed to some clients and 227 gzipped when requested by the client, since the two bodies would not 228 be identical byte-for-byte. 230 Servers MAY support Content-Range headers [RANGE] on GET requests, 231 but whether or not they do SHOULD be announced through the 232 variable mentioned below in section 10. 234 A successful PUT request to a document MUST result in: 236 * the request body being stored as the document's new content, 237 * parent and further ancestor folders being silently created as 238 necessary, with the document (name and version) being added to 239 its parent folder, and each folder added to its subsequent 240 parent, 241 * the value of its Content-Type header being stored as the 242 document's new content type, 243 * its version being updated, as well as that of its parent folder 244 and further ancestor folders, using a strong validator [HTTP, 245 section 7.2]. 247 The response MUST contain a strong ETag header, with the document's 248 new version (for instance a hash of its contents) as its value. 250 A successful DELETE request to a document MUST result in: 252 * the deletion of that document from the storage, and from its 253 parent folder, 254 * silent deletion of the parent folder if it is left empty by 255 this, and so on for further ancestor folders, 256 * the version of its parent folder being updated, as well as that 257 of further ancestor folders. 259 A successful OPTIONS request SHOULD be responded to as described in 260 the CORS section below. 262 A successful HEAD request SHOULD be responded to like to the 263 equivalent GET request, but omitting the response body. 265 5. Response codes 267 Response codes SHOULD be given as defined by [HTTP, section 6] and 268 [BEARER, section 3.1]. The following is a non-normative checklist 269 of status codes that are likely to occur in practice: 271 * 500 if an internal server error occurs, 272 * 429 if the client makes too frequent requests or is suspected 273 of malicious activity, 274 * 414 if the request URI is too long, 275 * 416 if Range requests are supported by the server and the Range 276 request can not be satisfied, 277 * 401 for all requests that don't have a bearer token with 278 sufficient permissions, 279 * 404 for all DELETE and GET requests to documents that do not 280 exist on the storage, 281 * 304 for a conditional GET request whose pre-condition 282 fails (see "Versioning" below), 283 * 409 for a PUT request where any folder name in the path 284 clashes with an existing document's name at the same 285 level, or where the document name coincides with an 286 existing folder's name at the same level. 287 * 412 for a conditional PUT or DELETE request whose pre-condition 288 fails (see "Versioning" below), 289 * 507 in case the account is over its storage quota, 290 * 4xx for all malformed requests (e.g. foreign characters in the 291 path), as well as for all PUT and DELETE requests to 292 folders, 293 * 2xx for all successful requests. 295 Clients SHOULD also handle the case where a response takes too long 296 to arrive, or where no response is received at all. 298 6. Versioning 300 All successful requests MUST return an 'ETag' header [HTTP] with, in 301 the case of GET, the current version, in the case of PUT, the new 302 version, and in case of DELETE, the version that was deleted. All 303 successful GET requests MUST return an 'Expires: 0' header. PUT and 304 DELETE requests MAY have an 'If-Match' request header [COND], and 305 MUST fail with a 412 response code if that doesn't match the 306 document's current version. 308 GET requests MAY have a comma-separated list of revisions in an 309 'If-None-Match' header [COND], and SHOULD be responded to with a 304 310 response if that list includes the document or folder's current 311 version. A PUT request MAY have an 'If-None-Match: *' header [COND], 312 in which case it MUST fail with a 412 response code if the document 313 already exists. 315 In all 'ETag', 'If-Match' and 'If-None-Match' headers, revision 316 strings should appear inside double quotes ("). 318 A provider MAY offer version rollback functionality to its users, 319 but this specification does not define the user interface for that. 321 7. CORS headers 323 All responses MUST carry CORS headers [CORS]. The server MUST also 324 reply to OPTIONS requests as per CORS. For GET requests, a wildcard 325 origin MAY be returned, but for PUT and DELETE requests, the 326 response MUST echo back the Origin header sent by the client. 328 8. Session description 330 The information that a client needs to receive in order to be able 331 to connect to a server SHOULD reach the client as described in the 332 'bearer token issuance' sections below. It consists of: 334 * , consisting of 'https://' followed by a server 335 host, and optionally a server port and a path prefix as per 336 [IRI]. Examples: 337 * 'https://example.com' (host only) 338 * 'https://example.com:8080' (host and port) 339 * 'https://example.com/path/to/storage' (host, port and 340 path prefix; note there is no trailing slash) 341 * as per [OAUTH]. The token SHOULD be hard to 342 guess and SHOULD NOT be reused from one client to another. It 343 can however be reused in subsequent interactions with the same 344 client, as long as that client is still trusted. Example: 345 * 'ofb24f1ac3973e70j6vts19qr9v2eei' 346 * , always 'draft-dejong-remotestorage-04' for this 347 alternative version of the specification. 349 The client can make its requests using https with CORS and bearer 350 tokens, to the URL that is the concatenation of with 351 '/' plus one or more '/' strings indicating a path in the 352 folder tree, followed by zero or one strings, indicating 353 a document. For example, if is 354 "https://storage.example.com/bob", then to retrieve the folder 355 contents of the /public/documents/ folder, or to retrieve a 356 'draft.txt' document from that folder, the client would make 357 requests to, respectively: 359 * https://storage.example.com/bob/public/documents/ 360 * https://storage.example.com/bob/public/documents/draft.txt 362 9. Bearer tokens and access control 364 A bearer token represents one or more access scopes. These access 365 scopes are represented as strings of the form , 366 where the string SHOULD be lower-case alphanumerical, other 367 than the reserved word 'public', and can be ':r' or ':rw'. 368 The access the bearer token gives is the sum of its access scopes, 369 with each access scope representing the following permissions: 371 '*:rw') any request, 373 '*:r') any GET or HEAD request, 375 ':rw') any requests to paths that start with 376 '/' '/' or '/public/' '/', 378 ':r') any GET or HEAD requests to paths that start with 379 '/' '/' or '/public/' '/', 381 As a special exceptions, GET requests to a document (but not a 382 folder) whose path starts with '/public/' are always allowed. They, 383 as well as OPTIONS requests, can be made without a bearer token. 384 Unless [KERBEROS] is used (see section 10 below), all other requests 385 SHOULD present a bearer token with sufficient access scope, using a 386 header of the following form (no double quotes here): 388 Authorization: Bearer 390 In addition, providing the access token via a HTTP query parameter 391 for GET requests MAY be supported by the server, although its use 392 is not recommended, due to its security deficiencies; see [BEARER, 393 section 2.3]. 395 10. Application-first bearer token issuance 397 To make a remoteStorage server available as 'the remoteStorage of 398 at ', exactly one link of the following format 399 SHOULD be added to the WebFinger record [WEBFINGER] of at 400 : 402 { 403 "href": , 404 "rel": "remotestorage", 405 "properties": { 406 "http://remotestorage.io/spec/version": , 407 "http://tools.ietf.org/html/rfc6749#section-4.2": , 408 ... : ... , 409 } 410 } 412 Here and are as per "Session 413 description" above, and SHOULD be either null or a 414 URL where an OAuth 2.0 implicit-grant flow dialog [OAUTH] is 415 presented. 417 If is a URL, the user can supply their credentials 418 for accessing the account (how, is out of scope), and allow or 419 reject a request by the connecting application to obtain a bearer 420 token for a certain list of access scopes. Note that an account 421 will often belong to just one human user, but may also belong to a 422 group of multiple users (the remoteStorage of at ). 424 If is null, the client will not have a way to obtain 425 an access token, and SHOULD send all requests without Authorization 426 header, and rely on Kerberos [KERBEROS] instead for requests that 427 would normally be sent with a bearer token, but servers SHOULD NOT 428 impose any such access barriers for resources that would normally 429 not require an access token. 431 The '...' ellipses indicate that more properties may be present. 432 Non-breaking examples that have been proposed so far, include a 433 "http://tools.ietf.org/html/rfc6750#section-2.3" property, set to 434 the string value "true" if the server supports passing the bearer 435 token in the URI query parameter as per section 2.3 of [BEARER], 436 instead of in the request header. 438 Another example is "http://tools.ietf.org/html/rfc7233" with a 439 string value of "GET" if Content-Range headers are supported for 440 GET requests as per [RANGE], "PUT" if they are supported for PUT 441 requests, and "GET,PUT" if supported for both. 443 Both these proposals are non-breaking extensions, since the client 444 will have a way to work around it if these features are not present 445 (e.g. retrieve the protected resource asynchronously in the first 446 case, or request the entire resource in the second case). 448 A "http://remotestorage.io/spec/web-authoring" property has been 449 proposed with a string value of the fully qualified domain name to 450 which web authoring content is published if the server supports web 451 authoring as per [AUTHORING]. Note that this extension is a breaking 452 extension in the sense that it divides users into "haves", whose 453 remoteStorage accounts allow them to author web content, and 454 "have-nots", whose remoteStorage account does not support this 455 functionality. 457 The server MAY expire bearer tokens, and MAY require the user to 458 register applications as OAuth clients before first use; if no 459 client registration is required, then the server MAY ignore the 460 client_id parameter in favor of relying on the redirect_uri 461 parameter for client identification. 463 11. Storage-first bearer token issuance 465 The provider MAY also present a dashboard to the user, where they 466 have some way to add open web app manifests [MANIFEST]. Adding a 467 manifest to the dashboard is considered equivalent to clicking 468 'accept' in the dialog of the application-first flow. Removing one 469 is considered equivalent to revoking its access token. 471 As an equivalent to OAuth's 'scope' parameter, a 'datastores-access' 472 field SHOULD be present in the root of such an application manifest 473 document, with entries -> '{"access": "readonly"}' for 474 'r' or '{"access": "readwrite"}' for 'rw', as 475 prescribed in [DATASTORE]. 477 When the user gestures they want to use a certain application whose 478 manifest is present on the dashboard, the dashboard SHOULD redirect 479 to the application or open it in a new window. To mimic coming back 480 from the OAuth dialog, it MAY add 'access_token' and 'scope' 481 fields to the URL fragment. 483 Regardless of whether 'access_token' and 'scope' are specified, it 484 SHOULD add a 'remotestorage' field to the URL fragment, with a 485 value of the form '@' . When the application detects 486 this parameter, it SHOULD resolve the WebFinger record for 487 at and extract the and 488 information. 490 If no access_token was given, then the application SHOULD also 491 extract the information from WebFinger, and continue 492 as per application-first bearer token issuance. 494 Note that whereas a remoteStorage server SHOULD offer support for 495 the application-first flow with WebFinger and OAuth, it MAY choose 496 not to support the storage-first flow, provided that users will 497 easily remember their '@' WebFinger address at that 498 provider. Applications SHOULD, however, support both flows, which 499 means checking the URL for a 'remotestorage' parameter, but giving 500 the user a way to specify the WebFinger address if there is none. 502 If a server provides an application manifest dashboard, then it 503 SHOULD merge the list of applications there with the list of 504 issued access tokens as specified by OAuth into one list. Also, 505 the interface for revoking an access token as specified by OAuth 506 SHOULD coincide with removing an application from the dashboard. 508 Servers MAY also provide a way to create access tokens directly from 509 their user interface. Such functionality would be aimed mainly at 510 developers, to manually copy and paste a token into a script or 511 debug tool, thus bypassing the need for an OAuth dance. Clients 512 SHOULD NOT rely on this in production. 514 12. Example wire transcripts 516 The following examples are not normative ("\" indicates a line was 517 wrapped). 519 12.1. WebFinger 521 In application-first, an in-browser application might issue the 522 following request, using XMLHttpRequest and CORS: 524 GET /.well-known/webfinger?resource=acct:michiel@michielbdejon\ 525 g.com HTTP/1.1 526 Host: michielbdejong.com 528 and the server's response might look like this: 530 HTTP/1.1 200 OK 531 Access-Control-Allow-Origin: * 532 Access-Control-Allow-Methods: GET 533 Access-Control-Allow-Headers: If-Match, If-None-Match 534 Access-Control-Expose-Headers: ETag, Content-Length 535 Content-Type: application/jrd+json 537 { 538 "links":[{ 539 "href": "https://michielbdejong.com:7678/inbox", 540 "rel": "post-me-anything" 541 }, { 542 "href": "https://michielbdejong.com/me.jpg", 543 "rel": "avatar" 544 }, { 545 "href": "https://3pp.io:4439/storage/michiel", 546 "rel": "remotestorage", 547 "properties": { 548 "http://remotestorage.io/spec/version": "draft-dejong-re\ 549 motestorage-04", 550 "http://tools.ietf.org/html/rfc6749#section-4.2": "https\ 551 ://3pp.io:4439/oauth/michiel", 552 "http://tools.ietf.org/html/rfc6750#section-2.3": false, 553 "http://tools.ietf.org/html/rfc7233": false, 554 "http://remotestorage.io/spec/web-authoring": false 555 } 556 }] 557 } 559 12.2. OAuth dialog form 561 Once the in-browser application has discovered the server's OAuth 562 end-point, it will typically redirect the user to this URL, in 563 order to obtain a bearer token. Say the application is hosted on 564 https://drinks-unhosted.5apps.com/ and wants read-write access to 565 the account's "myfavoritedrinks" scope: 567 GET /oauth/michiel?redirect_uri=https%3A%2F%2Fdrinks-unhosted.5\ 568 apps.com%2F&scope=myfavoritedrinks%3Arw&client_id=https%3A%2F%2Fdrinks-\ 569 unhosted.5apps.com&response_type=token HTTP/1.1 570 Host: 3pp.io 572 The server's response might look like this (truncated for brevity): 574 HTTP/1.1 200 OK 576 577 578 579 Allow access? 580 ... 582 12.3. OAuth dialog form submission 584 When the user submits the form, the request would look something 585 like this: 587 POST /oauth HTTP/1.1 588 Host: 3pp.io:4439 589 Origin: https://3pp.io:4439 590 Content-Type: application/x-www-form-urlencoded 591 Referer: https://3pp.io:4439/oauth/michiel?redirect_uri=https%3\ 592 A%2F%2Fdrinks-unhosted.5apps.com%2F&scope=myfavoritedrinks%3Arw&client_\ 593 id=https%3A%2F%2Fdrinks-unhosted.5apps.com&response_type=token 595 client_id=https%3A%2F%2Fdrinks-unhosted.5apps.com&redirect_uri=\ 596 https%3A%2F%2Fdrinks-unhosted.5apps.com%2F&response_type=token&scope=my\ 597 favoritedrinks%3Arw&state=&username=michiel&password=something&allow=Al\ 598 low 600 To which the server could respond with a 302 redirect, back to the 601 origin of the requesting application: 603 HTTP/1.1 302 Found 604 Location:https://drinks-unhosted.5apps.com/#access_token=j2YnGt\ 605 XjzzzHNjkd1CJxoQubA1o%3D&token_type=bearer&state= 607 12.4. OPTIONS preflight 608 When an in-browser application makes a cross-origin request which 609 may affect the server-state, the browser will make a preflight 610 request first, with the OPTIONS verb, for instance: 612 OPTIONS /storage/michiel/myfavoritedrinks/ HTTP/1.1 613 Host: 3pp.io:4439 614 Access-Control-Request-Method: GET 615 Origin: https://drinks-unhosted.5apps.com 616 Access-Control-Request-Headers: Authorization 617 Referer: https://drinks-unhosted.5apps.com/ 619 To which the server can for instance respond: 621 HTTP/1.1 200 OK 622 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 623 Access-Control-Allow-Methods: GET, PUT, DELETE 624 Access-Control-Allow-Headers: Authorization, Content-Length, Co\ 625 ntent-Type, Origin, X-Requested-With, If-Match, If-None-Match 627 12.5. Initial PUT 629 An initial PUT may contain an 'If-None-Match: *' header, like this: 631 PUT /storage/michiel/myfavoritedrinks/test HTTP/1.1 632 Host: 3pp.io:4439 633 Content-Length: 91 634 Origin: https://drinks-unhosted.5apps.com 635 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 636 Content-Type: application/json; charset=UTF-8 637 Referer: https://drinks-unhosted.5apps.com/? 638 If-None-Match: * 640 {"name":"test","@context":"http://remotestorage.io/spec/modules\ 641 /myfavoritedrinks/drink"} 643 And the server may respond with either a 201 Created or a 200 OK 644 status: 646 HTTP/1.1 201 Created 647 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 648 ETag: "1382694045000" 650 12.6. Subsequent PUT 651 A subsequent PUT may contain an 'If-Match' header referring to the 652 ETag previously returned, like this: 654 PUT /storage/michiel/myfavoritedrinks/test HTTP/1.1 655 Host: 3pp.io:4439 656 Content-Length: 91 657 Origin: https://drinks-unhosted.5apps.com 658 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 659 Content-Type: application/json; charset=UTF-8 660 Referer: https://drinks-unhosted.5apps.com/? 661 If-Match: "1382694045000" 663 {"name":"test", "updated":true, "@context":"http://remotestorag\ 664 e.io/spec/modules/myfavoritedrinks/drink"} 666 And the server may respond with a 412 Conflict or a 200 OK status: 668 HTTP/1.1 200 OK 669 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 670 ETag: "1382694048000" 672 12.7. GET 674 A GET request would also include the bearer token, and optionally 675 an If-None-Match header: 677 GET /storage/michiel/myfavoritedrinks/test HTTP/1.1 678 Host: 3pp.io:4439 679 Origin: https://drinks-unhosted.5apps.com 680 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 681 Referer: https://drinks-unhosted.5apps.com/? 682 If-None-Match: "1382694045000", "1382694048000" 684 And the server may respond with a 304 Not Modified status: 686 HTTP/1.1 304 Not Modified 687 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 688 ETag: "1382694048000" 690 Or a 200 OK status, plus a response body: 692 HTTP/1.1 200 OK 693 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 694 Content-Type: application/json; charset=UTF-8 695 Content-Length: 106 696 ETag: "1382694048000" 697 Expires: 0 699 {"name":"test", "updated":true, "@context":"http://remotestora\ 700 ge.io/spec/modules/myfavoritedrinks/drink"} 702 If the GET URL would have been "/storage/michiel/myfavoritedrinks/", 703 a 200 OK response would have a folder description as the response 704 body: 706 HTTP/1.1 200 OK 707 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 708 Content-Type: application/ld+json 709 Content-Length: 171 710 ETag: "1382694048000" 711 Expires: 0 713 {"@context":"http://remotestorage.io/spec/folder-version","ite\ 714 ms":{"test":{"ETag":"1382694048000","Content-Type":"application/json; \ 715 charset=UTF-8","Content-Length":106}}} 717 If the GET URL would have been a non-existing document like 718 "/storage/michiel/myfavoritedrinks/x", the response would have a 404 719 Not Found status, and no ETag header: 721 HTTP/1.1 404 Not Found 722 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 724 12.8. DELETE 726 A DELETE request may look like this: 728 DELETE /storage/michiel/myfavoritedrinks/test HTTP/1.1 729 Host: 3pp.io:4439 730 Origin: https://drinks-unhosted.5apps.com 731 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 732 Content-Type: application/json; charset=UTF-8 733 Referer: https://drinks-unhosted.5apps.com/? 734 If-Match: "1382694045000" 736 And the server may respond with a 412 Conflict or a 200 OK status: 738 HTTP/1.1 412 Conflict 739 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 740 ETag: "1382694048000" 742 13. Distributed versioning 744 This section is non-normative, and is intended to explain some of 745 the design choices concerning ETags and folder listings. At the 746 same time it will hopefully help readers who intend to develop an 747 application that uses remoteStorage as its per-user data storage. 748 When multiple clients have read/write access to the same document, 749 versioning conflicts may occur. For instance, client A may make 750 a PUT request that changes the document from version 1 to version 751 2, after which client B may make a PUT request attempting to change 752 the same document from version 1 to version 3. 754 In this case, client B can add an 'If-Match: "1"' header, which 755 would trigger a 412 Conflict response code, since the current 756 version ("2") does not match the version required as a condition by 757 the header If-Match header ("1"). 759 Client B is now aware of the conflict, and may consult the user, 760 saying the update to version 3 failed. The user may then choose, 761 through the user interface of client B, whether version 2 or 762 version 3 should be kept, or maybe the document should be reverted 763 on the server to version 1, or a merged version 4 is needed. Client 764 B may then make a request that puts the document to the version the 765 user wishes; this time setting an 'If-Match: "2"' header instead. 767 Both client A and client B would periodically poll the root 768 folder of each scope they have access to, to see if the version 769 of the root folder changed. If it did, then one of the versions 770 listed in there will necessarily have changed, and the client can 771 make a GET request to that child folder or document, to obtain 772 its latest version. 774 Because an update in a document will result in a version change of 775 its containing folder, and that change will propagate all the way 776 to the root folder, it is not necessary to poll each document for 777 changes individually. 779 As an example, the root folder may contain 10 directories, 780 each of which contain 10 directories, which each contain 10 781 documents, so their paths would be for instance '/0/0/1', '/0/0/2', 782 etcetera. Then one GET request to the root folder '/' will be 783 enough to know if any of these 1000 documents has changed. 785 Say document '/7/9/2' has changed; then the GET request to '/' will 786 come back with a different ETag, and entry '7/' will have a 787 different value in its JSON content. The client could then request 788 '/7/', '/7/9/', and '/7/9/2' to narrow down the one document that 789 caused the root folder's ETag to change. 791 Note that the remoteStorage server does not get involved in the 792 conflict resolution. It keeps the canonical current version at all 793 times, and allows clients to make conditional GET and PUT requests, 794 but it is up to whichever client discovers a given version 795 conflict, to resolve it. 797 14. Security Considerations 799 To prevent man-in-the-middle attacks, the use of https instead of 800 http is important for both the interface itself and all end-points 801 involved in WebFinger, OAuth, and (if present) the storage-first 802 application launch dashboard. 804 A malicious party could link to an application, but specifying a 805 remoteStorage account address that it controls, thus tricking the 806 user into using a trusted application to send sensitive data to the 807 wrong remoteStorage server. To mitigate this, applications SHOULD 808 clearly display to which remoteStorage server they are sending the 809 user's data. 811 Applications could request scopes that the user did not intend to 812 give access to. The user SHOULD always be prompted to carefully 813 review which scopes an application is requesting. 815 An application may upload malicious html pages and then trick the 816 user into visiting them, or upload malicious client-side scripts, 817 that take advantage of being hosted on the user's domain name. The 818 origin on which the remoteStorage server has its interface SHOULD 819 therefore NOT be used for anything else, and the user SHOULD be 820 warned not to visit any web pages on that origin. In particular, the 821 OAuth dialog and launch dashboard or token revokation interface 822 SHOULD be on a different origin than the remoteStorage interface. 824 Where the use of bearer tokens is impractical, a user may choose to 825 store documents on hard-to-guess URLs whose path after 826 starts with '/public/', while sharing this URL only 827 with the intended audience. That way, only parties who know the 828 document's hard-to-guess URL, can access it. The server SHOULD 829 therefore make an effort to detect and stop brute-force attacks that 830 attempt to guess the location of such documents. 832 The server SHOULD also detect and stop denial-of-service attacks 833 that aim to overwhelm its interface with too much traffic. 835 15. IANA Considerations 837 This document registers the 'remotestorage' link relation, as well 838 as the following WebFinger properties: 839 * "http://remotestorage.io/spec/version" 840 * "http://tools.ietf.org/html/rfc6749#section-4.2" 841 * "http://tools.ietf.org/html/rfc6750#section-2.3" 842 * "http://tools.ietf.org/html/rfc7233" 843 * "http://remotestorage.io/spec/web-authoring" 845 16. Acknowledgements 847 The authors would like to thank everybody who contributed to the 848 development of this protocol, including Kenny Bentley, Javier Diaz, 849 Daniel Groeber, Bjarni Runar, Jan Wildeboer, Charles Schultz, Peter 850 Svensson, Valer Mischenko, Michiel Leenaars, Jan-Christoph 851 Borchardt, Garret Alfert, Sebastian Kippe, Max Wiehle, Melvin 852 Carvalho, Martin Stadler, Geoffroy Couprie, Niklas Cathor, Marco 853 Stahl, James Coglan, Ken Eucker, Daniel Brolund, elf Pavlik, Nick 854 Jennings, Markus Sabadello, Steven te Brinke, Matthias Treydte, 855 Rick van Rein, Mark Nottingham, Julian Reschke, and Markus 856 Lanthaler, among many others. 858 17. References 860 17.1. Normative References 862 [WORDS] 863 Bradner, S., "Key words for use in RFCs to Indicate Requirement 864 Levels", BCP 14, RFC 2119, March 1997. 866 [IRI] 867 Duerst, M., "Internationalized Resource Identifiers (IRIs)", 868 RFC 3987, January 2005. 870 [WEBFINGER] 871 Jones, P., Salguerio, G., Jones, M, and Smarr, J., 872 "WebFinger", RFC7033, September 2013. 874 [OAUTH] 875 "Section 4.2: Implicit Grant", in: Hardt, D. (ed), "The OAuth 876 2.0 Authorization Framework", RFC6749, October 2012. 878 17.2. Informative References 880 [HTTPS] 881 Rescorla, E., "HTTP Over TLS", RFC2818, May 2000. 883 [HTTP] 884 Fielding et al., "Hypertext Transfer Protocol (HTTP/1.1): 885 Semantics and Content", RFC7231, June 2014. 887 [COND] 888 Fielding et al., "Hypertext Transfer Protocol (HTTP/1.1): 889 Conditional Requests", RFC7232, June 2014. 891 [RANGE] 892 Fielding et al., "Hypertext Transfer Protocol (HTTP/1.1): 893 Conditional Requests", RFC7233, June 2014. 895 [SPDY] 896 Mark Belshe, Roberto Peon, "SPDY Protocol - Draft 3.1", http:// 897 www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1, 898 September 2013. 900 [JSON-LD] 901 M. Sporny, G. Kellogg, M. Lanthaler, "JSON-LD 1.0", W3C 902 Proposed Recommendation, 903 http://www.w3.org/TR/2014/REC-json-ld-20140116/, January 2014. 905 [CORS] 906 van Kesteren, Anne (ed), "Cross-Origin Resource Sharing -- 907 W3C Candidate Recommendation 29 January 2013", 908 http://www.w3.org/TR/cors/, January 2013. 910 [MANIFEST] 911 Mozilla Developer Network (ed), "App manifest -- Revision 912 330541", https://developer.mozilla.org/en- 913 US/Apps/Build/Manifest$revision/566677, April 2014. 915 [DATASTORE] 916 "WebAPI/DataStore", MozillaWiki, retrieved May 2014. 917 https://wiki.mozilla.org/WebAPI/DataStore#Manifest 919 [KERBEROS] 920 C. Neuman et al., "The Kerberos Network Authentication Service 921 (V5)", RFC4120, July 2005. 923 [BEARER] 924 M. Jones, D. Hardt, "The OAuth 2.0 Authorization Framework: 925 Bearer Token Usage", RFC6750, October 2012. 927 [AUTHORING] 928 "Using remoteStorage for web authoring", reSite wiki, retrieved 929 September 2014. https://github.com/michielbdejong/resite/wiki 930 /Using-remoteStorage-for-web-authoring 932 18. Authors' addresses 934 Michiel B. de Jong 935 IndieHosters 937 Email: michiel@michielbdejong.com 939 F. Kooman 940 (independent) 942 Email: fkooman@tuxed.net