If you wish to make an apple pie from scratch, you must first invent the universe.

But I just wanted some god damned apple pie. Thanks a lot Carl Sagan

Lately I’ve been thinking about how complex software is. How needlessly difficult and tedious it is, and has been, for the majority of my time, and the time of everyone working with it. Here are a couple of talks I recently watched on the subject

I think about this a lot. I think about it every time I run up against a stupid problem that should have been solved years or decades ago. Or times when I’m wondering “Are we even really doing this the right way?”

Just because something has become the standard doesn’t mean it’s right.

I got curious. I wondered…how long does it take to create a login form. How long would it take ME to create a login form. From scratch…inventing as much or as little of the universe as I need to, as I go.

A web login form, at least as far as I’m concerned is made up of the following various parts:

  • An HTML form consisting of
    • A login field
    • A password field
    • A big stupid button

This is our “front end” the thing the user sees that is usually very pretty and shiny and abstracts away all of the ugly things that happen onnn the seeerver (make sure to read that in a spooky way, kthx).

But we do need some server goings on. Otherwise this form is just a toy that doesn’t really do anything. And in that way it’s a lot like my other projects.

On the server we’ll need:

  • Some kind of server language to check the login and possibly set a session
  • Possibly a database

How hard could it be?

I create a new directory in my dev directory and create this index.html file:

<form>
  <input type="text" id="username" placeholder="Enter your username" required>
  <input type="password" id="password" placeholder="Enter your password" required>
  <input type="submit" value="Login">
</form>

And nothing else. Traditionally, you’d want a whole bunch of other boilerplate HTML crap in here, like HTML, and head and body tags. But those are for chumps, slow chumps. All we want to do is make a login form, alright, calm down.

So now we have a form in all of its wondrous glory

Great. We’re pretty much halfway done. But then…this form doesn’t really talk to anything, does it? And here’s I think were most of the uninitiated will meet their first roadblock.

You need a server to do this kind of thing. Whether it’s one you have locally, or one you host, or one hosted for you. But you need a server to login. The act implies a server, “whatever that is,” some might say.

Ok, but lucky for me I have multiple servers at hand. Let’s just choose the one that I have open now, for the sake of time. It’s a remote PHP server where I do a lot of my projects.

Then I create a PHP file that looks like this:

<?php

session_start();

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST["username"];
    $password = $_POST["password"];


    if ($username === "admin" && $password === "password") {
        $_SESSION["loggedin"] = true;
        echo "Login successful!";
        exit;
    } else {
        $error = "Invalid username or password";
    }
}

Simple so far. You send a “post” here from a form and it checks if the username is admin and the password is password.

Time developing so far: 10 minutes

Now. We actually have to go back. Because our original form isn’t wired into this server. We have to add an “action” attribute to the form tag to get it to post.

<form method="POST" action="/epoch/login.php">
  <input type="text" id="username" name="username" placeholder="Enter your username" required>
  <input type="password" id="password" name="password" placeholder="Enter your password" required>
  <input type="submit" value="Login">
</form>

Now we open the form in the local server and click and…it doesn’t load.

It doesn’t load through my local VSCode live preview server. It doesn’t load when I access it locally from a browser.

And here’s why. This error in the console:

I tried some header tweaks to allow this to work but no dice. And for the sake of time (since I am timing myself as I do this) I just upload the damn login HTML file to live on the remote server with everything else. The amount of time I’ve employed this as a band-aid with a sigh is incredible.

Oh, and I also have not mentioned that you are going to want an HTTPS certificate if you’re going to make a login form. That’s really a prerequisite. Mostly so the browser doesn’t yell at you about not having one.

Ok so ultimately I thought this didn’t solve the issue. But after some debugging I found I needed to refresh the page…ugh

Time developing so far: 12 minutes

I’ve got a “login successful” message. We’re done right?

Well…here’s the thing. This is a login form if you want to hard code a username and password. But you’re not wanting to hard code a username and password. You’re going to want to create a database with a username and password in it.

This allows people to register, to save their passwords, to have additional data like statuses and names and emails associated with their login data.

And if you want this thing to be more useful, you’re going to need a database. When you give a computer a cookie, am I right?

This is what I came up with:

<?php


session_start();


// function to create sqlite user database
function createUserDatabase() {
    $db = new SQLite3('users.db');
    $db->exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT UNIQUE, password TEXT)");
    return $db;
}


function createFiveTestUsers($db) {
    $users = [
        ["admin", "password"],
        ["user1", "pass1"],
        ["user2", "pass2"],
        ["user3", "pass3"],
        ["user4", "pass4"]
    ];


    foreach ($users as $user) {
        $stmt = $db->prepare("INSERT OR IGNORE INTO users (username, password) VALUES (:username, :password)");
        $stmt->bindValue(':username', $user[0], SQLITE3_TEXT);
        $stmt->bindValue(':password', password_hash($user[1], PASSWORD_DEFAULT), SQLITE3_TEXT);
        $stmt->execute();
    }
}


function validateUser($db, $username, $password) {
    $stmt = $db->prepare("SELECT * FROM users WHERE username = :username");
    $stmt->bindValue(':username', $username, SQLITE3_TEXT);
    $result = $stmt->execute();
    $user = $result->fetchArray(SQLITE3_ASSOC);


    if ($user && password_verify($password, $user['password'])) {
        return true;
    }
    return false;
}


createUserDatabase();
$db = createUserDatabase();
createFiveTestUsers($db);


if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST["username"];
    $password = $_POST["password"];


    if (validateUser($db, $username, $password)) {
        $_SESSION["loggedin"] = true;
        echo "Login successful!";
        exit;
    } else {
        $error = "Invalid username or password";
    }


    echo $error;
}


?>

And here’s what’s changed:

We now have functions to…create the database and schema (I often do this manually, but I did it in code to save time). I also created a function to populate test data to this same database.

I’ve added a function to validate the user and password

Then I’ve changed the login condition to check against that validation function.

And does it work?

Of course not.

I’m getting code 500 or server errors. Meaning something is wrong with my code.

In general it’s often been hard for me to debug PHP in these situations. “Use a debugger, idiot.” I hear you say. Though that’s probably overkill for the situation. I don’t even have that tool in my tool kit because this is a remote server with xdebug disabled. Ahhhh….so that was one of many dumb decisions I made/make/will be making.

So a good fix was to wrap the code in a try catch. Should I have done this from the beginning? Shut up. But I’ve done it now, and that’s what’s important. And this try catch has caught the error and is trying my patience.

The error? A permissions error. See, I stupidly had the directory we’re writing the SQLite database to set to 0755 and not 0777…HAHA. WHAT AN IDIOT.

It’s one of the oldest things in the book. One of the hits. If web development were music it would be an old jazz standard that we’re all sick of.

I should have checked. Just like the try catch, just like using a local server, just like having my server and client code in one place. Just like…just like…just like…

Time developing so far: 26 minutes

Done.

Not capital “D” done, mind you. The form is godawful ugly. But who wants to sit around fiddling with CSS. There’s little to no security on the table. We haven’t hashed any of our passwords. The index file is in html and the server file is in PHP, meaning the index file can’t take advantage of the PHP session file. There’s no registration system. There’s also very little error handling. And worse than all of this, I chose this as a career.

This took me 26 minutes, 65 lines of php code, 5 lines of html, and a databse file. I used Github copilot to code most of it, which spit out the code in seconds. I’m also a professional with years of knowledge specific to this exact domain.

It almost seems acceptable. But I’m not 100% sure that it is.

Stay tuned for me doing this in React or Vue or Angular or whatever. I’m sure that’s gonna go well

By rmarin

Leave a Reply

Your email address will not be published. Required fields are marked *