Background
I want to create a PHP application that eventually will be installed on a "countless" web servers.
The application is going to access the Google Drive associated with the web server's administrator Google account (it will basically write some files on user's cloud storage). So my PHP app will be authorized by the end-user to use its Google Drive storage. This is done (via the OAuth2 protocol) by connecting the Google OAuth2 service.
So basically I have to create a ClientID/Secret pair (on behalf of my Google Account) that is gonna be used to execute the authorization flow.
Google provides 3 authorization methods:
for web applications (web browsers over network)
for service account (my server to Google server)
for installed application (like Android, IPhone)
(1) is perhaps the best choice EXCEPT that I have to define a REDIRECT_URI where the authorization code will be sent. Because my APP will be installed on a "countless" different servers I don't know in advance the protocol, domain name and the path (also the URI) where the Google's response should be returned. If I would install this application only on 3 servers I could create upfront a ClientID/Secret pair for each of them. It's not the case.
(2) means to deploy my P12 private key with the PHP application and I don't feel comfortable with that!
(3) means to put the end-user to copy/paste an authorization token from a Google web page into my application web interface. I am trying to avoid doing that.
I already made it to work by using the method 1 when I know in advance the REDIRECT_URI. I also embedded the client_id/secret pair in the source code so the whole authorization process is user-friendly. But this is not going to work on a "countless" deployment scenario.
Questions
Which method and how should I use it in order to make the whole process safe for me (as developer) and for the client too (the web server administrator). Note that the authorization process should not involve the end-user to copy paste some codes. I want that step to be transparent/user-friendly for the end-user (no one likes copy-paste when it can be done automatically).
Should I embed my client_id/secret into the application or that's totally wrong? I suppose no end-user wants to go through the creation of its own ClientID in Google Developer Console, right? On the other hand why I would give my client_id/secret to an unknown end-user?
Final thoughts
I could create a proxy application on my (the developer) web server such that my PHP application (which is supposed to be deployed "everywhere") will send the authorization request to my proxy server (which has already its own client_id/secret) which in turn will redirect the call to the Google OAuth service which then REDIRECT_URI back the authorization code to my proxy and finally I will redirect back the response to the original sender (the PHP application). What do you think?
Some useful answers here and here or here.
#Edit: as I've already said earlier a proxy would be a solution. I've made it and it works. The same solutions I've received also from user pinoyyid. Thanks for your answer too.
A proxy is the only real option open to you. You can encode the originator URL in the "state" parameter, so that when the proxy receives the access token, it can call a webhook at the originator.
There are some contradictions in your question...
"The application is going to access the Google Drive associated with the web server's administrator Google account" and "So my PHP app will be authorized by the end-user to use its Google Drive storage." are mutually exclusive.
If the Drive storage belongs to the app, then the user isn't involved in any OAuth dialogue.
Could you edit your question to be clear who is the owner of the Drive storage as it greatly influences the OAuth flows.
Related
Let me clarify my use case:
I have a next.js application which is a plattform for listing real estate objects. I have several api routes which im using inside my next.js app. for example:
/api/createpost ->
Takes informations from my form on my next.js app and creates a database entry to perform a new post
/api/getposts ->
fetching all the real estate posts from my database and displays it
/api/login ->
logs in a user by checking the credentials in the database and sends a jwt
/api/register ->
registers a user by taking the credentials from a form from my next.js app, registering a user and creating an entry in my database
Now in order to secure my apis I want to make sure to check if there is a valid user session if anybody is calling one of the apis (except the register/login api) to get the expected result. Im doing this by calling the /api/login route and getting a valid user session. Until here everything just works fine. Apis like the /api/createpost can only be called if we have a valid user session.
Now I want to create a mobile app and I want to use my api routes from above to provide full functionality in my mobile app too. It should work the same, if i want to call the /api/createpost on my mobileapp for example, i need a valid user session.
But I want to restrict my api by asking for a key in my database which is pointing to my app and saying okay if you call the /api/createpost api, first of all i need to verify that its the mobile app asking. The mobile app will provide the key in the request then.
I didnt try this yet, but it should work i think. Now the big mess: If we call the /api/createpost and the api wants a valid token to check in the database, which will work for the mobile app, because we are giving it a valid token to check in the database, how can we provide a token if we are calling the api from inside our next.js application? Since I have to do the api call clientside, there is no way for me to provide a secret key or something to validate that the call is coming from my next.js application.
If your application is private
(to be used only by you or a few select people)
You can send a private API key over SSL with each request from your application to the server and verify it. Or you can limit your API to only accept requests from certain IPs.
If your application is public
Unfortunately there's no way to determine where the request is coming from, since anything your app can send, an attacker can send it manually.
Think about it, if your app is trying to make a request to your API, any user can intercept this request before its sent out of his/her machine, and send the exact same request from a different app on the same machine.
You might say, well I can encrypt the requests and responses so that they are of no use to the attacker. But such an encryption will require either a key that's already agreed upon, or some way to provide a new key at the beginning of each session.
If the key is already agreed upon, the app must contain it, as you've already guessed in the question, the attacker can retrieve this key no matter how well you try to hide it.
If the encryption key is a new key provided at the beginning of each session, that's almost how SSL works, your browser handles this transaction. Your server sends a public key to your browser to encrypt the requests which the server can then decrypt with a private key. In this case you've circled back to the same problem, how can you verify to whom you give out an encryption key? What would stop an attacker from requesting the encryption key?
There has to be some way you'd be able to design apps that don't require this restriction. I think the question you should be asking isn't how to restrict your api to a certain app, but how to design apps that don't require this restriction.
We might be able to help you out if you could tell us why you need this restriction.
Update
There is actually a way to verify that requests are coming from your app, but not with an api key.
For Webapps
You can use Google's reCAPTCHA to verify a user on your /register and '/login` routes, and provide an access token or start a valid user session on successful captcha response. With reCAPTCHA v3, you could even verify every user action without interrupting the user. This eliminates both the problems I mentioned in my answer above -
You don't have to store an api key into the app/web app.
The request can't be spoofed as it requires human user interaction within your app. The captcha verification success will arrive to your API from Google's reCAPTCHA server, not from your client app. This communication will be authenticated with a pre-mediated private API key shared by Google to you, which works in the same way as to how you authenticate your external domains.
For Android apps
A similar way to achieve the same thing would be via Android SafetyNet Attestation API. This checks the runtime environment and signs the response with a dynamically generated nonce that your app provides the SafetyNet API.
Please read its docs carefully to understand how you could create potential security loopholes and how to avoid them while using this API.
For iOS apps
DeviceCheck works in a similar way, except the device validity is provided to you by Apple server.
Important edit: "secured" is not the right word here! You cannot tell that a request comes from your app just because the domain is yours. The domain name is not a safe information, as it can be altered easily. See #Mythos comments below.
Initial answer:
Web applications access is secured not based on an API key, but based on a whitelist of domains. That's how we achieve security, because only you have access to the domain where you host your own application: so the request has to be coming from an app you own.
If you try some 3rd party services that provides API for web apps, that's often how they'll work: they will let you configure a set of whitelisted domains that can access your data.
If they provide you an API key, this API key is always meant to be used by a server, not a client-only app.
So if I understand you question correctly, you would do like this for each request:
Check the domain. If it's in the whitelist, perfect, you can keep going. This is meant for web apps (look for "CORS").
If not, check for a valid API token in the headers. This is meant for any app that can store this API token securely (another server for instance, or a mobile app in your scenario though I don't know mobile enough to tell how you store such a key)
I'm so confused about how to get authentication between an external, consumer website and a Laravel API right. What I'd like is to have a web app for which users are able to present information from the app to other people, using an external website that consumes the app's API. Here's an example of the basic setup in a bit more detail:
A Laravel 5.3 app that has a protected API endpoint api/status. Only authenticated users should be able to hit api/status, and the status returned is a particular status for the authenticated user.
An external website that consumes the Laravel API on behalf of a user, let's call her Alice. The necessary information is stored in the backend of Alice's website so that it can authenticate with the API on behalf of Alice. (The actual implementation I'm working on will be a WordPress site, and the API consumption will be done by a WordPress plugin that I am implementing; so any info stored will likely be stored in the WordPress database.)
The website has a /status page that displays Alice's status to anyone who browses to the page. (Ie, when the /status page is browsed to, an API call to the app is made on behalf of Alice. The returned status is specific to Alice, and is displayed to the person browsing the page.) People browsing to /status on Alice's website do NOT need to do any sort of authenticating to view the status on the page.
That is very simplified compared to my actual goal, but I hope it serves to keep the extraneous details to a minimum so we can focus on my actual question, which is what method of authentication should I use to achieve this?
One thing I DON'T want:
The person browsing Alice's website should NOT be able to use their browser's inspector to watch the API call and from that create further API calls on Alice's behalf on their own.
I have Passport installed on my Laravel App, but if I'm understanding things correctly I don't want to use the basic Access Token issuing workflow, as that would require the people browsing to Alice's website to authenticate using the Alices's credentials. For the same reason, I don't think I want an Implicit Grant Token.
Using a Password Grant Token would require storing Alice's password for the Laravel app on her website. Is it ok to store passwords like this in a WordPress database? It makes me nervous...
The other option available through Passport is to have Alice create a Personal Access Token and store that in her website backend as the token to use to authenticate. But the Laravel documentation seems to imply that Personal Access Tokens are meant for testing and development purposes, which makes me wary of going this route for a production plugin. Plus, doesn't using a PAT make it possible to do the thing I DON'T want above, since the PAT is simply passed in the request header? Or is that problem mitigated by the fact that the API interaction would be done over SSL?
Do I even need to go through Passport to achieve what I want here? Is there a better way?
I've been reading myself in circles trying to understand what the best practice for this kind of setup is. I'm sorry if this question isn't focused enough, but if anyone has any good advice, or can clarify things for me I would much appreciate it!
How to prevent someone just taking my API keys from the client side javascript code and starting to use my HERE subscription for some other use.
I noticed HERE provide an option to secure the API keys for a certain domain on the applications management page: "Secure app credentials against a specific domain". I have set up this option and also put domain there but I do not see any change on my app behavior.
The application still continue working fine on my PC. Shouldn't the HERE API stop working as web server is running on localhost and not on the defined domain.
My app is running fully on browser, and only static files come from the server (http://localhost:8083/index.html). I am using the HERE javascript API.
I tested also running the app on external cloud service on different domain than localhost. Results are the same. My conclusion is that the setting "Secure app credentials against a specific domain" just has no impact and does not work. Checked also the api response headers and all origins are accepted.
Access-Control-Allow-Origin: *
In your HERE dashboard, you can set the application id and application code to only work on a particular domain or set of domains. If the tokens are fixed to a domain, it won't matter if someone takes your tokens because only the listed domains can use them. If you don't secure the tokens to a domain, then someone will be able to use your tokens if they find them.
We currently have a node web server that does authentication of users including oauth2 to google and facebook. We would like for it to handle the serving of web pages while a stateless .NET web api handles the serving of the actual data (which is all requested asynchronously). My question is how to handle authentication to the web api?
I mean should the client even authenticate to the api (and if so how do we do the pass through authentication so that it is authenticated to both once authenticating against the web server) or should the web server authenticate the user and then just forward all api requests to the api along with a user id? What is the standard scheme used for this?
Thanks in advance.
This answer is late, but I'll post my thoughts for the heck of it. I would place a dummy api in your node app, just a simple pass-through for everything that is in your data api (the .net one). Then I'd lock down the data api so only your node server can talk to it. The short answer is that any api that you expose to the internet has to be locked down. If you do the above, you don't have to expose your data api to the internet. You get the added benefit of not having to deal with CORS - you can have a simple /api folder hanging off of your domain. You can also use this pass-through api to aggregate calls to multiple business apis if your solution ever grows. It's a very scalable architecture.
If you don't want to do the above, then you'll need to either place the data api on the same domain as the other site, or setup CORS so javascript/AJAX from one can call into the other. Once your data api can see cookies written by the other site, you'll need to authenticate them, probably very manually, in your .net api, since .net didn't write the auth cookie - node did.
I know there has to be an obvious solution to this problem and I am missing it, so I would much appreciate someone enlightening me so I don't spin my wheels...
I am writing an ASP.Net application that will interact with a service API (Evernote specifically). Evernote requires OAuth for security and before I can actually interact with the objects I need to obtain a token.
The workflow goes like this (explaining it to myself as much as anyone else!):
Build a url with my development api key and secret key and some other OAuth stuff, send it to Evernote to request an access token.
Send the url as a request to Evernote and pull the new access token out of the response
Build another url with the access token to request an authentication token for the user. This url goes to a page the user must interact with to login (if they haven't already) and then authorize my application to access their account. The last param of the url I build is a callback url which will be called from Evernote's servers.
If all goes well, Evernote will request the callback url and include the new authentication token as a param.
Once my server receives the callback with the embedded token I can use it so that my app can interact with the users' notes on subsequent requests.
The problem is that I'm writing this app on a local box, not an ISP under a public domain. So my callback is to the localhost server. Of course, localhost is relative, so Evernote can't resolve my callback... I can't ever receive an authentication token and debug at the same time.
There has to be a way around this problem because this authentication model is not unique to Evernote (by a longshot... Flickr uses it as do a lot of other services). So can someone tell me how to set things up so I can get the authentication token and still be able to debug on my local box?
Help is much appreciated!
OAuth is quite tough to implement. It may not be the answer you're looking for, but this is how I managed to get the job done:
Write some code on my local dev machine.
Run a bat file (or alternatively hook a post-build event in VS) that executes a msbuild deploy script and deploys the application to a test server.
Run the application on the test server. After obtaining the request token and requesting for authorization it redirects to the Evernote website.
After successful authorization the Evernote website redirects back to my test server and the authorized request token is exchanged for an access token.
Instead of debugging (I don't have VS on the test server) I examine the logs of the application (the logging I used was as simple as writing to a text file).
Rinse and repeat
For the purposes of testing I registered a temporary public subdomain (e.g. testing.oauth.mydomain.com) so that Evernote will be able redirect to that url.
According to this (How do I develop against OAuth locally?) the callback is issued by the browser, so it should be able to hit localhost.