How does timed JSON web signature serializer work? - flask

Can I restrict actions of my API to specific users if I generate a token like this:
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
expiration = 600
s = Serializer(current_app.config['SECRET_KEY'], expires_in = expiration)
return s.dumps({ 'id': kwargs.get('user_id') })
And the verification
#staticmethod
def verify_auth_token(token):
s = Serializer(app.config['SECRET_KEY'])
try:
data = s.loads(token)
except SignatureExpired:
return None # valid token, but expired
except BadSignature:
return None # invalid token
user = User.query.get(data['id'])
return user
I don't understand how this works and achieves security. The way I'm used to securing an API for example, a user wants to do HTTP PUT to /posts/10 I'd usually get the post's author ie user_id then query the database get the token for that user_id, if the request token matches the queried token then it is safe for the PUT. I've read this article and don't fully understand how it achieves security without storing anything in a database. Could someone explain how it works?

By signing and sending the original token upon login the server basically gives the front end an all access ticket to the data the user would have access to, and the front end uses that token (golden ticket) on all future requests for as long as the token is not expired (tokens can be made to have expiration or not). The server in turn knows the token has not been tampered with, because the signature is basically the encrypted hash of the users recognizable data (user_id, username, etc). So, if you change the token information from something like:
{"user_id": 1}
to something like:
{"user_id": 2}
then the signature would be different and the server immediately knows this token is invalid.
This provides an authentication method that exempts the server from having to have a session, because it validates the token every time.
Here is an example of what a token could look like (itsdangerous can use this format of JSON web tokens)

Related

Is it secure to use firebase tokens for authentication between my server and an application?

