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.
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.
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
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.
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.
You could try driving the synchronization the other way:
Once the user is authenticated into the web app, launch the native app from the web app via the custom URL scheme.
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.
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.
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.
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.
I'm faced with a difficult scenario regarding OAuth 2 authentication and Google's Calendar API.
I am attempting to write a PHP script which processes my personal calendar data after being triggered by a POST call from an external source. However, I am having trouble granting the script access to my account. Since the script runs entirely in the background, there is never an opportunity for me to enter the authorization code which is required for PHP command line tools making Google API requests.
I have looked into Service Accounts, which grant access to personal data without individual approval, but unfortunately this is only available to G Suite users, which I am not.
I have also attempted to run my script manually, enter the code to authenticate, then run it from my external source. This doesn't work either, because the authorization codes are apparently only good for the same mode of access where it was entered, and the response to the external source still shows as asking for the code.
Lastly, please note that responding with the authorization code from the external source is not an option. Which I can activate the trigger, I cannot change how it works or the data it passes.
TL;DR: How do I grant a PHP script (which runs only in the background) access to my personal Google Calendar data?
Thanks in advance!
Articulating this question actually helped me better formulate my Google searches, and I found the answer!
I'll leave this question/answer here so somebody can hopefully benefit from it -
Turns out that the Service Account is the way to go, but instead of trying to access your account's data via the service account, you need to share the calendars in question with the generated service account email address. Essentially, this makes the service account a "co-owner" of the calendar. Then, you simply make the request to the service account's own information.
Hope this helps!
I was trying to setup OAuth workflow using the sample application as given here
However for some reason, after I enter my okta user Id and password, I never gets the control back on my call-back URL and application just hangs indefinitely.
However the normal Javascript Singn-in widget (check this link) with the minimal authentication does work and I get the control back to the redirect URL. But this is not for an OAuth2 workflow... which is completely useless for me. Because all it does is provide authentication service using Okta tenant app and it will redirect you to your App URL. This does not provide any authorization grant workflow or other OAuth2 complex workflow. May be useful for some application but not for enterprise app where you want to retrieve user profiles, and create a login session based on user profile data retrieved from OKTA.
So my question is why is the OAuth workflow not working using the PHP application that uses JS sign-in-widget? And why there are no instructions or warning on this page for this costly service (this is not free and many org is probably paying for this)?
I spent almost a day trying to setup my Authorization server as per the instruction given on this link, but nothing works. Any idea what must be going wrong ?
Does this entire example works only after contacting OKTA support to enable the Authorization server feature? Because, I also saw a documentation here that says that this is Early Access (EA) feature (and it is probably recently added in OKTA? Extremely frustrating experience).
BTW I sent email to their customer support to enable this Authorization server feature just in case if I am missing something. If this does not work then I will have to create my own OAuth2 server using Laravel 5.4 PHP framework, which is probably the quickest solution and 100% free.
I also tried to test the Authorization server setup as per the instructions provided here.
I was successful in getting the following end point working:
/oauth2/:authorizationServerId/.well-known/openid-configuration
But I am unable to get any scope and claims using api end-point:
/api/v1/authorizationServers/:authorizationServerId/scopes
So in short, I am so far unable to test my Authorization server to get my authorization grant workflow working.
Where can I look for some troubleshooting advice?
Is there another way to check whether I have configured my OKTA Authorization server properly?
I found out that the JS script provided for the PHP sample is not right for the workflow I am working on. So after changing that JS Script, things started to work.
Edit: Also please note that Setting up Authorization server is a new feature (It is Early Access feature) in OKTA. It is not enabled by default. So you need to contact OKTA support team to enable the Authorization service endpoint and functionality provided by it.
We have a .Net Web API (v2) serving an SPA that I aim to secure using OAuth 2.
In my OWIN Startup.Configuration(), I set my Provider in IAppBuilder.UseOAuthAuthorizationServer to an override of OAuthAuthorizationServerProvider. I have this successfully providing refresh tokens.
I want to be sure that these tokens cannot be spoofed by someone implementing a similar provider. No searching I have done has given me any kind of answer.
Does OAuthAuthorizationServerProvider ensure that the refresh tokens received are those issued by itself, and not any similar code on a hacker's machine?
My own experiment seems to confirm that it does - I tried the same code running on two different machines and took the unexpired refresh token (with a long expiry of 24 hours) from the other machine, and got a 401 as I'd hope. But just in case I've wrongly convinced myself, I'd like some reassurance and/ or advice from someone who knows more about OAuth in the .Net Web API than I.
Thanks,
Paul
I'm sure I eventually found a definitive answer in the doc for OAuthAuthorizationServerProvider but now can't find it.
However, Dominick Baier, an expert in this field, says:
Access tokens are protected using the machine key. Protection of refresh tokens is up to you. You need to manually set the machineKey element in web.config (otherwise the key gets auto generated and might change over time).
His comment comes from a reply to a post on implementing OAuthAuthorizationServerProvider.
I'm writing a web application that reads my personal calendar data, crunches stats, and then spits them out for the world to see. I don't need an authorization flow. Is it possible to leverage the Google APIs without going through a user sign-in flow? In other words, I want my personal Google account permanently and securely signed in to my server without the risk of my token invalidating or having to re-auth.
Right now I'm signing myself in with an offline token, then uploading the authorization file onto my server, basically spoofing the server that I already auth'd. Is there not a cleaner way?
I've spent hours reading through the API docs and Auth docs, but haven't found and answer. If there is a page I've missed, please point me to it!
PS. I'm using the Calendars API through Python/Flask on Heroku, but that shouldn't matter.
An alternative approach is using a service account while sharing your calendar with that service account. See https://developers.google.com/accounts/docs/OAuth2ServiceAccount
So, you want to be remembered.
If you want to dispose of any kind of authenticacion but yet the user needs to be recognized you should be using a cookie.
On the server side that cookie should be used to select the offline token.
Of course, without that cookie the user needs to be authenticated in any way. I would make them reauth by Google so you get a new offline token.
Hope that it helps.