10

This question is kinda complimentary to "Share credentials between native app and web site", as we aim to share secrets in the opposite direction.

TL;TR: how can we securely share the user's authentication/authorization state from a Web Browser app to a Native Desktop app, so the same user doesn't have to authenticate additionally in the Native app?

TS;WM: We are working on the following architecture: a Web Application (with some HTML front-end UI running inside a Web Browser of user's choice), a Native Desktop Application (implementing a custom protocol handler), a Web API and an OAuth2 service, as on the picture.

Initially, the user is authenticated/authorized in the Web Browser app against the OAuth2 service, using the Authorization Code Grant flow.

Then, the Web Browser content can do one-way talking to the Native app, when the user clicks on our custom protocol-based hyperlinks. Basically, it's done to establish a secure bidirectional back-end communication channel between the two, conducted via the Web API.

We believe that, before acting upon any requests received via a custom protocol link from the Web Browser app, the Native app should first authenticate the user (who is supposed to be the same person using this particular desktop session). We think the Native app should as well use the Authorization Code flow (with PKCE) to obtain an access token for the Web API. Then it should be able to securely verify the origin and integrity of the custom protocol data, using the same Web API.

However, it can be a hindering experience for the user to have to authenticate twice, first in the Web Browser and second in the Native app, both running side-by-side.

Thus, the question: is there a way to pass an OAuth2 access token (or any other authorization bearer) from the Web Browser app to the Native app securely, without compromising the client-side security of this architecture? I.e., so the Native app could call the Web API using the identity from the Web Browser, without having to authenticate the same user first?

Personally, I can't see how we can safely avoid that additional authentication flow. Communication via a custom app protocol is insecure by default, as typically it's just a command line argument the Native app is invoked with. Unlike a TLS channel, it can be intercepted, impersonated etc. We could possibly encrypt the custom protocol data. Still, whatever calls the Native app would have to make to decrypt it (either to a client OS API or some unprotected calls to the Web API), a bad actor/malware might be able to replicate those, too.

Am I missing something? Is there a secure platform-specific solution? The Native Desktop app is an Electron app and is designed to be cross-platform. Most of our users will run this on Windows using any supported browser (including even IE11), but ActiveX or hacking into a running web browser instance is out of question.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 2
    Since you have a native app, you could embed a tiny localhost web server (on a specific port) in that app that listens to HTTP calls from the browser app (in javascript httprequest). You can use TLS in there with certificates in the web app (client) and in the native app (server). There's a timing/retry issue since the browser app must connect to something that's not started yet, but it seems it can be tuned appropriately. – Simon Mourier Mar 27 '19 at 12:42
  • @SimonMourier it's a great suggestion and I should have mentioned this what we prototyped for initially. Unfortunately, it had more issues than benefits. There are issues with [SSL for `localhost`](https://letsencrypt.org/docs/certificates-for-localhost/) and even if we simply used `http://127.0.0.1:port` which is considered "potentially trustworthy", we had issues with Windows default firewall settings and some of the most popular 3rd party antiviruses blocking the request. Determining the listening local `port` from the browser javascript is another problem. – noseratio Mar 27 '19 at 20:09
  • 1
    I suppose you tried to implement the web server with Windows' HttpListener (aka Http.sys)? This doesn't work well because it's tied to kernel and requires admin rights for config, server certifcates in local machine, etc. it's a PITA for simple things. But if you implement it with TcpListener + SslStream (speaking .NET), you can use current user certificates (you just need a common CA between app and browser) and it works w/o any specific right or firewall I believe. For the port, well, you can pick one that usually reserved but nobody uses in your environment. – Simon Mourier Mar 28 '19 at 00:02
  • @SimonMourier, TcpListener + SslStream + a local CA is an interesting idea, thanks. One concern we have about it, what could possibly prevent a malicious app from running a local server like that and listen for requests from the WB app, impersonating itself as our native app? We could further encrypt what we send to that channel, but that'd still involve using some private encryption keys on an untrusted client-side machine, wouldn't it? – noseratio Mar 29 '19 at 02:35
  • 1
    I've done some tests. The only problem (no firewall, no admin rights needed, that part is ok) is about certificates with Chrome/FF. You can create your own CA and cert but FF and Chrome complain the certificate (or its chain) is self signed (IE nor Edge are ok), even if you install the CA in local machine cert store. So you'd have to install a certificate that satisfies FF/Chrome (=> a public CA, or intermediate CA such as https://www.globalsign.com/en/certificate-authority-root-signing/ $$$), or configure security warning exclusions to them. – Simon Mourier Mar 29 '19 at 09:06
  • 1
    But even though, to propose SSL as a server in the native app, you have to ship the certificate with a pvk (like a .pfx). As for your last question, I'm wondering what you consider trusted and not on the machine. If this is one of your concern, you'll never be able to install a "secure channel", you'll always have to deploy private keys (or equivalent) somehow locally. Note that this discussion somewhat applies for any other similar channel (HTTP, URLs, TLS) you can think about (private keys storage, currentuser vs localmachine or even hardware, etc.) – Simon Mourier Mar 29 '19 at 09:06
  • @SimonMourier, thanks for going the extra miles to help me - as always, greatly appreciated! I guess I could create a custom CA and install on that machine. But so can do a bad actor app, and then have the browser app talking to it instead. Thus the browser app must somehow be able to verify that this custom CA is ours and not counterfeit and it's not clear to me how it can be done. It's like the chicken and the egg problem, it feels. Anyway, it's indeed about finding a trade-off between raising the bar to intruders and the app's usability as nothing is absolutely secure. – noseratio Mar 29 '19 at 11:42
  • 1
    Whatever you will put in place, if it runs on my machine I (the one who owns the hardware) can hack it. So, it's only a matter of raising the bar and targeting which component exactly. You should assess a formal threat model https://en.wikipedia.org/wiki/Threat_model so at least you and your team (and people on SO :-) agree on what are the reasonable attacks and how you fight them. For example, you seem to consider the browser as being implicitly safe, the native app not. – Simon Mourier Apr 01 '19 at 11:51
  • @noseratio if embedded secrets are acceptable then DCR (dynamic client registration) is a competitor for your case. But make sure this is supported by authorization server. Also, make sure to have initial request signing mechanism, which provide cryptographic binding for authenticity of registration request. – Kavindu Dodanduwa Apr 02 '19 at 02:14
  • @KavinduDodanduwa, sorry I actually somewhat confused myself and maybe you as well. It doesn't look like DCR is relevant here because it has to be done by installer and the installer itself would have to *interactively* authenticate against the registration server, which is not an option for us. – noseratio Apr 02 '19 at 05:10
  • @KavinduDodanduwa, The other link I mentioned, [Dynamic Client Authentication](https://hackernoon.com/strengthening-oauth2-for-mobile-f4f3925dbf18) appears to be a promo blog post about a commercial solution. Their product somehow "attests" the client app and makes sure it is still authentic and hasn't been tampered with, then issues it with an dynamic equivalent of `client_secret`. They claim that's how they mitigate the impersonation threat. This is interesting, but I have no idea if that claim is true and how they actually do it. – noseratio Apr 02 '19 at 06:32
  • 1
    @noseratio I have come across DCR but not Dynamic client authentication. According to the blog, this uses a centralized service. Client sends a request and if challenge is completed, client receive a token. This token is the one that used in token request. This approach looks promising if you can finalized initial challenge mechanism with the service – Kavindu Dodanduwa Apr 02 '19 at 07:02
  • @noseratio A sample service can utilize a challenge mechanism like a totp (https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm) which is associated with the client ID. – Kavindu Dodanduwa Apr 02 '19 at 07:05
  • @noseratio And what this mechanism avoiding is embedding client secrets, which can be obtained through reverse engineering. – Kavindu Dodanduwa Apr 02 '19 at 07:07
  • 1
    Everything comes down to how can you make sure the native app is really the native app and not some hacker's den (even from the server point of view, not only the web browser point of view). I don't think it's possible for a web browser or for a web server to make sure who's it's talking to without installing some secret in that native app (HTTPS cert needs pkv, client certificate as well, etc). It's possible to seriously raise the bar with other systems (Authenticode, Trusted Platform, etc.), but not with a web browser. – Simon Mourier Apr 02 '19 at 19:36

5 Answers5

7

The best solution : Single Sign On (SSO) using Custom URL Scheme

When I was checking your question, I remembered the Zoom app that I am using in my office. How it works ?

I have my Gmail account linked to a Zoom account (this is account linkage, which is outside the scope of implementation). When I open Zoom app, I can choose the option to login with Gmail. This opens my browser and take me to Gmail. If I am logged in to Gmail, I am redirected back to a page that asking me to launch Zoom app. How this app launch happen ? The application register a custom URL scheme when app get installed and the final redirect in browser targets this URL. And this URL passes a temporary secret, which Zoom application uses to obtain OAuth tokens. And token obtaining is done independent of the browser, a direct call with SSL to token endpoint of OAuth server.

Well this is Authorization code flow for native applications. And this is how Mobile applications use OAuth. Your main issue, not allowing user to re-login is solved. This is SSO in action.

There is a specification which define best practices around this mechanism. I welcome you to go through RFC8252 - OAuth 2.0 for Native Apps.

Challenge

You need to implement OS specific native code for each application distribution. Windows, Mac and Linux have different implementation support for custom URL scheme.

Advice

PKCE is mandatory (in IETF words SHOULD) for all OAuth grant types. There is this ongoing draft which talks about this. So include PKCE for your implementation too.

How PKCE protects token stealing

With PKCE, the redirect/callback response is protected from stealing. Even some other application intercept the callback, the token request cannot be recreated as the PKCE code_verifer is there.

Also, do not use a custom solution like passing secret through another channel. This will make things complicated when it comes to maintenance. Since this flow already exists in OAuth, you can benefit with libraries and guidance.

-----------------------------------------------------

Update : Protecting Token Request

While the custom URL scheme solves the problem of launching the native application, protecting token request can be challenging. There are several options to consider.

- Bind native application launch with a secret shared from browser

When browser based client launch the native client, it can invoke a custom API to generate a secret. This secret acts like a one time password (OTP). User has to enter this value in native app before it obtain tokens. This is a customization on top of Authorization code flow.

- Dynamic client registration & Dynamic client authentication

Embedding secrets into public clients is discouraged by OAuth specification. But as question owner points out, some malicious app may register itself to receive custom URL response and obtain tokens. In such occasion, PKCE can provide an added layer of security.

But still in an extreme case, if malicious app registers the URL plus use PKCE as the original application, then there can be potential threats.

One option is to allow dynamic client registration at the first time of application launch. Here, installer/distribution can include a secret that used along with DCR.

Also, it is possible to use dynamic client authentication through a dedicated service. Here, the application's token request contains a temporary token issued by a custom service. Custom service obtain a challenge from native application. This may be done through totp or a cryptographic binding based on an embedded secret. Also it is possible to utilize OTP (as mentioned in first note) issued through browser, which needs to be copy pasted manually by end user. Once validated, this service issue a token which correlate to the secret. In the token request, native client sends this token along with call back values. This way we reduce threat vectors even though we increase implementation complexity.

Summary

  • Use custom URL scheme to launch the native application
  • Browser app generate a temporary secret shared with a custom service
  • At native app launch, user should copy the secret to native app UI
  • Native app exchange this secret with custom service to obtain a token
  • This second token combined with call back authorization code (issued through custom url scheme) is used to authenticate to token endpoint
  • Above can be considered as a dynamic client authentication
  • Value exposed to user can be a hashed secret, hence original value is never exposed to end user or another client
  • DCR is also an option but embedded secrets are discouraged in OAuth world
Community
  • 1
  • 1
Kavindu Dodanduwa
  • 12,193
  • 3
  • 33
  • 46
  • 1
    How does "And this URL passes a temporary secret, which Zoom application uses to obtain OAuth tokens" works and how secure is it? This is the whole point of the question. – Simon Mourier Mar 29 '19 at 09:11
  • @SimonMourier The secret you receive is an intermediate secret. This is called authorization code in OAuth terms. The way to protect this is through PKCE. The first call that goes in browser has a hashed secret. Auth server correlate auth. code with this hashed secret. The token request contain secret as it is. Since now we use SSL it cannot be intercepted. And secret generated from client application is in runtime only. – Kavindu Dodanduwa Mar 29 '19 at 09:32
  • Please check https://tools.ietf.org/html/rfc7636#section-1 for explanation – Kavindu Dodanduwa Mar 29 '19 at 09:33
  • 1
    If I got it right, this still doesn't eliminate the need to authenticate and authorize twice: 1) in the web browser, whenever you've logged into your Gmail account; 2) in Zoom app, where it might be only an authorization step, provided Zoom uses the same browser type (presumably the default one) as you used for gmail. This sounds similar to how MS Team works, besides MS Teams is somewhat cheating and using an internal WebView popup to host the OAuth2 flow, rather than an instance of the default web browser. – noseratio Mar 29 '19 at 12:04
  • 1
    In the general sense, Single Sign On just means you're sharing the same credentials across all apps (that's what Zoom and a lot of software like Office 365 do). But the user still has to authenticate somehow using some UI, maybe simplified, at least twice (and then this can be cached for next sessions, persistent cookies can be used if the webbrowser is compatible with the native app webview, etc. so it gives a single login impression). But this doesn't answer the initial question. – Simon Mourier Mar 29 '19 at 12:38
  • Well, in a general sense, SSO means login once and share that same authenticated state against multiple applications. If you think about SAML, OAuth, SSO is built through a common medium, the browser. And in modern day we shouldn't look at the browser to be a cheating medium. There is a reason for applications to use this approach. If you want, yes, you can go ahead an implement your own solution to solve this. I just shared the best I know. – Kavindu Dodanduwa Mar 29 '19 at 16:57
  • One more thing, if you ignore the login part I mentioned, custom URL scheme should shed some light on how your app can be launched through browser. But communication secrets securely, that will require some manual work. For example, generate a correlated secret which needs to be manually pasted in your native application. Once done, native app cross check against server. When this happen browser app should have already pushed the secret data to server. But this is not at all OAuth or SSO. But instead some derived protocol from PKCE. – Kavindu Dodanduwa Mar 29 '19 at 17:05
  • So, basically you're saying the native app still has to authenticate somehow with the Web API (using the OAuth2 + PKCE flow you described or anything else), *before* it can verify/trust anything it takes in via a custom protocol through the hyperlinks from the web browser? – noseratio Apr 01 '19 at 00:20
  • Also, SSO is just another form of authentication. It only works without asking to log in both places will if the web browser used to run the web app and the web browser used by the native app for the authentication flow is the same (i.e., the same cookie container). Which is rarely the case even for Microsoft's own applications. E.g., Teams is Electon-based and it uses its own webview window for its OAuth2 flow. – noseratio Apr 01 '19 at 00:21
  • @noseratio IMO, yes. We are talking about two different apps running on two different platforms. One on browser and other a native one. You must avoid threats such as open redirects targeting your native apps. And if you think there are many threat vectors you should consider to avoid (https://tools.ietf.org/html/rfc6819). That's the reason I argue for using a secondary validation. – Kavindu Dodanduwa Apr 01 '19 at 02:23
  • @noseratio OAuth ideally should not use embedded browsers (https://tools.ietf.org/html/rfc6819#section-4.1.4 & https://auth0.com/blog/google-blocks-oauth-requests-from-embedded-browsers/). So one should choose the default browser to complete the flow. If that's the situation, then you can get the cookie based support. – Kavindu Dodanduwa Apr 01 '19 at 02:29
  • @noseratio added a summary. Hope this helped you. Just, as always, embedding secrets inside public client should be discouraged. So using custom mechanism to generate client secret + involving user to transmit a secret to native application is a better approach. – Kavindu Dodanduwa Apr 03 '19 at 02:57
  • 1
    @KavinduDodanduwa, thanks. It looks like a good summary of what's already described in the question and in my TOTP answer and I hope it can be useful to others. – noseratio Apr 03 '19 at 05:11
  • @noseratio Glad to help you with things I know. This question helped me to learn new things as well. Sure I will follow up the comment. – Kavindu Dodanduwa Apr 03 '19 at 07:00
2

As you mentioned, using a custom protocol handler is not a safe way to pass secrets, since another app may handle your protocol and intercept that secret.

If you are imposing a strict constraint that the communication channel between the native app and the web app is initiated from the web app, and that the native app has not previously established a secure channel (e.g. shared secret which could encrypt other secrets), then it is not possible to safely transmit a secret to the native app.

Imagine if this were possible, then PKCE would be redundant in an OAuth 2.0 Code Flow, since the server could have safely transmitted the access token in response to the authorization request, instead of requiring the code_verifier to be provided with the grant when obtaining the access token.

JoeGaggler
  • 1,207
  • 1
  • 13
  • 28
  • With PKCE still in place, what could prevent potential bad actors from completely replacing our `mynativeapp://` protocol handler with their own implementation and then just executing the whole [OAuth 2.0 Code + PCKE Flow](https://openid.net/2015/05/26/enhancing-oauth-security-for-mobile-applications-with-pkse/), providing their own `code_verifier` and our `client_id` to the OAuth server, thus fully impersonating our native app? – noseratio Apr 01 '19 at 03:28
  • 1
    @noseratio well if this is the case, you are literally doomed. It means the user's machine is completely overtaken by a malicious party. And if that's the case, why bother to spend time in writing an app with same url scheme plus steal embedded client id from compiled code ? I I was that malicious party then I will simply deploy a key logger to steal user credentials and use your application. I hope you get the point :) – Kavindu Dodanduwa Apr 01 '19 at 05:18
  • At the end of the day, user authentication at authorization plays a key role. PKCE simply add a cryptographic binding between browser based request and token request. But PKCE have no effect if user authentication is stolen. These are outside the scope of OAuth protocol – Kavindu Dodanduwa Apr 01 '19 at 05:24
  • @noseratio You may think about client secret to overcome the token request forgery. This is something against oauth spec. According to that public clients are taken as clients which cannot secure a secret. So embedded client secrets are discouraged. But if we compare it with private key embedding, I think you have much advantage when it comes to implementation burden. You may think about rollover of the client secret time to time to avoid reverse engineering. – Kavindu Dodanduwa Apr 01 '19 at 15:52
  • @noseratio Thank you for pointing it out. While DCR can be considered here, one must make sure it is supported by authorization server. Also, DCR is valid when we first distribute the client application. So if you think reverse engineering is not a problem, yes then DCR based client registration + protecting token request with client secret is an option to consider. – Kavindu Dodanduwa Apr 02 '19 at 02:11
2

Just got the following idea. It's simple and while it doesn't allow to fully automate the setup of a secure channel between Web Browser app and the Native app, it may significantly improve the user experience.

We can use Time-based One-Time Password algorithm (TOTP). In a way, it's similar to how we pair a Bluetooth keyboard to a computer or a phone.

The Web Browser app (where the user is already authenticated) could display a time-based code to the user, and the Native app should ask the user to enter that code as a confirmation. It would then use the code to authenticate against the Web API. That should be enough to establish a back-end channel between the two. The life time of the channel should be limited to that of the session within the Web Browser app. This approach might even eliminate the need for a custom protocol communication in the first place.

Still open to other ideas.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    The fact that your idea is not fully-automated actually has beneficial security side-effects: it requires that the user confirm that YOUR application is the one that receives the code, since the user has to deliberately input the OTP value into the approved application. – JoeGaggler Mar 28 '19 at 18:12
  • @JoeGaggler, but it still not as secure as (say) an SMS message would be, because it's all happening on the same desktop. A malicious app could be stealing the OTP codes by making screenshots. Then again, it's all about raising the bar to attacks. The same malicious app could just periodically make screenshots of the UI with data we aim to protect, or install or keylogger. – noseratio Mar 29 '19 at 11:49
  • @noseratio well I just saw this, my last comment in my answer, the usage of custom URL scheme should allow you to automate this. – Kavindu Dodanduwa Mar 29 '19 at 17:08
  • A late followup: https://twitter.com/noseratio/status/1291795407182290944?s=20 – noseratio Aug 08 '20 at 11:45
2

You could try driving the synchronization the other way:

  1. Once the user is authenticated into the web app, launch the native app from the web app via the custom URL scheme.
  2. If the native app is not authenticated, connect securely to the backend over HTTPS, create a record for the native app, retrieve a one time token associated with that record and then launch the web app in the user's browser with the token as a URL parameter.
  3. Since the user is authenticated in the browser, when the server sees the token it can bind the native app's record with the user account.
  4. Have the native app poll (or use some other realtime channel like push notifications or a TCP connection) the server to see if the token has been bound to a user account: once that happens you can pass a persistent auth token that the native app can store.
Femi
  • 64,273
  • 8
  • 118
  • 148
  • 1
    This is very interesting! One thing though, because the back-end API in step 2 of your proposed flow is open to any client (it's doesn't require a login - by definition), I could basically simulate what the native app is expected to do with a simple powershell script, and eventually get the token? How can the back-end API tell here the initial request is coming from my app and not from anything else? Or am I missing anything? – noseratio Apr 02 '19 at 18:43
  • 1
    You can't verify that it is your native client, not someone else, but that's like asking "how can I be sure they are using Chrome". If you're worried about native app impersonators (congratulations that you've built something that people want to impersonate) then add a confirmation step in the browser similar to what Google does (are you sure you want to grant access to this app) and require 2 factor or some other step to ensure the user has to be really committed to granting native access. – Femi Apr 03 '19 at 19:05
2

Did you think about using LDAP or Active Directory?

Also OAuth2 could be combined, here are a related question:
- Oauth service for LDAP authentication
- Oauth 2 token for Active Directory accounts

SSO should be easier then too, furthermore access-rights could be managed centralized.

Concerning general security considerations you could work with two servers and redirect form the one for the web-application to the other one after successful access check. That 2nd server can be protected so far that a redirect is required from the 1st server and an access check could be made independent again but without need to login another time, might be important to mention here the proposed usage of Oracle Access Manager in one linked answer for perimeter authentication.
This scenario with two servers could be also hidden by using a proxy-server in the frontend and making the redirects hidden, like that data-transfer between servers would be easier and secure too. The important point about my proposition is that access to the 2nd server is just not granted if anything is wrong and the data are still protected.

I read here some comments concerning 2FA and some other ideas like tokens, surely those things increase security and it would be good to implement them.

If you like the general idea, I'm willing to spend still some time on the details. Some questions might be helpful for me ;-)

EDIT:
Technically the design in detail might depend on the used external authentication provider like Oracle Access Manager or something else. So if the solution in general sounds reasonable for you it would be useful to elaborate some parameters for the choice of a external authentication provider, i.e. price, open-source, features, etc.
Nevertheless the general procedure then is that the provider issues a token and this token serves for authentication. The token is for unique one-time usage, the second link I posted above has some answers that explain token-usage very well related to security and OAuth.

EDIT2
The Difference between an own OAuth2 / OIDC server and a LDAP/AD server is that you need to program everything by yourself and can't use ready solutions. Nevertheless you're independent and if everything is programmed well perhaps even a bit more secure as your solution is not public available and therefore harder to hack - potential vulnerabilities just can't be known by others. Also you're more independent, never have to wait for updates and are free to change whatever you want at any time. Considering that several software-servers are involved and perhaps even hardware-servers the own solution might be limited scale-able, but that can't be know from outside and depends on your company / team. Your code base probably is slimmer than full-blown solutions as you've only to consider your own solution and requirements.
The weak point in your solution might be that you have to program interfaces to several things that exist ready for business-frameworks. Also it might be hard to consider every single point in a small team, large companies could have more overview and capacity to tackle every potential issue.

David
  • 5,882
  • 3
  • 33
  • 44
  • 1
    Not sure I follow how that could safely eliminate a need to authenticate from within the native app, but if you can elaborate, take your time, I'd happy to award another bounty! ;-) – noseratio Apr 03 '19 at 09:07
  • We use our own OAuth2 / OIDC server (not LDAP/AD based), if that matters. But let's say we use a well know services like Auth0 or Octa, would that be different? – noseratio Apr 03 '19 at 10:52
  • @noseratio see `EDIT2` :) – David Apr 03 '19 at 11:18