290 lines
8.0 KiB
Markdown
290 lines
8.0 KiB
Markdown
# Flask Authentication Application
|
|
|
|
A complete full-stack web application with user authentication built with Flask and SQLite.
|
|
|
|
## 📁 Project Structure
|
|
flask_app/
|
|
├── app.py # Main Flask application
|
|
├── database.py # Database setup and functions
|
|
├── users.py # User authentication functions
|
|
├── users.db # SQLite database
|
|
├── requirements.txt # Python dependencies
|
|
├── venv/ # Virtual environment
|
|
└── templates/ # HTML templates
|
|
├── base.html
|
|
├── home.html
|
|
├── login.html
|
|
└── register.html
|
|
|
|
text
|
|
|
|
## 🚀 Quick Start
|
|
|
|
### 1. Create Project Structure
|
|
|
|
```bash
|
|
cd Desktop
|
|
mkdir flask_app
|
|
cd flask_app
|
|
|
|
# Create all project files at once
|
|
cat > app.py << 'PYEOF'
|
|
from flask import Flask, render_template, request, redirect, url_for, session, flash
|
|
import database
|
|
import users
|
|
|
|
app = Flask(__name__)
|
|
app.secret_key = 'your-secret-key-change-this-in-production'
|
|
|
|
database.init_db()
|
|
|
|
@app.route('/')
|
|
def home():
|
|
return render_template('home.html')
|
|
|
|
@app.route('/register', methods=['GET', 'POST'])
|
|
def register():
|
|
if request.method == 'POST':
|
|
username = request.form['username']
|
|
email = request.form['email']
|
|
password = request.form['password']
|
|
|
|
success, message = users.register_user(username, email, password)
|
|
|
|
if success:
|
|
flash('Registration successful! Please login.', 'success')
|
|
return redirect(url_for('login'))
|
|
else:
|
|
flash(message, 'error')
|
|
|
|
return render_template('register.html')
|
|
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
if request.method == 'POST':
|
|
username = request.form['username']
|
|
password = request.form['password']
|
|
|
|
success, result = users.login_user(username, password)
|
|
|
|
if success:
|
|
session['user_id'] = result['id']
|
|
session['username'] = result['username']
|
|
flash('Login successful!', 'success')
|
|
return redirect(url_for('home'))
|
|
else:
|
|
flash(result, 'error')
|
|
|
|
return render_template('login.html')
|
|
|
|
@app.route('/logout')
|
|
def logout():
|
|
session.clear()
|
|
flash('You have been logged out.', 'success')
|
|
return redirect(url_for('home'))
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=True)
|
|
PYEOF
|
|
|
|
cat > database.py << 'DBEOF'
|
|
import sqlite3
|
|
|
|
def init_db():
|
|
conn = sqlite3.connect('users.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
email TEXT UNIQUE NOT NULL,
|
|
password TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
''')
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def get_db_connection():
|
|
conn = sqlite3.connect('users.db')
|
|
conn.row_factory = sqlite3.Row
|
|
return conn
|
|
DBEOF
|
|
|
|
cat > users.py << 'UEOF'
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
import database
|
|
|
|
def register_user(username, email, password):
|
|
hashed_password = generate_password_hash(password, method='sha256')
|
|
|
|
try:
|
|
conn = database.get_db_connection()
|
|
conn.execute('INSERT INTO users (username, email, password) VALUES (?, ?, ?)',
|
|
(username, email, hashed_password))
|
|
conn.commit()
|
|
conn.close()
|
|
return True, None
|
|
except sqlite3.IntegrityError as e:
|
|
return False, 'Username or email already exists!'
|
|
|
|
def login_user(username, password):
|
|
conn = database.get_db_connection()
|
|
user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()
|
|
conn.close()
|
|
|
|
if user and check_password_hash(user['password'], password):
|
|
return True, user
|
|
else:
|
|
return False, 'Invalid username or password!'
|
|
UEOF
|
|
|
|
# Create templates folder
|
|
mkdir templates
|
|
|
|
cat > templates/base.html << 'HTML1EOF'
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Flask Auth App{% endblock %}</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; max-width: 500px; margin: 50px auto; padding: 20px; }
|
|
.alert { padding: 10px; margin: 10px 0; border-radius: 5px; }
|
|
.success { background: #d4edda; color: #155724; }
|
|
.error { background: #f8d7da; color: #721c24; }
|
|
form { display: flex; flex-direction: column; gap: 10px; }
|
|
input, button { padding: 10px; margin: 5px 0; }
|
|
.nav { margin-bottom: 20px; }
|
|
.nav a { margin-right: 15px; text-decoration: none; color: #007bff; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="nav">
|
|
<a href="{{ url_for('home') }}">Home</a>
|
|
{% if session.get('user_id') %}
|
|
<a href="{{ url_for('logout') }}">Logout</a>
|
|
{% else %}
|
|
<a href="{{ url_for('login') }}">Login</a>
|
|
<a href="{{ url_for('register') }}">Register</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
<div class="alert {{ category }}">{{ message }}</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
{% block content %}{% endblock %}
|
|
</body>
|
|
</html>
|
|
HTML1EOF
|
|
|
|
cat > templates/home.html << 'HTML2EOF'
|
|
{% extends "base.html" %}
|
|
|
|
{% block title %}Home{% endblock %}
|
|
|
|
{% block content %}
|
|
<h1>Welcome to Flask Auth App</h1>
|
|
{% if session.get('user_id') %}
|
|
<h2>Hello, {{ session.get('username') }}!</h2>
|
|
<p>You are logged in.</p>
|
|
<p>This is your dashboard.</p>
|
|
{% else %}
|
|
<p>Please <a href="{{ url_for('login') }}">login</a> or <a href="{{ url_for('register') }}">register</a>.</p>
|
|
{% endif %}
|
|
{% endblock %}
|
|
HTML2EOF
|
|
|
|
cat > templates/register.html << 'HTML3EOF'
|
|
{% extends "base.html" %}
|
|
|
|
{% block title %}Register{% endblock %}
|
|
|
|
{% block content %}
|
|
<h2>Register</h2>
|
|
<form method="POST" action="{{ url_for('register') }}">
|
|
<input type="text" name="username" placeholder="Username" required>
|
|
<input type="email" name="email" placeholder="Email" required>
|
|
<input type="password" name="password" placeholder="Password" required>
|
|
<button type="submit">Register</button>
|
|
</form>
|
|
<p>Already have an account? <a href="{{ url_for('login') }}">Login here</a></p>
|
|
{% endblock %}
|
|
HTML3EOF
|
|
|
|
cat > templates/login.html << 'HTML4EOF'
|
|
{% extends "base.html" %}
|
|
|
|
{% block title %}Login{% endblock %}
|
|
|
|
{% block content %}
|
|
<h2>Login</h2>
|
|
<form method="POST" action="{{ url_for('login') }}">
|
|
<input type="text" name="username" placeholder="Username" required>
|
|
<input type="password" name="password" placeholder="Password" required>
|
|
<button type="submit">Login</button>
|
|
</form>
|
|
<p>Don't have an account? <a href="{{ url_for('register') }}">Register here</a></p>
|
|
{% endblock %}
|
|
HTML4EOF
|
|
2. Create Virtual Environment
|
|
bash
|
|
python -m venv venv
|
|
venv\Scripts\Activate.ps1
|
|
If you get an error:
|
|
|
|
bash
|
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
|
venv\Scripts\Activate.ps1
|
|
3. Install Dependencies
|
|
bash
|
|
pip install flask werkzeug flask-wtf
|
|
pip freeze > requirements.txt
|
|
4. Run the Application
|
|
bash
|
|
python app.py
|
|
Visit: http://127.0.0.1:5000
|
|
|
|
📋 File Purpose
|
|
File Purpose Contains
|
|
app.py Main Flask application Routes, request handling, session management
|
|
database.py Database operations Connection functions, table creation, SQL queries
|
|
users.py User authentication Register, login, password hashing functions
|
|
templates/ HTML templates Base template and page-specific templates
|
|
requirements.txt Dependencies List of Python packages needed
|
|
users.db Database file SQLite database with user data
|
|
🔒 Security Notes
|
|
This is a learning example, not production-ready
|
|
|
|
Passwords are hashed using Werkzeug
|
|
|
|
Never store plain text passwords
|
|
|
|
Use stronger secret keys in production
|
|
|
|
🚀 Next Steps
|
|
Add password reset functionality
|
|
|
|
Implement email verification
|
|
|
|
Add user profile pages
|
|
|
|
Create a simple blog system
|
|
|
|
Deploy to Heroku or Render
|
|
|
|
📚 Resources
|
|
Flask Documentation
|
|
|
|
SQLite Documentation
|
|
|
|
Jinja2 Template Documentation
|