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.
Recommended Free Tools
| 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
- 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
- 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.
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
- 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
- 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.
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.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →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.
Quick Recap
Best Value
- 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.




