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)



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