Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Requesting resources with insufficient access #203

Closed
Denisthemalice opened this issue Feb 28, 2021 · 18 comments
Closed

Requesting resources with insufficient access #203

Denisthemalice opened this issue Feb 28, 2021 · 18 comments

Comments

@Denisthemalice
Copy link

The current text from Section 9.4 (Requesting Resources With Insufficient Access) states:

" If the client instance calls an RS without an access token, or with an invalid access token, the RS MAY respond
to the client instance with an authentication header indicating that GNAP needs to be used to access the resource".

The text omits to mention which HTTP error code should be returned.

The error codes from RFC 7231 and from RFC 7325 should be used, in particular:

400 Bad Request Section 6.5.1 of RFC 7231
401 Unauthorized Section 3.1 of RFC 7235
403 Forbidden Section 6.5.3 of RFC 7231
404 Not Found Section 6.5.4 of RFC 7231
405 Method Not Allowed Section 6.5.5 of RFC 7231
406 Not Acceptable Section 6.5.6 of RFC 7231

When a client instance calls an RS without an access token, or with an invalid access token different HTTP errors types may be returned, in particular:

401 "Unauthorized" which indicates an authentication error. It always includes a WWW-Authenticate header that describes how to authenticate.

403 "Forbidden" which indicates that the server understood the request but that the client instance does not have permission to access this resource, even if it has been authenticated. A server that wishes to make public why the request has been forbidden can describe that reason in the response payload (if any).

Note: A server that wishes to hide the existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found).

404 "Not Found" which indicates either that the server did not find a current representation for the target resource or that the server is not willing to disclose that one exists.

405 "Method not allowed" which indicates that the method received in the request-line is known by the server but not supported by the target resource. In that case, the server MUST generate an Allow header field containing a list of the target resource’s currently supported methods.

Discussion:

401 "Unauthorized", despite its name, is dedicated to authentication errors whereas 403 "Forbidden" is dedicated to authorization errors.

When the request is recognized by the server but sent "without an access token or with an invalid access token", the HTTP status 403 Forbidden should be used. If the server wants to make known why a request is forbidden, it can provide the reason in the payload.

This means that the use of an WWW-Authenticate header is not adequate in case of a HTTP status 403 Forbidden.

In the context of GNAP, we should define the elements that can appear in the response body of a 403 Forbidden error. Such elements would be a combination (AND or OR) of URLs for Authentication Servers (ASs) and for each of them a combination (AND or OR) of privileges types (i.e. attributes and/or rights), where attributes types may optionally be associated with attribute values.

@jricher
Copy link
Collaborator

jricher commented Feb 28, 2021

GNAP has absolutely no business defining the response body of an API that it is protecting. Let's say we define a JSON body, but the API being protected is in XML or ProtoBuf or CBOR. Any error responses we help define will need to be in the HTTP headers, as well as suggestions on response codes.

@Denisthemalice
Copy link
Author

When an authorization error is detected, the RS should respond with 403 Forbidden as defined in Section 6.5.3 of RFC 7231.

The full text is copied below.

6.5.3. 403 Forbidden

The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.
A server that wishes to make public why the request has been forbidden **can describe that reason in
the response payload (if any).

If authentication credentials were provided in the request, the server considers them insufficient to grant access.
The client SHOULD NOT automatically repeat the request with the same credentials. The client MAY repeat the request
with new or different credentials. However, a request might be forbidden for reasons unrelated to the credentials.
An origin server that wishes to "hide" the current existence of a forbidden target resource MAY instead respond
with a status code of 404 (Not Found).

The text does not prescribe the use of a header for the response, but the use of the response payload. We are not going to change RFC 7231. When accessing a resource at a RS, major errors should be described in the document with their reasons codes. In particular 401 "Unauthorized" which indicates an authentication error may also happen if an end-user authentication is required by the RS before the presentation of an access token. In such a case, it includes a WWW-Authenticate header that describes how to authenticate.

Since GNAP is not an authentication framework, but an authorization framework, a GNAP method should never appear under an WWW-Authenticate header.

In the response payload, we need to allow the RS to provide some guidance to the client (and then the AS) about which kinds of privileges are expected by the RS in order to fulfill the request.

My proposal is to use a combination (AND or OR) of URLs for Authentication Servers (ASs) and for each of them a combination (AND or OR) of privileges types (i.e. attributes and/or rights), where attributes types may optionally be associated with attribute values.

@fimbault
Copy link
Collaborator

fimbault commented Mar 1, 2021

