October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

Flask Login API: Choose a Safer Session-Based Test Flow

A practical Flask login API example with a clear JSON contract, password hashing, session handling, and tests for valid and invalid authentication outcomes.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build a small Flask login API by defining its JSON contract, storing password hashes, and keeping the authenticated user ID in Flask’s signed session. Then use Flask’s test client to check successful login, invalid credentials, protected access, and logout—without starting a live server.

Define the API contract first

Flask’s official tutorial demonstrates browser-oriented registration and login views backed by SQLite and sessions, not a ready-made JSON or REST API. The example below adapts those mechanics to JSON and spells out its own response contract. See the Flask tutorial’s authentication views and its full tutorial overview.

As an Amazon Associate I earn from qualifying purchases.

This compact example assumes an existing SQLite table named user with id, username, and password columns, where password stores a hash. It also assumes the application factory provides get_db(), configured database access, and a secret key. The tutorial’s database setup is one way to supply that foundation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Route Request Success Failure
POST /auth/login JSON object with username and password 200 JSON: {"message":"logged in"}; authenticated session established 400 for missing fields; 401 for unknown username or incorrect password
POST /auth/logout No required body 200 JSON: {"message":"logged out"}; session cleared None defined in this example
GET /auth/private Authenticated session cookie 200 JSON: {"username":"..."} 401 JSON when unauthenticated

Returning one generic 401 message for both incorrect usernames and passwords avoids revealing which usernames exist through this endpoint. That is a design choice, not a Flask requirement; it can make account-entry errors less specific for legitimate users. An API may instead choose distinct messages after considering its threat model.

#1 Best Overall
Yubico - Security Key C NFC - Basic Compatibility - Multi-Factor authentication (MFA) Security Key and passkey, Connect via USB-C or NFC, FIDO Certified
  • POWERFUL SECURITY KEY: The Security Key C NFC is the essential physical passkey for protecting your digital life from phishing attacks. It ensures only you can access your accounts.
  • WORKS WITH 1000+ ACCOUNTS: Compatible with Google, Microsoft, and Apple. A single Security Key C NFC secures 100 of your favorite accounts, including email, password managers, and more.
  • FAST & CONVENIENT LOGIN: Plug in your Security Key C NFC via USB-C and tap it, or tap it against your phone (NFC) to authenticate. No batteries, no internet connection, and no extra fees required.
  • TRUSTED PASSKEY TECHNOLOGY: Uses the latest passkey standards (FIDO2/WebAuthn & FIDO U2F) but does not support One-Time Passwords. For complex needs, check out the YubiKey 5 Series.
  • BUILT TO LAST: Made from tough, waterproof, and crush-resistant materials. Manufactured in Sweden and programmed in the USA with the highest security standards.

Implement registration and authentication safely

Hash passwords and bind SQL values

Registration should validate required fields on the server, reject or handle duplicate usernames, hash the submitted password, and insert the hash—not the raw password. Flask’s tutorial uses generate_password_hash() and check_password_hash(). It also uses SQL placeholders for user-supplied values, which avoids constructing queries by concatenating input.

from flask import Blueprint, jsonify, request, session, g, current_app
from werkzeug.security import check_password_hash, generate_password_hash

bp = Blueprint("auth", __name__, url_prefix="/auth")

@bp.post("/register")
def register():
    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return jsonify(error="Expected a JSON object"), 400

    username = data.get("username")
    password = data.get("password")
    if not isinstance(username, str) or not username.strip() or not isinstance(password, str) or not password:
        return jsonify(error="username and password are required"), 400

    db = get_db()
    try:
        db.execute(
            "INSERT INTO user (username, password) VALUES (?, ?)",
            (username, generate_password_hash(password)),
        )
        db.commit()
    except db.IntegrityError:
        return jsonify(error="Username already exists"), 409

    return jsonify(message="registered"), 201

@bp.post("/login")
def login():
    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return jsonify(error="Expected a JSON object"), 400

    username = data.get("username")
    password = data.get("password")
    if not isinstance(username, str) or not isinstance(password, str) or not username or not password:
        return jsonify(error="username and password are required"), 400

    user = get_db().execute(
        "SELECT * FROM user WHERE username = ?", (username,)
    ).fetchone()

    if user is None or not check_password_hash(user["password"], password):
        return jsonify(error="Invalid username or password"), 401

    session.clear()
    session["user_id"] = user["id"]
    return jsonify(message="logged in"), 200

