Getting started with Golang web application (and authentication) Part 1

Getting started with Golang web application (and authentication) Part 1

How to implement an authentication system on a web application using Go

The purpose of this article is to create an authentication system using Golang. For weeks, I searched for tutorials that performed this kind of task but the results were the same: implementing Golang backend as JSON or Rest API. Therefore, I built mine and wrote a blog about it. This is part 1 of the article. I hope it is insightful and maybe you can learn a thing or two from it. The frontend will be in HTML and styled with Bootstrap, using a MySQL database and Go as the backend:

  • Setting up the project
  • Setting up the frontend
  • Setting up the backend

The finished project can be found here: Github

We start by creating a new folder called golangwebauth and cd into it. 1.png Create a templates folder, an errorpages folder inside the templates directory, initialize our go mod and create a main.go file. 2.png

We set up our frontend by creating some HTML pages in the templates directory. First, our index.html file serves as a landing page, dashboard.html serves as the logged-in page after the user logs in, login.html as the login page and register.html serving as the register page. Inside the errorpages directory: 401.html page for error 401 - http.StatusUnauthorized and 400.html for the error 400 - http.StatusBadRequest. 3.png

Insert the code for the files: index.html:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
    integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">

  <title>Web App Authentication in Go</title>
</head>

<body>
  <div class="form-group">
    <div class="form-group col-md-2">
    </div>
    <div class="form-group col-md-5">
      <h1>Simple authentication in Go</h1>
    </div>
    <div class="form-group col-md-3">
    </div>
    <div class="form-group col-md-2">
    </div>
  </div>

  <div class="form-row form-group">
    <div class="form-group col-md-2">
    </div>
    <div class="form-group col-md-4">
      <a class="btn btn-primary" href="/register">Register</a>
      <a class="btn btn-outline-secondary" href="/login">Login</a>
    </div>
    <div class="form-group col-md-4">
    </div>
    <div class="form-group col-md-2">
    </div>
  </div>

  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
    integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous">
  </script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous">
  </script>
</body>

</html>


login.html:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Login - Go Auth</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
    integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
</head>

<body>
  <form method="POST">
    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <h1>Login - Go Auth</h1>
      </div>
      <div class="form-group col-md-4">

      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <label for="email">Email</label>
        <input type="email" class="form-control" id="email" name="email" placeholder="Email">
      </div>
      <div class="form-group col-md-4">

      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <label for="password">Password</label>
        <input type="password" class="form-control" name="password" id="password" placeholder="Password">
      </div>
      <div class="form-group col-md-4">
      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <button type="submit" class="btn btn-primary">Sign In</button>
        <a href="/register" type="submit" class="btn btn-outline-primary">Sign Up</a>
        <a href="/" type="submit" class="btn btn-outline-secondary">Home</a>
      </div>
      <div class="form-group col-md-4">
      </div>
      <div class="form-group col-md-2">
      </div>
    </div>
  </form>

</body>

</html>


register.html:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Register - Go Auth</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
    integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
</head>

<body>
  <form id="registerForm" method="POST">
    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <h1>Register - Go Auth</h1>
      </div>
      <div class="form-group col-md-4">

      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <label>First Name</label>
        <input type="text" class="form-control" id="FirstName" name="FirstName" placeholder="First Name" required>
      </div>
      <div class="form-group col-md-4">

      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <label>Last Name</label>
        <input type="text" class="form-control" id="LastName" name="LastName" placeholder="Last Name" required>
      </div>
      <div class="form-group col-md-4">
      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <label>Email</label>
        <input type="email" class="form-control" id="email" name="email" placeholder="Email" required>
      </div>
      <div class="form-group col-md-4">

      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <label for="password">Password</label>
        <input type="password" class="form-control" id="password" name="password" placeholder="Password" required>
      </div>
      <div class="form-group col-md-4">
      </div>
      <div class="form-group col-md-2">
      </div>
    </div>

    <div class="form-row form-group">
      <div class="form-group col-md-2">
      </div>
      <div class="form-group col-md-4">
        <button type="submit" class="btn btn-primary">Sign Up</button>

      </div>
      <div class="form-group col-md-4">
      </div>
      <div class="form-group col-md-2">
      </div>
    </div>
  </form>

  <a href="/login" type="submit" class="btn btn-outline-primary">Sign in</a>
  <a href="/" type="submit" class="btn btn-outline-secondary">Home</a>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>

<script>
  $('#registerForm').validate({ // initialize the plugin
    rules: {
      "FirstName": {
        required: true,
        minlength: 3
      },
      "LastName": {
        required: true,
        minlength: 3
      },
      "email": {
        required: true,
        email: true
      },
      "password": {
        required: true,
        minlength: 8

      }
    },
    messages: {
      "FirstName": {
        required: "Please, enter your first name",
        minlength: "Minimum length should be 3 digits",
      },
      "LastName": {
        required: "Please, enter your last name",
        minlength: "Minimum length should be 3 digits",
      },
      "email": {
        required: "Please, enter an email",
        email: "Email is invalid"
      },
      "password": {
        required: "Please,enter a password",
        minlength: "Minimum length should be 8"
      }
    },
    submitHandler: function (form) {
      form.submit();
    }
  });
</script>

</html>


dashboard.html:


<!DOCTYPE html>
<html lang="en">

<head>
  <title>Dashboard</title>

  <meta charset="UTF-8" />
  <link href="https://fonts.googleapis.com/css?family=Nunito+Sans:400,400i,700,900&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
    integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
  <link rel="stylesheet" href="../asset/css/style.css" crossorigin="anonymous">
  <link rel="stylesheet" href="../asset/css/responsive.css" crossorigin="anonymous">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">


