Wot-thing-description: Clarifying use of multiple security schemes in the security term

Created on 14 May 2020  路  15Comments  路  Source: w3c/wot-thing-description

The specification allows for the security to be an array of strings and example 11 shows this. However, there is no evidence of this feature being used in the testing inputs and also the explanation in the specification is not very clear. I am guessing that this pattern was taken from OpenAPI but we should explain this better and experiment in the Plugfests.

Also, what happens if there are conflicting security schemes described in the array?

Security

Most helpful comment

it still not clear to me how the Example 11 should be consumed and also the existence of a "feature" that no one actually uses bothers me.

True, it is not clear. I can make two guesses:

  • The request to the thing should carry information for digest authentication at the proxy (in header) and well as a bearer token for the thing itself (also in header). This doesn't work if both use Authorization header.
  • The request to the proxy requires digest authentication and the proxy creates a request to the thing with bearer token internally. If this is the case, the security schema of the thing is only relevant to the proxy and not the end-user consumer, so it is not needed as an AND scheme.

Do you want to say that it would be better to apply an OR scheme, which would reduce the TD complexity/size here?

Yes, it would reduce the size and make it clearer. In fact, without reading the specs document, it is hard to guess that the array is to apply an AND scheme (and in which order) rather than OR.

This is a breaking change, but in my opinion, array type is more suitable for OR and if required, a dictionary may be used to specify an AND scheme, e.g.: "security": {"scheme1": { "scheme2": {}}}.

All 15 comments

@mmccool any inputs on this?

In OpenAPI, the security schemes array is applied in an OR fashion (see step 2). But TD applies them in an AND fashion.:

Here is a more complex example: a TD snippet showing digest authentication on a proxy combined with bearer token authentication on the Thing.
https://www.w3.org/TR/wot-thing-description/#security-digest-example

In other words, the use of different security configurations within multiple forms provides a way to combine security mechanisms in an "OR" fashion. In contrast, putting multiple security configurations in the same security member combines them in an "AND" fashion, since in that case they would all need to be satisfied to allow activation of the Interaction Affordance.
https://www.w3.org/TR/wot-thing-description/#example-14

Apart from conflicting schemes (e.g. basic and various types of bearer tokens passed as Authorization header), the specification leads to very large TDs when each endpoint supports few security schemes. For example, an API secured with OAuth2 may support few flows, as offered by the authorization server. The only way to specify those is to repeat each form instance for every supported flow:

{
    ...
    "securityDefinitions": {
        "oauth2_code": {
            "scheme": "oauth2",
            "flow": "code",
            ...
        },
        "oauth2_client": {
            "scheme": "oauth2",
            "flow": "clientCredentials",
            ...
        },
        "oauth2_password": {
            "scheme": "oauth2",
            "flow": "password",
            ...
        }
    },
    "security": ["oauth2_code"],
    ...
    "properties": {
        "status": {
            ...
            "forms": [{
                "href": "https://scopes.example.com/status"
            },{
                "href": "https://scopes.example.com/status",
                "security": ["oauth2_client"]
            },{
                "href": "https://scopes.example.com/status",
                "security": ["oauth2_password"]
            }]
        }
    },
    ...
}

@farshidtz thanks for pointing out at the correct places. This partly solves the issue since these paragraphs explain it quite well. However, it still not clear to me how the Example 11 should be consumed and also the existence of a "feature" that no one actually uses bothers me.

Apart from conflicting schemes (e.g. basic and various types of bearer tokens passed as Authorization header), the specification leads to very large TDs when each endpoint supports few security schemes. For example, an API secured with OAuth2 may support few flows, as offered by the authorization server. The only way to specify those is to repeat each form instance for every supported flow:

Do you want to say that it would be better to apply an OR scheme, which would reduce the TD complexity/size here?

it still not clear to me how the Example 11 should be consumed and also the existence of a "feature" that no one actually uses bothers me.

