Expiring Links Tutorial: Python Flask One-Time Link Generator

One time links are essential where users want to share resources with co-worker or friend’s effortlessly but at the same time restrict other from access.

Its works best because the link automatically inactive if some one go to the link for the first time. That means the same link will not work for the second time. or for other user.

Also we can set time so if no one enters the link it will automatically inactive after the specific time.

Now begin our program

  1. first the user will generate the link. and share..
  2. Other user will go to link and get access.
  3. the link(token) will be deleted from the active token list so it will not work anymore
  4. If no one access the link it will automatically remove from the shared link after specific time.

Generate link

we will use python secrets library to generate the unique token for the link

def generate_token():
    return secrets.token_urlsafe(16)

this is existing users list.( its example for the users who have account)


main_user_ids = ['user1', 'user2', 'user3']

So this users will be able to create the link for friends and co-workers

we will store the active token in python dictionary right now. For real world application you can use database like sql, mongodb or whatever you want

active_tokens = {}

This is code what the main users will get in the browser. You can use render template and place hte index.html in the template folder. but for simplicity we applied it directly.

@app.route('/', methods=['GET'])
def index():
    #return render_template('index.html')
    return """
    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Generate Connection Link</title>
</head>
<body>
    <h1>Generate Connection Link</h1>
    <form action="/generate_link" method="post">
        <label for="user_id">Enter User ID:</label><br>
        <input type="text" id="user_id" name="user_id"><br><br>
        <input type="submit" value="Generate Link">
    </form>
</body>
</html>
"""

Here the main users will enter the user name and generate the link on behalf of them

This is the second part of the code where generate the key from the previous code form action generate link

from datetime import datetime, timedelta
TOKEN_EXPIRY_DURATION = timedelta(hours=1)

# Route to generate a connection link
@app.route('/generate_link', methods=['POST'])
def generate_link():
    user_id = request.form.get('user_id') if request.form.get('user_id') in main_user_ids else None
    if user_id:
        token = generate_token()
        expiry_time = datetime.now() + TOKEN_EXPIRY_DURATION
        active_tokens[token] = (user_id, expiry_time)
        return f"Generated link from user {user_id}: <a href='/connect/{token}'>Click here to connect</a>"  # Return the generated token to the user
    else:
        return "Invalid user ID"

Here in the server side get the user name from the user . if user name exists then create an unique token and link on behalf of the user

Next part Verify the users access



# Route to handle the connection link
@app.route('/connect/<token>', methods=['GET'])
def connect(token):
    if token in active_tokens:
        user_id, expiry_time = active_tokens[token]
        if datetime.now() < expiry_time:
            # Connection successful, do something with the user_id
            del active_tokens[token]  # Mark the token as used
            return f"You have grant access by {user_id}"
        else:
            return "Token expired"
    else:
        return "Invalid token"

In this part the link is received from the user and verify if that exist. if exist and have expiration time . It grant the permission to that user and delete link(token) from the active tokens.

The complete code


from flask import Flask, request, redirect, url_for, render_template
from datetime import datetime, timedelta
import secrets

app = Flask(__name__)

# Active tokens and their expiry times
active_tokens = {}

# Duration for the token to remain valid (e.g., 1 hour)
TOKEN_EXPIRY_DURATION = timedelta(hours=1)

# Main user IDs
main_user_ids = ['user1', 'user2', 'user3']

# Function to generate a unique token
def generate_token():
    return secrets.token_urlsafe(16)

# Route to render the form
@app.route('/', methods=['GET'])
def index():
    #return render_template('index.html')
    return """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Generate Connection Link</title>
</head>
<body>
    <h1>Generate Connection Link</h1>
    <form action="/generate_link" method="post">
        <label for="user_id">Enter Your User ID( this represents the main users in the main usrs list. enter a name from the uses list main_user_id for example <b>user1</b> :</label><br>
        <input type="text" id="user_id" name="user_id"><br><br>
        <input type="submit" value="Generate Link">
    </form>
</body>
</html>
"""

# Route to generate a connection link
@app.route('/generate_link', methods=['POST'])
def generate_link():
    user_id = request.form.get('user_id') if request.form.get('user_id') in main_user_ids else None
    if user_id:
        token = generate_token()
        expiry_time = datetime.now() + TOKEN_EXPIRY_DURATION
        active_tokens[token] = (user_id, expiry_time)
        return f"Generated link from user {user_id}: <a href='/connect/{token}'>Click here to connect</a>"  # Return the generated token to the user
    else:
        return "Invalid user ID"



# Route to handle the connection link
@app.route('/connect/<token>', methods=['GET'])
def connect(token):
    if token in active_tokens:
        user_id, expiry_time = active_tokens[token]
        if datetime.now() < expiry_time:
            # Connection successful
            del active_tokens[token]  # Mark the token as used
            return f"You have grant access by {user_id}"
        else:
            return "Token expired"
    else:
        return "Invalid token"

if __name__ == '__main__':
    app.run(debug=True)



Create your first chat application with Flask and Socketio

We will use socketio for real-time messaging

First install our dependency for this application


pip install flask
pip install flask-socketio


How it will work.

Every user will get an unique key.

If the the other user submit the other users key that will send the message. its like sending message to unique username

Generate Unique Key for every user


import string
import random
def generate_key():
    key_length = 4
    characters = string.digits
    return ''.join(random.choice(characters) for _ in range(key_length))
generated_key=generate_key()
print(generated_key)


Its a 4 digit key for testing purpose. In real application must use long key with other randomness to make sure two key will not be same. Instead of key we can use unique user name. Thas another topic . For simplicity we will use this 4 digit key. Uptate it later on your necessary.

Our application structure….

stept by step Explanation

when the user load the page on borwer they will go to this page

@app.route('/')
def index():
    return render_template('index.html')

After loading the page they will connect with the socketio they will get an uniuqe key

and this key will send to the user by emiting the key to the client

