How to send password to REST service securely? - python

I am using Flask-Restful to build a REST service. The iOS device will then connect to this REST backend to sync the local data.
The service will be accessed over a https connection.
The REST service is stateless and the user has to authenticate upon each request. Hence the username and password will be sent in clear format to the REST service. The backend will hash the password and check against the existing hashed password in the database.
api.add_resource(Records, '/rest/records/<string:email>/<string:password>/<string:ios_sync_timestamp>')
Now one problem I see with this approach is that the username and password are in clear format as part of the GET url. The server log will obviously track this. Now if my backend was ever hacked into, the log files would compromise all the usernames and passwords.
What is the best solution to this? I was thinking maybe sending username and password as POST arguments, but how do I that with GET requests then?
class Records(Resource):
def get(self, email, password, ios_sync_timestamp):
pass
def post(self, email, password, ios_sync_timestamp):
pass

To authenticate each requests with a username and password like you want, you should use: Basic Authentication.
To use it, it's pretty simple and it works with all HTTP methods (GET, POST, ...). You just need to add an HTTP header into the request:
Authorization: Basic <...>
The <...> part is the username:password encoded in base64.
For example, if your login is foo and your password is bar. The HTTP header should have this line:
`Authorization: Basic Zm9vOmJhcg==`
Through your HTTPS connection, it's secure.
EDIT: Using Flask, you can use Flask HTTP auth to achieve this "automatically".

