Authentication token to only apply to one id - python

I am using this tutorial to incorporate authentication into my app: http://blog.miguelgrinberg.com/post/restful-authentication-with-flask
At the moment I have the following route:
#app.route('/checkin/venue/<int:venue_id>', methods = ['POST'])
#auth.login_required
My verify_password function is quite similar to that specified in the tutorial except I am accessing my own db.
The issue is that when I generate a token, it can be used across multiple venue_id's even though the token was generated using the credentials of a singlevenue.
Is there a way that I could pass the venue_id variable to the function verify_password(email_or_token, password) so when I call verify_auth_token I will be able to check that the venue_id encoded in the token actually corresponds to that made in the call:
#app.route('/checkin/venue/<int:venue_id>', methods = ['POST'])
Thanks for your help.

You don't say this explicitly, but I assume you have the venue_id in the token, correct? If not, you should add it. The token can store any data you want so add the venue_id there in addition to the user_id.
So what you need is to compare the venue_id given in your request URL against the one in the token. And this is easy to do, since you can access the venue id in your URL as request.view_args['venue_id'].
So assuming you followed the design in my tutorial, you now have a User.verify_auth_token(token) method that decodes the token and verifies it. You can add an argument to this method that is the venue_id, and incorporate that verification in the logic of that method.
Then in your verify_password callback you can do something like this:
#auth.verify_password
def verify_password(token, password):
user = User.verify_auth_token(token, request.view_args.get('venue_id', 0))
if not user:
return False
g.user = user
return True
Note that I chose a default of 0 for the case of a request that does not include a venue_id argument. This is just to avoid a crash. In your verification function you can choose to accept a venue_id of 0 as meaning that this request works for all venues, so in that case you skip the check on the venue.
Hope this helps!

Related

Django coding - Why is it necessary to return two identical arguments?

I am looking to add email account verification in Django and found a nice open source code that I can adopt.
But there is one line which I'm not familiar with.
Why does the function "password_reset_confirm" need to return two **kwargs in separate brackets and
how each of them is used in the class "PasswordResetConfirm"?
This question might be related to Python rather than Django. But anyway, thank you for your help!
urls.py
url(r"^password/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-
z]{1,13}-[0-9A-Za-z]{1,20})/$",views.password_reset_confirm, name="reset-
password-confirm",)
views.py
from django.contrib.auth import views as django_views
class PasswordResetConfirm(django_views.PasswordResetConfirmView):
template_name = "account/password_reset_from_key.html"
success_url = reverse_lazy("account:reset-password-complete")
token = None
uidb64 = None
def form_valid(self, form):
response = super(PasswordResetConfirm, self).form_valid(form)
account_events.customer_password_reset_event(user=self.user)
return response
def password_reset_confirm(request, uidb64=None, token=None):
kwargs = {
"template_name": "account/password_reset_from_key.html",
"success_url": reverse_lazy("account:reset-password-complete"),
"token": token,
"uidb64": uidb64,
}
return PasswordResetConfirm.as_view(**kwargs)(request, **kwargs)
First variable (uidb64) is just base64-encoded ID of user, extracted from database. By this field Django can determine which user is requesting password reset.
Second variable (token) is an actual reset token that is used to verify user request. User can get this token only from email that was sent to him, so this verifies that user has access to provided email address, so we can proceed with password reset.
Why we can't use token alone? There are 2 reasons for that
Django doesn't store password reset tokens in database. A lot of other frameworks will do that and after user clicks password reset URL, token will be found in database and from that database entry, user requesting reset will be determined. Instead, Django uses some clever cryptography methods and generates this token from SECRET_KEY stored in database, current user password hash and time of generating this token. After that, only time can be extracted from token and by fetching user data together with SECRET_KEY from settings, Django can verify that password reset token is valid for specified user. Time of token generation is here, so every token can expire after some time.
User ID cannot be easily embedded in token, as time is, as User ID format can be customized (you can embed your own user model that uses for example UUID instead of numerical value). That's why Django is encoding this ID using Base64 - it guarantees that every format of user ID can be easily embedded in URL. For that reason, User ID can have different length and format, so it won't be easy to extract it from token string.
As for passing kwargs twice, here is quick explanation by example:
In python, you can return function from calling any other function or method (most often those kind of functions are called factories):
def some_factory():
def some_function():
print('abc')
return some_function
my_function = some_factory()
In this example, print won't be called as we are not executing some_function, some_factory returns it, so we can use it later:
my_function()
Now this print will be called and we will see abc in console output. But instead of assigning returned function to some variable or passing it somewhere, you can call it immediately:
some_factory()()
This is where the second parentheses come from. Of course both functions can take some arguments, then you will provide arguments for factory inside first pair of parentheses and arguments to some_function in second.
Back to your example, it is actually invalid, you shouldn't pass full kwargs to as_view in PasswordResetConfirm. It should take only first two (template_name and success_url). Actual view (second parentheses) should take other two (token and uidb64).
2nd thing that is wrong in your code is that you're calling as_view on every request. It is designed to be called only once, when creating this view. Instead of wrapping it in separate function, use it directly in your urls.py:
url(
r"^password/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$",
PasswordResetConfirm.as_view(
template_name="account/password_reset_from_key.html"
success_url=reverse_lazy("account:reset-password-complete"),
), name="reset-password-confirm",
)