also save the key in the python dictionary with the socketio sid to handle users connection activity later

When the user connected with socketio

@socketio.on('connect')
def on_connect():
    print('User connected')
    key = generate_key()
    active_connections[key] = request.sid
    emit('key_assigned', {'key': key})

When the user disconnected from socketio

If the user diconnected we will remove the user from the python dictionary.( its like active user management)

@socketio.on('disconnect')
def on_disconnect():
    print('User disconnected')
    for key, sid in active_connections.items():
        if sid == request.sid:
            del active_connections[key]
            break

When the user will send menssage to specific user.( the logic handles on serverside socketio)

@socketio.on('send_message')
def send_message(data):
    key = data['key']
    message = data['message']
    if key in active_connections:
        recipient_sid = active_connections[key]
        emit('receive_message', {'message': message}, to=recipient_sid)
    else:
        emit('receive_message', {'message': 'Does not Match'}, to=request.sid)


The Complete Server Side code


from flask import Flask, render_template,request
from flask_socketio import SocketIO, emit
import random
import string

app = Flask(__name__)
socketio = SocketIO(app)
active_connections = {} 


def generate_key():
    key_length = 4
    characters = string.digits
    return ''.join(random.choice(characters) for _ in range(key_length))

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('connect')
def on_connect():
    print('User connected')
    key = generate_key()
    active_connections[key] = request.sid
    emit('key_assigned', {'key': key})

@socketio.on('disconnect')
def on_disconnect():
    print('User disconnected')
    for key, sid in active_connections.items():
        if sid == request.sid:
            del active_connections[key]
            break

@socketio.on('send_message')
def send_message(data):
    key = data['key']
    message = data['message']
    if key in active_connections:
        recipient_sid = active_connections[key]
        emit('receive_message', {'message': message}, to=recipient_sid)
    else:
        emit('receive_message', {'message': 'Does not Match'}, to=request.sid)

if __name__ == '__main__':
    socketio.run(app, debug=True)


the client side code( the code we will send to browser when load the landing page index.html)

Step by step guide
First add the scoketuio client side library

<!DOCTYPE html>
<html>
<head>
    <title>Private Chat Application Prototype</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    
</head>
<body>

    <script>

    </script>
</body>
</html>

Add html content to show the message and users key

<!DOCTYPE html>
<html>
<head>

</head>
<body>
    <h1>Python Scoketio Chat Prototype</h1>

<!--User unique key will be shows here -->
    <p>Your Unique Key: <span id="uniqueKey"></span></p>

<!--This is where user will enter the friends key to send message to friend -->
    <input type="text" id="friendKey" placeholder="Enter Friend's Key"><br>
    <button onclick="startChat()">Start Chat</button><br>


<!-- This is where the send and received message will shown
    <div id="chat"></div>


<!-- This is where user will write the message
    <div>
        <input type="text" id="messageInput" placeholder="Enter your message">
        <button onclick="sendMessage()">Send</button>
    </div>

    <script>


    </script>
</body>
</html>

The logical part javascript

connect to socket io

const socket = io();

       const socket = io();
        socket.on('key_assigned', (data) => {
            const uniqueKeySpan = document.getElementById('uniqueKey');
            uniqueKeySpan.textContent = data.key;
        });

When key sent by server this code will extract the key from data and place it to the html with the id name ‘uniquekey’

Initiate Chat

        function startChat() {
            friendKey = document.getElementById('friendKey').value;
            const message = "Hello, let's chat!";
            socket.emit('send_message', { key: friendKey, message: message });
        }

this function will get key which user enter to start chat and send it to the another if that user is active

when the pervious user send the message other user will get it from client side with this code

       socket.on('receive_message', (data) => {
            const chatDiv = document.getElementById('chat');
            const messageDiv = document.createElement('div');
            messageDiv.textContent = `Friend: ${data.message}`;
            chatDiv.appendChild(messageDiv);
        });

This will recieve the message from server and set it to the html chat div .

Start continuouis chat

if the user initiate chat . user can send countinous chat as long as the receiver is active

as friend key already stored when the initiate the chat. so user do not have to add the key every time when send message

        function sendMessage() {
            const message = document.getElementById('messageInput').value;
            if (friendKey && message) {
                socket.emit('send_message', { key: friendKey, message: message });
                const chatDiv = document.getElementById('chat');
                const messageDiv = document.createElement('div');
                messageDiv.textContent = `You: ${message}`;
                chatDiv.appendChild(messageDiv);
            }
        }

The complete code javascript code for client side


    <script>
        const socket = io();

        // Store the friend's key for sending messages
        let friendKey = '';

        // Listen for the 'key_assigned' event to display the assigned key
        socket.on('key_assigned', (data) => {
            const uniqueKeySpan = document.getElementById('uniqueKey');
            uniqueKeySpan.textContent = data.key;
            console.log(data.key);
        });


        function startChat() {
            friendKey = document.getElementById('friendKey').value;
            const message = "Hello, let's chat!";
            socket.emit('send_message', { key: friendKey, message: message });
        }

        function sendMessage() {
            const message = document.getElementById('messageInput').value;
            if (friendKey && message) {
                socket.emit('send_message', { key: friendKey, message: message });
                const chatDiv = document.getElementById('chat');
                const messageDiv = document.createElement('div');
                messageDiv.textContent = `You: ${message}`;
                chatDiv.appendChild(messageDiv);
            }
        }

        socket.on('receive_message', (data) => {
            const chatDiv = document.getElementById('chat');
            const messageDiv = document.createElement('div');
            messageDiv.textContent = `Friend: ${data.message}`;
            chatDiv.appendChild(messageDiv);
        });


    </script>

The complete client side code (index.html)


<!DOCTYPE html>
<html>
<head>
    <title>Private Chat Prototype With Socketio and Flask</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