@bp.before_app_request
def load_logged_in_user():
    user_id = session.get("user_id")
    g.user = None if user_id is None else get_db().execute(
        "SELECT * FROM user WHERE id = ?", (user_id,)
    ).fetchone()

@bp.post("/logout")
def logout():
    session.clear()
    return jsonify(message="logged out"), 200

@bp.get("/private")
def private():
    if g.user is None:
        return jsonify(error="Authentication required"), 401
    return jsonify(username=g.user["username"]), 200

The snippet leaves get_db() and the database schema to the application, so adapt the integrity-error reference to the database wrapper in use. Register the blueprint in the app factory with app.register_blueprint(auth.bp) (assuming the module is named auth); this keeps authentication routes grouped separately from other views.

Rank #2
Yubico - YubiKey 5C NFC - Multi-Factor authentication (MFA) Security Key and passkey, Connect via USB-C or NFC, FIDO Certified - Protect Your Online Accounts
  • POWERFUL SECURITY KEY: The YubiKey 5C NFC is the most versatile physical passkey, protecting your digital life from phishing attacks. It ensures only you can access your accounts
  • WORKS WITH 1000+ ACCOUNTS: Compatible with popular accounts like Google, Microsoft, and Apple. A single YubiKey 5C NFC secures 100+ of your favorite accounts, including email, password managers, and more
  • FAST & CONVENIENT LOGIN: Plug in your YubiKey 5C NFC via USB and tap it, or tap it against your phone (NFC), to authenticate. No batteries, no internet connection, and no extra fees required
  • MOST SECURE PASSKEY: Supports FIDO2/WebAuthn, FIDO U2F, Yubico OTP, OATH-TOTP/HOTP, Smart card (PIV), and OpenPGP. That means it’s versatile, working almost anywhere you need it
  • PRIMARY & SPARE KEYS: Just like having a spare house key, we recommend buying two YubiKeys - one for daily use and one as a spare. That way you’ll never get locked out of your accounts

Make session handling and configuration deliberate

On successful login, the code clears prior session state before recording the user ID. Each request that needs identity reloads the corresponding account from that identifier. Logout clears the session, so a following protected request is unauthenticated.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Flask’s default session is serialized into a client-side cookie and cryptographically signed. The browser can read its contents; the signature prevents modification without the secret key, not visibility. Keep the session limited to suitable identifiers and non-sensitive state. Replace any public tutorial or development secret with a strong random production value stored outside public source code: Flask warns that a known key can permit session-cookie modification. See the session documentation and deployment guidance.

Rank #3
Yubico - YubiKey 5 NFC - Multi-Factor authentication (MFA) Security Key and passkey, Connect via USB-A or NFC, FIDO Certified - Protect Your Online Accounts
  • POWERFUL SECURITY KEY: The YubiKey 5 NFC is the most versatile physical passkey, protecting your digital life from phishing attacks. It ensures only you can access your accounts
  • WORKS WITH 1000+ ACCOUNTS: Compatible with popular accounts like Google, Microsoft, and Apple. A single YubiKey 5 NFC secures 100+ of your favorite accounts, including email, password managers, and more
  • FAST & CONVENIENT LOGIN: Plug in your YubiKey 5 NFC via USB and tap it, or tap it against your phone (NFC), to authenticate. No batteries, no internet connection, and no extra fees required
  • MOST SECURE PASSKEY: Supports FIDO2/WebAuthn, FIDO U2F, Yubico OTP, OATH-TOTP/HOTP, Smart card (PIV), and OpenPGP. That means it’s versatile, working almost anywhere you need it
  • PRIMARY & SPARE KEYS: Just like having a spare house key, we recommend buying two YubiKeys - one for daily use and one as a spare. That way you’ll never get locked out of your accounts

Test the flow with Flask’s test client

The test client sends requests directly to the application without starting a live server. Configure a testing app and create a client fixture; below, create_app and init_database stand for the project’s app factory and database setup. Flask documents fixtures, request helpers, redirects, and session inspection in its testing guide.

import pytest
from app import create_app
from app.db import init_database

@pytest.fixture
def app(tmp_path):
    db_path = tmp_path / "test.sqlite"
    app = create_app({
        "TESTING": True,
        "DATABASE": str(db_path),
        "SECRET_KEY": "test-only-secret",
    })
    with app.app_context():
        init_database()
    yield app

@pytest.fixture
def client(app):
    return app.test_client()

