idnits 2.17.1 draft-dejong-remotestorage-05.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 (18 June 2015) is 3206 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-05 IndieHosters 3 F. Kooman 4 Intended Status: Proposed Standard (independent) 5 Expires: 30 November 2015 18 June 2015 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 30 November 2015. 36 Copyright Notice 38 Copyright (c) 2015 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 If not exactly one Content-Type header was received as part of a 248 PUT request, the Content-Type header value contains non-ASCII 249 characters, or it is unreasonably long, the server MAY refuse to 250 process the request, and instead respond with a descriptive error 251 message in the body, and a http response code from the 4xx range. 253 The response MUST contain a strong ETag header, with the document's 254 new version (for instance a hash of its contents) as its value. 256 A successful DELETE request to a document MUST result in: 258 * the deletion of that document from the storage, and from its 259 parent folder, 260 * silent deletion of the parent folder if it is left empty by 261 this, and so on for further ancestor folders, 262 * the version of its parent folder being updated, as well as that 263 of further ancestor folders. 265 A successful OPTIONS request SHOULD be responded to as described in 266 the CORS section below. 268 A successful HEAD request SHOULD be responded to like to the 269 equivalent GET request, but omitting the response body. 271 5. Response codes 273 Response codes SHOULD be given as defined by [HTTP, section 6] and 274 [BEARER, section 3.1]. The following is a non-normative checklist 275 of status codes that are likely to occur in practice: 277 * 500 if an internal server error occurs, 278 * 429 if the client makes too frequent requests or is suspected 279 of malicious activity, 280 * 414 if the request URI is too long, 281 * 416 if Range requests are supported by the server and the Range 282 request can not be satisfied, 283 * 401 for all requests that don't have a bearer token with 284 sufficient permissions, 285 * 404 for all DELETE and GET requests to documents that do not 286 exist on the storage, 287 * 304 for a conditional GET request whose pre-condition 288 fails (see "Versioning" below), 289 * 409 for a PUT request where any folder name in the path 290 clashes with an existing document's name at the same 291 level, or where the document name coincides with an 292 existing folder's name at the same level. 293 * 412 for a conditional PUT or DELETE request whose pre-condition 294 fails (see "Versioning" below), 295 * 507 in case the account is over its storage quota, 296 * 4xx for all malformed requests (e.g. foreign characters in the 297 path), as well as for all PUT and DELETE requests to 298 folders, 299 * 2xx for all successful requests. 301 Clients SHOULD also handle the case where a response takes too long 302 to arrive, or where no response is received at all. 304 6. Versioning 306 All successful requests MUST return an 'ETag' header [HTTP] with, in 307 the case of GET, the current version, in the case of PUT, the new 308 version, and in case of DELETE, the version that was deleted. All 309 successful GET requests MUST return an 'Expires: 0' header. PUT and 310 DELETE requests MAY have an 'If-Match' request header [COND], and 311 MUST fail with a 412 response code if that doesn't match the 312 document's current version. 314 GET requests MAY have a comma-separated list of revisions in an 315 'If-None-Match' header [COND], and SHOULD be responded to with a 304 316 response if that list includes the document or folder's current 317 version. A PUT request MAY have an 'If-None-Match: *' header [COND], 318 in which case it MUST fail with a 412 response code if the document 319 already exists. 321 In all 'ETag', 'If-Match' and 'If-None-Match' headers, revision 322 strings should appear inside double quotes ("). 324 A provider MAY offer version rollback functionality to its users, 325 but this specification does not define the user interface for that. 327 7. CORS headers 329 All responses MUST carry CORS headers [CORS]. The server MUST also 330 reply to OPTIONS requests as per CORS. For GET requests, a wildcard 331 origin MAY be returned, but for PUT and DELETE requests, the 332 response MUST echo back the Origin header sent by the client. 334 8. Session description 336 The information that a client needs to receive in order to be able 337 to connect to a server SHOULD reach the client as described in the 338 'bearer token issuance' sections below. It consists of: 340 * , consisting of 'https://' followed by a server 341 host, and optionally a server port and a path prefix as per 342 [IRI]. Examples: 343 * 'https://example.com' (host only) 344 * 'https://example.com:8080' (host and port) 345 * 'https://example.com/path/to/storage' (host, port and 346 path prefix; note there is no trailing slash) 347 * as per [OAUTH]. The token SHOULD be hard to 348 guess and SHOULD NOT be reused from one client to another. It 349 can however be reused in subsequent interactions with the same 350 client, as long as that client is still trusted. Example: 352 * 'ofb24f1ac3973e70j6vts19qr9v2eei' 353 * , always 'draft-dejong-remotestorage-05' for this 354 alternative version of the specification. 356 The client can make its requests using https with CORS and bearer 357 tokens, to the URL that is the concatenation of with 358 '/' plus one or more '/' strings indicating a path in the 359 folder tree, followed by zero or one strings, indicating 360 a document. For example, if is 361 "https://storage.example.com/bob", then to retrieve the folder 362 contents of the /public/documents/ folder, or to retrieve a 363 'draft.txt' document from that folder, the client would make 364 requests to, respectively: 366 * https://storage.example.com/bob/public/documents/ 367 * https://storage.example.com/bob/public/documents/draft.txt 369 9. Bearer tokens and access control 371 A bearer token represents one or more access scopes. These access 372 scopes are represented as strings of the form , 373 where the string SHOULD be lower-case alphanumerical, other 374 than the reserved word 'public', and can be ':r' or ':rw'. 375 The access the bearer token gives is the sum of its access scopes, 376 with each access scope representing the following permissions: 378 '*:rw') any request, 380 '*:r') any GET or HEAD request, 382 ':rw') any requests to paths that start with 383 '/' '/' or '/public/' '/', 385 ':r') any GET or HEAD requests to paths that start with 386 '/' '/' or '/public/' '/', 388 As a special exceptions, GET requests to a document (but not a 389 folder) whose path starts with '/public/' are always allowed. They, 390 as well as OPTIONS requests, can be made without a bearer token. 391 Unless [KERBEROS] is used (see section 10 below), all other requests 392 SHOULD present a bearer token with sufficient access scope, using a 393 header of the following form (no double quotes here): 395 Authorization: Bearer 397 In addition, providing the access token via a HTTP query parameter 398 for GET requests MAY be supported by the server, although its use 399 is not recommended, due to its security deficiencies; see [BEARER, 400 section 2.3]. 402 10. Application-first bearer token issuance 404 To make a remoteStorage server available as 'the remoteStorage of 405 at ', exactly one link of the following format 406 SHOULD be added to the WebFinger record [WEBFINGER] of at 407 : 409 { 410 "href": , 411 "rel": "http://tools.ietf.org/id/draft-dejong-remotestorage", 412 "properties": { 413 "http://remotestorage.io/spec/version": , 414 "http://tools.ietf.org/html/rfc6749#section-4.2": , 415 ... : ... , 416 } 417 } 419 Here and are as per "Session 420 description" above, and SHOULD be either null or a 421 URL where an OAuth 2.0 implicit-grant flow dialog [OAUTH] is 422 presented. 424 If is a URL, the user can supply their credentials 425 for accessing the account (how, is out of scope), and allow or 426 reject a request by the connecting application to obtain a bearer 427 token for a certain list of access scopes. Note that an account 428 will often belong to just one human user, but may also belong to a 429 group of multiple users (the remoteStorage of at ). 431 If is null, the client will not have a way to obtain 432 an access token, and SHOULD send all requests without Authorization 433 header, and rely on Kerberos [KERBEROS] instead for requests that 434 would normally be sent with a bearer token, but servers SHOULD NOT 435 impose any such access barriers for resources that would normally 436 not require an access token. 438 The '...' ellipses indicate that more properties may be present. 439 Non-breaking examples that have been proposed so far, include a 440 "http://tools.ietf.org/html/rfc6750#section-2.3" property, set to 441 the string value "true" if the server supports passing the bearer 442 token in the URI query parameter as per section 2.3 of [BEARER], 443 instead of in the request header. 445 Another example is "http://tools.ietf.org/html/rfc7233" with a 446 string value of "GET" if Content-Range headers are supported for 447 GET requests as per [RANGE], "PUT" if they are supported for PUT 448 requests, and "GET,PUT" if supported for both. 450 Both these proposals are non-breaking extensions, since the client 451 will have a way to work around it if these features are not present 452 (e.g. retrieve the protected resource asynchronously in the first 453 case, or request the entire resource in the second case). 455 A "http://remotestorage.io/spec/web-authoring" property has been 456 proposed with a string value of the fully qualified domain name to 457 which web authoring content is published if the server supports web 458 authoring as per [AUTHORING]. Note that this extension is a breaking 459 extension in the sense that it divides users into "haves", whose 460 remoteStorage accounts allow them to author web content, and 461 "have-nots", whose remoteStorage account does not support this 462 functionality. 464 The server MAY expire bearer tokens, and MAY require the user to 465 register applications as OAuth clients before first use; if no 466 client registration is required, then the server MAY ignore the 467 client_id parameter in favor of relying on the redirect_uri 468 parameter for client identification. 470 11. Storage-first bearer token issuance 472 The provider MAY also present a dashboard to the user, where they 473 have some way to add open web app manifests [MANIFEST]. Adding a 474 manifest to the dashboard is considered equivalent to clicking 475 'accept' in the dialog of the application-first flow. Removing one 476 is considered equivalent to revoking its access token. 478 As an equivalent to OAuth's 'scope' parameter, a 'datastores-access' 479 field SHOULD be present in the root of such an application manifest 480 document, with entries -> '{"access": "readonly"}' for 481 'r' or '{"access": "readwrite"}' for 'rw', as 482 prescribed in [DATASTORE]. 484 When the user gestures they want to use a certain application whose 485 manifest is present on the dashboard, the dashboard SHOULD redirect 486 to the application or open it in a new window. To mimic coming back 487 from the OAuth dialog, it MAY add 'access_token' and 'scope' 488 fields to the URL fragment. 490 Regardless of whether 'access_token' and 'scope' are specified, it 491 SHOULD add a 'remotestorage' field to the URL fragment, with a 492 value of the form '@' . When the application detects 493 this parameter, it SHOULD resolve the WebFinger record for 494 at and extract the and 495 information. 497 If no access_token was given, then the application SHOULD also 498 extract the information from WebFinger, and continue 499 as per application-first bearer token issuance. 501 Note that whereas a remoteStorage server SHOULD offer support for 502 the application-first flow with WebFinger and OAuth, it MAY choose 503 not to support the storage-first flow, provided that users will 504 easily remember their '@' WebFinger address at that 505 provider. Applications SHOULD, however, support both flows, which 506 means checking the URL for a 'remotestorage' parameter, but giving 507 the user a way to specify the WebFinger address if there is none. 509 If a server provides an application manifest dashboard, then it 510 SHOULD merge the list of applications there with the list of 511 issued access tokens as specified by OAuth into one list. Also, 512 the interface for revoking an access token as specified by OAuth 513 SHOULD coincide with removing an application from the dashboard. 515 Servers MAY also provide a way to create access tokens directly from 516 their user interface. Such functionality would be aimed mainly at 517 developers, to manually copy and paste a token into a script or 518 debug tool, thus bypassing the need for an OAuth dance. Clients 519 SHOULD NOT rely on this in production. 521 12. Example wire transcripts 523 The following examples are not normative ("\" indicates a line was 524 wrapped). 526 12.1. WebFinger 528 In application-first, an in-browser application might issue the 529 following request, using XMLHttpRequest and CORS: 531 GET /.well-known/webfinger?resource=acct:michiel@michielbdejon\ 532 g.com HTTP/1.1 533 Host: michielbdejong.com 535 and the server's response might look like this: 537 HTTP/1.1 200 OK 538 Access-Control-Allow-Origin: * 539 Access-Control-Allow-Methods: GET 540 Access-Control-Allow-Headers: If-Match, If-None-Match 541 Access-Control-Expose-Headers: ETag, Content-Length 542 Content-Type: application/jrd+json 544 { 545 "links":[{ 546 "href": "https://michielbdejong.com:7678/inbox", 547 "rel": "post-me-anything" 548 }, { 549 "href": "https://michielbdejong.com/me.jpg", 550 "rel": "avatar" 551 }, { 552 "href": "https://3pp.io:4439/storage/michiel", 553 "rel": "http://tools.ietf.org/id/draft-dejong-remotestorag\ 554 e", 555 "properties": { 556 "http://remotestorage.io/spec/version": "draft-dejong-re\ 557 motestorage-05", 558 "http://tools.ietf.org/html/rfc6749#section-4.2": "https\ 559 ://3pp.io:4439/oauth/michiel", 560 "http://tools.ietf.org/html/rfc6750#section-2.3": false, 561 "http://tools.ietf.org/html/rfc7233": false, 562 "http://remotestorage.io/spec/web-authoring": false 563 } 564 }] 565 } 567 12.2. OAuth dialog form 569 Once the in-browser application has discovered the server's OAuth 570 end-point, it will typically redirect the user to this URL, in 571 order to obtain a bearer token. Say the application is hosted on 572 https://drinks-unhosted.5apps.com/ and wants read-write access to 573 the account's "myfavoritedrinks" scope: 575 GET /oauth/michiel?redirect_uri=https%3A%2F%2Fdrinks-unhosted.5\ 576 apps.com%2F&scope=myfavoritedrinks%3Arw&client_id=https%3A%2F%2Fdrinks-\ 577 unhosted.5apps.com&response_type=token HTTP/1.1 578 Host: 3pp.io 580 The server's response might look like this (truncated for brevity): 582 HTTP/1.1 200 OK 584 585 586 587 Allow access? 588 ... 590 12.3. OAuth dialog form submission 592 When the user submits the form, the request would look something 593 like this: 595 POST /oauth HTTP/1.1 596 Host: 3pp.io:4439 597 Origin: https://3pp.io:4439 598 Content-Type: application/x-www-form-urlencoded 599 Referer: https://3pp.io:4439/oauth/michiel?redirect_uri=https%3\ 600 A%2F%2Fdrinks-unhosted.5apps.com%2F&scope=myfavoritedrinks%3Arw&client_\ 601 id=https%3A%2F%2Fdrinks-unhosted.5apps.com&response_type=token 603 client_id=https%3A%2F%2Fdrinks-unhosted.5apps.com&redirect_uri=\ 604 https%3A%2F%2Fdrinks-unhosted.5apps.com%2F&response_type=token&scope=my\ 605 favoritedrinks%3Arw&state=&username=michiel&password=something&allow=Al\ 606 low 608 To which the server could respond with a 302 redirect, back to the 609 origin of the requesting application: 611 HTTP/1.1 302 Found 612 Location:https://drinks-unhosted.5apps.com/#access_token=j2YnGt\ 613 XjzzzHNjkd1CJxoQubA1o%3D&token_type=bearer&state= 615 12.4. OPTIONS preflight 617 When an in-browser application makes a cross-origin request which 618 may affect the server-state, the browser will make a preflight 619 request first, with the OPTIONS verb, for instance: 621 OPTIONS /storage/michiel/myfavoritedrinks/ HTTP/1.1 622 Host: 3pp.io:4439 623 Access-Control-Request-Method: GET 624 Origin: https://drinks-unhosted.5apps.com 625 Access-Control-Request-Headers: Authorization 626 Referer: https://drinks-unhosted.5apps.com/ 628 To which the server can for instance respond: 630 HTTP/1.1 200 OK 631 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 632 Access-Control-Allow-Methods: GET, PUT, DELETE 633 Access-Control-Allow-Headers: Authorization, Content-Length, Co\ 634 ntent-Type, Origin, X-Requested-With, If-Match, If-None-Match 636 12.5. Initial PUT 638 An initial PUT may contain an 'If-None-Match: *' header, like this: 640 PUT /storage/michiel/myfavoritedrinks/test HTTP/1.1 641 Host: 3pp.io:4439 642 Content-Length: 91 643 Origin: https://drinks-unhosted.5apps.com 644 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 645 Content-Type: application/json; charset=UTF-8 646 Referer: https://drinks-unhosted.5apps.com/? 647 If-None-Match: * 649 {"name":"test","@context":"http://remotestorage.io/spec/modules\ 650 /myfavoritedrinks/drink"} 652 And the server may respond with either a 201 Created or a 200 OK 653 status: 655 HTTP/1.1 201 Created 656 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 657 ETag: "1382694045000" 659 12.6. Subsequent PUT 661 A subsequent PUT may contain an 'If-Match' header referring to the 662 ETag previously returned, like this: 664 PUT /storage/michiel/myfavoritedrinks/test HTTP/1.1 665 Host: 3pp.io:4439 666 Content-Length: 91 667 Origin: https://drinks-unhosted.5apps.com 668 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 669 Content-Type: application/json; charset=UTF-8 670 Referer: https://drinks-unhosted.5apps.com/? 671 If-Match: "1382694045000" 673 {"name":"test", "updated":true, "@context":"http://remotestorag\ 674 e.io/spec/modules/myfavoritedrinks/drink"} 676 And the server may respond with a 412 Conflict or a 200 OK status: 678 HTTP/1.1 200 OK 679 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 680 ETag: "1382694048000" 682 12.7. GET 684 A GET request would also include the bearer token, and optionally 685 an If-None-Match header: 687 GET /storage/michiel/myfavoritedrinks/test HTTP/1.1 688 Host: 3pp.io:4439 689 Origin: https://drinks-unhosted.5apps.com 690 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 691 Referer: https://drinks-unhosted.5apps.com/? 692 If-None-Match: "1382694045000", "1382694048000" 694 And the server may respond with a 304 Not Modified status: 696 HTTP/1.1 304 Not Modified 697 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 698 ETag: "1382694048000" 700 Or a 200 OK status, plus a response body: 702 HTTP/1.1 200 OK 703 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 704 Content-Type: application/json; charset=UTF-8 705 Content-Length: 106 706 ETag: "1382694048000" 707 Expires: 0 709 {"name":"test", "updated":true, "@context":"http://remotestora\ 710 ge.io/spec/modules/myfavoritedrinks/drink"} 712 If the GET URL would have been "/storage/michiel/myfavoritedrinks/", 713 a 200 OK response would have a folder description as the response 714 body: 716 HTTP/1.1 200 OK 717 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 718 Content-Type: application/ld+json 719 Content-Length: 171 720 ETag: "1382694048000" 721 Expires: 0 723 {"@context":"http://remotestorage.io/spec/folder-version","ite\ 724 ms":{"test":{"ETag":"1382694048000","Content-Type":"application/json; \ 725 charset=UTF-8","Content-Length":106}}} 727 If the GET URL would have been a non-existing document like 728 "/storage/michiel/myfavoritedrinks/x", the response would have a 404 729 Not Found status, and no ETag header: 731 HTTP/1.1 404 Not Found 732 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 734 12.8. DELETE 736 A DELETE request may look like this: 738 DELETE /storage/michiel/myfavoritedrinks/test HTTP/1.1 739 Host: 3pp.io:4439 740 Origin: https://drinks-unhosted.5apps.com 741 Authorization: Bearer j2YnGtXjzzzHNjkd1CJxoQubA1o= 742 Content-Type: application/json; charset=UTF-8 743 Referer: https://drinks-unhosted.5apps.com/? 744 If-Match: "1382694045000" 746 And the server may respond with a 412 Conflict or a 200 OK status: 748 HTTP/1.1 412 Conflict 749 Access-Control-Allow-Origin: https://drinks-unhosted.5apps.com 750 ETag: "1382694048000" 752 13. Distributed versioning 754 This section is non-normative, and is intended to explain some of 755 the design choices concerning ETags and folder listings. At the 756 same time it will hopefully help readers who intend to develop an 757 application that uses remoteStorage as its per-user data storage. 758 When multiple clients have read/write access to the same document, 759 versioning conflicts may occur. For instance, client A may make 760 a PUT request that changes the document from version 1 to version 761 2, after which client B may make a PUT request attempting to change 762 the same document from version 1 to version 3. 764 In this case, client B can add an 'If-Match: "1"' header, which 765 would trigger a 412 Conflict response code, since the current 766 version ("2") does not match the version required as a condition by 767 the header If-Match header ("1"). 769 Client B is now aware of the conflict, and may consult the user, 770 saying the update to version 3 failed. The user may then choose, 771 through the user interface of client B, whether version 2 or 772 version 3 should be kept, or maybe the document should be reverted 773 on the server to version 1, or a merged version 4 is needed. Client 774 B may then make a request that puts the document to the version the 775 user wishes; this time setting an 'If-Match: "2"' header instead. 777 Both client A and client B would periodically poll the root 778 folder of each scope they have access to, to see if the version 779 of the root folder changed. If it did, then one of the versions 780 listed in there will necessarily have changed, and the client can 781 make a GET request to that child folder or document, to obtain 782 its latest version. 784 Because an update in a document will result in a version change of 785 its containing folder, and that change will propagate all the way 786 to the root folder, it is not necessary to poll each document for 787 changes individually. 789 As an example, the root folder may contain 10 directories, 790 each of which contain 10 directories, which each contain 10 791 documents, so their paths would be for instance '/0/0/1', '/0/0/2', 792 etcetera. Then one GET request to the root folder '/' will be 793 enough to know if any of these 1000 documents has changed. 795 Say document '/7/9/2' has changed; then the GET request to '/' will 796 come back with a different ETag, and entry '7/' will have a 797 different value in its JSON content. The client could then request 798 '/7/', '/7/9/', and '/7/9/2' to narrow down the one document that 799 caused the root folder's ETag to change. 801 Note that the remoteStorage server does not get involved in the 802 conflict resolution. It keeps the canonical current version at all 803 times, and allows clients to make conditional GET and PUT requests, 804 but it is up to whichever client discovers a given version 805 conflict, to resolve it. 807 14. Security Considerations 809 To prevent man-in-the-middle attacks, the use of https instead of 810 http is important for both the interface itself and all end-points 811 involved in WebFinger, OAuth, and (if present) the storage-first 812 application launch dashboard. 814 A malicious party could link to an application, but specifying a 815 remoteStorage account address that it controls, thus tricking the 816 user into using a trusted application to send sensitive data to the 817 wrong remoteStorage server. To mitigate this, applications SHOULD 818 clearly display to which remoteStorage server they are sending the 819 user's data. 821 Applications could request scopes that the user did not intend to 822 give access to. The user SHOULD always be prompted to carefully 823 review which scopes an application is requesting. 825 An application may upload malicious html pages and then trick the 826 user into visiting them, or upload malicious client-side scripts, 827 that take advantage of being hosted on the user's domain name. The 828 origin on which the remoteStorage server has its interface SHOULD 829 therefore NOT be used for anything else, and the user SHOULD be 830 warned not to visit any web pages on that origin. In particular, the 831 OAuth dialog and launch dashboard or token revokation interface 832 SHOULD be on a different origin than the remoteStorage interface. 834 Where the use of bearer tokens is impractical, a user may choose to 835 store documents on hard-to-guess URLs [CAPABILITIES] whose path 836 after starts with '/public/', while sharing this URL 837 only with the intended audience. That way, only parties who know the 838 document's hard-to-guess URL, can access it. The server SHOULD 839 therefore make an effort to detect and stop brute-force attacks that 840 attempt to guess the location of such documents. 842 The server SHOULD also detect and stop denial-of-service attacks 843 that aim to overwhelm its interface with too much traffic. 845 15. IANA Considerations 847 This document registers the following WebFinger properties: 848 * "http://remotestorage.io/spec/version" 849 * "http://tools.ietf.org/html/rfc6749#section-4.2" 850 * "http://tools.ietf.org/html/rfc6750#section-2.3" 851 * "http://tools.ietf.org/html/rfc7233" 852 * "http://remotestorage.io/spec/web-authoring" 854 16. Acknowledgements 856 The authors would like to thank everybody who contributed to the 857 development of this protocol, including Kenny Bentley, Javier Diaz, 858 Daniel Groeber, Bjarni Runar, Jan Wildeboer, Charles Schultz, Peter 859 Svensson, Valer Mischenko, Michiel Leenaars, Jan-Christoph 860 Borchardt, Garret Alfert, Sebastian Kippe, Max Wiehle, Melvin 861 Carvalho, Martin Stadler, Geoffroy Couprie, Niklas Cathor, Marco 862 Stahl, James Coglan, Ken Eucker, Daniel Brolund, elf Pavlik, Nick 863 Jennings, Markus Sabadello, Steven te Brinke, Matthias Treydte, 864 Rick van Rein, Mark Nottingham, Julian Reschke, and Markus 865 Lanthaler, among many others. 867 17. References 868 17.1. Normative References 870 [WORDS] 871 Bradner, S., "Key words for use in RFCs to Indicate Requirement 872 Levels", BCP 14, RFC 2119, March 1997. 874 [IRI] 875 Duerst, M., "Internationalized Resource Identifiers (IRIs)", 876 RFC 3987, January 2005. 878 [WEBFINGER] 879 Jones, P., Salguerio, G., Jones, M, and Smarr, J., 880 "WebFinger", RFC7033, September 2013. 882 [OAUTH] 883 "Section 4.2: Implicit Grant", in: Hardt, D. (ed), "The OAuth 884 2.0 Authorization Framework", RFC6749, October 2012. 886 17.2. Informative References 888 [HTTPS] 889 Rescorla, E., "HTTP Over TLS", RFC2818, May 2000. 891 [HTTP] 892 Fielding et al., "Hypertext Transfer Protocol (HTTP/1.1): 893 Semantics and Content", RFC7231, June 2014. 895 [COND] 896 Fielding et al., "Hypertext Transfer Protocol (HTTP/1.1): 897 Conditional Requests", RFC7232, June 2014. 899 [RANGE] 900 Fielding et al., "Hypertext Transfer Protocol (HTTP/1.1): 901 Conditional Requests", RFC7233, June 2014. 903 [SPDY] 904 Mark Belshe, Roberto Peon, "SPDY Protocol - Draft 3.1", http:// 905 www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1, 906 September 2013. 908 [JSON-LD] 909 M. Sporny, G. Kellogg, M. Lanthaler, "JSON-LD 1.0", W3C 910 Proposed Recommendation, 911 http://www.w3.org/TR/2014/REC-json-ld-20140116/, January 2014. 913 [CORS] 914 van Kesteren, Anne (ed), "Cross-Origin Resource Sharing -- 915 W3C Candidate Recommendation 29 January 2013", 916 http://www.w3.org/TR/cors/, January 2013. 918 [MANIFEST] 919 Mozilla Developer Network (ed), "App manifest -- Revision 920 330541", https://developer.mozilla.org/en- 921 US/Apps/Build/Manifest$revision/566677, April 2014. 923 [DATASTORE] 924 "WebAPI/DataStore", MozillaWiki, retrieved May 2014. 925 https://wiki.mozilla.org/WebAPI/DataStore#Manifest 927 [KERBEROS] 928 C. Neuman et al., "The Kerberos Network Authentication Service 929 (V5)", RFC4120, July 2005. 931 [BEARER] 932 M. Jones, D. Hardt, "The OAuth 2.0 Authorization Framework: 933 Bearer Token Usage", RFC6750, October 2012. 935 [AUTHORING] 936 "Using remoteStorage for web authoring", reSite wiki, retrieved 937 September 2014. https://github.com/michielbdejong/resite/wiki 938 /Using-remoteStorage-for-web-authoring 940 [CAPABILITIES] 941 J. Tennison (ed.), "Good Practices for Capability URLs", 942 http://www.w3.org/TR/capability-urls/, February 2014. 944 18. Authors' addresses 946 Michiel B. de Jong 947 IndieHosters 949 Email: michiel@michielbdejong.com 950 F. Kooman 951 (independent) 953 Email: fkooman@tuxed.net