</head>
<body>
    <h1>Private Chat Prototype With socketio and flask</h1>
    <p>Your Unique Key: <span id="uniqueKey"></span></p>
    <input type="text" id="friendKey" placeholder="Enter Friend's Key"><br>
    <button onclick="startChat()">Start Chat</button><br>
    <div id="chat"></div>
    <div>
        <input type="text" id="messageInput" placeholder="Enter your message">
        <button onclick="sendMessage()">Send</button>
    </div>

    <script>
        const socket = io();

        // Store the friend's key for sending messages
        let friendKey = '';

        // Listen for the 'key_assigned' event to display the assigned key
        socket.on('key_assigned', (data) => {
            const uniqueKeySpan = document.getElementById('uniqueKey');
            uniqueKeySpan.textContent = data.key;
            console.log(data.key);
        });


        function startChat() {
            friendKey = document.getElementById('friendKey').value;
            const message = "Hello, let's chat!";
            socket.emit('send_message', { key: friendKey, message: message });
        }

        function sendMessage() {
            const message = document.getElementById('messageInput').value;
            if (friendKey && message) {
                socket.emit('send_message', { key: friendKey, message: message });
                const chatDiv = document.getElementById('chat');
                const messageDiv = document.createElement('div');
                messageDiv.textContent = `You: ${message}`;
                chatDiv.appendChild(messageDiv);
            }
        }

        socket.on('receive_message', (data) => {
            const chatDiv = document.getElementById('chat');
            const messageDiv = document.createElement('div');
            messageDiv.textContent = `Friend: ${data.message}`;
            chatDiv.appendChild(messageDiv);
        });


    </script>
</body>
</html>



Structure

├── app.py
└── templates
    └── index.html

How you will test. it?

First run the code. and open the localhost link http://127.0.0.1:5000/ in the browser. Another one in in new incognito mode

So it will act like two unique user for your testing. Then Enter the other users unique key ( which works like unique username here) Then send message . Thats all. customize the code your necessary. Happy coding!

You can also get this project code from github
https://github.com/01one/flask-socketio-chat-application

Ajax example with flask

One of the best ajax applied in real world is username checker. In this example we will check if there is existing username

This is code /project we will

  • use ajax to search username
  • if username exist or does not exist send to message to the user
  • Also create new user with html form and flask if username exists

Our first step: Check if username exists or not

Imagine we have this existing username

# Sample list of existing usernames
existing_usernames = ["user1", "user2", "user3"]

when the user enter a username we will check it from server(Check username)


@app.route('/check_username', methods=['POST'])
def check_username():
	username = request.form['username']
	print(username)
	if username in existing_usernames:
		return jsonify({'message': 'Username already exists! Please choose a different username.'})
	elif not is_valid_username(username):
		return jsonify({'message': 'Username contains special characters. Only letters, numbers, and underscores are allowed.'})
	else:
		return jsonify({'message': 'Username is available!'})


When new account is created username will be added to the username list (Create New User)


@app.route('/create_profile', methods=['POST'])
def create_profile():
	username = request.form['username']
	if username not in existing_usernames:
		existing_usernames.append(username)
		return  jsonify({'message': f"Welcome, {username}! Your profile has been created successfully."})
	else:
		return jsonify({'message': 'Username already exists! Please choose a different username.'})


In the client side we are using jquery to make everything simpler.

When the user will type it will automatically send to server with ajax and return the response

We will also make sure if the username exists the response text will show in green color if not it will show in red color.


$(document).ready(function () {
	$('#username').keyup(function () {
		var username = $(this).val();
		if (username.length === 0) {
			$('#usernameMessage').text("");
			return;
		}
		if (/\s/.test(username)) {
			$('#usernameMessage').text("Username cannot contain spaces.");
			return;
		}
		if (!/^[a-zA-Z0-9]+$/.test(username)) {
			$('#usernameMessage').text("Username cannot contain special characters.");
			return;
		}
		$.ajax({
			type: 'POST',
			url: '/check_username',
			data: { 'username': username },
			success: function (response) {
				$('#usernameMessage').text(response.message);
				if (response.message === 'Username is available!') {
					$('#usernameMessage').css('color', 'green');
				} else {
					$('#usernameMessage').css('color', 'red');
				}
			}
		});
	});




	$('#createProfileForm').submit(function (e) {
		e.preventDefault();
		var username = $('#username').val();
		if (username.length === 0) {
			$('#usernameMessage').text("Username cannot be empty.");
			return;
		}
		if (/\s/.test(username)) {
			$('#usernameMessage').text("Username cannot contain spaces.");
			return;
		}
		if (!/^[a-zA-Z0-9]+$/.test(username)) {
			$('#usernameMessage').text("Username cannot contain special characters.");
			return;
		}
		$.ajax({
			type: 'POST',
			url: '/create_profile',
			data: { 'username': username },
			success: function (response) {
				$('#usernameMessage').text(response.message);
			}
		});
	});
});



The complete code

structure

├── app.py
└── templates
    └── username-id-generate.html

The serverside code


import re
from flask import Flask, render_template, request, jsonify

app = Flask(__name__)

# Sample list of existing usernames
existing_usernames = ["user1", "user2", "user3"]


def is_valid_username(username):
	# Double check
	return re.match("^[a-zA-Z0-9_]*$", username) is not None


@app.route('/')
def index():
	return render_template('username-id-generate.html')


@app.route('/check_username', methods=['POST'])
def check_username():
	username = request.form['username']
	print(username)
	if username in existing_usernames:
		return jsonify({'message': 'Username already exists! Please choose a different username.'})
	elif not is_valid_username(username):
		return jsonify({'message': 'Username contains special characters. Only letters, numbers, and underscores are allowed.'})
	else:
		return jsonify({'message': 'Username is available!'})


@app.route('/create_profile', methods=['POST'])
def create_profile():
	username = request.form['username']
	if username not in existing_usernames:
		existing_usernames.append(username)
		return  jsonify({'message': f"Welcome, {username}! Your profile has been created successfully."})
	else:
		return jsonify({'message': 'Username already exists! Please choose a different username.'})



