1. Home
  2. ChainLetter Technical Doc...
  3. Third-Party API
  4. 3a. Beginner Code - PHP – Integrate ChainLetter Login Button

3a. Beginner Code – PHP – Integrate ChainLetter Login Button

With just PHP and a simple client-side form, you can quickly add a ChainLetter login button to your site This article will show you how to use the PHP server functions to access the ChainLetter API and display a login button for users.

Please note that the example code provided here should not be used as is for production environments. It contains insecure practices for demonstration purposes in order to minimize dependencies. I’ll point these out along the way and give you recommendations for secure alternatives.

Here’s a step-by-step guide to creating your ChainLetter login button:

Step 1: Configuration

First, you will need to create a config.ini file with your ChainLetter API credentials. However, storing sensitive data in INI files isn’t safe for production. Instead, use environment variables or a secure secret management system.

Here’s an example of what your config.ini file might look like:

CL_APIURL="https://yourprivatelabel.chainletter.io" 
CL_APIKEY="your_api_key_here"

Step 2: Handling Requests

The sendRequest function uses PHP’s cURL to make HTTP requests to the API. It prepares the URL, headers, and body based on parameters.

A critical security note: the example disables SSL peer verification (CURLOPT_SSL_VERIFYPEER is set to false). This is extremely dangerous since it makes your application vulnerable to man-in-the-middle attacks. Always verify the SSL certificate in production.

function sendRequest($url, $headers, $body, $method) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

    // !!!! DO NOT DO THIS IN PRODUCTION !!!!
    // This is only here to allow the demo to work on localhost
    // You should always verify the SSL certificate of the server you are connecting to
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 

    if ($method === 'POST') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}

Step 3: Registering Users and Retrieving a Token

Before a user can log in, their account needs to be registered with ChainLetter using the registerUser function. After registration, the getUserToken function retrieves a token for the user.

function registerUser($user) {
     global $config;

    if (!$user['userRegistered']) {
        $url = $config['CL_APIURL'] . '/api/v1/server/register_user';

        $headers = array(
            'apikey: ' . $config['CL_APIKEY'],
            'Content-Type: application/json'
        );

        $body = json_encode([
            'remote_uuid' => $user['username'],
            'remote_email' => $user['email']
        ]);

        $response = sendRequest($url, $headers, $body, 'POST');

        return $response;
    }

    return true;
}

And once they are registered…

function getUserToken($user) {
    global $config;

    $url = $config['CL_APIURL'] . '/api/v1/server/get_user_token';
    $headers = array(
        'apikey: ' . $config['CL_APIKEY'],
        'remote_uuid: ' . $user['username'],
        'Content-Type: application/json'
    );

    $response = sendRequest($url, $headers, null, 'GET');

    if (!$response) {
        return null;
    }

    $responseData = json_decode($response, true);

    if (!$responseData['success']) {
        return null;
    }

    return $responseData['token'];
}

Step 4: Handling User Login

On the login route, your script checks if the correct POST data has been sent. If a user with the provided credentials is found, the script initiates the user registration process with ChainLetter and retrieves a user token.

Please do not hardcode user credentials for verification as shown in the example. Instead, implement a proper authentication system with hashed passwords.

// Login route
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/login') {
    $postData = file_get_contents('php://input');
    $requestData = json_decode($postData, true);

    if (!isset($requestData['username'])) {
        http_response_code(400);
        echo json_encode(['message' => "Username required"]);
        return;
    }

    // Mock user login
    if ($requestData['username'] == "janephp456" && $requestData['password'] == "password") {
        $currentUser = [
            'name' => "Jane Php",
            'email' => "[email protected]",
            'username' => "janephp456",
            'userRegistered' => false
        ];

        // !!! This is the important part !!!
        // Register user with ChainLetter, get a token, then send the token to the client
        // This doesn't need to be in the login route, it can be in any other server route
        $registerUserResult = registerUser($currentUser);
        if (!$registerUserResult) {
            echo json_encode(['message' => "User Registration Failed."]);
            return;
        }

        $token = getUserToken($currentUser);

        if (!$token) {
            echo json_encode(['message' => "Token Request Failed."]);
            return;
        }

        // By embedding the token in the form, the client can submit the form to ChainLetter via POST, allowing the cookie to be set

        $inputScheme = isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : ($_SERVER['HTTPS'] ? 'https' : 'http');
        echo json_encode([
            'message' => "Logged in",
            'form' => '
            <form action="' . $config['CL_APIURL'] . '/api/v1/client/post_user_token" method="POST">
              <div class="form-group">
                <input name="authorization" type="hidden" value="AUTH ' . $token . '" />
                <input name="return_url" type="hidden" value="' . $inputScheme . '://' . $_SERVER['HTTP_HOST'] . '" />
                <input type="submit" value="Access ChainLetter" />
              </div>
            </form>
            '
        ]);
        return;
    }

    echo json_encode(['message' => "Incorrect Login or Password"]);
}

