Search code examples
resthttpoauthrestful-authenticationapi-design

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:

  • Returning a 200 OK success with an indication that authentication is required or permitted in the response body somehow. This is inelegant and doesn't extend well to resources of different content-types, as well as requiring the client to know when it is accessing a resource which may require this authentication scheme.
  • Returning a 403 Forbidden along with a link to authenticate in either the headers or body. The issue is that this is requires distinguishing from a 403 Forbidden generated due to permissions issues, and feels out of line with what we want 403 to mean.
  • Returning a 401 Unauthorized. This has the same problem of requiring the client to break it apart from 401 generated by our platform when the user isn't logged in. There is the additional issue that 401 Unauthorized is part of the HTTP challenge-response authentication scheme, but we are not implementing a challenge-response flow here. The client never touches the credentials.
  • Creating a new 4XX status code to allow the client to easily separate this condition from other possible failures. This seems like the cleanest from a technical point of view, but making new status codes isn't something we probably need to be doing!

Solution

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