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

Question: OAuth Client Type for native apps #24

Open
jose-ibanez opened this issue Aug 28, 2017 · 19 comments
Open

Question: OAuth Client Type for native apps #24

jose-ibanez opened this issue Aug 28, 2017 · 19 comments

Comments

@jose-ibanez
Copy link

I'm developing an iOS app that uses the Bungie API, and it seems that any native application developed using the Bungie.Net API that requires user authentication would be forced to re-authenticate users every hour.

Reading Bungie.Net API's OAuth Documentation, I am supposed to select an OAuth Client Type for my application "as described in section 2.1 of the OAuth 2.0 specification." RFC 6749 clearly states that "A native application is a public client installed and executed on the device used by the resource owner." Then, I also see back in your OAuth documentation that "A public client differs from a confidential client in that... it will not receive a refresh token in response to a token request." So, if I'm reading this correctly, any native app will have to re-authenticate the user every hour, which is obviously not ideal.

Am I misinterpreting the documentation? Could a native app be considered a confidential client if the API Key is stored in the application binary in an encrypted format and any transient tokens are stored in the OS's secure storage, such as iOS's Keychain? If not, would you consider providing public clients refresh tokens?

@Tetron-bng
Copy link

Jose, you are interpreting the documentation correctly. OAuth requires that authentication providers distinguish between public and confidential clients and configuration time. However, Bungie.net has no mechanism to determine that the client really is confidential at run time.

@vpzed
Copy link

vpzed commented Aug 28, 2017

It's too bad that there isn't some refresh token setup for public clients because it drives folks who should be using a public client type to configure a confidential client type and putting their credentials at risk.

@floatingatoll
Copy link

floatingatoll commented Aug 29, 2017 via email

@thisbarker
Copy link

What actions will be taken if a mobile application registers as confidential and transmits their client_secret through the user's device? To force a user to open a new browser tab every 60 minutes is a bit strange.

@jose-ibanez
Copy link
Author

So, yeah, any native application could be subject to decompiling, or some inspection of app resources, so it's assumed that anything included in the application binary could be extracted. For public clients, that would be your API Key.

On the other hand, the OAuth spec believes that native apps can provide adequate security for tokens.

...dynamically issued credentials such as access tokens or refresh tokens can receive an acceptable level of protection. At a minimum, these credentials are protected from hostile servers with which the application may interact. On some platforms, these credentials might be protected from other applications residing on the same device.

Of course, anything that goes over the wire could be intercepted using a proxy. The same is true for the official Destiny app, which seems to use a cookie based authentication mechanism.

From a user experience perspective, forcing a user to sign in every hour is awful. iOS or Android users might sign in twice, but by the third time you pop a web browser they'll give you a one star review and uninstall your app. It would be difficult to develop anything useful with such a restriction in place.

Could you consider providing refresh tokens to public clients?

@floatingatoll
Copy link

floatingatoll commented Aug 30, 2017 via email

@thisbarker
Copy link

@floatingatoll Their iOS application still uses a web-view, as @jose-ibanez pointed out.

@floatingatoll
Copy link

floatingatoll commented Aug 30, 2017 via email

@vthornheart-bng
Copy link
Contributor

vthornheart-bng commented Aug 31, 2017

Good conversation, thank you all for the input! When we get time, I'll loop back around and investigate further, and discuss options and opinions mentioned here with the rest of the team. We're pressed for time at the moment, but I just want to make sure you guys know that we're not dropping the issue and will loop back.

@Tetron-bng
Copy link

Agreed. Any observations from the industry in general. How do other apps/oauth servers deal with this problem?

@ascendancyy
Copy link

ascendancyy commented Sep 1, 2017

Those are just profiles to help base a decision on what OAuth flows to use I think. They define a confidential client as one that can maintain a secret regardless of how it is implemented. So if you're able accomplish that then you should safely be able to run as a confidential client. If you cannot accomplish that then you should not be issued a refresh token. As mentioned refresh tokens are equivalent to credentials.