Step 5: Returning a Login Form

The script generates a form with hidden fields, including the user token and return URL. It sets the action to the ChainLetter endpoint for user validation.

<?
// Sample client-side form (to be sent to the client after user authentication):
?>
<form action="https://api.chainletter.io/api/v1/client/post_user_token" method="POST">
  <div class="form-group">
    <input name="cl-authorization" type="hidden" value="AUTH your_user_token_here" />
    <input name="return_url" type="hidden" value="your_return_url_here" />
    <input type="submit" value="Access ChainLetter" />
  </div>
</form>

Step 6: Serving Static Files

The script also demonstrates how to serve static files, like index.html or style.css, directly from the file system. In production, use a proper web server to serve static files, such as nginx or Apache.

// Serve static files
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET') {
    if ($_SERVER['REQUEST_URI'] === '/') {
        // Serve index.html
        echo file_get_contents(__DIR__ . '/index.html');
    } elseif ($_SERVER['REQUEST_URI'] === '/style.css') {
        // Serve style.css
        echo file_get_contents(__DIR__ . '/style.css');
    }
}

Putting It All Together

<?php
// Read the ini file
// Don't use ini files or global variables in production, use environment variables instead
$config = parse_ini_file(__DIR__ . '/config.ini');

function sendRequest($url, $headers, $body, $method) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

    // !!!! DO NOT DO THIS IN PRODUCTION !!!!
    // This is only here to allow the demo to work on localhost
    // You should always verify the SSL certificate of the server you are connecting to
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 

    if ($method === 'POST') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}

function getUserToken($user) {
    global $config;

    $url = $config['CL_APIURL'] . '/api/v1/server/get_user_token';
    $headers = array(
        'apikey: ' . $config['CL_APIKEY'],
        'remote_uuid: ' . $user['username'],
        'Content-Type: application/json'
    );

    $response = sendRequest($url, $headers, null, 'GET');

    if (!$response) {
        return null;
    }

    $responseData = json_decode($response, true);

    if (!$responseData['success']) {
        return null;
    }

    return $responseData['token'];
}

function registerUser($user) {
    global $config;

    if (!$user['userRegistered']) {
        $url = $config['CL_APIURL'] . '/api/v1/server/register_user';

        $headers = array(
            'apikey: ' . $config['CL_APIKEY'],
            'Content-Type: application/json'
        );

        $body = json_encode([
            'remote_uuid' => $user['username'],
            'remote_email' => $user['email']
        ]);

        $response = sendRequest($url, $headers, $body, 'POST');

        return $response;
    }

    return true;
}

// Login route
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/login') {
    $postData = file_get_contents('php://input');
    $requestData = json_decode($postData, true);

    if (!isset($requestData['username'])) {
        http_response_code(400);
        echo json_encode(['message' => "Username required"]);
        return;
    }

    // Mock user login
    if ($requestData['username'] == "janephp456" && $requestData['password'] == "password") {
        $currentUser = [
            'name' => "Jane Php",
            'email' => "[email protected]",
            'username' => "janephp456",
            'userRegistered' => false
        ];

        // !!! This is the important part !!!
        // Register user with ChainLetter, get a token, then send the token to the client
        // This doesn't need to be in the login route, it can be in any other server route
        $registerUserResult = registerUser($currentUser);
        if (!$registerUserResult) {
            echo json_encode(['message' => "User Registration Failed."]);
            return;
        }

        $token = getUserToken($currentUser);

        if (!$token) {
            echo json_encode(['message' => "Token Request Failed."]);
            return;
        }

        // By embedding the token in the form, the client can submit the form to ChainLetter via POST, allowing the cookie to be set

        $inputScheme = isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : ($_SERVER['HTTPS'] ? 'https' : 'http');
        echo json_encode([
            'message' => "Logged in",
            'form' => '
            <form action="' . $config['CL_APIURL'] . '/api/v1/client/post_user_token" method="POST">
              <div class="form-group">
                <input name="authorization" type="hidden" value="AUTH ' . $token . '" />
                <input name="return_url" type="hidden" value="' . $inputScheme . '://' . $_SERVER['HTTP_HOST'] . '" />
                <input type="submit" value="Access ChainLetter" />
              </div>
            </form>
            '
        ]);
        return;
    }

    echo json_encode(['message' => "Incorrect Login or Password"]);
}

// Serve static files
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET') {
    if ($_SERVER['REQUEST_URI'] === '/') {
        // Serve index.html
        echo file_get_contents(__DIR__ . '/index.html');
    } elseif ($_SERVER['REQUEST_URI'] === '/style.css') {
        // Serve style.css
        echo file_get_contents(__DIR__ . '/style.css');
    }
}
?>

The html file you need to test it…

<!DOCTYPE html>
<html>

<head>
    <title>Login Form</title>
    <link rel="stylesheet" href="style.css" />
</head>

