github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/docs/remote.md (about)

     1  [Table of contents](README.md#table-of-contents)
     2  
     3  # Proxy for remote data/API
     4  
     5  The client side applications in Cozy are constrained and cannot speak with
     6  external websites to avoid leaking personal data. Technically, it is made with
     7  the Content Security Policy. These rules are very strict and it would be a pity
     8  to not allow a client side app to load public informations from a source like
     9  Wikipedia. Our proposal is to make client side apps able to query external
    10  websites of their choices, but these requests will be made via the cozy-stack
    11  (as a proxy) and will be logged to check later that no personal data was leaked
    12  unintentionally. We can see the data loaded from the external website as a
    13  document with a doctype: it's just not a doctype local to our cozy, but a remote
    14  one.
    15  
    16  A client side app can request data from external websites by doing these three
    17  steps:
    18  
    19  1. Declaring a remote doctype and its requests
    20  2. Declaring permissions on these doctypes in the manifest
    21  3. Calling the `/remote` API of cozy-stack
    22  
    23  ## Declaring a remote doctype
    24  
    25  Doctypes are formalized in this repository:
    26  [github.com/cozy/cozy-doctypes](https://github.com/cozy/cozy-doctypes). Each
    27  doctype has its own directory inside the repository. For a remote doctype, it
    28  will include a file called `request` that will describe how the cozy-stack will
    29  request the external website.
    30  
    31  Let's take an example:
    32  
    33  ```
    34  $ tree cozy-doctypes
    35  cozy-doctypes
    36  ├── [...]
    37  ├── org.wikidata.entity
    38  │   └── request
    39  └── org.wikidata.search
    40      └── request
    41  
    42  $ cat cozy-doctypes/org.wikidata.entity/request
    43  GET https://www.wikidata.org/wiki/Special:EntityData/{{entity}}.json
    44  Accept: application/json
    45  
    46  $ cat cozy-doctypes/org.wikidata.search/request
    47  GET https://www.wikidata.org/w/api.php?action=wbsearchentities&search={{q}}&language=en&format=json
    48  ```
    49  
    50  Here, we have two remote doctypes. Each one has a request defined for it.
    51  
    52  The format for the request file is:
    53  
    54  -   the verb and the URL on the first line
    55  -   then some lines that describe the HTTP headers
    56  -   then a blank line and the body if the request is a POST
    57  
    58  The URL can only use the default port. But, for development and testing in
    59  local, this rule can be disabled with the `--remote-allow-custom-port` flag
    60  of the `cozy-stack serve` command.
    61  
    62  For the path, the query-string, the headers, and the body, it's possible to have
    63  some dynamic part by using `{{`, a variable name, and `}}`.
    64  
    65  Some templating helpers are available to escape specific variables using `{{`
    66  function name - space - variable name `}}`. These helpers are only available for
    67  the body part of the template.
    68  
    69  Available helpers:
    70  
    71  -   `json`: for json parts (`{ "key": "{{json val}}" }`)
    72  -   `html`: for html parts (`<p>{{html val}}</p>`)
    73  -   `query`: for query parameter of a url (`http://foobar.com?q={{query q}}`)
    74  -   `path`: for path component of a url (`http://foobar.com/{{path p}}`)
    75  
    76  Values injected in the URL are automatically URI-escaped based on the part they
    77  are included in: namely as a query parameter or as a path component.
    78  
    79  **Note**: by default, the User-Agent is set to a default value ("cozy-stack" and
    80  a version number). It can be overriden in the request description.
    81  
    82  Example:
    83  
    84  ```
    85  GET https://foobar.com/{{path}}?q={{query}}
    86  Content-Type: {{contentType}}
    87  
    88  {
    89    "key": "{{json value}}",
    90    "url": "http://anotherurl.com/{{path anotherPath}}?q={{query anotherQuery}}",
    91  }
    92  ```
    93  
    94  ## Declaring permissions
    95  
    96  Nothing special here. The client side app must declare that it will use these
    97  doctypes in its manifest, like for other doctypes:
    98  
    99  ```json
   100  {
   101      "...": "...",
   102      "permissions": {
   103          "search": {
   104              "description": "Required for searching on wikidata",
   105              "type": "org.wikidata.search"
   106          },
   107          "entity": {
   108              "description": "Required for getting more informations about an entity on wikidata",
   109              "type": "org.wikidata.entity"
   110          }
   111      }
   112  }
   113  ```
   114  
   115  ## Calling the remote API
   116  
   117  ### GET/POST `/remote/:doctype`
   118  
   119  The client side app must use the same verb as the defined request (`GET` in our
   120  two previous examples). It can use the query-string for GET, and a JSON body for
   121  POST, to give values for the variables.
   122  
   123  Example:
   124  
   125  ```http
   126  GET /remote/org.wikidata.search?q=Douglas+Adams HTTP/1.1
   127  Host: alice.cozy.localhost
   128  ```
   129  
   130  It is possible to send some extra informations, to make it easier to understand
   131  the request. If no variable in the request matches it, it won't be send to the
   132  remote website.
   133  
   134  Example:
   135  
   136  ```http
   137  POST /remote/org.example HTTP/1.1
   138  Host: alice.cozy.localhost
   139  Content-Type: application/json
   140  ```
   141  
   142  ```json
   143  {
   144      "query": "Qbhtynf Nqnzf",
   145      "comment": "query is rot13 for Douglas Adams"
   146  }
   147  ```
   148  
   149  **Note**: currently, only the response with a content-type that is an image,
   150  JSON or XML are accepted. Other content-types are blocked the time to evaluate
   151  if they are useful and their security implication (javascript is probably not
   152  something we want to allow).
   153  
   154  ### GET `/remote/_all_doctypes`
   155  
   156  This endpoint lists all the known remote doctypes. A permission on
   157  `io.cozy.doctypes` for `GET` is needed to query this endoint.
   158  
   159  Example:
   160  
   161  ```http
   162  GET /remote/_all_doctypes HTTP/1.1
   163  ```
   164  
   165  ```http
   166  HTTP/1.1 200 OK
   167  Content-Type: application/json
   168  ```
   169  
   170  ```json
   171  ["cc.cozycloud.dacc", "cc.cozycloud.errors", "org.wikidata.entity", "org.wikidata.search"]
   172  ```
   173  
   174  ### GET `/remote/assets/:asset-name`
   175  
   176  The client application can fetch a list of predefined assets via this route. The
   177  resources available are defined in the configuration file.
   178  
   179  Example:
   180  
   181  ```http
   182  GET /remote/assets/bank HTTP/1.1
   183  Host: alice.cozy.localhost
   184  ```
   185  
   186  ## Logs
   187  
   188  The requests are logged as the `io.cozy.remote.requests` doctype, with the
   189  doctype asked, the parameter (even those that have not been used, like `comment`
   190  in the previous example), and the application that has made the request.
   191  
   192  ## Secrets
   193  
   194  It is possible to make the stack inject a secret in a request. 
   195  
   196  For example, if we want to make the stack inject a `token` for 
   197  the request, then:
   198  
   199  1- We need to store the secret in CouchDB, in the 
   200  `secrets/io-cozy-remote-secrets` database. The document must 
   201  have the remote doctype as an id, and the secret in another field, 
   202  like this:
   203  
   204  ```json
   205  {
   206    "_id": "cc.cozycloud.foobar",
   207    "token": "1c7f3ba03bd801391a91543d7eb8149c"
   208  }
   209  ```
   210  
   211  2- Use this secret in the remote doctype declaration:
   212  
   213  ```http
   214  GET https://foobar.com/baz/ HTTP/1.1
   215  Authorization: Bearer {{secret_token}}
   216  ```
   217  
   218  You can see that we store `token` in the `io-cozy-remote-secret`
   219  but we use `secret_token` in the remote doctype declaration, this
   220  is a convention to follow. Like that, we quickly know what come 
   221  from secrets and what come from the caller. 
   222  
   223  If you use secret and you get a 400 Bad Request error 
   224  `a variable is used in the template, but no value was given` 
   225  check if you follow this convention. 
   226  
   227  ## For developers
   228  
   229  If you are a developer and you want to use a new remote doctype, it can be
   230  difficult to first make it available in the github.com/cozy/cozy-doctypes
   231  repository and only then test it. So, the cozy-stack serve command has a
   232  `--doctypes` option to gives a local directory with the doctypes. You can fork
   233  the repository, clone it, work on a new doctype inside, test it locally, and
   234  when OK, make a pull request for it.