Generating single access token with Django OAuth2 Toolkit

I'm using the latest Django OAuth2 Toolkit (0.10.0) with Python 2.7, Django 1.8 and Django REST framework 3.3
While using the grant_type=password, I noticed some weird behavior that any time the user asks for a new access token:
curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u"<client_id>:<client_secret>" http://localhost:8000/o/token/
A new access token and refresh token is created. The old access and refresh token are still usable until token timeout!
My Issues:
What I need is that every time a user asks for a new access token,
the old one will become invalid, unusable and will be removed.
Also, is there a way that the password grunt type wont create refresh
token. I don't have any use for that in my application.
One solution I found is that REST Framework OAuth provides a configuration for One Access Token at a time. I'm not eager to use that provider, but I might wont have a choice.
If you like to remove all previous access tokens before issuing a new one, there is a simple solution for this problem: Make your own token view provider!
The code bellow will probably help you to achieve that kind of functionality:
from oauth2_provider.models import AccessToken, Application
from braces.views import CsrfExemptMixin
from oauth2_provider.views.mixins import OAuthLibMixin
from oauth2_provider.settings import oauth2_settings
class TokenView(APIView, CsrfExemptMixin, OAuthLibMixin):
permission_classes = (permissions.AllowAny,)
server_class = oauth2_settings.OAUTH2_SERVER_CLASS
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
oauthlib_backend_class = oauth2_settings.OAUTH2_BACKEND_CLASS
def post(self, request):
username = request.POST.get('username')
try:
if username is None:
raise User.DoesNotExist
AccessToken.objects.filter(user=User.objects.get(username=username), application=Application.objects.get(name="Website")).delete()
except Exception as e:
return Response(e.message,status=400)
url, headers, body, status = self.create_token_response(request)
return Response(body, status=status, headers=headers)
The part you should notice is the Try-Except block. In there we finding the Access tokens and removing them. All before we creating a new one.
You can look at how to create your own Provider using OAuthLib.
Also, this might be useful as well: TokenView in django-oauth-toolkit. You can see there the original Apiview. As you said, you were using this package.
As for the refresh_token, as previously mentioned in other answers here, you can't do what you are asking. When looking at the code of oauthlib password grunt type, you will see that in its initialization, refresh_token is set to True. Unless you change the Grunt type it self, it can't be done.
But you can do the same thing we did above with the access tokens.
Create the token and then delete the refresh token.
What I need is that every time a user asks for a new access token, the
old one will become invalid, unusable and will be removed.
Giving a new token when you ask for one seems like an expected behavior. Is it not possible for you to revoke the existing one before asking for the new one?
Update
If you are determined to keep just one token -
The class OAuth2Validator inherits OAuthLib's RequestValidator and overrides the method save_bearer_token. In this method before the code related to AccessToken model instance creation and its .save() method you can query (similar to this) to see if there is already an AccessToken saved in DB for this user. If found the existing token can be deleted from database.
I strongly suggest to make this change configurable, in case you change your mind in future (after all multiple tokens are issued for reasons like this)
A more simpler solution is to have your own validator class, probably one that inherits oauth2_provider.oauth2_validators.OAuth2Validator and overrides save_bearer_token. This new class should be given for OAUTH2_VALIDATOR_CLASS in settings.py
Also, is there a way that the password grunt type wont create refresh
token. I don't have any use for that in my application.
Django OAuth Toolkit depends on OAuthLib.
Making refresh_token optional boils down to create_token method in BearerToken class of oAuthLib at this line and the class for password grant is here. As you can see the __init__ method for this class takes refresh_token argument which by default is set to True. This value is used in create_token_response method of the same class at the line
token = token_handler.create_token(request, self.refresh_token)
create_token_response method in OAuthLibCore class of Django OAuth toolkit is the one, I believe, calls the corresponding create_token_response in OAuthLib. Observe the usage of self.server and its initialization in __init__ method of this class, which has just the validator passed as an argument but nothing related to refresh_token.
Compare this with OAuthLib Imlicit grant type's create_token_response method, which explicitly does
token = token_handler.create_token(request, refresh_token=False)
to not create refresh_token at all
So, unless I missed something here, tldr, I don't think Django OAuth toolkit exposes the feature of optional refresh_token.
Here's an example of just making it directly:
from oauthlib.common import generate_token
from oauth2_provider.models import AccessToken, Application
from django.utils import timezone
from dateutil.relativedelta import relativedelta
tok = generate_token()
app = Application.objects.first()
user = User.objects.first()
access_token = AccessToken.objects.create(user=user, application=app, expires=timezone.now() + relativedelta(hours=1), token=tok)
The accepted answer still fails to clear the RefreshToken. Below code should revoke both the refresh and access token.
from oauth2_provider.models import RefreshToken
def clear_token(user):
"""
Clear all user authorized tokens.
"""
for token in RefreshToken.objects.filter(user=user, revoked__isnull=True):
token.revoke()

Django Test : SESSION_KEY failing assertIn

I am using the Django Test client (django.test.Client) to run view tests. Upon attempting to use the Test Client on my index function that handles post requests for logins, it continually fails the test even though the authentication successfully occurs.
Heres my test:
def test_login(self):
response = self.client.post('/login/', {'username':'user', 'password':'pass'})
print response.content
self.assertIn(SESSION_KEY, self.client.session)
So the reason i know the login process successfully works is because response.content yields HTML data from another view that can only be access if request.user.is_authenticated() is true. In other words, they must be logged in for response.content to yield the "logged in page". So given this, i can tell that the function obviously works for its practical purpose of logging the user in, however, i've been scouring the docs for hours trying to figure out why i can't access SESSION_KEY from the client session. All my reading suggests that the django test client is in fact stateful in nature and should store the session.
Can someone shed some light on this?
Ok after much searching and asking around on #django, i made a working solution for Django 1.6.x
from django.contrib.auth import SESSION_KEY, get_user_model
from django.test import Client
def setUp(self):
self.client = Client()
def test_login_view(self):
user_pk = get_user_model()._default_manager.get(username__exact='test_username_here').pk
response = self.client.post('/login/', {'username':'test_username_here', 'password':'test_password_here'})
self.assertEqual(self.client.session[SESSION_KEY], user_pk)
The test_login_view function will be the one evaluating the view in my app that handles user logins from the template form. First, i grab user_pk which is the real primary key of the given user in the database. I used get_user_model() instead of User.objects.get() because the former allows you to reference regardless of whether the User model is modified or not. Of course you can use the latter as well. Second, i go ahead and send the post request using the test client just like a standard user's browser would. Finally, i discovered that self.client.session[SESSION_KEY] contains the primary key of the logged in user. (If the login was successful, otherwise, it will simply yield a KeyError)

flask-login session gets destroyed on every apache restart