<body>
    <!-- Simple Login Form Example, nothing custom here -->
    <div class="container" id="logincontainer">
        <h2>"Your Site" Login Form</h2>
        <p style="text-align: center;">
            This is an example login form that will log you in to "Your Site" with a normal user account. <br />
            There is nothing custom here, it's just the normal login form that you would use to log in to your site.
        </p>

        <br />
        <br />
        <strong>Username:</strong> janephp456
        <br />
        <strong>Password:</strong> password
        <br />
        <br />

        <div class="form-group">
            <label for="username">Username:</label>
            <input type="text" id="username" placeholder="Enter your username" value="janephp456">
        </div>
        <div class="form-group">
            <label for="password">Password:</label>
            <input type="password" id="password" placeholder="Enter your password" value="password">
        </div>
        <div class="form-group">
            <button onclick="login()">Login</button>
        </div>
        <div id="result" class="result"></div>
    </div>
    <div class="container start-hidden" id="formcontainer">
        <h2>Logged In to "Your Site"</h2>
        <p style="text-align: center;">Clicking this button will log you in to ChainLetter with a linked account.</p>
    </div>

    <script>
        let debounce = false;
        function login() {

            if (debounce) {
                return;
            }

            debounce = true;
            var username = document.getElementById("username").value;
            var password = document.getElementById("password").value;

            // Make a GET request to our own site's login function and hopefully get a form back
            fetch("/login", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify({
                    username: username,
                    password: password
                })

            })
                .then(response => response.json())
                .then(data => {
                    // Replace the content of the result div with the API response
                    document.getElementById("result").innerHTML = data.message;

                    if (data.form) {
                        document.getElementById("logincontainer").classList.add("fade-out");
                        document.getElementById("formcontainer").classList.add("fade-in");
                        document.getElementById("formcontainer").classList.remove("start-hidden");

                        // Add the form to the page
                        document.getElementById("formcontainer").innerHTML = document.getElementById("formcontainer").innerHTML + data.form;
                    } else {
                        debounce = false;
                    }
                })
                .catch(error => {
                    console.error(error);
                    document.getElementById("result").innerHTML = "An error occurred.";
                    debounce = false;
                });
        }
    </script>
</body>

</html>

And a simple style.css stylesheet…

body {
  font-family: Arial, sans-serif;
  background-color: #f2f2f2;
}

.container {
  max-width: 400px;
  margin: 0 auto;
  padding: 20px;
  background-color: #fff;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  max-height: 100rem;
  margin-top: 5rem;
  overflow: hidden;
}

.start-hidden {
  display: none;
}

.container h2 {
  text-align: center;
  margin-bottom: 20px;
}

.form-group {
  margin-bottom: 20px;
}

.form-group label {
  display: block;
  font-weight: bold;
  margin-bottom: 5px;
}

.form-group input {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

.form-group button {
  width: 100%;
  padding: 10px;
  background-color: #4caf50;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.result {
  margin-top: 20px;
  padding: 10px;
  background-color: #f2f2f2;
  border-radius: 5px;
}

.fade-out {
  animation: fadeOut 0.5s ease-in-out forwards;
  overflow: hidden;
}

@keyframes fadeOut {
  0% {
    opacity: 1;
  }

  99% {
    max-height: 0px;
    opacity: 0;
  }

  100% {
    max-height: 0px;
    display: none;
  }
}

.fade-in {
  animation: fadeIn 2s ease-in-out forwards;
  overflow: hidden;
}

@keyframes fadeIn {
  0% {
    opacity: 0;
    max-height: 0px;
    display: none;
  }

  1% {
    opacity: 0;
    max-height: 0px;
    display: block;
  }

  100% {
    opacity: 1;
    max-height: 100rem;
    display: block;
  }
}

PHP has a built-in development server that is suitable for a quick run and test scenario. Here’s how to use it:

Start the Built-in PHP Server

Open your terminal or command prompt, navigate to the directory of your PHP application, and run the following command:

php -S localhost:3112

This starts the PHP built-in server, making your application available at http://localhost:3112. Now you can easily test the functionality of your login button.

Development Note: While the built-in PHP server is great for development and quick tests due to its simplicity in getting an application up and running, it’s not designed for production use. It can only serve one request at a time and lacks the robustness and features required for a production environment.

Recommendations for a Secure PHP Web Application:

  • Eliminate hardcoded credentials or API keys from your file base. Use environmental variables or a secret management solution.
  • Validate SSL certificates in your cURL requests to prevent MITM attacks.
  • Store sensitive session information securely.
  • Implement robust error handling.
  • Do not expose any API secrets on the client side or within your markup.
  • Use proper user authentication practices—never hardcode user validation checks, and always hash passwords.
  • Serve static content through a secure web server.

By following these recommendations and best practices, you’ll be able to integrate ChainLetter login functionality safely and effectively into your PHP-based web application. Happy coding!

Tags

How can we help?