if __name__ == '__main__':
	app.run(debug=True)



The client side code


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Create Profile</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        body {
            font-family: 'Helvetica Neue', sans-serif;
            background-color: #f0f2f5;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        #createProfileForm {
            background: #fff;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            width: 350px;
        }
        h1 {
            text-align: center;
            font-size: 28px;
            margin-bottom: 20px;
            color: #333;
        }
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
            color: #555;
        }
        input[type="text"] {
            width: 100%;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-sizing: border-box;
            font-size: 16px;
        }
        input[type="submit"] {
            width: 100%;
            padding: 12px;
            border: none;
            border-radius: 5px;
            background-color: #007bff;
            color: white;
            font-size: 18px;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }
        input[type="submit"]:hover {
            background-color: #0056b3;
        }
        #usernameMessage {
            color: red;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <form id="createProfileForm">
        <h1>Create Your Profile</h1>
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" autocomplete="off" required>
        <span id="usernameMessage"></span>
        <input type="submit" value="Create Profile">
    </form>
    <script>
        $(document).ready(function () {
            $('#username').keyup(function () {
                var username = $(this).val();
                if (username.length === 0) {
                    $('#usernameMessage').text("");
                    return;
                }
                if (/\s/.test(username)) {
                    $('#usernameMessage').text("Username cannot contain spaces.");
                    return;
                }
                if (!/^[a-zA-Z0-9]+$/.test(username)) {
                    $('#usernameMessage').text("Username cannot contain special characters.");
                    return;
                }
        $.ajax({
            type: 'POST',
            url: '/check_username',
            data: {'username': username},
            success: function (response) {
                $('#usernameMessage').text(response.message);
                if (response.message === 'Username is available!') {
                    $('#usernameMessage').css('color', 'green');
                } else {
                    $('#usernameMessage').css('color', 'red');
                }
            }
        });
    });

            $('#createProfileForm').submit(function (e) {
                e.preventDefault();
                var username = $('#username').val();
                if (username.length === 0) {
                    $('#usernameMessage').text("Username cannot be empty.");
                    return;
                }
                if (/\s/.test(username)) {
                    $('#usernameMessage').text("Username cannot contain spaces.");
                    return;
                }
                if (!/^[a-zA-Z0-9]+$/.test(username)) {
                    $('#usernameMessage').text("Username cannot contain special characters.");
                    return;
                }
                $.ajax({
                    type: 'POST',
                    url: '/create_profile',
                    data: {'username': username},
                    success: function (response) {
                        $('#usernameMessage').text(response.message);
                    }
                });
            });
        });
    </script>
</body>
</html>



Get the project code from github

https://github.com/01one/flask-ajax-example

Flask testing environment ssl solution with “adhoc”

Why?

When I started developing video calling application at one point I faced this situation. In single pc(on localhost 127.0.0.1) everything works very well but when I need to test another pc on the same network for example(wifi) . Before on public production environment I wanted to test my own private network which was established with a WiFi router. I host on the my local ip which was assigned by the router) . The issue was in this situation by default the browser will block the microphone and camera and other essential permission without ssl.

The solution

ssl_context=’adhoc’ in this settings flask automatically generates a temporary, self-signed SSL certificate. And this will enable https connection. (remember its only for testing and not for production environment)

An example code

from flask import Flask

app = Flask(__name__)

# HTML with embedded JavaScript for accessing camera and microphone
html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Video and Audio Streaming</title>
</head>
<body>
    <h1>Video and Audio Streaming</h1>
    <div>
        <h2>Video Stream</h2>
        <video id="video" width="640" height="480" autoplay></video>
    </div>
    <div>
        <h2>Audio Stream</h2>
        <audio id="audio" controls autoplay></audio>
    </div>
    <script>
        // Accessing video and audio streams from the browser
        navigator.mediaDevices.getUserMedia({ video: true, audio: true })
            .then(function (stream) {
                var video = document.getElementById('video');
                var audio = document.getElementById('audio');

                // Set the video source to the stream
                video.srcObject = stream;

                // Set the audio source to the stream
                audio.srcObject = stream;
            })
            .catch(function (error) {
                console.error('Error accessing media devices:', error);
            });
    </script>
