Event authorization in JS SDK with Flask

Published January 23, 2024
Modules
Difficulty
Powered by
e-point logo

In the digital age, understanding and engaging with your customers on a personalized level has become a pivotal factor in business success. Synerise provides an efficient solution to this with its advanced tracking capabilities. By implementing the authorization of events with the Synerise tracking script, businesses can open doors to a plethora of opportunities.

Employing JWT (JSON Web Tokens) authentication for these events ensures data security, giving businesses the confidence in knowing their customer data is protected. JWT, being a compact and self-contained way for securely transmitting information, is an industry-standard for authorization. Synerise’s insistence on using JWT with the RS256 algorithm provides an additional layer of security.

In essence, authorizing events with Synerise is not just about sending and receiving data. It’s about transforming the way businesses interact with their customers, providing them with unique, personalized experiences, all the while ensuring the utmost data security. This integration is an essential step for companies aiming for growth, increased customer satisfaction, and improved ROI.

In our step-by-step guide to authorizing events with Synerise, we’ll be leveraging two prominent technologies: Python Flask and JavaScript React. Flask is a lightweight web framework for Python, ideal for creating small to medium-sized web applications with ease and efficiency. On the other hand, React is a renowned JavaScript library developed by Facebook, designed for building user interfaces in a modular and reactive manner. Together, Flask will power our backend, providing necessary endpoints and logic, while React will drive our frontend, ensuring a seamless user experience. This combination allows for a robust and scalable solution, optimized for both development and production scenarios.

After you complete this integration and your website is ready to authorize events with JWT, you can start requiring authorization for sensitive events. For details on enabling JWT requirements for events, see “Event authentication settings”.

Diagram that shows the logic of JWT authentication
Overview of JWT implementation logic

Prerequisites


  • Public RSA key added to Synerise. If keys were not added before, check how you can add them here.
  • Local machine with installed Python and Node environments.
  • Tracking code added to your React website.
  • Basic skills in React and Python.

Process


The logic is described in Authenticating requests with JSON Web Tokens (JWT) and this use case presents an example implementation step by step.

  1. Implement back-end
  2. Implement front-end

Implement back-end


Before creating the front-end, we must create a backend—our bridge to the Synerise platform. The PyJWT and cryptography libraries ensure that a secure JWT is created according to Synerise’s standards. The primary endpoint, /generate-jwt (created with Flask), facilitates this by generating a JWT from customer data.

Install libraries

Using the pip install Flask PyJWT cryptography command, install three Python packages: Flask, PyJWT, and cryptography.

Explanation of these packages:

  • Flask - Lightweight web framework for Python. We will use it to create the web server and define the API endpoint to generate JWT and return it to our React application.
  • PyJWT - Python library which allows encoding and decoding JSON Web Tokens (JWT).
  • cryptography - Python library that offers secure cryptographic operations, essential for RS256 algorithm support in generating JWTs in our project.
Note: When developing any Python application, it’s a best practice to use virtual environments! Virtual environments allow you to create isolated Python environments for different projects, avoiding conflicts between dependencies.

Implement the Flask application

The application imports the private RSA key, which is needed to sign the JWT.
The /generate-jwt endpoint of the application accepts POST requests with customer information (email and UUID) and uses that data to generate a JWT compliant with Synerise’s requirements.
Error handling is included in the code. To better understand the code, read the comments.

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

app = Flask(__name__)

# Load the RSA private key for JWT signing. 
# Synerise requires JWTs to be signed with RSA. 

with open('private.pem', 'r') as f: 
    PRIVATE_KEY = f.read() 

@app.route('/generate-jwt', methods=['POST']) 
def generate_jwt(): 

    """ 
    Endpoint to generate JWT using the RS256 algorithm, as required by Synerise. This method expects a JSON payload containing the "email" and "uuid" of a customer. 
    """ 

    # Retrieve the JSON payload from the request 

    data = request.get_json() 

    # Ensure both 'email' and 'uuid' are present in the request 
    # As per documentation, the JWT payload should include customer's email and UUID. 

    if not data or 'email' not in data or 'uuid' not in data: 
        return jsonify({'error': 'Missing email or uuid in request'}), 400 

    email = data['email'] 
    uuid = data['uuid'] 

    # JWT header as defined in the documentation 
    # Synerise requires the JWT to use the RS256 algorithm. 

    headers = { 
        "alg": "RS256", 
        "typ": "JWT" 
    } 

    # JWT payload as per Synerise's requirements 

    payload = { 
        "exp": datetime.datetime.utcnow() + datetime.timedelta(days=7),  # Token expiry set to 7 days as stated in the documentation 
        "uuid": uuid,   # customer's UUID 
        "email": email  # customer's email 
    } 

    # Generate the JWT token 
    # The token is signed with the RSA private key as required by the documentation. 
    token = jwt.encode(payload, PRIVATE_KEY, algorithm="RS256", headers=headers) 


    return jsonify({'jwt': token}) 

 