Let me summarize what I understand you're suggesting. When a response 403 Forbidden is sent, structured headers (as per rfc8941) would define what privileges could be handled by the RS. The the client would make its GNAP request based on that info.

Something like:
Accept-GNAP-AS: "as1.com", "as2.com" -> meaning OR
Accept-GNAP-Attribute: "foo", 'bar", ("dolphin", "denver") -> meaning foo OR bar OR (dolphin AND denver)
(didn't check against a parser to check if that works)

Is that correct?

It doesn't really map to the complexity of access_token semantics though. Compared to section 2.1, how would we know how to map to the request? The location is clear (the RS), the type/actions/datatypes isn't (the json is really more complex than my structured field). So it could work, but only for basic structures such as role based attributes, cf #192 (but the multi-token would work based on the OR statement). The full variety of possibilities in GNAP would be hard to manage.

Here's how that would look like:

"access_token": [
    {
        "label": "token1",
        "access": [
            {
                "roles": [
                    "foo"
                ],
                "locations": [
                    "https://rs.example.com"
                ]
            }
        ]
    },
    {
        "label": "token2",
        "access": [
            {
                "roles": [
                    "bar"
                ],
                "locations": [
                    "https://rs.example.com"
                ]
            }
        ]
    },
   {
        "label": "token3",
        "access": [
            {
                "roles": [
                    "dolphin", 
                    "denver"
                ],
                "locations": [
                    "https://rs.example.com"
                ]
            }
        ]
    }
]

would be sent to either as1 or as2.

@Denisthemalice
Copy link
Author

You got the spirit of the idea. However, the example above is not general enough.
For each AS, the privileges (not only the attributes) may be different.

Note: Privileges = Attributes and/or Rights

I would consider something like:

Accept-GNAP-AS: "as1.com", "as2.com" -> meaning OR
Accept-GNAP-Privileges: "as1.com"; right; attributes : (uid, role values: "foo", 'bar", ("dolphin", "denver"); (uid, group-memberships)
Accept-GNAP-Privileges: "as2.com"; right

On the second line, the Identifier of the first AS is copied first to mention that what follows relates to AS1 only, followed by the magic word "right" which means that a capability for the requested method and the requested object may be presented OR a set attributes may also be presented, like a unique Id for the RS AND, some role values like "foo" or "bar, or both the roles values "dolphin" and "denver" OR a unique Id for the RS AND all the group memberships known to the AS.

On the last line, a capability issued by AS2 with the requested method and the requested object will be accepted by the RS.
There is no need to duplicate the method and the URL of the target resource, since it is already known by the client.

If the method is incorrect, 405 "Method not allowed" SHALL have precedence over 403 "Forbidden".

@fimbault
Copy link
Collaborator

fimbault commented Mar 1, 2021

So (assuming we could use that kind of syntax within a structured header, need to check) :

  • "right" would basically mean that the client would request a type/actions/datatypes structure, which solves a part of the issue I mentioned earlier. It would have to know what to ask for by a separate mean (out of band, just like today).
  • uid would probably be a sub (in the JWT parlance).
  • not sure I got the "group-memberships" part, what is it supposed to do?
  • indeed the target RS is always the same here, but I was mimicking the current GNAP structure. Notice that in that model, the client is not considered as an untrusted device, but is central to decide what to call (RS and AS, depending on its need). The client might not even have to supply the RS location to the AS in that scenario, except if it is useful to reach to the RO somehow (case where RO != end-user and a direct interaction is expected / the case where the consent can be checked via a rule engine is easier).

@Denisthemalice
Copy link
Author

With "right", the client does not need "to know what to ask for by a separate mean". Since the information is supplied in addition to the error 403 "Forbidden" , it means that the client sent a correct method and a resource URl but that the request failed ONLY because the access token when missing or was incorrect. So the client knows exactly what to ask for the next time. There is no need for an out of band mechanism, ... like yesterday.

uid would NOT be a sub since sub does not have a sufficient granularity in terms of privacy (see below).

When attributes is indicated by the RS, it can be some sort of role or some sort of functional or hierarchical group membership.

If the client has never visited the RS before, it needs to call first the RS (unless its code is screwed to a single AS).

The client needs to be able to tell to the AS which kind of user identifier should be used, in particular whether a globally unique identifier should be used or whether an identifier unique for the RS should be used.

If the client does not supply the RS location to the AS, the AS has no way to use a user identifier that would be specific to the RS only.

@fimbault
Copy link
Collaborator

fimbault commented Mar 1, 2021

Ok that's clearer for me now. Thxs! - At a high level at least, all this would need to be tested.

@agropper
Copy link

agropper commented Mar 1, 2021

I'm having a hard time following this conversation. The client presents a capability to the RS. The capability is derived or filtered from whatever rights are granted by the RS to the AS. The client does not necessarily have a direct relationship with the AS because there could have been many user agents or clients in the capability delegation chain.

@fimbault
Copy link
Collaborator

fimbault commented Mar 1, 2021

The point raised by Denis here is that he'd like to not always use capabilities (rights), but attribute based access also.

The delegation use case is very different, and indeed requires capabilities to work.

The needs are pretty orthogonal, and I think both (rights and attributes) make sense for their specific use case. None exactly match the usual jwt implementation.

@agropper
Copy link

agropper commented Mar 1, 2021

I'm good with both. The SSI folks seem eager to focus on capabilities rather than attributes. I'd like to stay on their good side for everybody's sake.

@Denisthemalice
Copy link
Author

fimbault wrote:

The delegation use case is very different, and indeed requires capabilities to work.

The delegation use case is certainly very different, but it does not necessarily require capabilities to work.

If the RS server is part of a service, it can advertise the name of that service in a RS Discovery response.
Then the client may target the access token to both that RS and that service name.

When the target RS re-sends that token to another RS that is a member of that service, it may accept it if it contains both the name of the service and an identifier of the target RS that it considers to be also a member of the service.

@fimbault
Copy link
Collaborator

fimbault commented Mar 18, 2021

@Denisthemalice yes that's possible, but pure delegation through ocaps supports advanced features such direct attenuation. Not that it applies to every use case, but it avoids resending the initial token. Like you're chief of staff, you get a token for a RS, but you can further delegate to your team members under your own authority (possibly propagating reduced privileges). So that's a different scenario.

I think we'd be better off by sticking to attributes and rights in the context of GNAP, and let people implement whatever they want on top of it.

@Denisthemalice
Copy link
Author

This thread is drifting away from the first message that I posted. The current text in section 9.4 starts with:

" If the client instance calls an RS without an access token, or with an invalid access token, the RS MAY respond
to the client instance with an authentication header indicating that GNAP needs to be used to access the resource".

The text omits to mention which HTTP error code(s) should be returned.

The error codes from RFC 7231 and from RFC 7325 should be used, in particular:

400 Bad Request Section 6.5.1 of RFC 7231
401 Unauthorized Section 3.1 of RFC 7235
403 Forbidden Section 6.5.3 of RFC 7231
404 Not Found Section 6.5.4 of RFC 7231
405 Method Not Allowed Section 6.5.5 of RFC 7231
406 Not Acceptable Section 6.5.6 of RFC 7231

A key point is to make the difference between 401 and 403:

401 "Unauthorized" which indicates an authentication error. It always includes a WWW-Authenticate header that describes how to authenticate.

403 "Forbidden" which indicates that the server understood the request but that the client instance does not have permission to access this resource, even if it has been authenticated. A server that wishes to make public why the request has been forbidden can describe that reason in the response payload (if any).

It is a simple as this. However this implies to change the definition of a Resource Server from:

Resource Server (RS): server that provides operations on protected resources,
where operations require a valid access token issued by an AS.

to:

Resource Server (RS) : any system that contains resources on which clients are willing to perform an operation on them.

@fimbault
Copy link
Collaborator

fimbault commented Mar 24, 2021

Note that there were other proposals related to errors, to be reviewed

@jricher
Copy link
Collaborator

jricher commented Apr 14, 2021

  • GNAP will not specify exact error codes for the APIs it protects, but will give guidance to use an appropriate error-class code with response headers
  • AS-discovery is discussed in GNAP-RS created by Extract RS-focused protocol elements #246

@jricher jricher added the Pending Close Issue will be closed unless there are changes to consensus label Apr 14, 2021
@Denisthemalice
Copy link
Author

This issue has been marked as "Pending Close", however the topic is rather important.

@jricher : When a request/command is sent, the error codes should be mentioned.
This is one of the pillars for interoperability and testing.

I can't understand why you refuse to mention all of them. The text proposed above would take less than one half page.

@jricher : This issue is fully unrelated with "AS-discovery is discussed in GNAP-RS created by #246".

This issue should not be closed until all error codes are mentioned in the draft document

@aaronpk
Copy link
Collaborator

aaronpk commented Apr 17, 2021

The reason for marking the issue as "pending close" was stated above.

@Denisthemalice
Copy link
Author

@aaronpk: Your reply does not respond to my technical arguments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants