08 - Implement Signup in Service and Repository Layers
Tags: go, postgres, authentication, docker
Published: 2020-11-12 04:27:17

---
title: 08 - Implement Signup in Service and Repository Layers
published: true
description: Setting up an account server in Golang
tags: golang, postgres, authentication, docker
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/itrdrjja1xh8p29qy96s.png
series: Full Stack Memorization App
---

The last thing we did in our account application was to create and test a `Signup` handler. In this tutorial, we'll get work on the logic for signing up a user by adding service and repository layer method implementations. 

Our goal over the next 3 tutorials will be to get the application working for signing up a user, including storing a newly created user in a database, and creating authorization tokens for a newly signed up, or a signed in user. 

If at any point you are confused about file structure or code, go to [Github repository](https://github.com/JacobSNGoodwin/memrizr) and check out the branch for the previous lesson to be in sync with me!

If you prefer video, check out the video version below!
{% youtube 1HldvZUdaP4 %}

And for an overview of what the app we're building, see this.
{% youtube Ez2dOyxmGDE %}

## Current Progress

The diagram below shows the methods in each application layer that we'll eventually need to implement, along with check marks next to those we've already completed. We started out by working on the `me` handler and `UserService.Get` method. We did this first just do get a feel for creating and unit testing handlers and services.

![08 Account Application Progress](https://dev-to-uploads.s3.amazonaws.com/i/4f0jheocionotzm6oquv.png)

Now, though, we want to learn how to put these pieces together into a functioning web API application!

Looking at the code code inside of the `Signup` handler, we see that we reached out to `UserService.Signup` and `TokenService.NewPairFromUser`. We'll be working on creating concrete implementations of these methods in the next two tutorials so that we can send HTTP requests to our application and create real users inside of a PostgresQL database!

```go
  // ...data binding and instantiation of User model...
  
	err := h.UserService.Signup(c, u)

	// ...error handling ...

	// ...create token pair as strings
	tokens, err := h.TokenService.NewPairFromUser(c, u, "")
```

In order to store users in an actual database, we'll need to implement a `UserRepository`, along with its `Create` method. This repository will accept a connection to a Postgres database. This Postgres database will be created inside of a docker container. I know it's been a while, but recall that we do have a `docker-compose.yml` file at the root of the project for defining the containers required to run our application.

In the next tutorial we'll work on creating tokens in the `NewPairFromUser` method. There's a little bit of context I'll need to provide on these token pairs, so it will require a second tutorial. 

In the third tutorial, we're going to connect all of these pieces together by injecting a database connection into our UserRepository, injecting the UserRepository into the UserService, and then injecting the TokenService and UserService into the handler layer. 

At that point, we'll finally be able to fire up our live reload environment and send requests to sign up users!

Are you intimidated yet? ... Or are you significantly more resilient than me?

After we have these pieces in place, we'll have implemented some of the most difficult parts of this project.

## Add Create to UserRepository Interface

Let's add the expectation that our `UserRepository` should `Create` users inside of `~/model/interfaces.go`.

```go
// UserRepository defines methods the service layer expects
// any repository it interacts with to implement
type UserRepository interface {
	FindByID(ctx context.Context, uid uuid.UUID) (*User, error)
	Create(ctx context.Context, u *User) error
}
```

If a user is successfully created, it should update the underlying `User` passed to `Create`, otherwise an error should be returned.

### Add Create to Mock Implementation

You're probably getting a warning now since our `mockUserRepository` does not yet implement the `Get` method. We add the implementation inside `~/mocks/user_repository.go`. This will also allow us to test our `UserService.Signup` method. As always, check out the [testify docs](https://pkg.go.dev/github.com/stretchr/testify) for more information about creating these mocks.

```go
// Create is a mock for UserRepository Create
func (m *MockUserRepository) Create(ctx context.Context, u *model.User) error {
	ret := m.Called(ctx, u)

	var r0 error
	if ret.Get(0) != nil {
		r0 = ret.Get(0).(error)
	}

	return r0
}
```

## UserService Signup

We've already scaffolded out this method inside of `~/service/user_service.go`. Let's now add the implementation details.  

If you look at the top of the file, you'll see that we've already provided access to a `model.UserRepository` in our `UserService struct` which will allow us to access the `Create` method. 

```go
// Signup reaches our to a UserRepository to sign up the user.
// UserRepository Create should handle checking for user exists conflicts
func (s *UserService) Signup(ctx context.Context, u *model.User) error {

	if err := s.UserRepository.Create(ctx, u); err != nil {
		return err
	}

	// If we get around to adding events, we'd Publish it here
	// err := s.EventsBroker.PublishUserUpdated(u, true)

	// if err != nil {
	// 	return nil, apperrors.NewInternal()
	// }

	return nil
}
```

Wow! That was really easy! 

Not so fast! We don't want to store passwords as plain text because that would make life really easy for hackers if our database were breached. 

A few tutorials back, I said that we'd have to add more complexity and logic to some of our service methods, so here we go! We're going to add a functions for encrypting a password and another for comparing a supplied password with the encrypted password.

Before continuing, let me warn you that I'm not even a cryptography novice, but I'm going to add some code based on what I've found out there for generating a "salt" and then hashing the password with [scrypt](https://en.wikipedia.org/wiki/Scrypt). 

## Password Utility Functions

Lets add a file called `~/service/passwords.go`. You could also create a sub-package or a root-level package. There are many opinions on how to structure utility applications out there, but I'm just going to created a file inside of our service layer as we don't plan to use this code elsewhere (maybe this is a dumb decision).

```go
package service

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"strings"

	"golang.org/x/crypto/scrypt"
)

func hashPassword(password string) (string, error) {
	// example for making salt - https://play.golang.org/p/_Aw6WeWC42I
	salt := make([]byte, 32)
	_, err := rand.Read(salt)
	if err != nil {
		return "", err
	}

	// using recommended cost parameters from - https://godoc.org/golang.org/x/crypto/scrypt
	shash, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
	if err != nil {
		return "", err
	}

	// return hex-encoded string with salt appended to password
	hashedPW := fmt.Sprintf("%s.%s", hex.EncodeToString(shash), hex.EncodeToString(salt))

	return hashedPW, nil
}

func comparePasswords(storedPassword string, suppliedPassword string) (bool, error) {
	pwsalt := strings.Split(storedPassword, ".")

	// check supplied password salted with hash
	salt, err := hex.DecodeString(pwsalt[1])

	if err != nil {
		return false, fmt.Errorf("Unable to verify user password")
	}

	shash, err := scrypt.Key([]byte(suppliedPassword), salt, 32768, 8, 1, 32)

	return hex.EncodeToString(shash) == pwsalt[0], nil
}
```

The `hashPassword` function creates a hashed version of the provided password by:

1. Creating a 32-byte salt using golang's random package. 
2. Creating a key, or hash, from a byte slice of the provided password, the salt we just created, and so-called *cost parameters*. The cost parameters provide a tradeoff between computational power required to encrypt the password and difficulty of recreating the encrypted password via brute-force attacks. We'll use the cost parameters recommended by the [scrypt docs](https://pkg.go.dev/golang.org/x/crypto/scrypt). The final parameter, 32, corresponds to the number of bytes for the key/hash.
3. In order to check a user's password when they try to login at a later date, we'll need the same salt we just generated. So what we'll do is store the salt with the hashed password. We do this by using `fmt.Sprintf`, appending the salt to the hashed password with a "." in between. We encode both the hash and salt as hexadecimal strings. 

We also create a comparePassword function. This function:

1. Splits the string we created in the previous function to separate the salt and hashed password.
2. Hashes the supplied password, or what the user enters when logging in with the salt. 
3. Compares the above value with the hashed password from the database. If the two match, we know the user has entered the correct password!


## Applying hashPassword to Signup

Let's update our `Signup` method to use the hashed password!

```go
func (s *UserService) Signup(ctx context.Context, u *model.User) error {
	pw, err := hashPassword(u.Password)

	if err != nil {
		log.Printf("Unable to signup user for email: %v\n", u.Email)
		return apperrors.NewInternal()
	}

	// now I realize why I originally used Signup(ctx, email, password)
	// then created a user. It's somewhat un-natural to mutate the user here
	u.Password = pw
	if err := s.UserRepository.Create(ctx, u); err != nil {
		return err
	}

	// ...

	return nil
}
```

That's it for this service method. We'll be able to test it by using the UserRepository mock we previously updated!

## Unit Test UserService Signup

Unit tests for the Signup method have been added below and can be found in `~/service/user_service_test.go`. I want to mention that I did learn something new in creating this test. 

In the success case, we chain on a `Run` method to our testify mock call, which executes some code on the provided arguments to the `Create` method call. In this case, we add a `UID` to the user, which mocks actual repository behavior. 

```go
func TestSignup(t *testing.T) {
	t.Run("Success", func(t *testing.T) {
		uid, _ := uuid.NewRandom()

		mockUser := &model.User{
			Email:    "bob@bob.com",
			Password: "howdyhoneighbor!",
		}

		mockUserRepository := new(mocks.MockUserRepository)
		us := NewUserService(&USConfig{
			UserRepository: mockUserRepository,
		})

		// We can use Run method to modify the user when the Create method is called.
		//  We can then chain on a Return method to return no error
		mockUserRepository.
			On("Create", mock.AnythingOfType("*context.emptyCtx"), mockUser).
			Run(func(args mock.Arguments) {
				userArg := args.Get(1).(*model.User) // arg 0 is context, arg 1 is *User
				userArg.UID = uid
			}).Return(nil)

		ctx := context.TODO()
		err := us.Signup(ctx, mockUser)

		assert.NoError(t, err)

		// assert user now has a userID
		assert.Equal(t, uid, mockUser.UID)

		mockUserRepository.AssertExpectations(t)
	})

	t.Run("Error", func(t *testing.T) {
		mockUser := &model.User{
			Email:    "bob@bob.com",
			Password: "howdyhoneighbor!",
		}

		mockUserRepository := new(mocks.MockUserRepository)
		us := NewUserService(&USConfig{
			UserRepository: mockUserRepository,
		})

		mockErr := apperrors.NewConflict("email", mockUser.Email)

		// We can use Run method to modify the user when the Create method is called.
		//  We can then chain on a Return method to return no error
		mockUserRepository.
			On("Create", mock.AnythingOfType("*context.emptyCtx"), mockUser).
			Return(mockErr)

		ctx := context.TODO()
		err := us.Signup(ctx, mockUser)

		// assert error is error we response with in mock
		assert.EqualError(t, err, mockErr.Error())

		mockUserRepository.AssertExpectations(t)
	})
}
```

Make sure to run the tests, and also fail them, to make sure the tests work as expected.

`go test -v ./service`. 

## PGUserRepository Get and FindByID

We can now take a couple of different paths. We can either work on the TokenService or on the UserRepository. We'll work on the TokenService next time as it requires a decent amount of explanation.

Let's create a new folder called `repository` for our concrete repository implementations. Inside, add `pg_user_repository.go`, where we clearly name this implementation as one which uses Postgres.

Inside of this file, we're going to add an import to a new package called SQLX, which adds some utility methods on top of the built-in golang SQL library. This SQLX will be the only dependency of our UserRepository.

We'll also use a factory to initialize our UserRepository as in the service layer implementations.

```go
package repository

import (
	"github.com/jmoiron/sqlx"
)

// PGUserRepository is data/repository implementation
// of service layer UserRepository
type PGUserRepository struct {
	DB *sqlx.DB
}

// NewUserRepository is a factory for initializing User Repositories
func NewUserRepository(db *sqlx.DB) model.UserRepository {
	return &PGUserRepository{
		DB: db,
	}
}
```

> Also, I recently realized that my service and repository structs should probably be package private (starting with a lower-case letter). Our factories return an interface, and we don't want users to instantiate repository or service structs manually. That's the whole purpose of this factory pattern! We'll fix this in a few tutorials.  

If your IDE or editor is any good, you should see an error because our implementation of a UserRepository does not have a `Create` method! We'll also need a `FindByID` method since we defined that method in our `UserRepository` interface while working on the "me" handler. 

```go
// Create reaches out to database SQLX api
func (r *PGUserRepository) Create(ctx context.Context, u *model.User) error {
	query := "INSERT INTO users (email, password) VALUES ($1, $2) RETURNING *"

	if err := r.DB.Get(u, query, u.Email, u.Password); err != nil {
		// check unique constraint
		if err, ok := err.(*pq.Error); ok && err.Code.Name() == "unique_violation" {
			log.Printf("Could not create a user with email: %v. Reason: %v\n", u.Email, err.Code.Name())
			return apperrors.NewConflict("email", u.Email)
		}

		log.Printf("Could not create a user with email: %v. Reason: %v\n", u.Email, err)
		return apperrors.NewInternal()
	}
	return nil
}

// FindByID fetches user by id
func (r *PGUserRepository) FindByID(ctx context.Context, uid uuid.UUID) (*model.User, error) {
	user := &model.User{}

	query := "SELECT * FROM users WHERE uid=$1"

	// we need to actually check errors as it could be something other than not found
	if err := r.DB.Get(user, query, uid); err != nil {
		return user, apperrors.NewNotFound("uid", uid.String())
	}

	return user, nil
}
```

In these methods, we create query strings, then execute them with application data by calling SQLX methods. 

The `Get` method on the DB (sqlx connection) for inserting may seem a little strange, but this is a way we can use "Returning" in the SQL query and apply it to the user, `u`.

We then check for a very specific error returned by the SQL library when a unique violation occurs. If that is the case, let's return an `apperrors.NewConflict` error. Otherwise, we'll return an InternalServerError. 

We'll configure the "email" column of our user database table to be unique in a couple of tutorials, so any attempt to add an existing email will produce a unique violation error. 

Let's leave this for now. We'll inject a db connection, and in turn inject this repository into the UserService in a couple tutorials. I just wanted to give you an idea of what the repository layer will look like. 


## Setup a Docker Container for Postgres

Now I want to do something that doesn't necessarily fit in with this tutorial, but will save us some time later. Let's set up a Postgres container for our docker development environment. 

We can setup a basic development Postgres container by adding the following to our *docker-compose.yml* file (at the project root).

Make sure to add the `dependency` key to the config of our `account` service. This makes sure to not spin up the container if there's an issue spinning up the `postgres-auth` container. 

Also set up a named volume for the Postgres container's data. You can add this volume under the `volumes` key with no indentation (at the same indentation as the `services` key).

```yaml

...

 postgres-auth:
    image: "postgres:alpine"
    environment:
      - POSTGRES_PASSWORD=password
    ports:
      - "5432:5432"
    #   Set a volume for data and initial sql script
    #   May configure initial db for future demo
    volumes:
      - "pgdata_auth:/var/lib/postgresql/data"
      # - ./init:/docker-entrypoint-initdb.d/
    command: ["postgres", "-c", "log_statement=all"]

...

  account:
    build:
      context: ./auth
      target: builder
    image: account # if we don't give image name, traefik won't create router 🤷‍♂️
    expose:
      - "8080" # seems necessary for Traefik to have internal expose of port
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.account.rule=Host(`wordmem.test`) && PathPrefix(`/api/account`)"
    environment:
      - ENV=dev
    volumes:
      - ./auth:/go/src/app
    depends_on:
      - postgres-auth

volumes:
  pgdata_auth:
```

Let's make sure we can run this. 

`cd ..` to get to the root folder and run

`docker-compose up`.

Hopefully you get logs showing your account and postgres containers up and running!

![Docker Logs](https://dev-to-uploads.s3.amazonaws.com/i/b5kh5st0hqbsx60pywte.PNG))

## Conclusion

Alright, I'm going to make an abrupt stop here. Thanks again for joining along!

Next time we're going to work on creating our idToken (which also serves as an access token), as well as a refreshToken. We'll describe the differences between these tokens and how to create them.

Until then, ¡cuídense!