def register(client, username="ada", password="correct horse"):
    return client.post("/auth/register", json={
        "username": username, "password": password
    })

def login(client, username="ada", password="correct horse"):
    return client.post("/auth/login", json={
        "username": username, "password": password
    })

Check success, invalid input, and protected access

def test_login_and_protected_route(client):
    assert register(client).status_code == 201

    response = login(client)
    assert response.status_code == 200
    assert response.json == {"message": "logged in"}

    protected = client.get("/auth/private")
    assert protected.status_code == 200
    assert protected.json == {"username": "ada"}

def test_unknown_username_and_wrong_password(client):
    register(client)

    unknown = login(client, username="nobody")
    wrong_password = login(client, password="not the password")
    assert unknown.status_code == 401
    assert wrong_password.status_code == 401
    assert unknown.json == wrong_password.json

def test_missing_fields_and_non_json(client):
    missing = client.post("/auth/login", json={"username": "ada"})
    non_json = client.post("/auth/login", data="username=ada")
    assert missing.status_code == 400
    assert non_json.status_code == 400

def test_anonymous_access_is_denied(client):
    response = client.get("/auth/private")
    assert response.status_code == 401

Verify logout removes authentication

def test_logout_clears_session(client):
    register(client)
    assert login(client).status_code == 200

    with client.session_transaction() as session:
        assert "user_id" in session

    response = client.post("/auth/logout")
    assert response.status_code == 200

    with client.session_transaction() as session:
        assert "user_id" not in session

    assert client.get("/auth/private").status_code == 401

For a browser-oriented view that redirects rather than returning JSON, follow redirects in the request and assert the destination response. Flask’s tutorial also demonstrates parametrized invalid-input checks and inspecting session state after logout. Use the response assertions that match the contract you actually implement rather than mixing redirect and JSON expectations.

Rank #4
Yubico - Security Key NFC - Basic Compatibility - Multi-Factor Authentication (MFA) Key, Connect via USB-A or NFC, FIDO Certified
  • POWERFUL SECURITY KEY: The Security Key NFC is the essential physical passkey for protecting your digital life from phishing attacks. It ensures only you can access your accounts.
  • WORKS WITH 1000+ ACCOUNTS: Compatible with Google, Microsoft, and Apple. A single Security Key NFC secures 100 of your favorite accounts, including email, password managers, and more.
  • FAST & CONVENIENT LOGIN: Plug in your Security Key NFC via USB-A and tap it, or tap it against your phone (NFC) to authenticate. No batteries, no internet connection, and no extra fees required.
  • TRUSTED PASSKEY TECHNOLOGY: Uses the latest passkey standards (FIDO2/WebAuthn & FIDO U2F) but does not support One-Time Passwords. For complex needs, check out the YubiKey 5 Series.
  • BUILT TO LAST: Made from tough, waterproof, and crush-resistant materials. Manufactured in Sweden and programmed in the USA with the highest security standards.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What these tests establish—and what they do not

These tests exercise the stated contract: registration, successful credentials, unknown username, incorrect password, missing input, authenticated and anonymous protected access, and logout. They are examples of assertions, not a report that the code has been run in a particular project. Adapt fixtures, schema initialization, imports, and database behavior to the application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Coverage tools can reveal branches that tests never execute, but even 100% test coverage does not prove that software is bug-free. Flask’s tutorial makes that limitation explicit in its testing and coverage discussion. This session-based example does not define JWTs, token revocation, rate limiting, CSRF policy, or a complete deployment threat model; those require separate decisions based on the client and application architecture.

Best Value
FIDO2 U2F Security Key Passkey Two-Factor Authentication (2FA) USB Key PIN+Touch (Non-Biometric) USB-A Type TrustKey T110
  • Security Key : Protect your online accounts against unauthorized access by using FIDO2 and U2F authentication with T110. It's the world's most protective security key that works with windows, Mac OS, Linux as well as Chrome, Firefox, Edge and many other major browsers.
  • Certified with the new FIDO2 standard, T110 provides the benefit of fast login and strong protection against phishing, account takeover as well as many other online attactks.
  • Works with : Bank of America, Github, Google, Microsoft, DUO, Twitter, Facebook, Dropbox, Apple, ebay, BINANCE, mor and more.
  • Fits USB-A port : Insert the T110 security key into the USB-A port of each service and log in conveniently with one touch
  • For the driver download and user guide, please visit TrustKey Solutions Home support page.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.