Login and Registration Flows¶
This document explains the registration, login, and logout flows used in the SignBridge authentication system. The main logic is placed in app/auth/routes.py.
Registration¶
The registration flow creates a new user account.
Route Definition¶
Defines the route for registration and supports both GET and POST requests.
@auth_bp.route('/register', methods=['GET', 'POST'])
Rate Limiting¶
Limits registration attempts to 5 POST requests per minute.
@limiter.limit("5 per minute", methods=['POST'])
Authenticated User Check¶
Redirects to the dashboard page if the user is already authenticated (logged in).
if current_user.is_authenticated:
return redirect(url_for('main.index'))
Form Validation¶
Validates the submitted registration form using SignupForm from app/auth/forms.py. This form employs custom security validators:
Password Complexity(Requirements): Utilises the
password_complexityvalidator fromapp/auth/validators.pyto ensure that passwords are at least 12 characters, consist of uppercase, lowercase, digits, and special characters.Unique Credentials: Utilises the
unique_emailandunique_usernamevalidators to ensure that the email and username are not already in use within the database.Bot Protection: Utilses the Google reCAPTCHA v2 via
RecaptchaFieldto prevent automated sign-ups/sign-ins.
form = SignupForm()
if form.validate_on_submit():
Creating the User¶
Creates a new User object (defined in app/models.py) using the submitted username and email address. Email addresses are converted to lowercase and removed of leading whitespace.
user = User(username=form.username.data, email=form.email.data.lower().strip())
Password Hashing (With Flask-Bcrypt)¶
Hashes the user’s password using the set_password() method from the User model in app/models.py.
user.set_password(form.password.data)
Adding the User¶
Adds the user to the database session.
db.session.add(user)
Database Flush¶
Flushes the session to obtain an unique ID for the new user before committing to the database.
db.session.flush()
Token Generation¶
Generates an API token for the new user via the get_token() method on the User model.
user.get_token()
Saving Changes¶
Commits the registration data to the database.
db.session.commit()
Duplicate User Handling¶
Handles duplicate usernames or email addresses by catching the IntegrityError exception from SQLAlchemy and rolling back the session.
except sa.exc.IntegrityError:
db.session.rollback()
flash('Username or email already exists.')
return redirect(url_for('auth.register'))
Redirecting After Registration¶
Redirects the user to the login page after successful registration.
flash('Congratulations, you are now a registered user!')
return redirect(url_for('auth.login'))
Login¶
The login flow authenticates users and creates a session.
Route Definition¶
Defines the login route and allows both GET and POST requests.
@auth_bp.route('/login', methods=['GET', 'POST'])
Rate Limiting¶
Limits login attempts to 5 POST requests per minute using a custom key function.
def login_key():
username = request.form.get("username")
if username:
return f"login:{username.lower().strip()}"
return get_remote_address()
@limiter.limit("5 per minute", key_func=login_key, methods=['POST'])
Session Blocking Check¶
Checks if a user is blocked before accessing any pages.
@auth_bp.before_app_request
def check_if_blocked():
session.permanent = True
if current_user.is_authenticated and current_user.is_blocked:
logout_user()
flash("Your account has been blocked. Contact an admin (admin.signbridge+support@gmail.com).")
return redirect(url_for('auth.login'))
Authenticated User Check¶
Redirects authenticated users to the dashboard.
if current_user.is_authenticated:
return redirect(url_for('user.dashboard'))
Form Validation¶
Validates the submitted login form using LoginForm from app/auth/forms.py.
form = LoginForm()
if form.validate_on_submit():
Finding the User¶
Searches for a matching user account using the User model from app/models.py.
user = db.session.scalar(
sa.select(User).where(User.username == form.username.data)
)
Blocked Account Check¶
Checks whether the user is blocked and validates block duration.
if user.is_blocked:
if user.blocked_until and datetime.now(timezone.utc) >= user.blocked_until:
user.is_blocked = False
user.blocked_until = None
user.failed_login_attempts = 0
db.session.commit()
Password Verification¶
Verifies the user entered password against the stored hash using the check_password() method.
if not user.check_password(form.password.data):
Failed Login Attempts¶
Increments the failed_login_attempts counter on the User model.
user.failed_login_attempts += 1
Account Blocking¶
Blocks the account by setting the is_blocked flag and setting a temporary block timer.
if user.failed_login_attempts >= MAX_LOGIN_ATTEMPTS:
user.is_blocked = True
user.blocked_until = datetime.now(timezone.utc) + timedelta(minutes=BLOCK_DURATION_MINS)
Resetting Failed Attempts¶
Resets failed login attempts after successful authentication.
user.failed_login_attempts = 0
Token Generation¶
Generates a new API token after successful login.
user.get_token()
Creating the Session¶
Creates a login session for the authenticated user using Flask-Login.
login_user(user, remember=form.remember_me.data)
Redirect Handling¶
Redirects the user to the originally requested page if it is safe, otherwise sends them to the dashboard.
next_page = request.args.get('next')
if next_page and urlsplit(next_page).netloc == '':
return redirect(next_page)
return redirect(url_for('user.dashboard'))
Logout¶
The logout flow terminates the user’s session.
Route Definition¶
Defines the logout route.
@auth_bp.route('/logout')
Logging Out the User¶
Removes the user’s authenticated session using logout_user() from Flask-Login.
logout_user()
Logging Event¶
Logs logout activity if the user is authenticated.
if current_user.is_authenticated:
current_app.logger.info(f"User {current_user.username} has logged out.")
Redirecting After Logout¶
Users are redirected to the homepage after logging out.
return redirect(url_for('main.index'))