</body>
</html>
"""

# Route to serve the HTML content
@app.route('/')
def index():
    return html_content

if __name__ == "__main__":
    app.run(host='0.0.0.0',debug=True, ssl_context='adhoc')

Though This solved the https issue temporarily

How to implement json web token (jwt) in flask

What is json web token?

JSON Web Token (JWT) is like a digital ID card for websites and apps. It’s often issued after a successful login and securely tells a website about the user and their accessibility

Secure Cookies also do the same thing. but there is some core differences

Feature JSON Web Token (JWT) Secure Cookie
Storage Client-side (browser) Server-side (browser stores only a key)
Contents User data, authorization info, expiration (self-contained) Typically just a session ID
CSRF Vulnerability Less prone More susceptible
Scalability Excellent Potentially limiting under heavy loads
State Stateless Stateful
Ideal Use Cases APIs, microservices, reduced server-side storage Traditional web apps, CSRF mitigated

now create our json web token

The structure

Understanding JSON Web Token (JWT) Structure
Part What it Contains Encoded Format
Header Information about the type of token and the signing algorithm used. Base64Url-encoded JSON object
Payload The actual data (claims) about the user, like their ID, roles, or issued-at time. Base64Url-encoded JSON object
Signature A cryptographic signature that helps to verify the token hasn’t been tampered with. Generated by combining the encoded header, encoded payload, and a secret key.

Now create our json webtoken with pyhon

We will use hashlib and hmac library for this

As json web token contains three part. We will add this three part in the token. Also we will apply encode decode. So only we will able to know whats inside token payload.



import base64
import hashlib
import hmac
import json
import os
import secrets
import time
import re

# Define constants
SECRET_KEY = '123'
EXPECTED_ISSUER = 'expected_issuer'
EXPECTED_AUDIENCE = 'expected_audience'

# Define regular expression pattern for issuer name
ISSUER_PATTERN = r'^[a-zA-Z0-9_-]+$'
#ISSUER_PATTERN = r'^[a-z0-9_-]{5,20}$'  # 5-20 characters, lowercase letters, numbers, underscore, hyphen

def encode_jwt(payload, expiration_time):
    header = {'typ': 'JWT', 'alg': 'HS256'}
    header_json = json.dumps(header, separators=(',', ':')).encode('utf-8')
    
    # Include expiration time in the payload
    payload['exp'] = int(time.time()) + expiration_time
    
    payload_json = json.dumps(payload, separators=(',', ':')).encode('utf-8')

    encoded_header = base64.urlsafe_b64encode(header_json).decode('utf-8').rstrip('=')
    encoded_payload = base64.urlsafe_b64encode(payload_json).decode('utf-8').rstrip('=')

    signature = hmac.new(SECRET_KEY.encode('utf-8'), f"{encoded_header}.{encoded_payload}".encode('utf-8'), hashlib.sha256)
    encoded_signature = base64.urlsafe_b64encode(signature.digest()).decode('utf-8').rstrip('=')

    jwt_token = f"{encoded_header}.{encoded_payload}.{encoded_signature}"
    return jwt_token

def decode_jwt(jwt_token):
    # Check if the token contains three parts
    parts = jwt_token.split('.')
    if len(parts) != 3:
        return {'error': 'Invalid token format'}

    encoded_header, encoded_payload, encoded_signature = parts

    # Validate and decode the payload
    try:
        payload = base64.urlsafe_b64decode(encoded_payload + '=' * (-len(encoded_payload) % 4)).decode('utf-8')
        decoded_payload = json.loads(payload)
    except (json.JSONDecodeError, UnicodeDecodeError):
        return {'error': 'Invalid payload format'}

    # Validate expiration time
    if 'exp' in decoded_payload:
        if not isinstance(decoded_payload['exp'], int):
            return {'error': 'Expiration time must be an integer'}
        if decoded_payload['exp'] < int(time.time()):
            return {'error': 'Token expired'}

    # Validate issuer using regular expression
    if 'iss' in decoded_payload:
        issuer = decoded_payload['iss']
        if not re.match(ISSUER_PATTERN, issuer):
            return {'error': 'Invalid issuer name format'}

        if issuer != EXPECTED_ISSUER:
            return {'error': 'Invalid issuer'}

    # Validate audience
    if 'aud' in decoded_payload:
        if not isinstance(decoded_payload['aud'], str):
            return {'error': 'Audience must be a string'}
        if EXPECTED_AUDIENCE not in decoded_payload['aud']:
            return {'error': 'Invalid audience'}

    # Validate signature
    expected_signature = base64.urlsafe_b64encode(hmac.new(SECRET_KEY.encode('utf-8'), f"{encoded_header}.{encoded_payload}".encode('utf-8'), hashlib.sha256).digest()).decode('utf-8').rstrip('=')
    if not secrets.compare_digest(encoded_signature, expected_signature):
        return {'error': 'Invalid signature'}

    return decoded_payload
    
payload = {'user_id': 1, 'username': 'Our UserName', 'iss': 'expected_issuer', 'aud': 'expected_audience'}


# Set expiration time in seconds (e.g., 1 hour)
expiration_time = 3600

# Encoding the payload into a JWT with expiration time
jwt_token = encode_jwt(payload, expiration_time)
print("Encoded JWT with expiration:", jwt_token)

# Decoding the JWT to retrieve the payload
decoded_payload = decode_jwt(jwt_token)
print("Decoded Payload:", decoded_payload)






Simple implementation in flask

from flask import Flask, request, jsonify, render_template, redirect, url_for, make_response
import base64
import hashlib
import hmac
import json
import datetime

app = Flask(__name__)

# Secret key for encoding/decoding JWT
SECRET_KEY = '234231465457'

def encode_jwt(payload):
	header = {'typ': 'JWT', 'alg': 'HS256'}
	header_json = json.dumps(header, separators=(',', ':')).encode('utf-8')
	payload_json = json.dumps(payload, separators=(',', ':')).encode('utf-8')

	# Base64 encoding header and payload
	encoded_header = base64.urlsafe_b64encode(header_json).decode('utf-8')
	encoded_payload = base64.urlsafe_b64encode(payload_json).decode('utf-8')

	# Creating signature
	signature = hmac.new(SECRET_KEY.encode('utf-8'), f"{encoded_header}.{encoded_payload}".encode('utf-8'), hashlib.sha256)
	encoded_signature = base64.urlsafe_b64encode(signature.digest()).decode('utf-8')

	# Combining all parts to form JWT
	jwt_token = f"{encoded_header}.{encoded_payload}.{encoded_signature}"

	return jwt_token

def decode_jwt(jwt_token):
	encoded_header, encoded_payload, encoded_signature = jwt_token.split('.')
	payload = base64.urlsafe_b64decode(encoded_payload.encode('utf-8')).decode('utf-8')
	return json.loads(payload)

@app.route('/')
def home():
	# Check if user is already logged in
	jwt_token = request.cookies.get('jwt_token')
	if jwt_token:
		try:
			payload = decode_jwt(jwt_token)
			print(payload)
			return redirect(url_for('dashboard'))
		except:
			pass  # Invalid token, proceed to login
	return render_template('index.html')

@app.route('/login', methods=['POST'])
def login():
	# Dummy authentication
	if request.form['username'] == 'username' and request.form['password'] == 'password':
		# Create JWT token with payload
		payload = {'username': request.form['username']}
		jwt_token = encode_jwt(payload)
		
		# Set the JWT token in a cookie
		response = make_response(redirect(url_for('dashboard')))
		response.set_cookie('jwt_token', jwt_token, httponly=True, expires=datetime.datetime.now() + datetime.timedelta(minutes=30))
		
		return response
	else:
		return jsonify({'message': 'Invalid username or password'}), 401

@app.route('/dashboard')
def dashboard():
	# Retrieve JWT token from the cookie
	jwt_token = request.cookies.get('jwt_token')
	if jwt_token:
		try:
			payload = decode_jwt(jwt_token)
			return render_template('dashboard.html', username=payload['username'])
		except:
			return jsonify({'message': 'Invalid token'}), 401
	else:
		return jsonify({'message': 'Token missing'}), 401
		
@app.route('/logout')
def logout():
	# Clear the JWT token from the cookie
	response = make_response(redirect(url_for('home')))
	response.set_cookie('jwt_token', '', expires=0)
	return response
		
		

if __name__ == '__main__':
	app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <p>our example code username is username and password is password</p>
    <form action="/login" method="post">
        <label for="username">Username:</label><br>
        <input type="text" id="username" name="username"><br>
        <label for="password">Password:</label><br>
        <input type="password" id="password" name="password"><br><br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

dashboard.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Protected Dashboard</title>
</head>
<body>
    <h1>Welcome, {{ username }}!</h1>
    <p>This is your protected dashboard.</p>
    <p>Feel free to explore.</p>
    <a href="/logout">Logout</a>
</body>
</html>

Get this complete example code from github

Building a Simple and Secure API with Python Flask

Server-Side: Flask API Development

We will build a simple API that will tell the current time. And then add more feature.

The explanation and the code

The code



from flask import Flask, request, jsonify
from datetime import datetime

app = Flask(__name__) 

# Define a route to get the current time
@app.route('/get-current-time', methods=['POST'])
def get_current_time():
    # Validate the API key
    api_key = request.headers.get('Authorization')
    if api_key != '12345': 
        return jsonify({"error": "Unauthorized"}), 401 

    # Get and format the current time
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # Return the time in JSON format
    return jsonify({"currentTime": current_time}), 200 

if __name__ == '__main__':
    app.run(port=5000, debug=True) 





Route Definition: Use the @app.route() decorator to define the URL path (/get-current-time) for your API endpoint. This specific endpoint is configured to handle POST requests.
Endpoint Logic: Inside the get_current_time() function (which handles requests to your endpoint), retrieve the API key from the incoming request's headers
Authorization Check: Implement a security measure by verifying that the provided API key matches the correct value. If the key is invalid, return a JSON error response with a 401 (Unauthorized) HTTP status code.

Response Preparation: Construct a JSON dictionary (response_data) to hold the current time value.

Client-Side: Calling the API




import requests

url = 'http://localhost:5000/get-current-time'
api_key = '12345'

headers = {
  'Authorization': api_key,
  'Content-Type': 'application/json',
}

try:
  response = requests.post(url, headers=headers)

  if response.status_code == 200:
    response_data = response.json()
    current_time = response_data.get('currentTime')
    print("Current time received from the API:", current_time)
  else:
    print("Error:", response.status_code, response.text)  # Include response text
except requests.exceptions.RequestException as e:
  print("Error On request:", e)





In the client side we will use requests module to retrieve the time with the API request.

requests module is not a built-in module in Python. So if you have not installed you need to install it first.

pip install requests

The code and the explanations

url = 'http://localhost:5000/get-current-time'
api_key = 'oCZ2DNROIRSyL058'

headers = {
  'Authorization': api_key,
  'Content-Type': 'application/json',
}

Set API URL and Key: Configure the address of your API endpoint and your valid API key.
Create Headers: Build a dictionary containing your API key for authorization and the content type (application/json).


Testing the code

  • First we need to run the code we have add in the top for server side. It will run a server on local host http://localhost:5000/
  • Then we need to run another python script that is written for the client side. That’s all! It will print the time which comes from the server.

This was just for essential approach. We can make it more practical. For example we need check if a number is prime or not but we do not want to write the core functionality in our client side code. So we can write the API for it .. And call when ever we need and wherever we are!

We will use Miller-Rabin Primality Test for more efficiency to check a number is prime or not.

The code that checks the number is prime or not




import random

def is_prime_miller_rabin(n, iterations=40):
    """Performs the Miller-Rabin Primality Test to determine if a number is likely prime.

    This test is probabilistic, meaning it's highly accurate but not foolproof.
    Increasing the 'iterations' value improves the accuracy of the result.

    Args:
        n: The integer to test for primality.
        iterations: Number of test rounds to perform (default is 40).

    Returns:
        True if the number is likely prime, False if it's not.
    """

    if n <= 1:
        return False
    if n <= 3:
        return True
    if n % 2 == 0:
        return False

    # Decompose n-1 into the form 2^r * d
    d = n - 1
    r = 0
    while d % 2 == 0:
        d //= 2
        r += 1

    # Witness loop
    for _ in range(iterations):
        a = random.randint(2, n - 2)
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True



# Test the code
number_to_test = int(input("Enter a number to check for primality: "))
if is_prime_miller_rabin(number_to_test):
    print(f"{number_to_test} is likely a prime number.")
else:
    print(f"{number_to_test} is not a prime number.") 





If you are interested in prime numbers you also can read this article

Implemented In The server code( API)




from flask import Flask, request, jsonify
from datetime import datetime
import random

app = Flask(__name__) 

def is_prime_miller_rabin(n, iterations=40):
    """Performs the Miller-Rabin Primality Test to determine if a number is likely prime."""
    if n <= 1:
        return False
    if n <= 3:
        return True
    if n % 2 == 0:
        return False

    # Decompose n-1 into the form 2^r * d
    d = n - 1
    r = 0
    while d % 2 == 0:
        d //= 2
        r += 1

    # Witness loop
    for _ in range(iterations):
        a = random.randint(2, n - 2)
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True

# Define a route to check if a number is prime
@app.route('/check-prime', methods=['POST'])
def check_prime():
    # Validate the API key
    api_key = request.headers.get('Authorization')
    if api_key != '12345': 
        return jsonify({"error": "Unauthorized"}), 401 

    # Get the number from the request
    data = request.get_json()
    number_to_test = data.get('number')

    # Check if the number is prime
    is_prime = is_prime_miller_rabin(number_to_test)

    # Return the result
    return jsonify({"number": number_to_test, "isPrime": is_prime}), 200 

if __name__ == '__main__':
    app.run(port=5000, debug=True) 






Calling from the client side




import requests

url = 'http://127.0.0.1:5000/check-prime'
headers = {'Authorization': '12345'}
data = {'number': 172983479832623897492387592384682937659382477}

response = requests.post(url, json=data, headers=headers)
print(response.json())






Limit the API calling: Setting the Rate-Limiting

We can specifically set the daily limit or hourly limit for specific user to save the computation limits. For this we will use a wonderful library apscheduler . I first used this library on video calling project for user data management scheduler.

Install the library with

pip install apscheduler

Now our code will look like this




from flask import Flask, request, jsonify
from datetime import datetime, timedelta
import random
from apscheduler.schedulers.background import BackgroundScheduler

app = Flask(__name__)

def is_prime_miller_rabin(n, iterations=40):
    """Performs the Miller-Rabin Primality Test to determine if a number is likely prime."""
    if n <= 1:
        return False
    if n <= 3:
        return True
    if n % 2 == 0:
        return False

    # Decompose n-1 into the form 2^r * d
    d = n - 1
    r = 0
    while d % 2 == 0:
        d //= 2
        r += 1

    # Witness loop
    for _ in range(iterations):
        a = random.randint(2, n - 2)
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True

# Initialize the scheduler
scheduler = BackgroundScheduler()

# Define the users and their limits
users = {"12345": {"limit": 5}, "45365": {"limit": 5}}

# Function to reset the limits
def reset_limits():
    print("Resetting limits...")
    for user_info in users.values():
        user_info["limit"] = 5

# Schedule the job to run every 24 hours
scheduler.add_job(reset_limits, 'interval', hours=24)

# Start the scheduler
scheduler.start()

# Define a route to check if a number is prime
@app.route('/check-prime', methods=['POST'])
def check_prime():
    # Validate the API key
    api_key = request.headers.get('Authorization')
    if api_key not in users:
        return jsonify({"error": "Unauthorized"}), 401

    # Get the number from the request
    limit = users[api_key]['limit']
    if not limit == 0:
        data = request.get_json()
        number_to_test = data.get('number')

        # Check if the number is prime
        is_prime = is_prime_miller_rabin(number_to_test)

        # Update the request limit
        users[api_key]['limit'] -= 1

        # Return the result
        return jsonify({"number": number_to_test, "isPrime": is_prime}), 200
    else:
        return jsonify({"error": "Request limit exceeded. Please try again later."}), 429

if __name__ == '__main__':
    app.run(port=5000, debug=True)






Client Side will be unchanged!




import requests

url = 'http://127.0.0.1:5000/check-prime'
headers = {'Authorization': '12345'}
data = {'number': 172983479832623897492387592384682937659382477}

response = requests.post(url, json=data, headers=headers)
print(response.json())





Enhance Security Level

For demonstration purposes, this example stores the API key directly in the code. In production environments, always store API keys securely in a database with hashing to protect sensitive information.

With timestamp make the API key ultimately unique when generate new key for user




import secrets
import string
import time

def generate_unique_api_key(length=32):
    """Generate a unique API key using a combination of random characters and timestamp."""
    timestamp = str(int(time.time()))  # Get current timestamp as a string
    alphabet = string.ascii_letters + string.digits
    random_part = ''.join(secrets.choice(alphabet) for _ in range(length - len(timestamp)))
    key = timestamp + random_part
    return key

# Generate a unique API key of length 32
api_key = generate_unique_api_key()
print("unique API key:", api_key)






For You

For more easy to use, I added the code in the github repository

Flask File Handling Made Easy: Upload and Download Files

Table of content

Very basic example

code_features

  • Show the user a file upload page
  • Store the uploaded file in the server

This is our first example… Run this code first. Then read the step by step explanation. That will be more effective. Because this tutorial is all about practice. In this article covers simple upload, upload to database, securing the upload by adding limiting file extension and adding download link… Run the example first then read the article…

First Example: Simple Upload

copy the code and run first. the client side HTML is directly added this in python script instead of template for simplicity. it will run on http://127.0.0.1:5000. open that in browser and upload and submit the file, you can get the upload file in the same directory where your python script is. In the next examples. We will store in to a folder in next example. And Later into a database.



from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/')
def upload_file():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>
    '''

@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        f.save(f.filename)
        return 'successfully uploaded file'

if __name__ == '__main__':
    app.run(debug=True)

    
    
    

Step by step explanation

  • from flask import Flask, render_template, request
    Import the necessary module

Understanding app = Flask(__name__) for Flask Web Development

When you write app = Flask(__name__), you're creating the core Flask application object. This line is essential in building web applications with Flask. Here's a breakdown of its importance:

Resource Location: The __name__ variable instructs Flask where to locate resources crucial for your web app, such as:
Templates: HTML files that define the structure and appearance of your website.
Static Files: Supporting files like CSS (stylesheets), JavaScript, and images.
Flexible Configurations: Using __name__ allows Flask to adapt configurations based on your development environment (local testing vs. live deployment).

The page where user can upload the file



<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>


    

    






We can add this in templates folder in flask as upload-file.html other names. for simplicity directly included in the python script.
<form action=”/uploader” method=”POST” enctype=”multipart/form-data”>

This HTML file upload form provide a structured mechanism for users to submit files to a web server.

action=”/uploader” : This attribute acts as the destination address for the uploaded file. It directs the web server to a specific processing location (in this case, the “/uploader” route).


method=”POST” : This attribute specifies a secure transfer method. The “POST” method embeds the file within the body of the HTTP request, enhancing data protection.

enctype=”multipart/form-data” : This attribute is essential for file uploads. It establishes an encoding format that seamlessly transmits both files and standard text-based form data.

This code just upload the file in the server and store the in the same directory. In the next example We will learn how to store the file on specific directory.




@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        f.save(f.filename)
        return 'successfully uploaded file'

    
    


Second Example: Storing the uploaded files in a specific folder



from flask import Flask, render_template, request
import os

app = Flask(__name__)

UPLOAD_FOLDER = 'user-uploads'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

@app.route('/')
def upload_file():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>
    '''

@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)
        f.save(file_path)
        return 'File uploaded successfully'

if __name__ == '__main__':
    app.run(debug=True)



    
    
    

We have created a folder named ‘upload files’ . If not created the code wil automatically create the folder



import os
UPLOAD_FOLDER = 'user-uploads'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

    
    

We applied python os module to create the folder. 

import os

Integrates the 'os' module, enabling operating system's file management capabilities.



UPLOAD_FOLDER = 'uploads'
keyword-relevant name ('uploads') for the directory where uploaded files will be stored.

if not os.path.exists(UPLOAD_FOLDER):
Verifies the existence of the 'uploads' directory.

os.makedirs(UPLOAD_FOLDER)
If the 'uploads' directory is absent, this command generates it along with any necessary subdirectories.


app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
Define the configuration variable 
Imagine UPLOAD_FOLDER as a designated storage location within your Flask application for uploaded files. The line app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER does the following:

Assigns a Name: It gives this storage location a clear label, 'UPLOAD_FOLDER'.
Defines a Path: It links this label to the actual folder path on your system where uploaded files will be saved.

In the next example we will learn to store the uploaded files in the database.


Third Example: Storing the uploaded files in a database

Storing user data securely and effectively is crucial for many applications. A database is the optimal solution for long-term storage. SQL databases like SQLite, PostgreSQL, and MySQL are excellent options, offering varying features to suit your needs.

For enhanced security and flexibility, consider using an object-relational mapper (ORM). ORMs streamline database interactions within your code. In this example, we’ll demonstrate how to utilize the Peewee ORM with Python’s built-in SQLite database. This approach allows for effortless migration to other SQL databases in the future.

Install peewee orm

pip install peewee

And now copy , run and test the code..



from flask import Flask, render_template, request
from peewee import SqliteDatabase, Model, CharField
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)

# SQLite database initialization
db = SqliteDatabase('uploads.db')

# Define the model for file uploads
class Upload(Model):
    filename = CharField()
    filepath = CharField()

    class Meta:
        database = db

# Create tables if they don't exist
db.connect()
db.create_tables([Upload], safe=True)
db.close()

@app.route('/')
def upload_file():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>
    '''

# Save file and file information to database
@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        if f:
            filename = secure_filename(f.filename)
            file_path = os.path.join('upload files', filename)
            f.save(file_path)

            # Save file info to database
            Upload.create(filename=filename, filepath=file_path)

            return 'File uploaded successfully'

if __name__ == '__main__':
    app.run(debug=True)


    
    
    

Forth Example: Securing the The file upload by limiting the upload extensions

Though we can tell the user which type of files to upload. But we can not relay only on that. So for more security and specially server and other users security we must limit the upload files extensions. which files are allowed by the server.



import os
from flask import Flask, flash, request, redirect, url_for, render_template
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = 'user-uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = 'your_secret_key_here'


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        # If the user does not select a file, the browser submits an
        # empty file without a filename.
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash('File uploaded successfully')
            return redirect(url_for('upload_file'))
        else:
            flash('Invalid file type. Allowed types are: png, jpg, jpeg, gif, bmp')
            return redirect(request.url)
    return render_template('upload.html')


if __name__ == '__main__':
    app.run(debug=True)



    
    
    

Create link to the file and add downloading option

In this example we will show the download link to the user and make the filename very unique.



import os
from flask import Flask, flash, request, redirect, url_for, render_template
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = 'user-uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'


def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        
        file = request.files['file']
        
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash('File uploaded successfully')
            return redirect(url_for('upload_file'))
        
        flash('Invalid file type. Allowed types are: png, jpg, jpeg, gif, bmp')
        return redirect(request.url)

    return render_template('upload-page.html')


if __name__ == '__main__':
    app.run(debug=True)



    
    
    

The upload-page.html




<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>File Upload</title>
</head>
<body>
	<h2>Upload a File</h2>
	<form action="/" method="POST" enctype="multipart/form-data">
		<input type="file" name="file" />
		<input type="submit"/>
	</form>
	{% with messages = get_flashed_messages() %}
		{% if messages %}
			<ul>
			{% for message in messages %}
				<li>{{ message }}</li>
			{% endfor %}
			</ul>
		{% endif %}
	{% endwith %}
</body>
</html>



    
    
    

How To Generate URL And Route Dynamically In Flask

We can set route dynamically instead of fixed route.

For example In the pre-determined route links generate like this example.com/users/profile

In the dynamically gendered route will be like this  example.com/users/custom_profile_name


We will use

@app.route('/<username>')

The code will be like this

@app.route('/<username>')
def show_profile(username):
    return username

We can use anything in the <username> here

So if we  load page like example.com/anything_here

It will show the route. This is the complete example. Copy and try and customize the code by yourself.



from flask import Flask, url_for

app = Flask(__name__)

@app.route('/<username>')
def show_profile(username):
    return username

@app.route('/')
def index():
    example_link = url_for('show_profile', username='example')
    return f"You are on index route. Go to <a href='{example_link}'>/example</a> for an example."

if __name__ == '__main__':
    app.run(debug=True)