The supplemental info on the security considerations of the OAuth spec mention in section 4.1.1 different threat models - including accessing secrets.

  Attack: Obtain Secret From Source Code or Binary:

   This applies for all client types.  For open source projects, secrets
   can be extracted directly from source code in their public
   repositories.  Secrets can be extracted from application binaries
   just as easily when the published source is not available to the
   attacker.  Even if an application takes significant measures to
   obfuscate secrets in their application distribution, one should
   consider that the secret can still be reverse-engineered by anyone
   with access to a complete functioning application bundle or binary.

   Countermeasures:

   o  Don't issue secrets to public clients or clients with
      inappropriate security policy (Section 5.2.3.1).
   o  Require user consent for public clients (Section 5.2.3.2).
   o  Use deployment-specific client secrets (Section 5.2.3.4).
   o  Revoke client secrets (Section 5.2.3.6).

 Attack: Obtain a Deployment-Specific Secret:

   An attacker may try to obtain the secret from a client installation,
   either from a web site (web server) or a particular device (native
   application).

   Countermeasures:

   o  Web server: Apply standard web server protection measures (for
      config files and databases) (see Section 5.3.2).
   o  Native applications: Store secrets in secure local storage
      (Section 5.3.3).
   o  Revoke client secrets (Section 5.2.3.6).

In section 3.7 it mentions:

Deployment-independent "client_id" with pre-registered "redirect_uri"
      and with "client_secret"  This is an option for native
      applications only, since web applications would require different
      redirect URIs.  This category is not advisable because the client
      secret cannot be protected appropriately (see Section 4.1.1).  Due
      to its security weaknesses, such client identities have the same
      trust level as deployment-independent clients

Now for section 5.2.3.4:

 An authorization server may issue separate client identifiers and
   corresponding secrets to the different installations of a particular
   client (i.e., software package).  The effect of such an approach
   would be to turn otherwise "public" clients back into "confidential"
   clients.

   For web applications, this could mean creating one "client_id" and
   "client_secret" for each web site on which a software package is
   installed.  So, the provider of that particular site could request a
   client id and secret from the authorization server during the setup
   of the web site.  This would also allow the validation of some of the
   properties of that web site, such as redirect URI, web site URL, and
   whatever else proves useful.  The web site provider has to ensure the
   security of the client secret on the site.

   For native applications, things are more complicated because every
   copy of a particular application on any device is a different
   installation.  Installation-specific secrets in this scenario will
   require obtaining a "client_id" and "client_secret" either

   1.  during the download process from the application market, or

   2.  during installation on the device.

   Either approach will require an automated mechanism for issuing
   client ids and secrets, which is currently not defined by OAuth.

   The first approach would allow the achievement of a certain level of
   trust in the authenticity of the application, whereas the second
   option only allows the authentication of the installation but not the
   validation of properties of the client.  But this would at least help 
   to prevent several replay attacks.  Moreover, installation-specific
   "client_ids" and secrets allow the selective revocation of all
   refresh tokens of a specific installation at once.

Basically if Bungie came up with a way to automate issuing of client ids and secrets we could "turn otherwise 'public' clients back into 'confidential' clients." ~~~Unfortunately such a thing isn't defined in the spec. I have no idea how feasible it would be for them to implement if it's even a good idea to do so.~~~

I haven't read all of this damned thing so there are probably other solutions for what we have currently. In my case I'm trying to build a web app so the web crypto api seemed like something that could help. Possibly the user-agent would retrieving the id and secret from a server at runtime and store them encrypting/decrypting on use. You would of course have to ensure only the proper clients can get the id and secret.

If secure storage is possible then it seems the only problem is handling the id and secret. As mentioned you can't hardcode them as they would basically be public. Some sort of secure delivery mechanism would need to be put in place. ~~~But I'm still not sure how necessary it is to protect those two elements... It could be possible that exposing them would not be a big problem. In which case running as confidential would only require having some place to secure the tokens.~~~

@ascendancyy
Copy link

ascendancyy commented Sep 3, 2017

Turns out there is a spec for dynamic registration: RFC7591.

Something else that seemed interesting was the Assertion Framework (An abstract framework for OAuth client credentials/grants)
Two examples would be:

And again concerning the question about native app there is this spec draft that mentions in section 8.4:

 Native apps, except when using a mechanism like Dynamic Client
   Registration [RFC7591] to provision per-instance secrets, are
   classified as public clients. . .

@huehnerfaust
Copy link

