I have flask server that works with SQLAlchemy, and JWT tokens.
I'm sending HTTP post request from the client that contains google-id-token.
I'm sending from the this way:
#Headers( "Content-Type: application/json" )
#POST("/auth")
Call<JsonObject> getGenericJSON(#Body JsonObject obj);
On the server:
First thing - the request gets to the security file:
from models.user import UserModel
def authenticate(username, password):
# user = username_mapping.get(username, None)
print("DEBUG: username="+username)
print("DEBUG: password="+password)
user = UserModel.find_by_username(username)
if user and user.password == password:
print ("Returning User") #email, id, last_run, password, routes
print (user)
return user
def identity(payload): #unique for jwt. Takes in a payload - the token. We'll exctract the id from the payload, and get the user_id
print("DEBUG: In identity")
user_id = payload['identity']
# return userid_mapping.get(user_id, None)
return UserModel.find_by_id(user_id)
I want to access the google token, but I don't know where is the POST request getting with this JWT authentication..
But I can't find it. I know it's supposed to be simple. It is sent for sure becuase the parser requires it. Thanks.
Edit:
I've tried :
request_data = User.parser.parse_args()
But its get to the user get function and not the post function, so it crushes...
Try token = self.request_args.get('token'). Replace 'token' with whatever identifier you're sending the token with.
Related
I have no idea what is going on here.
I have user authentication being performed using flask-login.
In lieu of a password, I am logging users in through Yahoo! social authentication, where their email is being stored and queried to find users in my SQL database.
I followed Miguel Grinberg's mega tutorial for the database implementation and flask-login implementation.
What is weird is that when I run my app locally, I never run into this problem. And also, when I initially push to heroku, everything initially works fine.
The issue is happening seeminly at random, where users are logged out (there is absolutely no reason for this on the flask end), and then once they are logged out, they are unable to log back into flask-login - only AnonymousUser is returned.
What is even more weird, is that I am able to return information about my user object.
Here is my user model class:
from app import db, login
from flask_login import UserMixin
# Follow this link for database management https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
refresh_token = db.Column(db.String(128))
access_token = db.Column(db.String(128))
access_token_expiry = db.Column(db.DateTime)
email = db.Column(db.String(120), index=True, unique=True)
def __repr__(self):
return '<User {}>'.format(self.email)
#login.user_loader
def load_user(id):
return User.query.get(int(id))
And here is my login route:
#bp.route('/login/callback')
def login_callback():
# Exchange user code for authentication token, which we will store as a "social_id", unique to each user
user_code = request.args.get('code', type=str)
if os.environ.get('FLASK_ENV') == 'production':
client_id = os.environ.get('YAHOO_CLIENT_ID')
client_secret = os.environ.get('YAHOO_CLIENT_SECRET')
redirect_uri = os.environ.get('YAHOO_REDIRECT_URI')
else:
client_id = os.environ.get('DEV_YAHOO_CLIENT_ID')
client_secret = os.environ.get('DEV_YAHOO_CLIENT_SECRET')
redirect_uri = os.environ.get('DEV_YAHOO_REDIRECT_URI')
uri = 'https://api.login.yahoo.com/oauth2/get_token'
data = {
'client_id': client_id,
'client_secret': client_secret,
'redirect_uri': redirect_uri,
'code': user_code,
'grant_type': 'authorization_code'
}
response = requests.post(uri, data)
r_data = response.json()
access_token = r_data['access_token']
refresh_token = r_data['refresh_token']
expires_in = r_data['expires_in']
access_token_expiry = datetime.utcnow() + timedelta(0, expires_in)
# From the response, we get an authentication token, which we can use to create api requests,
# as well as a user unique refresh token, which we will store as a unique user
# To check if user exists in our database, we will request the users email from Yahoo!, which has to be unique
uri = 'https://api.login.yahoo.com/openid/v1/userinfo'
data = {
'Content-type': 'application/json',
'Authorization': 'Bearer ' + access_token
}
response = requests.get(uri, headers=data)
r_data = response.json()
user_email = r_data['email']
# Check email, see if user already exists
user = User.query.filter_by(email=user_email).first()
# If email does not exist, create user
if user is None:
u = User(email=user_email, refresh_token=refresh_token,
access_token=access_token, access_token_expiry=access_token_expiry)
db.session.add(u)
db.session.commit()
user = User.query.filter_by(email=user_email).first()
print('User Created')
else:
# Check if refresh token has changed, if it has, update the refresh token
if user.refresh_token != refresh_token:
u = User.query.filter_by(email=user_email).first()
u.access_token = access_token
u.access_token_expiry = access_token_expiry
u.refresh_token = refresh_token
print('User refresh token refreshed')
db.session.commit()
login_user(user=user, remember=True)
print(user.refresh_token)
print('User Logged In')
return redirect(url_for('main.index'))
The bulk of the login route is the yahoo social authentication, but the login method is called near the end.
Everything worked fine for approximately the past year, and just recently this issue started to occur. I have NO IDEA what is going on, and am pulling my hair out trying to debug this. Please, can anyone give me some sort of indication what could be going wrong here?
I have included a picture showing the error when it happens. Again, nothing changed, just randomly forced to re-login, when this error appears, which doesn't allow me to log in again. You can see that the user's refresh token is printed, thus the user being passed to login_user() is a valid user.
Any information is greatly greatly appreciated.
I have an endpoint that needs an Access Token for authentication.
Also in this endpoint, there's a serializer UUID field that is not required.
What I would like to do is:
In case the UUID field is passed in the body, check if this UUID is valid and registered in the Database.
If it's not valid it'll raise a DRF ValidationError.
If no UUID field is passed then the request needs to be authenticated with an Access Token.
Is there a way to have this kind of seletive auth verification?
Well, after some time I figured out the solution.
I opened the permission to access the view to any user, and inside the post method, I verified if the user is anonymous in order to check the request body keys. In case a valid JWT access token is passed, a user would be found and this would return False. If any of these conditions return True a simple credential error response is given.
permission_classes = [
AllowAny,
]
def post(self, request, *args, **kwargs):
if request.user.is_anonymous:
request_token = request.POST.get('token')
token = Token.objects.filter(token=request_token)
if token.exists() and token.values()[0]['status'] == 'valid':
pass
else:
return Response(
data={"Invalid Credentials": "Pass a valid credential."},
status=status.HTTP_200_OK,
i'm doing my own project.
communicate python program - django server.
first is when program send information about signup(like name, password, id etc.) server return success signal.
next step is when program send login information about sign(like name, password), server return jwt token and program receive jwt token.
I'm try everything what i know... but i don't know how to return jwt token to python program.
any idea?
Assuming you already have a proper way to generate the token correctly:
create an endpoint to login with credentials (note the csrf_exempt to allow POST calls from your program)
path('/login', csrf_exempt(login)) )
create a view to process the request - to protect the credentials, expect them as the payload of a POST request:
#require_POST
def login(request):
username = request.data.get('username', None)
password = request.data.get('password', None)
if username is None:
return HttpResponseBadRequest('username is missing')
if password is None:
return HttpResponseBadRequest('password is missing')
# validate the user/credentials
your_function_to_validate(username, password)
jwt = your_function_to_generate_and_save_the_JWT(username, password)
return HttpResponse(jwt)
call the endpoint using the Python program:
url = base_url + '/login'
credentials = {
'username': 'admin',
'password': '12345'
}
res = post(url, data=credentials)
if res.status_code != 200:
# deal with bad credentials
pass
jwt = res.data
Background:
I'm trying to prototype a quick token-based authentication using Flask-Restful and PyJWT. The idea is that I will have a form with email and password and when user clicks submit, it will generate a token and save it in client side browser and use it in any subsequent requests until the token expires.
Trouble
In my prototype, I was able to create a token using JWT but I don't have an idea of how to pass the JWT into a subsequent request. When I do it in the Postman, it works because I can specify the Authorization header with token in there. But when I login in through UI and token is generated, I do not know how to pass the token generated into a subsequent request (/protected) by making the token persists in the header until it expires. Currently when I login from UI and go to /protected, the Authorization header is missing in /protected header.
Code
class LoginAPI(Resource):
# added as /login
def get(self):
"""
renders a simple HTML with email and password in a form.
"""
headers = {'Content-Type': 'text/html'}
return make_response(render_template('login.html'), 200, headers)
def post(self):
email = request.form.get('email')
password = request.form.get('password')
# assuming the validation has passed.
payload = {
'user_id': query_user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=10)
}
token = jwt\
.encode(payload, current_app.config['JWT_SECRET'], current_app.config['JWT_ALGORITHM'])\
.decode('utf-8')
# Is below the right way to set the token into header to be used in subsequent request?
# request.headers.authorization = token
# when {'authorization': token} below as a header, the header only shows up for /login not for any subsequent request.
return make_response({'result': 'success', 'token': token}, 200, {'authorization': token} )
class ProtectedAPI(Resource):
#check_auth
def get(self):
return jsonify({'result': 'success', 'message': 'this is a protected view.'})
# decorator to check auth and give access to /protected
def check_auth(f):
#wraps(f)
def authentication(*args, **kws):
# always get a None here.
jwt_token = request.headers.get('authorization', None)
payload = jwt.decode(jwt_token, 'secret_key', algorithms='HS512'])
# other validation below skipped.
return f(*args, **kws)
return authentication
To persist the access_token you have to store on the client and pass it into your header every time you call your backend and check the authenticity of the token every time.
Maybe a stupid question here:
Is Requests(A python HTTP lib) support Django 1.4 ?
I use Requests follow the Official Quick Start like below:
requests.get('http://127.0.0.1:8000/getAllTracks', auth=('myUser', 'myPass'))
but i never get authentication right.(Of course i've checked the url, username, password again and again.)
The above url 'http://127.0.0.1:8000/getAllTracks' matches an url pattern of the url.py of a Django project, and that url pattern's callback is the 'getAllTracks' view of a Django app.
If i comment out the authentication code of the 'getAllTracks' view, then the above code works OK, but if i add those authentication code back for the view, then the above code never get authenticated right.
The authentication code of the view is actually very simple, exactly like below (The second line):
def getAllTracks(request):
if request.user.is_authenticated():
tracks = Tracks.objects.all()
if tracks:
# Do sth. here
Which means if i delete the above second line(with some indents adjustments of course), then the requests.get() operation do the right thing for me, but if not(keep the second line), then it never get it right.
Any help would be appreciated.
In Django authentication works in following way:
There is a SessionMiddleware and AuthenticationMiddleware. The process_request() of both these classes is called before any view is called.
SessionMiddleware uses cookies at a lower level. It checks for a cookie named sessionid and try to associate this cookie with a user.
AuthenticationMiddleware checks if this cookie is associated with an user then sets request.user as that corresponding user. If the cookie sessionid is not found or can't be associated with any user, then request.user is set to an instance of AnonymousUser().
Since Http is a stateless protocol, django maintains session for a particular user using these two middlewares and using cookies at a lower level.
Coming to the code, so that requests can work with django.
You must first call the view where you authenticate and login the user. The response from this view will contain sessionid in cookies.
You should use this cookie and send it in the next request so that django can authenticate this particular user and so that your request.user.is_authenticated() passes.
from django.contrib.auth import authenticate, login
def login_user(request):
user = authenticate(username=request.POST.get('username'), password=request.POST.get('password'))
if user:
login(request, user)
return HttpResponse("Logged In")
return HttpResponse("Not Logged In")
def getAllTracks(request):
if request.user.is_authenticated():
return HttpResponse("Authenticated user")
return HttpResponse("Non Authenticated user")
Making the requests:
import requests
resp = requests.post('http://127.0.0.1:8000/login/', {'username': 'akshar', 'password': 'abc'})
print resp.status_code
200 #output
print resp.content
'Logged In' #output
cookies = dict(sessionid=resp.cookies.get('sessionid'))
print cookies
{'sessionid': '1fe38ea7b22b4d4f8d1b391e1ea816c0'} #output
response_two = requests.get('http://127.0.0.1:8000/getAllTracks/', cookies=cookies)
Notice that we pass cookies using cookies keyword argument
print response_two.status_code
200 #output
print response_two.content
'Authenticated user' #output
So, our request.user.is_authenticated() worked properly.
response_three = requests.get('http://127.0.0.1:8000/hogwarts/getAllTracks/')
Notice we do not pass the cookies here.
print response_three.content
'Non Authenticated user' #output
I guess, auth keyword for Requests enables HTTP Basic authentication which is not what is used in Django. You should make a POST request to login url of your project with username and password provided in POST data, after that your Requests instance will receive a session cookie with saved authentication data and will be able to do successful requests to auth-protected views.
Might be easier for you to just set a cookie on initial authentication, pass that back to the client, and then for future requests expect the client to send back that token in the headers, like so:
r = requests.post('http://127.0.0.1:8000', auth=(UN, PW))
self.token = r.cookies['token']
self.headers = {'token': token}
and then in further calls you could, assuming you're in the same class, just do:
r = requests.post('http://127.0.0.1:8000/getAllTracks', headers=self.headers)