3

I am thinking to implement a REST service that needs to call another REST service that is protected by OAuth2.

My service is "always" called server to server by web applications.

The user interacts with the web applications using their authentication.

The server-to-server call is executed on behalf of the authenticated user.

The third party service uses the standard OAuth authentication that expects the browser to call a specific url passing a return url and the level of authorization required. The return url (that in my case must point to my service) must contain also a "state" that the service will use to validate the call and to know which user initiated the authorization (the service stores somewhere an association between the "state" and the authenticated user).

The problem is that the browser needs to call this return url directly and so without authentication.

It is my service that generates the URL to initiate the authentication. The "state" specified in the return url is stored in a database and associated with the authenticated user.

My question is: is it safe enough to use the "state" (see https://auth0.com/docs/protocols/oauth2/oauth-state) to protect this URL?

Can this URL be open and just check that the passed state is valid?

If it isn't enough, what can be done to secure this call without implementing a full authentication between the browser and the service?

When the service is called with the return URL it executes the following steps:

  1. It uses the authorisation code received to get the access-token and the refresh-token.
  2. It stores them in a record associated to the user (the service knows which user started the login process thanks to the association mentioned before).
  3. It redirects the browser back to the web application to a url that the web application passed when the request to login was initiated. This url must be "safe" (meaning that the state of the application does not change in any relevant ways as a consequence of opening that url).
Marco Altieri
  • 633
  • 5
  • 13

1 Answers1

3

By not protecting the return url you open yourself to the flowing scenario

  1. Malicious user A registers with web application
  2. Malicious user A obtains initial OAuth redirect to the third party service. He can do it easily by using user agent that does not follow the redirect automatically
  3. Malicious user A tricks naive user B into opening the redirect url and authenticating with the third party.
  4. Naive user B completes the process - your service gets the callback and assumes it is valid. It obtains access token and associates it with user A (the one that initiated the flow)
  5. User A logs in into the web applications and asks for his data - web application calls service which calls third party and now user A sees the user B data

The third step is the hardest to pull but consider thew fact that third party will tell the user B that it is Your application that is asking for permission so more trustworthy it is the easier the exploit is.

The simplest way around it would be to have the callback url on the web application, authenticate the user and simply forward the call to the service.

AGrzes
  • 526
  • 4
  • 10
  • When naive user B opens the redirect, the microservice will not ask for any authentication. I am going to update my question to add more details. – Marco Altieri Dec 12 '19 at 20:38
  • The step 3 covers initial redirect (from web application to third party) and the third party will authenticate the user and ask for access grant (to your application). Then the third party application will redirect back to your service and You will associate the data of user B with account of Your user A (based on state) – AGrzes Dec 13 '19 at 08:23
  • You are right. I was already thinking to validate the authentication code returned to make sure that it was the right user. You gave me a definitely good reason to do so. Thanks. – Marco Altieri Dec 13 '19 at 09:33
  • I will not accept your answer yet just in case someone else has other possible use cases that I need to take into account. – Marco Altieri Dec 13 '19 at 09:34
  • Do you agree that I can avoid the scenario you described checking that the authorization code that the service receives is for the same user that initialised the login? – Marco Altieri Dec 13 '19 at 11:12
  • Yes - but the devil is in details. I assume Your web app either have some kind of session (probably cookie based) or can ask the user to authenticate. Now if Your service is on another domain the browser will not give the identity data to you. If both are on single domain then you can proceed with some kind of signed cookie. If not you will have to come up with a way to establish user identity. – AGrzes Dec 13 '19 at 11:29
  • The idea is that I know who initiated the login (from the database) and from the authorization code I know who completed the authorization. If they do not match, I won't accept the call. Actually I think that when I try to use the authorization code to get the access token, I need to provide the user and so the provider itself can do the check for me (I will specify as user the one I have in the database). – Marco Altieri Dec 13 '19 at 11:56
  • I'm not sure I follow you. As I see this at the start you have user A known as UA to your web application. Web application sends this information to the service which generates state SA and stores mapping in DB. Now at return point you have user ? (as your service does not identify any user) presenting result of callback with state SA. Without authorization you have single piece of information so You do not have anything to compare it with. – AGrzes Dec 13 '19 at 12:28
  • As for providing user to the third party how do you plan to correlate identity? Say you have used A using email A@b.c in you web app, now third party know him as user Z using email Z@x.y. Do you have enough leverage to make third party to work with you on that - or is this established provider? – AGrzes Dec 13 '19 at 12:31
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/102164/discussion-between-agrzes-and-marco-altieri). – AGrzes Dec 13 '19 at 12:32