coppro
coppro

Reputation: 14516

HTTP API: Communicating the need to authenticate to a third party

I am working on a RESTful API that includes third-party integration. We use OAuth with the authorization code flow to authenticate against the third party. The user must log in to our service and then additionally to the third party so that our application can access the third party.

Some of our resources require interaction with the third party to complete (say GET /third-party-userpic). If the user is already logged in to that service, we will retrieve the access token from our data store and use that to retrieve the user's picture, easy!

If we don't hold valid credentials for that user to that service, however, we can't get the user's picture. This will occur on first use, and may also occur if the credentials expire or are revoked. In this case, we want to communicate back to the client that they need to visit the authorization URI and begin the OAuth flow.

My colleagues and I designing this have discussed several possibilities, including:

Upvotes: 3

Views: 599

Answers (3)

gevorg
gevorg

Reputation: 5055

What https://www.rfc-editor.org/rfc/rfc6750 suggests is following:

  • client is responsible for requesting access scopes, basically your main functionality and third party functionalities should be described as separate scopes
  • if client tries to access resource (in your case some third party functionality) without requesting access for it, server should respond with 403 response insufficient_scope error

insufficient_scope The request requires higher privileges than provided by the access token. The resource server SHOULD respond with the HTTP 403 (Forbidden) status code and MAY include the "scope" attribute with the scope necessary to access the protected resource.

WWW-Authenticate: Bearer realm="myprotectedresource", 
     error="insufficient_scope", 
     error_description="Insufficient scope for this resource scopes", scope="SOME_SCOPE"

Below is authorization flow diagram from same source

 +--------+                               +---------------+
 |        |--(A)- Authorization Request ->|   Resource    |
 |        |                               |     Owner     |
 |        |<-(B)-- Authorization Grant ---|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(C)-- Authorization Grant -->| Authorization |
 | Client |                               |     Server    |
 |        |<-(D)----- Access Token -------|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(E)----- Access Token ------>|    Resource   |
 |        |                               |     Server    |
 |        |<-(F)--- Protected Resource ---|               |
 +--------+                               +---------------+

Upvotes: 0

DaSourcerer
DaSourcerer

Reputation: 6606

To get this started: I find it hard to believe that displaying a user pic is a critical component, so I assume there are grater ramifications than a user being condemned to being represented by a default avatar.

Within this scenario, a 2xx-class response code is pretty much out of the question as the action has not been successful. While there is a mechanism on the serverside failing, the client has a chance to correct this (by authenticating via OAuth). This rules out the 5xx-class status codes, suggesting the 4xx-class.

As you correctly asserted, 401 is not quite right, as this code is specific to the requested resource. From RFC 7235, section 3.1:

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

However, the target resource in your case is fine; it is an external service being troublesome. Apart from that, 401 is taking you into the heart of the HTTP Authentication Framework which is introducing a number of practical issues.

The most fitting status code in this range is indeed 403. From RFC 7231, section 6.5.1:

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).

While this may sound unrelated at first, consider that

[...] a request might be forbidden for reasons unrelated to the credentials.

The RFC also reads:

The client MAY repeat the request with new or different credentials.

So this code is relaxed in that it is not specific to the requested resource, but considers other circumstances preventing a successful operation very well. This is also the very response code you will get when you follow the flowchart on Choosing an HTTP Status Code — Stop Making It Hard.

Upvotes: 1

Vasiliy Faronov
Vasiliy Faronov

Reputation: 12310

with an indication that authentication is required or permitted in the response body somehow

I think this depends a lot on “required” vs. “permitted”: can you otherwise complete the request successfully, with only a part of the result missing?

If you can (and want to), maybe something like this:

HTTP/1.1 200 OK
Date: Tue, 10 May 2016 01:08:07 GMT
Content-Type: application/vnd.whatever+json
Link: <https://third-party.example/authorize>;
    rel="https://example.com/doc/#authorize-3rd-party"
Warning: 299 - "part of the content is missing; authorize with third party"

...content with only the pic missing...

If you can’t (or don’t want to), then 424 (Failed Dependency), 403 (Forbidden) (but see comments), and 400 (Bad Request) all sound like they might be appropriate status codes. However, status codes are not a solution to every problem. It’s perfectly normal to refine their meaning in the response payload. There’s even a new standard for this—RFC 7807:

HTTP/1.1 400 Bad Request
Date: Tue, 10 May 2016 01:08:07 GMT
Content-Type: application/problem+json

{
    "type": "https://example.com/doc/#authorize-3rd-party",
    "title": "You need to authorize with a third party for this.",
    "authorizeAt": "https://third-party.example/authorize"
}

You’re very right in your misgivings about 401 (Unauthorized) and inventing your own status code.

Upvotes: 0

Related Questions