I am using flask-login https://github.com/maxcountryman/flask-login and the field remember in login_user does not seem to work.
The session gets destroyed after every restart of the apache ..ideally the remember field should take care of this.. even the session values gets destroyed. this is really frustrating... anyone knowing the solution please ping .. thanks
i am using login_user as
login_user(user, remember=True)
If anyone is suffering with this problem, you have to write the function user_loader properly.
#login_manager.user_loader
def load_user(id):
return "get the user properly and create the usermixin object"
I ran into this issue, but it was because we were setting Flask.secret_key to a new GUID on startup. We moved this to a configuration file (unique ID per environment) and now the session is persisted.
you have to set the get_auth_token in the user mixen as well as the user_loader
class User(UserMixin):
def get_auth_token(self):
"""
Encode a secure token for cookie
"""
data = [str(self.id), self.password]
return login_serializer.dumps(data)
And
#login_manager.token_loader
def load_token(token):
"""
Flask-Login token_loader callback.
The token_loader function asks this function to take the token that was
stored on the users computer process it to check if its valid and then
return a User Object if its valid or None if its not valid.
"""
#The Token itself was generated by User.get_auth_token. So it is up to
#us to known the format of the token data itself.
#The Token was encrypted using itsdangerous.URLSafeTimedSerializer which
#allows us to have a max_age on the token itself. When the cookie is stored
#on the users computer it also has a exipry date, but could be changed by
#the user, so this feature allows us to enforce the exipry date of the token
#server side and not rely on the users cookie to exipre.
max_age = app.config["REMEMBER_COOKIE_DURATION"].total_seconds()
#Decrypt the Security Token, data = [username, hashpass]
data = login_serializer.loads(token, max_age=max_age)
#Find the User
user = User.get(data[0])
#Check Password and return user or None
if user and data[1] == user.password:
return user
return None
Both of those methods use the module itsdangerous to encrypt the remember me cookie
from itsdangerous import URLSafeTimedSerializer
I wrote a blog post about how I did it
Flask-Login Auth Tokens

How to implement user_loader callback in Flask-Login