I'm thinking about using firebase to authenticate users. Users could sign up via a pyqt app. Their email address and password would be sent via a POST request to my server (Django). On my server, I would use firebase (pyrebase) to sign them up. I would then store the firebase token in the database on my server, and also return the token to the user and save it there locally. Afterwards, I would always (or until the user logs in again which would return a new token) use this token to authenticate the user and let them access my database. Would this be secure? Something like this:
On sign-up:
User:
token = requests.post(url, {"email": email, "pwd": pwd)
Server:
response = auth.create_user_with_email_and_password(email, pwd)
token = response['idToken']
c.execute('INSERT INTO Tabel (email, token) VALUES(?, ?)', (email, token))
return token
After sign-up:
User:
some_data = requests.post(url, {"token": token, "email": email)
Server:
token_in_database = c.execute("SELECT token FROM Tabel WHERE email = (?)", (email,)).fetchone()
if token == token_in_database:
return some_data
What you're describing is somewhat similar to what Firebase does with its Authentication tokens, so it can indeed work quite well. :)
A few things to keep in mind:
Be sure to only send the credentials from the client to the server, and the ID token from the server to the client, over a secure connection. This reduces the chance of a man-in-the-middle gaining access to either the credentials or the token.
If you want the authentication state of the user to be available to other Firebase products, don't forget to sign the user in with the custom token on the client.
Do you really need to store the token in your database? The Firebase servers do typically not do this, and instead verify the token when they first get it. They do cache the verification state locally, but there's no central database of current ID tokens which helps scalability.
You'll want the token to have a reasonably short expiration time, just in case a malicious actor gets access to it. Firebase itself uses ID tokens that are valid for 1 hour, and the SDKs automatically renew then after about 55 minutes.
If you want a shorter or longer lifetime for your tokens, consider using a session cookie which can be valid anywhere from 5 minutes to 2 weeks.

How to revoke refresh token on password reset?

I am using django-rest-framework-simplejwt to get access token and refresh token .
The problem is that refresh token is not becoming invalid if I change the password of the user. Basically I can continue to send refresh token and get new access tokens even after user has changed password.
What I would like instead is to ask user to re-submit the username and new password to get a new pair of access and refresh tokens.
How would I accomplish this?
PS: Just because I am curious, shouldn't this be the default behaviour of the library? In what case would we want to retain the refresh token after credentials have changed?
I figured how to get this working.
What I am did is put a signal that tracks if any required parameter has changed. If so, it blacklists all the refresh tokens associated with that user.
Here is the code:
First add 'rest_framework_simplejwt.token_blacklist' in installed apps. Then:
#receiver(signals.pre_save, sender=User)
def revoke_tokens(sender, instance, update_fields, **kwargs):
if not instance._state.adding: #instance._state.adding gives true if object is being created for the first time
existing_user = User.objects.get(pk=instance.pk)
if instance.password != existing_user.password or instance.email != existing_user.email or instance.username != existing_user.username:
# If any of these params have changed, blacklist the tokens
outstanding_tokens = OutstandingToken.objects.filter(user__pk=instance.pk)
# Not checking for expiry date as cron is supposed to flush the expired tokens
# using manage.py flushexpiredtokens. But if You are not using cron,
# then you can add another filter that expiry_date__gt=datetime.datetime.now()
for out_token in outstanding_tokens:
if hasattr(out_token, 'blacklistedtoken'):
# Token already blacklisted. Skip
continue
BlacklistedToken.objects.create(token=out_token)
WHat this code basically does is , gets all outstanding tokens for the user, then adds all of them to blacklist. You can get more info on outstanding/blacklisted tokens here.
https://github.com/davesque/django-rest-framework-simplejwt#blacklist-app

Manually validate flask-extended-jwt's access token

I have a SPA app that contains an form with an upload file field. I have a rest API whose endpoints are protected via flask-extended-jwt JWT. To authenticate the REST endpoints I use #jwt_required. I want to authenticate the upload request as well.
Because of the client side I can't add an Authorization Bearer header so I thought to add the access token as a hidden field when submitting the form.
What is the best way to manually validate the JWT access token after I read it from the form?
class Upload(Resource):
def post(self):
#TODO: check for access token
access_token = None
if 'access_token' in request.form and request.form['access_token']:
access_token = request.form['access_token']
else:
message = json.dumps({'message': 'Invalid or missing token', 'success': False})
return Response(response=message, status=401, mimetype='text/plain')
if access_token:
#TODO: validate_token(access_token)
Thank you
Author of flask-jwt-extended here. That's a great question. There is currently no supported way to do that in the extension, the grabbing the token from the request and decoding it are tightly coupled together. This would be hard to de-couple because there is a lot of conditional things that are going on when the full decode chain runs. For example, checking the CSRF value only if the request is sent in via a cookie, or differentiating between an access and refresh token for the sake of the blacklisting feature.
A generalized function could be created, it's signature would look something like decode_and_verify_jwt(encoded_token, is_access_token=True, check_csrf=False). However, this would complicate the rest of the code in flask_jwt_extended and be a rather confusing function to use for the general case.
I think in this case it would be easier just to add a fourth lookup in the extension, so you could use something like:
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'forms']
app.config['JWT_FORM_KEY'] = 'access_token'
# Use the rest of the application normally
If you want to make a ticket on the github page so I can track this, I would be happy to work on it.

Flask helper method to find user from header

I am playing around with Flask. I like that it is fairly thin and works for most of my requirements.
I would like to know what is your recommended way of retrieving the current logged in user. I would like every HTTP request, which is made, to pass/carry a token in the header, which is first retrieved by the login api
/user/login (params: username, password)
# returns {success: True, token: "<some-unique-string>"
Now is the subsequent APIs I would like to get the user object from the from passed token, like so
#app.route("/user/info", methods = ["GET"])
#apify
def user_get_info():
return {"name": current_user().name}
How could I have current_user read from the header without having to pass the request object every time ?
Any thoughts?
Every token is stored against a user. So you can make a query to get a user against a given token. Something like:
token = Token.query.get(token='token_value')
return jsonify({'name: User.query.get(id=token.user_id).name})
This can be done in one line as well but it totally depends upon your models and relationships.
Hope that helps. If I haven't understood your question correctly, do elaborate a little.

Why am I getting this Authentication required error even though I am using my client id and client secret for the Foursquare API?

I getting back into Python and wanted to use the pyfoursquare package to access the Foursquare API. I'm trying to get information about venues using the venues method in the API class. I'm primarily trying to find out whether a venue page is verified with Foursquare or not. When I provide my client id, client secret, and venue id I keep getting back an error that states "Authentication required", which doesn't makes sense because I'm providing that information. Any help would be great. Thank you.
import pyfoursquare as foursquare
client_id = ""
client_secret = ""
callback = ""
auth = foursquare.OAuthHandler(client_id, client_secret, callback)
api = foursquare.API(auth)
result = api.venues("4e011a3e62843b639cfa9449")
print result[0].name
Let me know if you would like to see the error message. Thanks again.
I believe you are skipping the step of grabbing your OAuth2 access token, so you're not technically authenticated.
Have a look at the following instructions, under "How to Use It":
https://github.com/marcelcaraciolo/foursquare
The lines that might be useful to you are:
#First Redirect the user who wish to authenticate to.
#It will be create the authorization url for your app
auth_url = auth.get_authorization_url()
print 'Please authorize: ' + auth_url
#If the user accepts, it will be redirected back
#to your registered REDIRECT_URI.
#It will give you a code as
#https://YOUR_REGISTERED_REDIRECT_URI/?code=CODE
code = raw_input('The code: ').strip()
#Now your server will make a request for
#the access token. You can save this
#for future access for your app for this user
access_token = auth.get_access_token(code)
print 'Your access token is ' + access_token