Flask uses itsdangerous to generate tokens

Flask uses itsdangerous to generate tokens

itsdangerous example

(venv)$python manage.py shell
>>> from manage import app
>>> from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
>>> s = Serializer(app.config['SECRET_KEY'], expires_in = 3600)
>>> token = s.dumps({ 'confirm': 23 })
>>> token
'eyJhbGciOiJIUzI1NiIsImV4cCI6MTM4MTcxODU1OCwiaWF0IjoxMzgxNzE0OTU4fQ.ey ...'
>>> data = s.loads(token)
>>> data
{u'confirm':23}
  1. itsdangerous provides a variety of methods to generate tokens, among which the TimedJSONWebSignatureSerializer class generates JSON Web Signatures (JWS) with expiration time. The parameter received by the constructor of this class is a key, which can be used in the flash program_ Key settings.

  2. dumps() method generates an encrypted signature for the specified data, and then serializes the data and signature to generate a token string. expires_ The in parameter sets the expiration time of the token, in seconds.

  3. To decode the token, the serialized object provides the loads() method, whose only parameter is the token string. This method

    The signature and expiration time will be verified. If passed, the original data will be returned. If the token provided to the loads() method is incorrect

    If it is true or expired, an exception is thrown.

Actual project use process

This example scenario is the user mailbox authentication function.

  • Add to user model class (models.py)

    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
    from flask import current_app
    from . import db
    class User(UserMixin, db.Model):
        
        # ...
        confirmed = db.Column(db.Boolean, default=False)  # Judge whether the user is authenticated. The default is False
        
        # Generate token token
        def generate_confirmation_token(self, expiration=3600):  # Expiration time
        	s = Serializer(current_app.config['SECRET_KEY'], expiration)  # Serialization key
         	return s.dumps({'confirm': self.id})
         
        # Validate token token 
        def confirm(self, token):
            s = Serializer(current_app.config['SECRET_KEY'])  # Serialization key
            try:
                data = s.loads(token)   # The method throws an exception when validation fails or the token times out
            except:
                return False
            if data.get('confirm') != self.id:  # Verify the user id to prevent the token generation method from being disclosed
            	return False
            self.confirmed = True   # Verification passed
            db.session.add(self)   # Save this user
            return True
    
  • Send token authentication email (views.py)

    from ..email import send_email  # Mailbox verification method
    
    @auth.route('/register', methods = ['GET', 'POST'])
    def register():
        form  = RegistrationForm()
        if form.validate_on_submit():
            # ...
            db.session.add(user)
            db.session.commit()
            token = user.generate_confirmation_token()  # Get authentication token
            send_email(user.email, 'Confirm Your Account', 'auth/email/confirm', 	                    user=user, token=token)   # Send mailbox and configure template parameters: user and token
     		flash('A confirmation email has been sent to you by email.')
            return redirect(url_for('main.index'))
    	return render_template('auth/register.html', form=form)
    
    # To prevent mail loss, send the authentication mail again. At this time, the user is current_ User (logged in user, i.e. target user).
    from flask_login import current_user
    @auth.route('/confirm')
    @login_required
    def resend_confirmation():
        token = current_user.generate_confirmation_token()
        send_email(current_user.email, 'Confirm Your Account',
                   'auth/email/confirm', user=current_user, token=token)
        flash('A new confirmation email has been sent to you by email.')
        return redirect(url_for('main.index'))
    
  • After user authentication, verify the token (views.py)

    from flask.ext.login import current_user
    
    @auth.route('/confirm/<token>')
    @login_required   # The fallk login extension method must be logged in before it can be called.
    def confirm(token):
        if current_user.confirmed:  # Judge whether the user has been authenticated
            return redirect(url_for('main.index'))
        if current_user.confirm(token):  # The user requests the route and calls the verification function for verification.
            flash('You have confirmed your account. Thanks!')  # success
        else:
            flash('The confirmation link is invalid or has expired.')  # fail
        return redirect(url_for('main.index'))
    
    1. Login provided by flask login_ The required modifier will protect the route. Therefore, after the user clicks the link in the confirmation email, he must log in first, and then execute the view function.
    2. This function first checks whether the logged in user has been confirmed. If so, it will redirect to the home page, because obviously there is no need to do anything at this time. This process can avoid the extra work caused by the user accidentally clicking the confirmation token many times.
    3. Since the token confirmation is completely completed in the User model, the view function only needs to call the confirm() method, and then display different Flash messages according to the confirmation results. After the confirmation is successful, the value of the confirmed attribute in the User model will be modified and added to the session. After the request is processed, the two operations will be submitted to the database.
  • When unconfirmed users are allowed to log in, but only one page is displayed. This page requires the user to confirm the account before obtaining permission, you can use before provided by Flask_ Request hook completion: (before_request hook can only be applied to requests belonging to this route. If you want to use a hook for program global requests, you must use the before_app_request decorator):

    @auth.before_app_request
    def before_request():
        if current_user.is_authenticated() \
            and not current_user.confirmed \
            and request.endpoint[:5] != 'auth.' \
            and request.endpoint != 'static':
            return redirect(url_for('auth.unconfirmed'))
    
    @auth.route('/unconfirmed')
    def unconfirmed():
        if current_user.is_anonymous() or current_user.confirmed:
            return redirect(url_for('main.index'))
        return render_template('auth/unconfirmed.html')
    

    Before when the following three conditions are met at the same time_ app_ The request handler will intercept the request and be redirected to the / auth/unconfirmed route, displaying a page to confirm the account related information, such as prompting to bind the mobile phone.

    1. The user is logged in (current_user.is_authenticated() must return True).
    2. The user's account has not been confirmed.
    3. The requested endpoint is not in the authentication route. (use request.endpoint to obtain permissions for accessing authentication routes, because the purpose of these routes is to allow users to confirm accounts or perform other account management operations.)

Keywords: Python Back-end Flask

Added by AcousticJames on Wed, 27 Oct 2021 05:01:47 +0300