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

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.

Related

How to Send JWT in Request Header when using Basic Authentication in Dash (Flask)

I am writing a Dash app that requires the user to login using Basic Authentication. The user credentials are then sent to the server in the request header with every request.
What I would rather happen is that a JWT is produced on the client-side and sent in the request header (replacing the basic auth credentials in the request header) as it is much more secure. However, I am having trouble working out how to replace the request authorization headers in Dash.
The flow would be:
User inputs their credentials;
Credentials are used on client-side to grab JWT;
JWT is sent in request header with each request.
To be precise, I am unsure how to carry out step 3 above in Dash.
I am currently using dash-auth for my basic authentication, Python 3.9, Dash 2.0.0.

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

WSO2 API POST Python Web Service - Empty or No Payload to server

I am not able to send request payload to my POST service from WSO2.
On rest console, my service is working.
From WSO2 server I am able to do curl to my server with successful response.
here is my API configuration
Payload to send:
{"query":"Hi I am a POST query parameter"}
My server is receiving {} as request payload. It expect RAW body in JSON (as above) in payload. I have tried all combinations for Parameter Type, but still not able to send payload to my server from WSO2.
How can I do this?
EDIT 1
I have tried all possible ways of sending data including following.
Am I doing something wrong here???
and
From both I get error that my payload is empty or incorrect!!
Edit 2
I am able to connect with Java based services but not with Python based services.
Do I need any special settings on my python server?
enable wirelogs and check following
payload is coming into the API manager (swagger -> AM )
Payload is going out from api manager (AM -> backend)
Also check the request headers coming in and going out and compare them with the stuff from curl request (successful request)
I am using Flask and I am afraid Flask can not deal with this issue currently.
I could reproduce this issue, the message send to back-end correctly, but Python only handle message until timeout.
Python Flask cannot receive post request from WSO2
The work-round may be using Java or Python get method.
I solved this problem by using apache to proxy this request.
I think this is related with wsgi.
Processing chunked encoded HTTP POST requests in python (or generic CGI under apache)
ProxyPass / http://localhost:8001/
ProxyPassReverse / http://localhost:8001/

How to send password to REST service securely?

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.

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.

Categories