diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ad512eed6db3ea1f012ac3b4eb3b7bea80b505ea
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+__pycache__
+
+# Database
+data.db
+
+# Virtualenv
+bam-venv
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3751e321dc608271925968ad1a5dbeade88e3c6
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+  "python.formatting.provider": "black",
+  "python.formatting.blackPath": "black",
+  "editor.formatOnSave": true,
+  "[python]": {
+    "editor.defaultFormatter": null
+  }
+}
diff --git a/bam/__init__.py b/bam/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ac4998f38cc3855f4e3aa7f86b9db30958882a9
--- /dev/null
+++ b/bam/__init__.py
@@ -0,0 +1,15 @@
+from flask import Flask
+from flask_sqlalchemy import SQLAlchemy
+from flask_bcrypt import Bcrypt
+from flask_login import LoginManager
+
+app = Flask(__name__)
+app.config["SECRET_KEY"] = "enterasecretkeyhere"
+app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///data.db"
+app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
+
+db = SQLAlchemy(app)
+bcrypt = Bcrypt(app)
+login_manager = LoginManager(app)
+
+from bam import routes
diff --git a/bam/forms.py b/bam/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..58352643ce0c29c9b777bff3dda840bae919de30
--- /dev/null
+++ b/bam/forms.py
@@ -0,0 +1,26 @@
+from flask_wtf import FlaskForm
+from wtforms import StringField, PasswordField, SubmitField, BooleanField
+from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
+from bam.models import User
+
+
+class RegistrationForm(FlaskForm):
+    email = StringField("Email", validators=[DataRequired(), Email()], render_kw={"placeholder": "Enter your email"})
+    password = PasswordField("Password", validators=[DataRequired()], render_kw={"placeholder": "Choose a strong password"})
+    confirm_password = PasswordField(
+        "Confirm Password", validators=[DataRequired(), EqualTo("password")],
+        render_kw={"placeholder":"Re-enter your password"}
+    )
+
+    submit = SubmitField("Sign Up")
+
+    def validate_email(self, email):
+        if User.query.filter_by(email=email.data).first():
+            raise ValidationError("A user with this email already exists.")
+
+
+class LoginForm(FlaskForm):
+    email = StringField("Email", validators=[DataRequired(), Email()], render_kw={"placeholder": "bookmaster@bam.com"})
+    password = PasswordField("Password", validators=[DataRequired()], render_kw={"placeholder": "masterofbooks"})
+    remember = BooleanField("Remember Me")
+    submit = SubmitField("Login")
diff --git a/bam/models.py b/bam/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f593fac5de87785d8fc02e4aa02ef8554d50915
--- /dev/null
+++ b/bam/models.py
@@ -0,0 +1,16 @@
+from bam import db, login_manager
+from flask_login import UserMixin
+
+
+@login_manager.user_loader
+def load_user(user_id):
+    return User.query.get(int(user_id))
+
+
+class User(db.Model, UserMixin):
+    id = db.Column(db.Integer, primary_key=True)
+    email = db.Column(db.String(120), unique=True, nullable=False)
+    password = db.Column(db.String(60), nullable=False)
+
+    def __repr__(self):
+        return f"User({self.email}, {self.password})"
diff --git a/bam/routes.py b/bam/routes.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4ef8ff7d07680aaac5e84dadfc85036afe7e980
--- /dev/null
+++ b/bam/routes.py
@@ -0,0 +1,53 @@
+from flask import render_template, url_for, flash, redirect
+from flask_login import login_user, current_user, logout_user
+from bam import app, db, bcrypt
+from bam.forms import RegistrationForm, LoginForm
+from bam.models import User
+
+
+@app.route("/")
+@app.route("/home")
+def home():
+    if current_user.is_authenticated:
+        return render_template("dash.html")
+    return render_template("home.html")
+
+
+@app.route("/register", methods=["GET", "POST"])
+def register():
+    if current_user.is_authenticated:
+        return redirect(url_for("home"))
+    form = RegistrationForm()
+    if form.validate_on_submit():
+        hashed_password = bcrypt.generate_password_hash(form.password.data).decode(
+            "utf-8"
+        )
+        user = User(email=form.email.data, password=hashed_password)
+        db.session.add(user)
+        db.session.commit()
+        flash(
+            f"Account successfully created! You may login now.", "success"
+        )
+        return redirect(url_for("login"))
+    return render_template("register.html", title="Register", form=form)
+
+
+@app.route("/login", methods=["GET", "POST"])
+def login():
+    if current_user.is_authenticated:
+        return redirect(url_for("home"))
+    form = LoginForm()
+    if form.validate_on_submit():
+        user = User.query.filter_by(email=form.email.data).first()
+        if user and bcrypt.check_password_hash(user.password, form.password.data):
+            login_user(user, remember=form.remember.data)
+            return redirect(url_for("home"))
+        flash("Hmmm .. those credentials don't seem to be right", "error")
+    return render_template("login.html", title="Login", form=form)
+
+
+@app.route("/logout")
+def logout():
+    logout_user()
+    # flash("You have been successfully logged out", "success")
+    return redirect(url_for("home"))
\ No newline at end of file
diff --git a/bam/static/home.css b/bam/static/home.css
new file mode 100644
index 0000000000000000000000000000000000000000..f5fb381f347bd9049f0468b6f52e242b2eeeff61
--- /dev/null
+++ b/bam/static/home.css
@@ -0,0 +1,38 @@
+.tagline {
+  font-size: 3.5rem;
+  text-align: center;
+  font-family: Roboto;
+  font-weight: 700;
+  text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.6);
+}
+
+.explanation {
+  text-align: center;
+  font-size: 1.5rem;
+  text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.6);
+}
+
+.jk {
+  font-size: 0.7rem;
+  color: rgba(0, 0, 0, 0.1);
+}
+
+@media screen and (max-width: 600px) {
+  .tagline {
+    font-size: 2.5rem;
+    margin-bottom: 10px;
+  }
+  .explanation {
+    font-size: 1.3rem;
+  }
+}
+
+@media screen and (min-width: 1200px) {
+  .tagline {
+    font-size: 4rem;
+    margin-bottom: 10px;
+  }
+  .explanation {
+    font-size: 1.6rem;
+  }
+}
diff --git a/bam/static/outform.css b/bam/static/outform.css
new file mode 100644
index 0000000000000000000000000000000000000000..c6d6ad972f1e282476a23a20964afb2febb16c71
--- /dev/null
+++ b/bam/static/outform.css
@@ -0,0 +1,109 @@
+.container {
+  background-color: var(--t-dark);
+  padding: 15px;
+  border-radius: 3px;
+}
+
+.f-message {
+  font-style: oblique;
+}
+
+h1 {
+  text-align: center;
+}
+
+form {
+  display: flex;
+  flex-direction: column;
+  gap: 5px;
+  min-width: min(90vw, 400px);
+}
+
+input {
+  font-family: Roboto;
+  font-size: 16px;
+  /* margin-bottom: 5px; */
+  border-radius: 3px;
+  padding: 17px;
+  background-color: var(--t-dark);
+  border: 1px solid black;
+  color: #eee;
+  transition: background 0.3s;
+  /* margin: 3px 0 10px; */
+}
+.field:hover {
+  background-color: rgba(0, 0, 0, 0.9);
+}
+.field:focus,
+.field:active {
+  background-color: rgba(30, 30, 30, 0.5);
+  border: 1px solid grey;
+}
+
+label {
+  text-transform: uppercase;
+  font-weight: 700;
+  font-size: 0.9rem;
+  letter-spacing: 1px;
+}
+
+.field-error {
+  font-style: oblique;
+  color: var(--red);
+  font-size: 0.9rem;
+}
+
+[type="submit"] {
+  background-color: var(--red);
+  border: none;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 1px;
+}
+
+[type="submit"]:hover {
+  background-color: var(--dark-red);
+  cursor: pointer;
+}
+
+label,
+[type="submit"] {
+  margin-top: 10px;
+}
+
+/* Checkbox - inspired by https://codepen.io/grayghostvisuals/pen/yOxGxz */
+.remember-row {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-top: 10px;
+}
+
+.remember-row input {
+  order: 2;
+  opacity: 0;
+}
+.checkbox-indicator {
+  display: flex;
+  height: 1.5rem;
+  width: 1.5rem;
+  border: 1px solid black;
+  align-items: center;
+  transition: border 0.2s ease;
+  background-color: var(--t-dark);
+}
+
+.checkbox-indicator svg {
+  height: 50%;
+  width: 100%;
+  fill: transparent;
+}
+
+.remember-row:hover .checkbox-indicator {
+  border: 1px solid grey;
+  background-color: rgba(30, 30, 30, 0.5);
+}
+
+.remember-input:checked + .checkbox-indicator svg {
+  fill: var(--red);
+}
diff --git a/bam/static/outlayout.css b/bam/static/outlayout.css
new file mode 100644
index 0000000000000000000000000000000000000000..3edf17fb34738c7c585be65ad1423fb8e5485cb5
--- /dev/null
+++ b/bam/static/outlayout.css
@@ -0,0 +1,100 @@
+@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap");
+html,
+body {
+  margin: 0;
+  padding: 0;
+  min-height: 100vh;
+}
+
+:root {
+  --red: #e50914;
+  --dark-red: #bb0009;
+  --t-dark: rgba(0, 0, 0, 0.8);
+}
+
+* {
+  box-sizing: border-box;
+}
+
+body {
+  background-image: url("https://i.imgur.com/4LLrhbS.jpg");
+  background-position: center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  color: white;
+  font-family: Roboto;
+}
+
+.maincontainer {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  min-height: 100vh;
+}
+
+.navbar {
+  height: 100px;
+  align-items: center;
+  padding: 20px 50px;
+  display: flex;
+  width: 100%;
+  gap: 10px;
+}
+
+.navbar .logo {
+  height: 100%;
+}
+
+.navbar .logo img {
+  height: 100%;
+  filter: drop-shadow(2px 2px 6px #000);
+}
+
+.navbar .space {
+  flex-grow: 1;
+}
+
+.navbar button {
+  background-color: var(--red);
+  border: none;
+  color: white;
+  padding: 5px 10px;
+  font-family: Roboto;
+  border-radius: 2px;
+  font-size: 1.1rem;
+  box-shadow: 1px 1px 5px black;
+  transition: background 0.3s;
+  cursor: pointer;
+}
+
+.navbar button:hover {
+  background-color: var(--dark-red);
+}
+
+.content {
+  flex-grow: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: 65ch;
+  max-width: 90vw;
+}
+
+.rowspacer {
+  height: 10vh;
+}
+
+.credits {
+  padding: 15px;
+  font-size: 0.7rem;
+  color: #460e0c;
+  opacity: 0.3;
+}
+
+@media screen and (max-width: 600px) {
+  .navbar {
+    padding: 15px;
+    height: 76px;
+  }
+}
diff --git a/bam/templates/dash.html b/bam/templates/dash.html
new file mode 100644
index 0000000000000000000000000000000000000000..439b8cc766edea2ed31d06659c115492fb0758c3
--- /dev/null
+++ b/bam/templates/dash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>BAM - Dashboard</title>
+  </head>
+  <body>
+    Yaaay - you are authorized!
+  </body>
+</html>
diff --git a/bam/templates/home.html b/bam/templates/home.html
new file mode 100644
index 0000000000000000000000000000000000000000..144154929581aea9d8044daac0978a123cae2ab3
--- /dev/null
+++ b/bam/templates/home.html
@@ -0,0 +1,12 @@
+{% extends "outlayout.html" %} {% block head %}
+<link rel="stylesheet" href="{{ url_for('static', filename='home.css') }}" />
+{% endblock head %} {% block content %}
+<div class="tagline">The only missing piece in your life.</div>
+<div class="explanation">
+  BAM is an all-in-one Book Management System built on the principles of
+  simplicity and minimalism
+</div>
+<div class="jk">
+  Nah, just kidding, it wasn't built with anything in mind lol
+</div>
+{% endblock content %}
diff --git a/bam/templates/login.html b/bam/templates/login.html
new file mode 100644
index 0000000000000000000000000000000000000000..6c7e0ef7807a1bb67c1d626ac2374dac2dba624e
--- /dev/null
+++ b/bam/templates/login.html
@@ -0,0 +1,39 @@
+{% extends "outlayout.html" %} {% block head %}
+<link rel="stylesheet" href="{{ url_for('static', filename='outform.css') }}" />
+{% endblock head %} {% block content %}
+<div class="container">
+  <form action="" method="POST">
+    {{ form.hidden_tag() }}
+    <h1>Sign In</h1>
+    {% with messages = get_flashed_messages(with_categories=True) %} {% if
+    messages %} {% for category, message in messages %}
+    <div class="f-message f-message-{{ category }}">{{ message }}</div>
+    {% endfor %} {% endif %} {% endwith %}
+    <!-- <div>Your account has been successfully created.</div> -->
+    {{ form.email.label() }} {{ form.email(class="field") }} {% for error in
+    form.email.errors %}
+    <div class="field-error">{{ error }}</div>
+    {% endfor %} {{ form.password.label() }} {{ form.password(class="field") }}
+    {% for error in form.password.errors %}
+    <div class="field-error">{{ error }}</div>
+    {% endfor %}
+    <label for="{{ form.remember.label.field_id }}" class="remember-row">
+      {{ form.remember(class="remember-input") }}
+      <span class="checkbox-indicator" aria-hidden="true">
+        <svg
+          version="1.1"
+          xmlns="http://www.w3.org/2000/svg"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+          viewBox="0 0 21 16"
+        >
+          <path
+            d="M21,2.1L7.3,16l-0.4-0.4l-0.3,0.3L0,9.3l2.1-2.1l4.9,4.9L18.9,0L21,2.1z"
+          />
+        </svg>
+      </span>
+      <div>{{ form.remember.label.text }}</div>
+    </label>
+    {{ form.submit() }}
+  </form>
+</div>
+{% endblock content %}
diff --git a/bam/templates/outlayout.html b/bam/templates/outlayout.html
new file mode 100644
index 0000000000000000000000000000000000000000..99a5aa7cffbeac91a620b0f4e35ed22982f4cc8b
--- /dev/null
+++ b/bam/templates/outlayout.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    {% if title %}
+    <title>BAM - {{ title }}</title>
+    {% else %}
+    <title>BAM</title>
+    {% endif %}
+    <link
+      rel="stylesheet"
+      href="{{ url_for('static', filename='outlayout.css') }}"
+    />
+    {% block head %}{% endblock %}
+  </head>
+  <body>
+    <div class="maincontainer">
+      <div class="navbar">
+        <div class="logo">
+          <a href="{{ url_for('home') }}">
+            <img src="https://i.imgur.com/t2ZlJ06.png" />
+          </a>
+        </div>
+        <div class="space"></div>
+        <a href="{{ url_for('login') }}"><button>Sign In</button></a>
+        <a href="{{ url_for('register') }}"><button>Register</button></a>
+      </div>
+      <div class="content">{% block content %}{% endblock %}</div>
+      <div class="rowspacer"></div>
+      <div class="credits">Built with ❤ using Flask</div>
+    </div>
+  </body>
+</html>
diff --git a/bam/templates/register.html b/bam/templates/register.html
new file mode 100644
index 0000000000000000000000000000000000000000..b66522c09c2d72626d9b6dbbfe69109554b102f5
--- /dev/null
+++ b/bam/templates/register.html
@@ -0,0 +1,22 @@
+{% extends "outlayout.html" %} {% block head %}
+<link rel="stylesheet" href="{{ url_for('static', filename='outform.css') }}" />
+{% endblock head %} {% block content %}
+<div class="container">
+  <form action="" method="POST">
+    {{ form.hidden_tag() }}
+    <h1>Create an account</h1>
+    {{ form.email.label() }} {{ form.email(class="field") }} {% for error in
+    form.email.errors %}
+    <div class="field-error">{{ error }}</div>
+    {% endfor %} {{ form.password.label() }} {{ form.password(class="field") }}
+    {% for error in form.password.errors %}
+    <div class="field-error">{{ error }}</div>
+    {% endfor %} {{ form.confirm_password.label() }} {{
+    form.confirm_password(class="field") }} {% for error in
+    form.confirm_password.errors %}
+    <div class="field-error">{{ error }}</div>
+    {% endfor %}
+    <input type="submit" value="Sign in" />
+  </form>
+</div>
+{% endblock content %}
diff --git a/create_db.py b/create_db.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6f7d5eb03e48a182b6b0eb43cf7731af9d02ba8
--- /dev/null
+++ b/create_db.py
@@ -0,0 +1,3 @@
+from bam import db
+
+db.create_all()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1752fdb71ae630dc609b565728f55259ab4ee559
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,27 @@
+appdirs==1.4.4
+bcrypt==3.2.0
+black==20.8b1
+cffi==1.14.4
+click==7.1.2
+dnspython==2.0.0
+email-validator==1.1.2
+Flask==1.1.2
+Flask-Bcrypt==0.7.1
+Flask-Login==0.5.0
+Flask-SQLAlchemy==2.4.4
+Flask-WTF==0.14.3
+idna==2.10
+itsdangerous==1.1.0
+Jinja2==2.11.2
+MarkupSafe==1.1.1
+mypy-extensions==0.4.3
+pathspec==0.8.1
+pycparser==2.20
+regex==2020.11.13
+six==1.15.0
+SQLAlchemy==1.3.22
+toml==0.10.2
+typed-ast==1.4.1
+typing-extensions==3.7.4.3
+Werkzeug==1.0.1
+WTForms==2.3.3
diff --git a/run.py b/run.py
new file mode 100644
index 0000000000000000000000000000000000000000..83f079b62e1fd7ba4b0ec30e7c1ff3b08c0ecec3
--- /dev/null
+++ b/run.py
@@ -0,0 +1,4 @@
+from bam import app
+
+if __name__ == "__main__":
+    app.run(debug=True)