</head>

<body class="success-body">
  <div class="card">
    <div class="flex invite-user">
      <p>Welcome {{.FirstName}} {{.LastName}} !</p>
      <a href="/logouth">Log out</a>
    </div>

    <h1>Successfully logged in </h1>
  </div>
</body>

</html>


400.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bad Request</title>

    <!-- Google font -->
    <link href="https://fonts.googleapis.com/css?family=Josefin+Sans:400,700" rel="stylesheet">

    <!-- Custom stlylesheet -->
    <link type="text/css" rel="stylesheet" href="/static/css/style.css" />

</head>

<body>

    <div id="notfound">
        <div class="notfound">
            <div class="notfound-404">
                <h1>4<span>0</span>0</h1>
            </div>
            <p>Bad request.</p>
            <a href="/">home page</a>
        </div>
    </div>

</body><!-- This templates was made by Colorlib (https://colorlib.com) -->

</html>


401.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Unauthorized Access</title>

    <!-- Google font -->
    <link href="https://fonts.googleapis.com/css?family=Josefin+Sans:400,700" rel="stylesheet">

    <!-- Custom stlylesheet -->
    <link type="text/css" rel="stylesheet" href="/static/css/style.css" />


</head>

<body>

    <div id="notfound">
        <div class="notfound">
            <div class="notfound-404">
                <h1>4<span>0</span>1</h1>
            </div>
            <p>Unauthorized Access.</p>
            <a href="/">home page</a>
        </div>
    </div>

</body><!-- This templates was made by Colorlib (https://colorlib.com) -->

</html>


We create a static directory in our base directory, create a css folder and add a style.css file. Inside the style.css, put in: 4.png

style.css:

* {
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
}

body {
  padding: 0;
  margin: 0;
}

#notfound {
  position: relative;
  height: 100vh;
  background-color: #222;
}

#notfound .notfound {
  position: absolute;
  left: 50%;
  top: 50%;
  -webkit-transform: translate(-50%, -50%);
      -ms-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
}

.notfound {
  max-width: 460px;
  width: 100%;
  text-align: center;
  line-height: 1.4;
}

.notfound .notfound-404 {
  height: 158px;
  line-height: 153px;
}

.notfound .notfound-404 h1 {
  font-family: 'Josefin Sans', sans-serif;
  color: #222;
  font-size: 220px;
  letter-spacing: 10px;
  margin: 0px;
  font-weight: 700;
  text-shadow: 2px 2px 0px #c9c9c9, -2px -2px 0px #c9c9c9;
}

.notfound .notfound-404 h1>span {
  text-shadow: 2px 2px 0px #ffab00, -2px -2px 0px #ffab00, 0px 0px 8px #ff8700;
}

.notfound p {
  font-family: 'Josefin Sans', sans-serif;
  color: #c9c9c9;
  font-size: 16px;
  font-weight: 400;
  margin-top: 0px;
  margin-bottom: 15px;
}

.notfound a {
  font-family: 'Josefin Sans', sans-serif;
  font-size: 14px;
  text-decoration: none;
  text-transform: uppercase;
  background: transparent;
  color: #c9c9c9;
  border: 2px solid #c9c9c9;
  display: inline-block;
  padding: 10px 25px;
  font-weight: 700;
  -webkit-transition: 0.2s all;
  transition: 0.2s all;
}

.notfound a:hover {
  color: #ffab00;
  border-color: #ffab00;
}

@media only screen and (max-width: 480px) {
  .notfound .notfound-404 {
    height: 122px;
    line-height: 122px;
  }

  .notfound .notfound-404 h1 {
      font-size: 122px;
  }
}

Now, the frontend is complete. Some pages or specific features might not work (e.g. dashboard.html with the welcome {{.FirstName}}) until we connect the backend, which is our next task.


The backend is where the bulk of the work is. We would be connecting the routes to the pages, authenticating by registering our users, storing their details in the MySQL database and logging our users in. In our main.go file, we apply the following code:

package main

import (
    "html/template"
    "log"
    "net/http"

    "github.com/gorilla/context"
)

var tpl = template.Must(template.ParseGlob("templates/*.html"))

func indexHandler(w http.ResponseWriter, r *http.Request) {
    tpl.ExecuteTemplate(w, "index.html", nil)
}

func registerHandler(w http.ResponseWriter, r *http.Request) {
    tpl.ExecuteTemplate(w, "register.html", nil)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
    tpl.ExecuteTemplate(w, "login.html", nil)
}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "/login", http.StatusPermanentRedirect)
}

func dashboardHandler(w http.ResponseWriter, r *http.Request) {
    tpl.ExecuteTemplate(w, "dashboard.html", nil)
}

func main() {
    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/login", loginHandler)
    http.HandleFunc("/logouth", logoutHandler)
    http.HandleFunc("/register", registerHandler)
    http.HandleFunc("/dashboard", dashboardHandler)

    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

    log.Println("Server started on: http://localhost:8000")
    err := http.ListenAndServe(":8000", context.ClearHandler(http.DefaultServeMux)) // context to prevent memory leak
    if err != nil {
        log.Fatal(err)
    }
}

Our main function handles the routing, starts our server and use a context package to prevent memory leaks when running our server. The http.Handle("/static/"...) helps us serve our CSS to the frontend: 5.png The tpl variable holds our templates, and the functions execute the templates: 6.png

Before running our application, we run: go get github.com/gorilla/context to install the context package. Then run our server by running go run main.go which starts on port 8000: 7.png Our home page (localhost:8000) shows this: 8.png

We create a .env file in our root directory which houses some environmental variables: 10.png