Wanted to implement a litte app and i can't realy decide which client model i should use.
Public or Confidential.
Public seems like the right aproach because. i'm building a webapp/spa where i don't want to include any secret api keys.

I still can't quite understand the difference between public and confidential Clients. (is it only the 3600s lifetime of the access token vs the 90days refresh token?)

To my understanding there is not such a think like a confidential client (every client could expose there secrect keys though debbuging), like DIM exposes there client_id and client_secret upon every token refresh in there http request. Likewise Braytech for example exposes it via the Authorization header as there client_sec and client_id in a base64 encoded string.

Why?

Should'nt they use some kind of proxy, where the proxy only knows the secrets api keys and only pass the access or refresh token back to the client?

What should i choose? The burden of relogging every hour in my app is kind of cumbersome and proxying could drastically increase traffic on my side? (If and only when the app find its users...)

@Tetron-bng
Copy link

Tetron-bng commented Mar 24, 2020

It's an age old question.

The confidential client gets a refresh token which means the user won't need to sign in again for a year as long as they use your app every 90 days.

The public client requires the user sign in once an hour. It is the most secure option for your scenario and really the only option if you are following the rules carefully (not everyone does). The user experience may not be as bad as you are imagining. Most of the time, the re-signin will happen without the user having to enter a user ID or password.

I advise against creating a proxy; it becomes a point on the open internet someone can attack and leverage to attack Bungie.net. You would also need to have it authenticate each of your users which is a complex task and hard to get right. You may be tempted to have a secret your proxy can verify that only your app knows, but that is the same as keeping the client secret in your app. You may as well just put the client secret in that app and not have the headache of a proxy.

My advice, try it as a public client first. It is easy to upgrade to a confidential client later if you must. You may find your scenario works well as a public client.

@huehnerfaust
Copy link

thank you for quick answer.

I will try as you say and go with the public client settings. Will see what the login, (which is maybe somehow cookied by the browser?) looks like.

@Tetron-bng
Copy link

The access token, which you need to add to each request header per the OAuth spec, will not be cookied by the browser. Depending on your platform, you can probably find a pre-rolled OAuth client. For example, I think npm has a few to choose from.

@thepoofy
Copy link

A potential solution to this would be to implement PKCE on the server side. This adds a significant layer of security for the code exchange process and is already baked into some OAuth client libraries.

I was just running into the missing refresh_token issue with a test app using a public client type. The every one hour re-authentication flow is a deal breaker for an app with what's really only read-only privileges. Adding PKCE support should allow clients to safely obtain refresh tokens without exposing client secrets and be done with this issue for good.

@floatingatoll
Copy link

floatingatoll commented May 14, 2020

PKCE is not a replacement for refresh tokens. The OAuth.net featured post for implementing PKCE indicates that PKCE is a safer way to retrieve an access token — but also shows that it's still retrieving a one-hour access token:

{
    "token_type":   "Bearer",
    "expires_in":   3600,
    "access_token": "eyJraWQiOiI3bFV0aGJyR2hWVmx...",

Auth0 also reversed their prior recommendation of PKCE alone and now indicates refresh token rotation alongside PKCE as their current guidance:

Auth0’s former guidance was to use the Authorization Code Flow with Proof Key for Code Exchange (PKCE) in conjuntion with Silent Authentication in SPAs. This is more secure solution than the Implicit Flow but not as secure as the Authorization Code Flow with Proof Key for Code Exchange (PKCE) with Refresh Token Rotation.
... long-lived Refresh Tokens are not suitable for SPAs because there is no persistent storage mechanism in a browser that can assure access by the intended application only. As there are vulnerabilities that can be exploited to obtain these high-value artifacts and grant malicious actors access to protected resources ...

If you expect users of your app to use your app for longer than 1 hour, or more than 1 time per 7 days, then the 'public' scope is inappropriate for your app; you'll need 'confidential' scope, and you'll need to handle refresh tokens.

@thepoofy
Copy link

Sorry I didn't mean to imply that PKCE flow was a alternative to using refresh tokens. I was suggesting that Bungie look into adding a PKCE enabled OAuth flow so we can avoid embedding the Client Secret in our native apps (aka SPA).

I haven't yet come across any OAuth flows that use such an aggressive refresh token rotation strategy as listed in that documentation. Thanks for that info.

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

No branches or pull requests

9 participants