True, it is not clear. I can make two guesses:

  • The request to the thing should carry information for digest authentication at the proxy (in header) and well as a bearer token for the thing itself (also in header). This doesn't work if both use Authorization header.
  • The request to the proxy requires digest authentication and the proxy creates a request to the thing with bearer token internally. If this is the case, the security schema of the thing is only relevant to the proxy and not the end-user consumer, so it is not needed as an AND scheme.

Do you want to say that it would be better to apply an OR scheme, which would reduce the TD complexity/size here?

Yes, it would reduce the size and make it clearer. In fact, without reading the specs document, it is hard to guess that the array is to apply an AND scheme (and in which order) rather than OR.

This is a breaking change, but in my opinion, array type is more suitable for OR and if required, a dictionary may be used to specify an AND scheme, e.g.: "security": {"scheme1": { "scheme2": {}}}.

It seems that this example was added by @mkovatsc at https://github.com/w3c/wot-thing-description/commit/44f12a941158242214a58a3573753ebb7152d9ae#diff-faaf197597ef2ac989637580d2d46fc4R1832 (you need to expand to go to the line). After reading your comments @farshidtz , I think that it is the first option where you need one type of credentials to connect to the proxy and another one to send requests to the Thing, thus really requiring two security access mechanism to be used at the same time for one interaction. @mkovatsc is this the intended use?

There were two reasons for the AND:

  1. To support separate schemes for a proxy and the endpoint. These can always be told apart since proxy schemes have a "proxy" attribute set. Note that the use of securityDefinitions means that both the proxy and the endpoint can use the same scheme type (but different credentials) without a problem. I thought we did have a test case for this, btw, but I'll have to look through the archives again.
  2. To support endpoints that use multiple schemes. At the time we were thinking in particular of object security schemes (which unfortunately did not make it into the final spec, but we might want to add them back in the future) combined with some other kind of endpoint authentication. In general schemes might describe different aspects of security (encryption, authentication, authorization) and the AND idea was to say that you had to satisfy them all. The OR is handled by activating different schemes in different forms.

So... I'd rather not change this without a really good reason since it would be a breaking change and also would not be an OBVIOUS breaking change, so older TDs might get misinterpreted. If we do want "OR" semantics maybe we can use some other JSON structure (eg an object) to specify it, while keeping the AND.

For use-case 1 above maybe we should also split that out, e.g. have a separate "proxySecurity" field. In that case "security" could be limited to a single value (getting rid of "AND") and then maybe we can add some way to combine schemes as part of the securityDefinitions (if it is needed, which we can discuss).

I'll have to look at the "complexity explosion" problem noted above in more detail later. The intent though is that you would not list all possible combinations, just the ones used by the implementation.

I'll study the problem some more and also bring it up in the Security TF call. At the very least we probably should clarify the intended use and rationale in the TD specification.

One very common use case for an OR scheme is to allow multiple OAuth 2.0 flows, as in the example above. I'll create another issue to propose the extension of OAuth2SecurityScheme for this purpose.

Discussion in security call:

  1. Seems there are use cases for both OR and AND
  2. OAuth2 issue can be resolved by having multiple flows internally, but this solves the problem only for oauth2
  3. There seems to be a general lack of discussion on formal models to combine security schemes
  4. The proxy use case could be handled a different way, e.g. by adding a "proxySecurity" field
  5. It would be nice to have a solution that maintains backward compatibility.

So given 1... and the desire in 5... is there a backward-compatible way to make the "OR" combinations less redundant?

Right now, this gives AND:

"security": ["sc1","sc2"]

and OR is expressed by having other options in other forms:

"security: ["sc3"]

Suppose we want to express ("sc1" AND "sc2") OR "sc3"

Option:

  1. Array of arrays: [["sc1","sc2"],"sc3"]. Problem: nesting depth changes AND to OR; special rule that array of one element can be treated as a string may not work
  2. Wrapper object: { "and": ["sc1","sc2"], "or": "sc3" }. Breaks compatibility.
  3. Farshid's suggestion above: {"scheme1": { "scheme2": {}}}. This is like a LISP CADR list... breaks compatibility.
  4. Another option would be to define "or" (and maybe "and" for completeness) schemes in "securityDefinitions":
     "securityDefinitions": {
          "sc1": ....
          "sc2": ...
          "sc3": ...
          "sc4": {
                "scheme": "and",
                "args": ["sc1","sc2"]
           },
           "sc5": {
                 "scheme": "or",
                 "args": ["sc4","sc3"]
           }
      }

Note: technically the "and" scheme is not required, but it makes deep expressions not-annoying.

As I said in today's call option 4 it is the best one. I wonder if we can compact it even further by allowing that the args array can also contain SecurityDefinition objects. For example:

"securityDefinitions": {
      "sc3": ...
       "sc5": {
             "scheme": "or",
             "args": [{
                      "scheme": "and",
                       "args": [{/* sc1 object */}, { /* sc2 objec */ }]
                }, "sc3"]
       }
  }

This way we give the option to define or not intermediate place holder security schemas (if the schema is not used standalone), leading to a more compact TD. However, it is harder to parse (and read).

+1 for option 4, it also allows combinations of AND and OR.

Moreover, as mentioned in the call, I suggest deprecating array type for security so that implementations stop using it (if ever started) and it could be removed in future versions of TD spec. Having two ways to define AND adds unnecessary complexity to specs and for consumers. This conversation also signifies that array for AND is confusing even when the spec describes it as so, especially for those familiar with OpenAPI. From a usability point of view, the TD can benefit from being descriptive on its own; not every developer is going to read the spec in details, if at all.

  1. Wrapper object: { "and": ["sc1","sc2"], "or": "sc3" }. Breaks compatibility.

Does this still break compatibility if the array type is kept for AND? The security attribute type could be: string or array of string or object.

For OAuth2 schemes, we should also consider that each scheme may have an scopes array. We need to clarify how the AND/OR operation applies to the scopes (union/intersection?)

This is important for validation, because:

The (scopes) values associated with a form should be chosen from those defined in an OAuth2SecurityScheme active on that form.

https://www.w3.org/TR/wot-thing-description/#oauth2securityscheme

Discussed in security call Aug 10:

  1. Proposed proceeding with a PR for option 4 so we can look at all the details, such as how to deal with multiple OAuth2 flows under AND/OR, scopes, etc. (McCool to do).
  2. Would like to target TD 1.1 with this PR as adding new schemes should be backward-compatible.

Separately, let's consider:

  1. Whether the array-of-flows suggestion can be made compatible with this approach or if we should drop it.
  2. More compact notations for AND/OR.
  3. Consider whether this would work if we allow anonymous definitions inside "security" (rather than named definitions) or would conflict with option 2 (this is a future possibility that I would rather not preclude...):
      "securityDefinitions": { "ns": { "scheme": "nosec"} }
      "security": [ "ns" ]

Instead, would like to be able to use:

      "security": [{"scheme": "nosec"}]

which can be further shortened to

      "security": {"scheme": "nosec"}

using the one-element-is-the-same-as-an-array-of-one-element rule.
Note this would be for TD 2.0, as it would break compatibility (such a TD would no longer validate as a 1.0 TD) and also makes the parser slightly more complicated (note also that in the current core profiles draft there is a rule that arrays can only have one type in them)

I would also support the more compact form, the current form gets criticism from early adopters for being too verbose and it is not very clear how it should be used. Also in research papers where every line counts, making the TD listings shorter would help me :blush:

which can be further shortened to
"security": {"scheme": "nosec"}

Yes please!

OK, I created a PR for the AND/OR scheme combiner: https://github.com/w3c/wot-thing-description/pull/944 That should take care of more complex use cases. Note my actual PR uses anyOf/allOf terms like in data schemas.

Since @benfrancis and @egekorkan were also keen to see the simplified usage with anonymous in-line definitions also being supported (supporting simple use cases), I'll make a separate PR for that as well (edit: done; see https://github.com/w3c/wot-thing-description/pull/945).

I don't think these two PRs will be in conflict, so we can (probably) do both.

Was this page helpful?
0 / 5 - 0 ratings