We deployed our flask application to AWS lambda and would like to restrict to access to it to:
Our development team (some external IP for everyone)
Wordpress server (static IP)
Bitbucket (for automatic deploys to lambda)
Google oAuth2 (callback function to lambda)
The first two are fairly easy to accomplish by whitelisting the respective IP in the AWS Gateway or in flask itself. However, the latter two are a bit more tricky since there's no static IP for the bitbucket pipeline nor when receiving the oauth2 callback from Google.
I've looked at the referer in the Http header to identify Google's callback which works but it can be spoofed easily...
Is there a sophisticated way of locking the app down to the above sources?
Here is the version I've got so far
def whitelist_handler():
whitelist_ips = os.getenv('WHITELIST_IPS')
allow_access = True
if whitelist_ips:
whitelist_ips = whitelist_ips.split(',')
referer = request.headers.get('Referer', '')
whitelist_domains = ['https://accounts.google.com/signin/']
if request.remote_addr not in whitelist_ips and not any([referer.startswith(domain) for domain in whitelist_domains]):
allow_access = False
if not allow_access:
abort(401)
Bitbucket
For Bitbucket, the pipelines do actually have static IP addresses as listed on the docs.
Google OAuth
For OAuth, I'm not sure what you're doing here but no part of the OAuth flow involves the provider (Google) needing to send web requests to your application. OAuth works entirely off redirecting the user (who should already have access through your other rules). You can read about this flow here.
So as long as your rules allow your users IPs then there's nothing you need to do for Google. This flow is why it's possible to use OAuth for local or intranet applications.
Related
We have
An existing Django backend with Python social auth for signing in with Google, providing web-based application and an API for the mobile app.
An iOS mobile app with GoogleSignIn pod.
Now we would like to allow mobile app users to sign in with Google inside the app, and then authenticate them on the backend, so that they can access their personal data via the app.
So my idea of the algorithm is:
App uses the GoogleSignIn and finally receives access_token.
App sends this access_token to the Backend.
Backend verifies this access_token, fetches/creates the user, returns some sessionid to the App.
App uses this sessionid for further requests.
The problem is with the third step: token verification. I found two ways of verifying:
1. Python social auth flow
As described in the docs:
token = request.GET.get('access_token')
user = request.backend.do_auth(token)
if user:
login(request, user)
return 'OK'
else:
return 'ERROR'
This would be a preferred flow, since it already has all the required steps and is working perfectly with the web app (like, accounts creation, defaults for newly created users, analytics collection, etc.).
But the problem is that the backend and the app use different CLIENT_IDs for the auth. This is due to the limitations in the Google Developers Console: when creating credentials, you need to select whether it will be a web app or an iOS app, and it cannot be both.
I tried to use different client ids (then backend cannot verify), tried to use web id inside the app (then the pod does not work), and tried to use app id inside the web (then the backend cannot verify again).
2. Google API Client Library
Another option is to utilize the way from the Google Sign-In for iOS documentation:
from google.oauth2 import id_token
from google.auth.transport import requests
try:
idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
userid = idinfo['sub']
except ValueError:
# Invalid token
pass
It worked, but here we're missing all the pipeline provided by social auth (e.g. we need to create a user somehow), and I could not find a proper way of starting the pipeline from the middle, and I'm afraid it would be quite fragile and bug-prone code.
Another problem with this solution is that in reality we also have Signed in with Apple and Sign in with Facebook, and this solution will demand ad-hoc coding for each of these backends, which also bring more mess and unreliability.
3. Webview
Third option would be not to use SDKs in the Swift and just use a web view with the web application, as in the browser.
This solves the problem with the pipeline and client ids.
But it doesn't look native, and some users may suspect phishing attempts (how does it differ from a malicious app trying to steal Google identity by crafting the same-looking form?). Also, I'm not sure it will play nicely with the accounts configured on the device. And it also will require us to open a browser even for signing in with Apple, which looks somewhat awkward. And we're not sure such an app will pass the review.
But, maybe, these all are minor concerns?
⁂
So, what do you think? Is there a fourth option? Or maybe improvements to the options above? How is it solved in your app?
I'm working on an API registration and authentication service application using python. Developers will be able to register their application (domain name of the application) and a random API key will be generated for the registered application.
Next, the registered application will send the API key to the API service with each API request. API server will authenticate the domain of the incoming request with the passed API key to confirm that the request is valid. I'm using Forwarded Host to validated the domain name of the API request, however it doesn't work as in some cases (when the opened page is the first page), Forward Host comes blank.
Are there a better approach to authenticate the request or any changes required in the API registration process to reliably authenticate the request? Some pointers will be helpful.
Using Authorization proxy
Samples are "3scale.net", offering free tier, other commercial solutions exist too.
Open source solution I am aware of is ApiAxle, which is much simpler, but still very useful.
The proxy takes care of managing access keys and forwards request back to real application only in case, it is really to be served.
Using Authorization service
Another solution is to have some internal service evaluating set of client provided keys (providerid, appid, accesskey, ...) are authrized or not. For this purpose, you have to:
set up authorization service
modify your code by adding 2-3 lines at the top of each call calling the authentication service.
Sample code for 3scale is here: https://github.com/3scale/3scale_ws_api_for_python
Conclusions
Authentication proxy makes the application simple and not bothering about who is asking. This can be advantage until your application needs to know who is asking.
Authentication service requires changing your code.
I have been reading a lot about this google endpoints and I have trying to something that is not quite easy to guess. After you create a google cloud endpoint server and you deploy it is open to any HTTP request (unauthenticated). In the cloud endpoint documentation (referring to using authentication) you can read about setting OAuth2.0 to authenticate users with google account but there is no documentation about restrict the endpoint service to a specific mobile app (android or ios) and discard all other HTTP requests. So the question is how to authenticate mobile apps (no users) and prevent HTTP request (unauthenticated)? I'am building my server API(enpoints) based on Python.
Thank you.
In order to restrict your Endpoint to a specific application you can use OAuth2. This is because the OAuth2 flow does user authentication and in-turn the OAuth2 flow inherently authenticates the application requesting the OAuth2 access.
These two client samples detail how to enable authenticated calls on the client side. You have to register your apps in the Developer Console at http://cloud.google.com/console/ .
https://github.com/GoogleCloudPlatform/appengine-endpoints-helloendpoints-android (Starting after the Note in the readme)
https://github.com/GoogleCloudPlatform/appengine-endpoints-helloendpoints-ios/ (Step 8 in README)
authedGreeting is the authenticated call and you would check the User object in the method's backend project for null. If empty then you can immediately throw an unauthorized exception.
https://github.com/GoogleCloudPlatform/appengine-endpoints-helloendpoints-java-maven
Specifically, optional Step 2 in the README tells Cloud Endpoints to start looking for OAuth2 tokens in the request. If the Endpoints exposed method has a User parameter. It will populate it with a user instance only if an OAuth2 token was found, was generally valid, and the token was issued to a client ID defined in the API annotation on the service class.
During the setup of your endpoints API, in clientIds list you provide your WEB_CLIENT_ID, ANDROID_CLIENT_ID, and IOS_CLIENT_ID for example. These values tell the Google App Engine that your application will respond to HTTPS requests from web browsers and Android/iOS installed applications.
When your clients first connect your server, they must obtain an OAuth 2.0 token in order to the communication be secure and that is the reason why you use the WEB_CLIENT_ID in your installed client application. This WEB_CLIENT_ID is unique to your Google Cloud app and through it your client becomes capable of obtain an access_token and a renew_token to communicate with your backend server and your server only. This is a cross-client authorization.
So, if you need only the WEB_CLIENT_ID to obtain the access_token and the renew_token, why you need the ANDROID_CLIENT_ID and IOS_CLIENT_ID? For security reasons.
The ANDROID_CLIENT_ID is linked to a RSA signature key through the SHA1 informed at backend setup. Thus your GAE app will grant (access_token, renew_token) only installed apps signed with the same key listed at your application console (and of course in your clientIds list)
Finally Android apps signed with different or not signed will not receive any access_token, being unable to establish the secure communication channel or even communicate with your server.
I have a website which uses Amazon EC2 with Django and Google App Engine for its powerful Image API and image serving infrastructure. When a user uploads an image the browser makes an AJAX request to my EC2 server for the Blobstore upload url. I'm fetching this through my Django server so I can check whether the user is authenticated or not and then the server needs to get the url from the App Engine server. After the upload is complete and processed in App Engine I need to send the upload info back to the django server so I can build the required model instances. How can I accomplish this? I was thinking to use urllib but how can I secure this to make sure the urls will only get accessed by my servers only and not by a web user? Maybe some sort of secret key?
apart from the Https call ( which you should be making to transfer info to django ), you can go with AES encryption ( use Pycrypto/ any other lib). It takes a secret key to encrypt your message.
For server to server communication, traditional security advice would recommend some sort of IP range restriction at the web server level for the URLs in addition to whatever default security is in place. However, since you are making the call from a cloud provider to another cloud provider, your ability to permanently control the IP address of either the client and the server may diminished.
That said, I would recommend using a standard username/password authentication mechanism and HTTPS for transport security. A basic auth username/password would be my recommendation(https:\\username:password#appengine.com\). In addition, I would make sure to enforce a lockout based on a certain number of failed attempts in a specific time window. This would discourage attempts to brute force the password.
Depending on what web framework you are using on the App Engine, there is probably already support for some or all of what I just mentioned. If you update this question with more specifics on your architecture or open a new question with more information, we could give you a more accurate recommendation.
SDC provides a secure tunnel from AppEngine to a private network elsewhere -- which could be your EC2 instance, if you run it there.
Google has an example of an OAuth2 client here
I am completelly new to OAuth2 and I would like to get this example working before I move to integrate OAuth2 with my application. What I have done is the following:
Register a test application
Get Client ID and Client Secret
Configure those values into client_secrets.json
Run the test app: python moderator.py
The application opens up a browser, where I can (as a user) authorize the application to access my account. But Google is complaining like this (400 Bad Request):
Error: redirect_uri_mismatch
The redirect URI in the request: http://localhost:8080/ did not match a registered redirect URI
Learn more
Request Details
from_login=1
scope=https://www.googleapis.com/auth/moderator
response_type=code
access_type=offline
redirect_uri=http://localhost:8080/
approval_prompt=auto
as=-xxxxxxxxxxxxx
pli=1
client_id=xxxxxxxxxxx.apps.googleusercontent.com
authuser=0
hl=en
I guess the localhost:8080 is coming from an internal web server started by moderator.py. My question is: has somebody goten this example to work? What other components do I need (apache configuration, DNS, ...)
I am very confused with OAuth2 and any help would be greatly appreciated.
First of all, sorry if my answer isn't very precise, because I'm also very new to OAuth (and even python)... and also sorry if it came too late, I don't usually access here.
Have you tried using this (worked for me):
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
Check this: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi
Here I have a piece of code with a complete OAuth flow working.
In OAuth 2.0, the redirect_uri parameter is usually registered with the provider. The provider should also be enforcing https-only redirect_uri.
You need to register the redirect_uri with Google here: https://code.google.com/apis/console/?pli=1#access
Perhaps try registering your external IP with Google (may require some port fowarding on your router)? If this fails, maybe you could use Python's SimpleServer, register your IP and get this server to handle the redirect.
Your redirect_uri is set to 'http://localhost:8080/' because you pass a default(I don't know how to describe it) flags parameter to run_flow(flow, storage, flags)
if you look at the define for run_flow() function you will find this:
It presumes it is run from a command-line application and supports the
following flags:
``--auth_host_name`` (string, default: ``localhost``)
Host name to use when running a local web server to handle
redirects during OAuth authorization.
``--auth_host_port`` (integer, default: ``[8080, 8090]``)
Port to use when running a local web server to handle redirects
during OAuth authorization. Repeat this option to specify a list
of values.
``--[no]auth_local_webserver`` (boolean, default: ``True``)
Run a local web server to handle redirects during OAuth
authorization.