Another solution instead of the Basic Auth in each call as suggested by Sandro Munda is to generate an API Key using a POST to first check credentials request and then passing it in the request headers. Then you can verify it in each API handler for a strict-grained checking or application-wide using a #before_request handler.
Workflow
Client sends a POST to the server with the credentials (username/pass)
Server replies with an API Key. Like an hexdigest of something secret.
from now on
Each time the client needs to send an API request it adds an header (let's call it X-API-Key with the API Key.

Related

Authenticate once using a json field with Flask-HTTPAuth and Flask-RESTX

I have set up an API using configured with auth protected endpoints as described in this excellent tutorial
https://blog.miguelgrinberg.com/post/restful-authentication-with-flask
My end user, however, wants to authenticate by passing a JSON and then remain authenticated until the session expires.
The current method of authentication uses headers, as in the tutorial
$ curl -u miguel:python -i -X GET http://127.0.0.1:5000/api/resource
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 30
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Thu, 28 Nov 2013 20:02:25 GMT
{
"data": "Hello, miguel!"
}
The users want to send the following instead
curl -L -X POST 'https://api.org/auth/?json={"client_id":"CLIENT ID","client_secret":"CLIENT SECRET","grant_type":"password"}' -H 'Content-Type: application/json'
There is clearly a way to authenticate once and remain authenticated because flask-restx endpoints display the attached image when you try to use an #auth.login_required decorated endpoint. Does anyone know what code this manual login code triggers and whether I can replicate the process by passing data received into an endpoint via JSON?
I have considered an internal redirect or curl-request but this seems unnecessarily clunky
manual login via API
The username/password prompt in your image is implemented by web browsers when the server uses the Basic Authentication method. If your client is not a web browser, then there is no user interface to request username and password, you have to implement that yourself.
Your assertion that "there is clearly a way" to authenticate and remain authenticated for the duration of the session is incorrect. The HTTP protocol is stateless, which means that every request stands on its own. You think that session authentication is possible because in some situations the data that you as a client are not providing is automatically inserted by the web browser or HTTP client. Examples:
When you log in to a website and then the website remembers who you are as you navigate from page to page, the browser is inserting a session cookie into all the requests that the client sends. The server writes information about the client in the session, so that it can recover it every time a request from the client is received.
When you use the username/password Basic Authentication solution that you referenced in your question, the browser saves the username and password that you entered, and inserts an Authorization header with them into all successive requests sent during the session. From the side of the server, every request comes with a username and a password and needs to be verified from scratch.
Finally, the JSON example that you show appears to be based on the OAuth protocol. This is a fairly extensive solution with many different flows. The one that you use in your example is the Password grant, in which the client passes username and password and receives an access token in exchange. This access token must be provided in all successive requests you make.
If your client application runs in the browser, then the two options that you have for the browser to help out with the authentication are:
Use session cookies. With Flask, this can be done with the Flask-Login extension.
Use Basic Authentication, to let the browser prompt the user for credentials, which are then automatically sent in successive requests. This is fully supported by the Flask-HTTPAuth.

Connect to Mojio REST api (Python, OAuth2)

I'm trying to connect to Mojio REST api authenticated with OAuth2 with Python server code. Here's the 'manual' from Mojio: https://docs.moj.io/#/document/view/doc_oauth
I'm doing the second option there 'Authorization Code'. I don't understand what is the 'redirect_uri' parametr for. What should I pass there when I'm developing on localhost?
Thanks
You might want to consider using the "Resource Owner Password Credentials Grant" vs normal UI based oAuth workflows. This allows you to capture the username and password in your application and authenticate with those credentials instead of a UI (which can be challenging if you are on a server). Of course this depends on your specific requirements.
To login, via "Resource Owner" flow...
HTTP POST
Uri:
https://accounts.moj.io/oauth2/token
Body:
grant_type=password&username=USERNAME&password=PASSWORD&redirect_uri=REDIRECT_URI&client_id=CLIENTID&client_secret=CLIENTSECRET&scope=SCOPE
Content-Type:
application/x-www-form-urlencoded
This token you get back will be short lived, I would recommend getting a refresh token (longer lifetime):
To get a refresh token:
HTTP POST
Uri:
https://accounts.moj.io/oauth2/token
Body:
grant_type=refresh_token&refresh_token=API_TOKEN_YOU_GOT_FROM_LOGIN&redirect_uri=REDIRECT_URI&client_id=CLIENTID&client_secret=CLIENTSECRET
Content-Type:
application/x-www-form-urlencoded

Passing token to the client

I'm developing an iOS app and using Django for backend. There are two apps I'm using in Django
Django OAuth Toolkit to support OAuth authentication
Python Social Auth to support social authentication
The social authentication process should be:
GET localhost/login/{application}
Authentication on Application site
Redirect to localhost/complete/{application}
Fetch {application}'s access token
Create a new user with my server's access token, and associate it with {application}'s access token
Redirect to localhost/accounts/profile
Then, I can use my server's access token to communicate with {application}.
But the client will see that the browser start with localhost/login/{application} and end with localhost/accounts/profile, and still don't know what is my server's access token, so my question is how to pass the access token to the client?
One solution is that redirect with access token as localhost/accounts/profile?token=MyServerToken, but how to add parameter when redirecting to profile url?
You likely already have what you need on the Django session for your user in question. That is, provided you are using the session middleware (auth of this type is nearly impossible without it), your identity provider specific tokens will usually be populated in the extra_data dict on the SocialUser model for the specific provider in question.
For example, assuming you have a reference to the Django user model (lets call it user):
access_token = user.social_auth.get(provider='google-oauth2').extra_data['access_token']
Unfortunately the specifics will vary depending on the backend you're working with. Remember that these tools are designed to let users authenticate against your app rather than to let you perform arbitrary actions against the product-specific APIs exposed by the various identity providers.
As for passing these tokens to the client, I'd need to know more about your use case. Chances are the identity provider in question set some session cookies on the client during its authentication flow. For example, if you sign in with Facebook, they set a few cookies which are automatically retrieved by the Facebook client-side javascript API. As such, there's no explicit sharing of tokens necessary between the server and the client.
Otherwise, if you must do it yourself, store them on a secure session cookie as follows:
response.set_cookie(social_auth_tokens,
value=your_data_here,
max_age=None, #cookie will expire at end of user session
expires=None,
path='/',
domain=None, #only readable by this domain
secure=True, #only transmitted over https
httponly=False) #readable by scripts running on the page
You should not pass the access token in the query string like /?token=my_token. Its not a secure way and definitely not recommended.
Some other approaches you can use are:
Approach-1: Setting server_access_token in response headers
You can set the access token in the response headers and send it using HTTPS protocol.
The token will be sent once and consumed by the client. Since the response headers are not passed in the subsequent requests, the token will be passed only once to the client. Client will then use it to make further requests by setting the token in the request headers.
class MySocialApplicationRedirectView(View):
def get(self, request, *args, **kwargs):
# Here, write your code to fetch the {application}'s access token,
# creating a new user with your server's access token, and then
# associating it with {application}'s access token
# assign the response to a variable and set the access token as a header in the response
response = HttpResponseRedirect('/accounts/profile/')
response['X-Auth-Token'] = 'my_server_access_token'
# can also use the below name as 'X-' prefixed headers are deprecated
# response['Auth-Token'] = 'my_server_access_token'
return response
Client can then retrieve the token from the headers and use this token to make further requests. In further requests, he must send the access token in request headers.
Approach-2: Setting server_access_token as a cookie
Another option is to set the server_access_token cookie in your response as #Ben mentioned.
response.set_cookie() would set the server_access_token cookie in the response and then the client can read the cookie and send this in further requests in the request headers.
class MySocialApplicationRedirectView(View):
def get(self, request, *args, **kwargs):
# Here, write your code to fetch the {application}'s access token,
# creating a new user with your server's access token, and then
# associating it with {application}'s access token
# assign the response to a variable and set the access token as a cookie in the response object
response = HttpResponseRedirect('/accounts/profile/')
response.set_cookie(key, value='my_server_access_token', ..other parameters )
return response
Note: For safety and security, all requests (both to obtain and use the tokens) must use HTTPS endpoints.
It does not answer your specific question, but I've solved similar problem using TastyPie. It was very straightforward, didn't have to handle more than one application though, but since it provides an API for any given application, shouldn't be a problem.

How to create python test script to access services hosted in GAE which require login

buddies
One of my GAE restful service needs login with admin account. And I'm writing an automation script in python for testing this service. The script simply do a HTTP POST and then check the returned response. The difficult part for me is how to authenticate the test script as an admin user.
I created an admin account for testing purpose. But I'm not sure how to use that account in my test script. Is there a way that my test script can use oath2 or other approach to authenticate itself as a test admin account?
Ok I think this might be what you are looking for, client libraries to authenticate and yeah I believe appengine now recommends using the oauth2 for any kind of authentication:
https://developers.google.com/accounts/docs/OAuth2#libraries
Then you get an auth token where you pass in headers on your restful request like:
# Your authenticated request
Authorization: Bearer TokenHere
Then in your handler you get it like:
try:
user = oauth.get_current_user('https://www.googleapis.com/auth/userinfo')
except NotAllowedError:
user = None
# then from the first link you should be able to access if
user.is_current_user_admin()
This is how I authenticate on android, but I only do this once and store it in session and just enable cookie jar on the httpclient.

Looking for advice to secure a private REST API written in python-flask

I am currently writing a rest API in python with the microframework Flask. It's a private API and it deals with user data. I plan to use this API to build a web and an Android app.
For now I use digest auth to secure private user data. For example if you want to post data on my service with the user bob you make a post request at myapi/story/create and provide bob's credentials with the digest pattern.
I am aware this is not a good solution because :
-Digest auth is not secure
-The client is not authenticated (How to secure requests not related with current user, for example create a new user ?)
I read a lot of stuff about oAuth but the 3-legged authentication seems overkill because I don't plan to open my API to third party.
The 2-legged oAuth won't fit because it only provides authentification for clients and not for users.
Another problem with oAuth is that I haven't found a comprehensive guide for implementing it in Python. I found the python-oauth2 library, but I don't understand the server example and I can't find additional documentation. Plus it seems that many aspects of oAuth are not covered in this example.
So my questions are :
Is there alternative scheme (not oAuth) for authenticate both client and user with a reasonable level of security ?
If oAuth is the best solution :
How to skip the authorization process (because users won't have to authorize third party clients)?
Is there detailled documentation for python-oauth2 or for any other Python library?
Any help or advice will be appreciated.
The simple answer is to expose your API via HTTPS only, and then use HTTP Basic authentication. I don't think there's really any reason to bother with Digest. Basic authentication is insecure, but is submitted with every request so you never need to worry about your authentication going stale or whatever. By tunneling it over HTTPS, you have a secure connection.
If you want to authenticate the client, you could use SSL client certificates. That said, in general it's pretty tough to really lock down the client against malicious users, so I would consider making the sign-up functions openly accessible and protect yourself from DOS etc via out-of-band account verification.
Have you already considered to use the Basic Authentication?
I haven't used yet the framework you mentioned, but I used the basic auth to protect some urls in an app based on web.py and worked fine.
Basically, you can use a token in base64 which is actually a standard http heeader.
Maybe this example can help you:
class Login:
def GET(self):
auth = web.ctx.env.get('HTTP_AUTHORIZATION')
authreq = False
if auth is None:
authreq = True
else:
auth = re.sub('^Basic ','',auth)
username,password = base64.decodestring(auth).split(':')
if (username,password) in settings.allowed:
raise web.seeother('/eai')
else:
authreq = True
if authreq:
web.header('WWW-Authenticate','Basic realm="Auth example"')
web.ctx.status = '401 Unauthorized'
return
If you are interested in basic authentication, here is a quick attribute which you can use to decorate your handlers http://www.varunpant.com/posts/basic-authentication-in-web-py-via-attribute. This example is primarily written in web.py context, but I guess it can be easily tweaked.
def check_auth(username, password):
return username == 'username' and password == 'password'
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = web.ctx.env['HTTP_AUTHORIZATION'] if 'HTTP_AUTHORIZATION' in web.ctx.env else None
if auth:
auth = re.sub('^Basic ', '', auth)
username, password = base64.decodestring(auth).split(':')
if not auth or not check_auth(username, password):
web.header('WWW-Authenticate', 'Basic realm="admin"')
web.ctx.status = '401 Unauthorized'
return
return f(*args, **kwargs)
return decorated

Categories