SPA and Native app with Backend+3rd party API. Auth code flow

Hello,

Here is the case. I have backend server which have to interact with third party API on-behalf of user. Also, I have SPA web, Android and iOS apps as clients for my backend. Users have to sign-in into my application using same 3rd party APIs accounts (there is no other way). The flow I came up with is following:

  1. On client (SPA, Android, iOS) user prompted to sign-in with third party account provider (f.e. microsoft).
  2. User redirected to third party provider sign-in page and after successful auth returned back to client app with auth code.
  3. Client app sends auth code to our backend server which in turn exchanges auth code for access/refresh tokens.
  4. Backend server stores third party access/refresh tokens and returns OK response with its own-generated auth tokens (not third party tokens).
  5. User can interact with our backend server and backend server can interact with third party API on-behalf of user.

It feels like a weird solution but I can’t figure out any other approaches to do this. Also, I can’t see anything bad in passing auth code to backend server over HTTPS. I registering my oAuth app as confidential client and only backend server knows the secret.

I found this thread but it seems like an excessive solution and there is nothing about mobile platforms.

So, is there any resources to read on this topic? Or may be you could advice something better then my approach? Also, some explanations why my solution is bad will be very useful.

Thank you!

Hey arturalbov,

I don’t see anything wrong with your solution. I have a couple of questions though:

  1. Are you exchanging the token and storing it in the backend server, because you are concerned about storing OAuth Client secrets in the client applications (thus not allowing them to do the code exchange)?
  2. How sensitive are these operations? Do you have some which are more sensitive than others i.e. read vs write?
  3. Are you just using your backend as a proxy to the 3rd party API, or does it do something else before returning the response back to the client application?

Looking forward to your answer!

Hi lpedrosa,

Thanks for your response!

  1. No. I exchanging the token and storing it in the backend server, because I need offline access to third party API on-behalf of user.
  2. I’m not sure if I understand your question. I have some read operations and some write operations to third party API.
  3. Not exactly. Some operations are more like a proxy. But also I have offline operations without user interaction.

Thanks for answering those questions.

Based on your answers, you could consider the following suggestions:

A. Storing tokens for read/proxy operations on client devices

Operations where the backend is only proxying requests to the third party API can be performed by the client application.

For that, you can initiate an Authorization Code flow with PKCE on the client application. This flow doesn’t require the client to present a secret, when exchanging the authorization code for a token.

The token can then be stored on the client’s device.

Pros:

  • The backend no longer needs to gives a proxy token to the client application
  • The client application can also store the refresh token, in order to refresh the access token, once it expires

Cons:

  • Depending on the scopes requested and how sensitive the 3rd party API operations are, your security model might not allow you to store these tokens on the client application
  • If the backend still needs to perform some transformations before delivering the result to the client application, it will still require a different token e.g. backend can run cron jobs at any time, the client application might not (phone has no network, etc.)

For more info on the second cons, please read the next section.

B. Storing tokens for long-lived/transformation operations in the backend

These type of operations might have a different security profile i.e. more sensitive, thus you probably don’t want the token on the client application.

Also, the lifecycle of these operations might differ from the typical client operation lifecycle e.g. they are longer lived.

Pros:

  • The backend can be considered more difficult to compromise than client applications, thus you can trust it with more sensitive tokens.

Cons:

  • If you’ve decided to also follow option A, you would have to ask twice to the end-user for consent, since you’ll have different scopes on the client tokens and the backend tokens.

C. Hybrid approach

This is similar to the approach you are using (it might actually be the same, so I apologise if I didn’t completely understood your explanation):

  • The OAuth Client asking for access is the backend
  • The Resource Owner (the end-user) grants access
  • The backend obtains a token that allows it to perform any of the consented operations e.g. read and write

So far so good.

Now you can mint another token which allows the client application to access the backend API. This token can have a stricter scope of access e.g. read, since the client application might not need to perform all the operations initially request by the backend.

In this scenario:

  • The OAuth Client asking for access is the client application
  • The Resource Owner (the backend application, which hold the original token) grants access
  • The client application obtains a token that allows it to perform a stricter set of operations

Pros:

  • If your backend is already exposing an API to the client applications, which exposes operations other than the ones using the 3rd party API, this is probably a good way to bundle them all in one token.
  • If a client application is breached, the blast radius can only big as the operations allowed by the client token (hopefully smaller than the one held by the backend)
  • Revoking a client application token does not impact the tokens stored in the backend. Again, this allows you to have better control over client application breaches, without impact to the UX. (You don’t have to ask for consent to the end-user, since the backend token is intact).

Cons:

  • This setup is a bit more complicated to build and understand
  • Probably not worth doing, if your backend is only proxying the 3rd party API i.e. doesn’t expose any other operations related to your use case

That was a bit long, and this is not a simple topic. I hope this helps you evaluate if your solution is good enough and what tweaks can you make, in order to keep it clean.

Bear in mind that I am not, by all means, an OAuth 2.0 expert. So take this advice with a grain of salt :wink:

Yes, I’m using hybrid approach. But with a little detail.

In fact, in my solution, obtaining authorization by OAuth client is “divided” between native app/SPA and our backend. So, native app completes first part of auth code flow. It requests authorization from the resource owner and receives an authorization grant (auth code). Then, native app sending auth code to backend server. Now, backend server completes second part of auth code flow (exchanging auth code for tokens).

And here is what bothers me. As I understand:

  1. Even if someone intercepts auth code, he need client secret to exchange it for tokens. But client secret is known only by our backend server.
  2. As follows from previous point, attacker could send intercepted auth code to our server and he will be successfully authenticated. Which means he’ll get access to victims account in our application.

May be PKCE could prevent this. When native app sends code_verifier along with auth code to our backend server.

Oh, I hope I didn’t confuse you…

Hum, let me see if I understood it correctly. Do you mean:

  • the native app is the server of the redirect_uri for your OAuth Client?
  • the backend is the server of the redirect_uri for your OAuth Client?

When an OAuth Client registers a redirect_uri, it does so in order to perform the code exchange just like you are doing. It is assumed that the server of that uri i.e. the server application that has a handler for that endpoint, has the OAuth credentials (e.g. client_id and client_secret) needed to exchange the code for a token.

The token exchange is done through a secure channel, involving the server application and the OAuth server. The client application doesn’t see the resulting token.

In your second point, it is only a concern if that operation returns a token back to the client. As I have explained previously, normally it doesn’t. The backend keeps that token safe.

Let me know if that makes sense.

Cheers

Ok, I’ll try to explain :slight_smile:

Yes, native app is the server of the redirect_uri for my OAuth Client. But, it doesn’t know client_secret. So, after it receives auth code it does nothing other than sending it to my backend server where code exchange happens.

Besides that I’m using third party API tokens for some operations, I also use third party API for authentication on my backend server. So, complete process looks like this:

  1. User navigates to my native app and sees “Login with third party” button
  2. User clicks the button and goes to third party auth page
  3. After that, it redirects back to native app with auth code
  4. Native app sends auth code to backend server
  5. Backend server exchanges auth code for tokens and verifying user identity
  6. If everything OK, backend server stores third party API tokens (if it’s new user), issues its own tokens for user and returns response to native app
  7. User successfully logged in

So, in theory, if someone can intercept auth code, he can login in my app under victims account. On the other hand, having backend server handling all flow, doesn’t prevent such thing… I’m really confused with it :smiley: