Irregular "write ECONNABORTED Error" with Flask request.authorization - flask

I'm building python web API through Flask. I'm trying to add authorization mechanism with flask.request.authorization method. It works great when correct user information are provided. However, when I was trying to input unauthorized user information, it returns "Error: write ECONNABORTED" irregularly. What are the possibles causes of this?
I use Postman and input the authorization info under "Authorization" tab in Postman with type "Basic Auth".
For example, when input "Username" as "eric" and "Password" as "secret" in Postman "Authrization" tab, it works fine. But if input "Username" as "debbie" and "Password" as "secret", it sometimes shows "Error: write ECONNABORTED"
app = Flask(__name__)
def auth_required(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if auth and auth.username == 'eric' and auth.password == 'secret':
return f(*args, **kwargs)
return 'could not verify'
return decorated
#app.route('/upload', methods=['GET', 'POST'])
#auth_required
def upload():
foo()
return render_template('upload.html')
"SSL certificate verification" is off, "Request timeout in ms" is set to 1000, "Use the system proxy" is checked

Related

GitHub user authentication in GitHub application through Django framework

I want to authenticate GitHub user to my GitHub application and serve to my local server 127.0.0.1:8000, but I am not able to take tokens.
This is how GitHub is showing authentication.
From GitHub documentation, I am not able to understand the process of authentication after generating private key, then how to create JWT and installation tokens ?
Could someone show me what to do next ?
You can follow "Obtaining an Access Token from a GitHub Application Webhook" (Jerrie Pelser), which itself takes from "JWT RSA & HMAC + ASP.NET Core" from Piotr Gankiewicz
Jerrie mentions as first step to convert your PEM file to XML format.
You can use an online tool or write a class
And you need your GitHub application Id:
You will find in the article the class JwtSecurityTokenHandler used to create the JSON Web Token from the XML key.
In Django:
#api_view(['POST'])
#permission_classes([AllowAny, ])
def authenticate_user(request):
try:
email = request.data['email']
password = request.data['password']
user = User.objects.get(email=email, password=password)
if user:
try:
payload = jwt_payload_handler(user)
token = jwt.encode(payload, settings.SECRET_KEY)
user_details = {}
user_details['name'] = "%s %s" % (
user.first_name, user.last_name)
user_details['token'] = token
user_logged_in.send(sender=user.__class__,
request=request, user=user)
return Response(user_details, status=status.HTTP_200_OK)
except Exception as e:
raise e
else:
res = {
'error': 'can not authenticate with the given credentials or the account has been deactivated'}
return Response(res, status=status.HTTP_403_FORBIDDEN)
except KeyError:
res = {'error': 'please provide a email and a password'}
return Response(res)

Django reset password url in email only works if copy and paste it, not click on it

When I request a password reset with the built in django machinery, it will generate a URL in the email like this:
https://example/accounts/reset/MTA/atspc3-7c45df8a600243fde3dfb60c44873f15/
In gmail if I go show URL, the text is indentical since it is being generated as a plain/text email. If I click the link in gmail, or outlook then it goes to (maybe after a redirect, I can't tell since its so fast):
https://example.com/accounts/reset/MTA/set-password/
And it says
Password reset unsuccessful
The password reset link was invalid, possibly because it has already been used. Please request a new password reset.
However if I copy and paste the URL into the browser, then it works. However no-one copies and paste the URL, they all click the link and say it does not work.
I don't get why clicking on the link does not work since it is correct?
I managed to add logging to Django's code, I added logging to:
contrib.auth.views -> PasswordResetConfirmView.dispatch (line 247)
As you can see from the following two logs, in both scenarios they are being called with exactly the same locals (I print locals()), however this view seems to set the token in the session, then redirect. however after the redirect, if one cicked the link in the email, the session cookie is gone, and hence it fails. I still don't understand why
COPY and paste URL (Working)
------------------------------------------------------------
# First call to dispatch:
token: atspc3-7c45df8a600243fde3dfb60c44873f15
Entered dispatch {'self': <django.contrib.auth.views.PasswordResetConfirmView object at 0x7f20e63c2220>, 'args': (<WSGIRequest: GET '/accounts/reset/MTA/atspc3-7c45df8a600243fde3dfb60c44873f15/'>,), 'kwargs': {'uidb64': 'MTA', 'token': 'atspc3-7c45df8a600243fde3dfb60c44873f15'}, 'token': 'atspc3-7c45df8a600243fde3dfb60c44873f15', '__class__': <class 'django.contrib.auth.views.PasswordResetConfirmView'>}
user is not None
token is NOT reset
token atspc3-7c45df8a600243fde3dfb60c44873f15
Verfiy sessions token atszyc-e7c578496e3438f9dee367fcbebfabb1
redirect url /accounts/reset/MTA/set-password/
Djnago logging start
------------------------------------------------------------
# Second call to dispatch (after redirect):
token: set-password
Entered dispatch {'self': <django.contrib.auth.views.PasswordResetConfirmView object at 0x7f20e63c2220>, 'args': (<WSGIRequest: GET '/accounts/reset/MTA/set-password/'>,), 'kwargs': {'uidb64': 'MTA', 'token': 'set-password'}, 'token': 'set-password', '__class__': <class 'django.contrib.auth.views.PasswordResetConfirmView'>}
user is not None
token is self reset, session token: atspc3-7c45df8a600243fde3dfb60c44873f15
CLICK link in email (Not working)
------------------------------------------------------------
# First call to dispatch:
token: atspc3-7c45df8a600243fde3dfb60c44873f15
Entered dispatch {'self': <django.contrib.auth.views.PasswordResetConfirmView object at 0x7f20d9a77190>, 'args': (<WSGIRequest: GET '/accounts/reset/MTA/atspc3-7c45df8a600243fde3dfb60c44873f15/'>,), 'kwargs': {'uidb64': 'MTA', 'token': 'atspc3-7c45df8a600243fde3dfb60c44873f15'}, 'token': 'atspc3-7c45df8a600243fde3dfb60c44873f15', '__class__': <class 'django.contrib.auth.views.PasswordResetConfirmView'>}
user is not None
token is NOT reset
token atspc3-7c45df8a600243fde3dfb60c44873f15
Verfiy sessions token atszyc-e7c578496e3438f9dee367fcbebfabb1
redirect url /accounts/reset/MTA/set-password/
------------------------------------------------------------
# Second call to dispatch (after redirect):
token: set-password
Entered dispatch {'self': <django.contrib.auth.views.PasswordResetConfirmView object at 0x7f20d9adde20>, 'args': (<WSGIRequest: GET '/accounts/reset/MTA/set-password/'>,), 'kwargs': {'uidb64': 'MTA', 'token': 'set-password'}, 'token': 'set-password', '__class__': <class 'django.contrib.auth.views.PasswordResetConfirmView'>}
user is not None
token is self reset, session token: None
Here are the log statements I added to contrib.auth.views -> PasswordResetConfirmView.dispatch (line 247) :
#method_decorator(never_cache)
def dispatch(self, *args, **kwargs):
log_info('------------------------------------------------------------')
token = kwargs['token']
log_info('token:', token)
log_info('Entered dispatch', str(locals()))
assert 'uidb64' in kwargs and 'token' in kwargs
self.validlink = False
self.user = self.get_user(kwargs['uidb64'])
if self.user is not None:
token = kwargs['token']
log_info('user is not None')
if token == self.reset_url_token:
session_token = self.request.session.get(INTERNAL_RESET_SESSION_TOKEN)
log_info('token is self reset, session token:', session_token)
if self.token_generator.check_token(self.user, session_token):
# If the token is valid, display the password reset form.
self.validlink = True
return super().dispatch(*args, **kwargs)
else:
log_info('token is NOT reset')
if self.token_generator.check_token(self.user, token):
# Store the token in the session and redirect to the
# password reset form at a URL without the token. That
# avoids the possibility of leaking the token in the
# HTTP Referer header.
self.request.session[INTERNAL_RESET_SESSION_TOKEN] = token
log_info('token', token)
log_info('Verfiy sessions token', self.request.session[INTERNAL_RESET_SESSION_TOKEN])
redirect_url = self.request.path.replace(token, self.reset_url_token)
log_info('redirect url', redirect_url)
return HttpResponseRedirect(redirect_url)
else:
log_info('failed token generator check')
Note, in both cases the request has the following attributes (I thought it might be a secure cookie issue):
request.scheme: https
request.is_secure: True
request.full path /accounts/reset/MTA/set-password/
or
request.full path /accounts/reset/MTA/atspc3-7c45df8a600243fde3dfb60c44873f15/
In the rare case someone else has this issue, I had in my settings file:
SESSION_COOKIE_SAMESITE = 'Strict'
One must change the cookie to persist from an outside domain (the email client) as follows:
SESSION_COOKIE_SAMESITE = 'Lax' # default value
Refer to https://docs.djangoproject.com/en/stable/ref/settings/#session-cookie-samesite

Django+React: What is the bet way to properly implement authentication and security on API requests from React to Django backend

I am working on a Django+React project. My React app is my only UI renderer (which means no template or any markup is being served by Django), while my Django app is only used for serving APIs for me to access my database resources.
On a particular React page, I have login and test buttons that send requests to a DRF API in my Django backend.
Login API View
class AuthLogin(generics.GenericAPIView):
serializer_class = LoginSerializer
authentication_classes = [SessionAuthentication]
permission_classes = ()
def get(self, request):
user = request.user
if not user and not user.is_authenticated or user.is_anonymous:
return Response(
{
"status": True,
"message": "Login page accessed"
}, status=status.HTTP_200_OK
)
else:
return Response(
{
"status": False,
"message": "You are currently logged in"
}, status=status.HTTP_400_BAD_REQUEST
)
def post(self, request, *args, **kwargs):
res = []
res_stat = status.HTTP_401_UNAUTHORIZED
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=False)
validated_data = serializer.validated_data
if validated_data:
user = dj_auth(username=request.data.get('username'),
password=request.data.get('password'))
if user and user.is_authenticated and not user.is_anonymous:
auth_login(request, user)
res_stat = status.HTTP_200_OK
res = {
"status": True,
"message": "Login successful!",
"login": {
"date_time": timezone.now(),
"user": {
'id': user.id,
'first_name': user.first_name,
'last_name': user.last_name,
'email': user.email,
}
}
}
else:
res_stat = status.HTTP_401_UNAUTHORIZED
res = {
"status": False,
"message": "Invalid username and/or password"
}
else:
res_stat = status.HTTP_401_UNAUTHORIZED
res = {
"status": False,
"message": "Invalid username and/or password"
}
return Response(res, status=res_stat)
Test API View
class Test(APIView):
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request, *args, **kwargs):
if request.user and request.user.is_authenticated and not request.user.is_anonymous:
return Response({
"test": "coolsss",
"user": request.user.id
}, status=status.HTTP_200_OK)
else:
return Response({
"error": "Mind to login first?"
}, status=status.HTTP_401_UNAUTHORIZED)
So far so good in terms of Django+React integration on the above APIs. However, I noticed a security issue and I'm not sure completely how to move on from this.
I did the test in an incognito browser window. But, I also tested it in Postman. What I noticed was I already closed my incognito browser before testing on to Postman using the same csrftoken and sessionid cookies. When I closed the browser, I assume the session is destroyed since it was in incognito.
Now when I run the same in Postman, the test endpoint still authenticating, recognizes the user, and returns the positive response. I'm not sure if that is supposed to happen, and what approach shall be used to resolve this.
One sample scenario I see where this would be a nightmare is say Kate logs in and leaves her workstation for a minute. Her workmate John sneaks in to her browser and steals the csrftoken and sessionid cookie values of her current session.
Kate came back and for some reason closed her browser. John, using Postman on his own computer, made some POST request to an API which Kate is authorized to access using the token and sessionid he stole.
With this problem in session/cookie-based authentication, I am considering implementing token-based authentication instead, even if the login view is more appropriate to be authenticated by a server session and csrftoken cookie.
However, if I use token-based, I would also be storing that token in local storage so React will have the knowledge that the user is still logged in and for it to use that token for subsequent requests. But then again, a geek can always go to dev tool and sneak into that token value in the local storage.
I am so stuck at how I should properly implement authentication and security in the backend, while keeping the frontend of the user's login state.
Token-based (JWT for example) is the go-to option for integrating a server-side service to a client-side app. And as for the problem you are facing, the simplest option would be adding an expire option to the token which still might not be the solution to all scenarios.
The fact is that when you create a client-side app, the client has control and you can't do much about it. protecting the token is the same as protecting a password that users should be thinking about. The most you can do is to make it harder for someone else to be able to simply take the token and use it somewhere else.
You can make multiple tokens with different specifications that the client-side has to generate and if all of them match, then you can serve the request. but again, that just makes it harder for someone else to duplicate the request not impossible.
If it was a compiled app that was hard to decompile and with the combination of some methods like using some certificate file that is also securely stored in the client's system, you could be more sure about the safety of your request but JS is too simple to read and duplicate.

Ionic Google social authentication to Django Rest Framework backend

I am trying to get social authentication working for my mobile app (an Ionic app on Android). Django rest framework backend with rest_framework_jwt, social_django, and rest_social_auth.
On my Ionic app I was using satellizer.js, however, I can't use InAppBrowser so now I am trying to do the following with cordova-plugin-googleplus:
Step#1 (On client/app)
if (provider == 'google') {
// Use Google API Natively to get tokens and user info
window.plugins.googleplus.login(
{
// TODO Get the WebClient from App settings
'webClientId': '[*.myclientid]', // optional clientId of your Web application from Credentials settings of your project - On Android, this MUST be included to get an idToken. On iOS, it is not required.
'offline': true, // optional, but requires the webClientId - if set to true the plugin will also return a serverAuthCode, which can be used to grant offline access to a non-Google server
}) ................
Result: This gets me a valid response with both a idToken, serverAuthCode, and a userId.
Step#2
I am not sure what the next step is. Originally, I was going to try using Django rest_social_auth to do the following from my client/app:
POST /api/login/social/
with data (json)
provider=google&code=ASLKDJASLDKJASLD
Which was supposed to return a JWT token (from my understanding of the docs), however, it is not passing the JWTAuthMixin as there is no value returned from a call to get_authorization_header(request).split() in that Mixin. These means that nothing is returned to my client/app except a 400 error.
Am I supposed to be adding a header to my Ionic app POST when passing my idToken or serverAuthCode? Or am I on the wrong side of the tracks...
Are there any implementation recommendations for this auth flow?
So far I did the following and it works.
1. On app/client
(The client uses satellizer.js and the cordova-plugin-googleplus)
if (provider == 'google') {
// Use Google API Natively to get tokens and user info
window.plugins.googleplus.login(
{
// TODO Get the WebClient from App settings
'webClientId': '*[googleclientid]*.apps.googleusercontent.com',
'offline': true
},
function (obj) {
$http.post(SERVER.url + '[MY BACKEND URL]' + '/google-oauth2/', {code: obj.idToken, servAuthCode: obj.serverAuthCode})
.success(function(data){
$auth.setToken(data.jwt_token);
/.. Do something ../
})
.error(function(data){
console.log("There was an error" + JSON.stringify(data));
});
},
function (msg) {
// TODO Set Error states
console.error('error: ' + msg);
}
);
}
Summary
The app calls the Google plus API googleplus.login method (sending my webClientId)
I post the resulting idToken and serverAuthCode obtained from google after login to my Django backend.
2. My backend methods
URL
My app/client hits the url(r'^[MY BACKEND URL]/(?P<backend>[\w-]+)/$', ObtainAuthToken.as_view(), ),
View
This calls the following view and functions:
class ObtainAuthToken(APIView):
permission_classes = (AllowAny,)
def post(self, request, backend):
data = request.data
user_tokenID = data['code']
server_auth_code = data['servAuthCode']
if user_tokenID and server_auth_code and verify_google_user_token_ID(user_tokenID):
# Get Google OAuth credentials for the verified GOOGLE user.
credentials = settings.GOOGLE_FLOW.step2_exchange(server_auth_code)
# Here we call PSA to authenticate like we would if we used PSA on server side.
user = register_by_access_token(request, backend, token=credentials.access_token)
# If user is active we get or create the REST token and send it back with user data
if user and user.is_active:
# Generate JWT token for user and pass back to client
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return JsonResponse({'id': user.id, 'name': user.username, 'jwt_token': token})
return JsonResponse({'status':'false','error':'Bad Credentials, check the Access Token and/or the UID'},
status=403)
def verify_google_user_token_ID(user_tokenID):
try:
google_http_request = google.auth.transport.requests.Request()
idinfo = verify_token(user_tokenID, request=google_http_request,
audience=settings.SOCIAL_AUTH_GOOGLE_OAUTH2_FULL_KEY)
# Or, if multiple clients access the backend server:
if idinfo['aud'] not in [settings.GOOGLE_APP_ID_ANDROID, settings.GOOGLE_APP_ID_WEB]:
raise crypt.AppIdentityError("Unrecognized client.")
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise crypt.AppIdentityError("Wrong issuer.")
return True
except crypt.AppIdentityError as e:
# Invalid token
return False
#psa('social:complete')
def register_by_access_token(request, backend, token):
backend = social_core.backends.google.GoogleOAuth2()
user = backend.do_auth(access_token=token, backend=backend)
if user:
return user
else:
return None
3. Back on the client
My client then looks at the response and takes the returned JWT and loads it to memory with $auth.setToken(data.jwt_token);
I think this works for now, but I still have to deal with token refresh and revocation etc.

Locust: Understanding cookie creation while using locust login

I am trying to use locust for login to my web application. I am at very beginning of using locust.
I am using the following code for login to application.
post_data = {'username': username, 'password': password,'Submit':'Login' }
with self.client.post('/mylogin-url/', post_data,
catch_response=True) as response:
print response.code
print response.content
This part always returns status code 200 but the login is not succesful, as the response content of source is not the same as the actual source after login
My web application creates cookies and redirect to an URL based on cookie after login. I am trying to understand whether the login operation does this cookie creation automatically from locust or do I need to add that part of cookie creation logic n the script itself.
Any help regarding this is greatly appreciated.
Thanks
You may need to look at this.
In your with block, you can parse the response and check if you get the correct response.
Suppose you should get cookie field in your response cookies if you login success, then you can do like this:
post_data = {'username': username, 'password': password,'Submit':'Login' }
with self.client.post('/mylogin-url/', post_data, \
catch_response=True) as response:
if 'cookie' not in respone.cookies:
response.failure('login failed')
It seems that the response in the html content tells that user hasn't logged in. In that case you can check if response text contains failure message
post_data = {'username': username, 'password': password,'Submit':'Login' }
with self.client.post('/mylogin-url/', post_data,
catch_response=True) as response:
if response.text.contains("Login failed"):
response.failure('login failed')