if __name__ == '__main__': 

    # Start the Flask application 

    app.run(debug=True)

Implement front-end


The front-end part is modular to ensure efficiency and easier maintenance.

The useSyneriseAuthentication hook creates an UUIDv5 for the customer and uses it in the request for a JWT.

Next, the hook is used in the LoginForm component, which collects the customer’s email that’s passed to the hook for creating a JWT. To better understand the code, read the comments.

Implement custom authentication hook

import { useState } from 'react'; 


/** 
 * Custom hook to handle Synerise authentication. 
 */ 
function useSyneriseAuthentication() { 
    const [loading, setLoading] = useState(false); 
    const [error, setError] = useState(null); 

 
    // Function to generate UUIDv5 based on a salt and unique identifier 
    const generateUUIDv5 = (uniqueIdentifier) => { 
        const salt = "someString"; 
        return uuid.uuid5(uuid.NAMESPACE_URL, (salt + uniqueIdentifier)); 
    }; 

 
    // Function to fetch JWT from the backend 
    const fetchJWT = async (email, uuidValue) => { 
        const response = await fetch('/generate-jwt', { 
            method: 'POST', 
            headers: { 
                'Content-Type': 'application/json', 
            }, 
            body: JSON.stringify({ email, uuid: uuidValue }), 
        }); 


        if (!response.ok) { 
            throw new Error("Failed to fetch JWT"); 
        } 

 
        const { jwt } = await response.json(); 
        return jwt; 
    }; 

 
    // Function to authenticate a user with Synerise 
    const authenticate = async (email) => { 
        setLoading(true); 
        setError(null); 

 
        try { 
            const existingIdentityHash = SR.client.getIdentityHash(); 
            const hashedEmail = SR.client.hashIdentity(email); 

            let uuidValue; 

            if (!existingIdentityHash || hashedEmail !== existingIdentityHash) { 
                uuidValue = generateUUIDv5(email); // Derive from a cookie in a real-world scenario 

                const jwt = await fetchJWT(email, uuidValue); 
                SR.client.setUuidAndidentityHash(hashedEmail, uuidValue); 
                SR.client.setAccessToken(jwt); 
            } else { 
                // Identity matches the current customer; Continue with the existing JWT 
                // This can be enhanced based on specific requirements 
            } 

            setLoading(false); 
        } catch (err) { 
            setError(err.message); 
            setLoading(false); 
        } 
    }; 

 
    return { authenticate, loading, error }; 
} 


export default useSyneriseAuthentication; 

Implement LoginForm component

Below you can find the example of useSyneriseAuthentication usage:

import { useState } from 'react'; 
import useSyneriseAuthentication from './useSyneriseAuthentication'; // Ensure you've created this hook as mentioned earlier. 


function LoginForm() { 
    const [email, setEmail] = useState(''); 
    const syneriseAuth = useSyneriseAuthentication(); 


    const handleSubmit = async (event) => { 
        event.preventDefault(); 

 
        // Initiate authentication process using the provided email 
        const success = await syneriseAuth.authenticate(email); 

 
        if (success) { 
            console.log("Authentication successful."); 
            // Here, you can redirect or update the UI as necessary. 
        } else { 
            console.log("Authentication failed."); 
            // Handle failure, maybe show an error message or retry. 
        } 
    }; 

 
    return ( 
        <div> 
            <h2>Login</h2> 
            <form onSubmit={handleSubmit}> 
                <div> 
                    <label>Email:</label> 
                    <input  
                        type="email"  
                        value={email}  
                        onChange={e => setEmail(e.target.value)}  
                        required 
                    /> 
                </div> 
                <div> 
                    <button type="submit">Login</button> 
                </div> 
            </form> 
        </div> 
    ); 
} 


export default LoginForm;

Test the solution and adapt it to fit your specific needs. For example, we recommend adding authentication on your website before a visitor can request the JWT and start sending events.

What’s next


Send events from your website as described in “Event tracking”.

Read more


Introduction to events.

😕

We are sorry to hear that

Thank you for helping improve out documentation. If you need help or have any questions, please consider contacting support.

😉

Awesome!

Thank you for helping improve out documentation. If you need help or have any questions, please consider contacting support.

Close modal icon Placeholder alt for modal to satisfy link checker