I'm attempting to use Flask and the Flask-Login extension to implement user authentication in a Flask app. The goal is to pull user account information from a database and then log in a user, but I'm getting stuck; however, I've narrowed it down to a particular part of Flask-Login behavior.
According to the Flask-Login documentation, I need to create a user_loader "callback" function. The actual purpose and implementation of this function has had me confused for a few days now:
You will need to provide a user_loader callback. This callback is used
to reload the user object from the user ID stored in the session. It
should take the Unicode ID of a user, and return the corresponding
user object. For example:
#login_manager.user_loader
def load_user(userid):
return User.get(userid)
Now, say I want the user to enter a name and password into a form, check against a database, and log in the user. The database stuff works fine and is no problem for me.
This 'callback' function wants to be passed a user ID #, and return the User object (the contents of which I'm loading from a database). But I don't really get what it's supposed to be checking/doing, since the user IDs are all pulled from the same place anyway. I can 'sort-of' get the callback to work, but it seems messy/hackish and it hits the database with every single resource that the browser requests. I really don't want to check my database in order to download favicon.ico with every page refresh, but flask-login seems like it's forcing this.
If I don't check the database again, then I have no way to return a User object from this function. The User object/class gets created in the flask route for logging in, and is thus out of scope of the callback.
What I can't figure out is how to pass a User object into this callback function, without having to hit the database every single time. Or, otherwise figure out how to go about doing this in a more effective way. I must be missing something fundamental, but I've been staring at it for a few days now, throwing all kinds of functions and methods at it, and nothing is working out.
Here are relevant snippets from my test code. The User class:
class UserClass(UserMixin):
def __init__(self, name, id, active=True):
self.name = name
self.id = id
self.active = active
def is_active(self):
return self.active
The function I made to return the user object to Flask-Login's user_loader callback function:
def check_db(userid):
# query database (again), just so we can pass an object to the callback
db_check = users_collection.find_one({ 'userid' : userid })
UserObject = UserClass(db_check['username'], userid, active=True)
if userObject.id == userid:
return UserObject
else:
return None
The 'callback', which I don't totally understand (must return the User object, which gets created after pulling from database):
#login_manager.user_loader
def load_user(id):
return check_db(id)
The login route:
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST" and "username" in request.form:
username = request.form["username"]
# check MongoDB for the existence of the entered username
db_result = users_collection.find_one({ 'username' : username })
result_id = int(db_result['userid'])
# create User object/instance
User = UserClass(db_result['username'], result_id, active=True)
# if username entered matches database, log user in
if username == db_result['username']:
# log user in,
login_user(User)
return url_for("index"))
else:
flash("Invalid username.")
else:
flash(u"Invalid login.")
return render_template("login.html")
My code 'kinda' works, I can log in and out, but as I said, it must hit the database for absolutely everything, because I have to provide a User object to the callback function in a different namespace/scope from where the rest of the login action takes place. I'm pretty sure I'm doing it all wrong, but I can't figure out how.
The example code provided by flask-login does it this way, but this only works because it's pulling the User objects from a global hard-coded dictionary, not as in a real-world scenario like a database, where the DB must be checked and User objects created after the user enters their login credentials. And I can't seem to find any other example code that illustrates using a database with flask-login.
What am missing here?
You will need to load the user object from the DB upon every request. The strongest reason for that requirement is that Flask-Login will check the authentication token every time to ensure its continuing validity. The calculation of this token may require parameters stored on the user object.
For example, suppose a user has two concurrent sessions. In one of them, the user changes their password. In subsequent requests, the user must be logged out of the second session and forced to login anew for your application to be secure. Think of the case where the second session is stolen because your user forgot to log out of a computer - you want a password change to immediately fix the situation. You might also want to give your admins the ability to kick a user out.
For such forced logout to happen, the authentication token stored in a cookie must 1) be based in part on the password or something else that changes each time a new password is set; 2) be checked before running any view, against the latest known attributes of the user object - which are stored in the DB.
I do share your concerns Edmond: hitting database each time when one needs to know user's role or name is insane. Best way would be to store your User object in session or even application-wide cache which gets updated from the DB each couple of minutes. I personally use Redis for that (that way website can be run by multiple threads/processes while using single cache entry point). I just make sure Redis is configured with password and non-default port, and any confidential data (like user hashes etc) are stored there in an encrypted form. Cache can be populated by a separate script running on specified interval, or separate thread can be spawned in Flask. Note: Flask-Session can be also configured to use (the same) redis instance to store session data, in that case instance with 'bytes' datatype will be needed, for a regular cache you might often go with instance type which automatically translates bytes into strings (decode_responses=True).
Here is my code, another User as data mapping object provide query_pwd_md5 method.
User login:
#app.route('/users/login', methods=['POST'])
def login():
# check post.
uname = request.form.get('user_name')
request_pwd = request.form.get('password_md5')
user = User()
user.id = uname
try:
user.check_pwd(request_pwd, BacktestUser.query_pwd_md5(
uname, DBSessionMaker.get_session()
))
if user.is_authenticated:
login_user(user)
LOGGER.info('User login, username: {}'.format(user.id))
return utils.serialize({'userName': uname}, msg='login success.')
LOGGER.info('User login failed, username: {}'.format(user.id))
return abort(401)
except (MultipleResultsFound, TypeError):
return abort(401)
User class:
class User(UserMixin):
"""Flask-login user class.
"""
def __init__(self):
self.id = None
self._is_authenticated = False
self._is_active = True
self._is_anoymous = False
#property
def is_authenticated(self):
return self._is_authenticated
#is_authenticated.setter
def is_authenticated(self, val):
self._is_authenticated = val
#property
def is_active(self):
return self._is_active
#is_active.setter
def is_active(self, val):
self._is_active = val
#property
def is_anoymous(self):
return self._is_anoymous
#is_anoymous.setter
def is_anoymous(self, val):
self._is_anoymous = val
def check_pwd(self, request_pwd, pwd):
"""Check user request pwd and update authenticate status.
Args:
request_pwd: (str)
pwd: (unicode)
"""
if request_pwd:
self.is_authenticated = request_pwd == str(pwd)
else:
self.is_authenticated = False

Categories