15 - Add Signin to Service and Repository Layers
Tags: go, gin, authentication, postgres
Published: 2020-12-27 23:30:10

---
title: 15 - Add Signin to Service and Repository Layers
published: true
description: Setting up an account server in Golang
tags: golang, gin, authentication, postgres
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/4nhvvuclt31976ch3ytp.png
series: Full Stack Memorization App
---

Last time, we created and tested a handler for signing in a user. This handler accepts and validates an email and password received in a JSON request body. Let's take a look at our progress diagram to see what we'll work on today!  

![15 - Signin Service and Repository Layers](https://dev-to-uploads.s3.amazonaws.com/i/i5b88v7k854pqvyx9dl3.png)

We'll add functionality to sign-in a user in the service layer. To do this, we'll need to be able to find a user in our database by email, and then verify that the password entered by the user matches the encrypted stored password from the database. To make this possible, we'll add a `FindByEmail` method to our `UserRepository` interface, mock, and Postgres implementations. This method will retrieve the user attempting to sign-in, which includes their hashed password. We'll also review and use the function we previously created to compare the user's supplied password with the password stored in the database. 

At the end of the tutorial, we'll spin up the application, create a new user with a known password, and then attempt to sign in as that user. 

As always, checkout the [repo on Github](https://github.com/JacobSNGoodwin/memrizr) with all of the code for this tutorial including complete tests a branch for each lesson!

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


*I will continue to create unit tests for handler- and service-layer methods. However, I will not include these in written or video tutorials unless I feel they provide some new information. I feel a lengthy portion of the tutorials has been dedicated to testing. I did this deliberately because sometimes setting up tests is more difficult than actual coding logic (seriously), and only a few tutorials write more than rudimentary tests.* 


## Add FindByEmail to UserRepository Interface

In `~/model/interfaces.go`, let's add an expectation that a `UserRepository` can find a user by email address.

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

You should now have errors because or `pGUserRepository` and `mockUserRepository` do not implement this newly added method. Let's add these implementations!

## Add FindByEmail to mockUserRepository

Though I mentioned that I won't go over unit tests unless necessary, I'll still add the mock implementations so you don't have any errors.

Add the following to `~/model/mocks/user_repository.go`.

```go
// FindByEmail is mock of UserRepository.FindByEmail
func (m *MockUserRepository) FindByEmail(ctx context.Context, email string) (*model.User, error) {
	ret := m.Called(ctx, email)

	var r0 *model.User
	if ret.Get(0) != nil {
		r0 = ret.Get(0).(*model.User)
	}

	var r1 error

	if ret.Get(1) != nil {
		r1 = ret.Get(1).(error)
	}

	return r0, r1
}
```

## Add FindByEmail to pGUserRepository

Let's now add the implementation to our the Postgres User Repository implementation, found in `~/repository/pg_user_repository.go`.

```go
// FindByEmail retrieves user row by email address
func (r *pGUserRepository) FindByEmail(ctx context.Context, email string) (*model.User, error) {
	user := &model.User{}

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

	if err := r.DB.GetContext(ctx, user, query, email); err != nil {
		log.Printf("Unable to get user with email address: %v. Err: %v\n", email, err)
		return user, apperrors.NewNotFound("email", email)
	}

	return user, nil
}
```

In this code block, we simply create a select query to get a user with the given email, and return a `NotFound` error if the user cannot be found (and for all possible errors, which is maybe a bit lazy). If the user is found, it will be populated on the `*model.User` and returned.

## User Service Signin Implementation

We can now reach out to `FindByEmail` within the `UserService`. Let's add this in `~/service/user_service.go`!

```go
// Signin reaches our to a UserRepository check if the user exists
// and then compares the supplied password with the provided password.
// If a valid email/password combo is provided, u will hold all
// available user fields
func (s *userService) Signin(ctx context.Context, u *model.User) error {
	uFetched, err := s.UserRepository.FindByEmail(ctx, u.Email)

	// Will return NotAuthorized to client to omit details of why
	if err != nil {
		return apperrors.NewAuthorization("Invalid email and password combination")
	}

	// verify password - we previously created this method
	match, err := comparePasswords(uFetched.Password, u.Password)

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

	if !match {
		return apperrors.NewAuthorization("Invalid email and password combination")
	}

	u = uFetched
	return nil
}
```

After storing the user in `uFetch`, we make a call to `comparePasswords` which compares the supplied password (from the HTTP request) and the retrieved password (from the database). We previously created this function in `~/service/passwords.go`. This function extracts the password salt, `pwsalt[1]`, and then hashes the *supplied* password with this salt. If this matches the hashed password from the database, `pwsalt[0]`, we know the user entered a valid password! We return whether or not the password is a match as a boolean. We can also return an error if decoding the stored password fails. 

```go
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
}
```

## Run Application

From the root of the project, you should now be able to run `docker-compose up`! Let's create a new user by posting a request to our `signup` endpoint (I've deleted all previous users from Postgres prior to this tutorial). I'll use *curl* here to keep things simple, and Postman in the video if you prefer to check that out!

```bash
➜  curl --location --request POST 'http://malcorp.test/api/account/signup' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "guy01@guy.com",
    "password": "avalidpassword123"
}'
```

We get a status 201, created, response with the following body.

```bash
HTTP/1.1 201 Created
Content-Length: 871
Content-Type: application/json; charset=utf-8
Date: Wed, 23 Dec 2020 01:39:09 GMT

{"tokens":{"idToken":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVpZCI6ImVlODZjYzJjLTg2ODEtNDU5YS1hOTc3LTNjYWY5NzUzZTE0YiIsImVtYWlsIjoiZ3V5MDFAZ3V5LmNvbSIsIm5hbWUiOiIiLCJpbWFnZVVybCI6IiIsIndlYnNpdGUiOiIifSwiZXhwIjoxNjA4Njg4NDQ5LCJpYXQiOjE2MDg2ODc1NDl9.haU3a-15xfoUYrpllkkuUphKFDqNfZKckmPZP6LRN7BGhe15DAONdirhLnH1n5QHFaqQ31eOs1nAleqln5MTzeG_YYdw4VhbQ53wve_b156SeMEvfm664js8fSQYsfTG_PBzkmkRaL62jcSaNmSWkKhzzT5bBeYlBd4lUBqGV1nw12Jj9WgF6oWoDHN786bSMQz25TWmkVyE1-082DHUdAjqnnVy7J_G-CU1Ozdv_6KUurUeVfqBj0D4irghcMnfnk75vBzFhyOShl2-RkXprRKvqjNo0u28Fd5BZ6ZKLAv6k_iUxK8rb-F3atozlFhdWaNL77w18XI4ZkZ2YieLPw","refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJlZTg2Y2MyYy04NjgxLTQ1OWEtYTk3Ny0zY2FmOTc1M2UxNGIiLCJleHAiOjE2MDg5NDY3NDksImp0aSI6IjIzODhkY2Y5LWVlODQtNGUzMy04ZGJlLTZmNTlkOGQ2NzFmNCIsImlhdCI6MTYwODY4NzU0OX0.Rsr2zFf62WYm2ExZXo5zVlq0Ot_jqnoUtL8xapjc75U"}}%
```

If we then send a request to the `signin` endpoint with the same email and password, we should hopefully get a token pair and a response of status 200 (ok).

```bash
➜ curl -i --location --request POST 'http://malcorp.test/api/account/signin' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "guy01@guy.com",
    "password": "avalidpassword123"
}'
HTTP/1.1 200 OK
Content-Length: 871
Content-Type: application/json; charset=utf-8
Date: Wed, 23 Dec 2020 01:39:51 GMT

{"tokens":{"idToken":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVpZCI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIsImVtYWlsIjoiZ3V5MDFAZ3V5LmNvbSIsIm5hbWUiOiIiLCJpbWFnZVVybCI6IiIsIndlYnNpdGUiOiIifSwiZXhwIjoxNjA4Njg4NDkxLCJpYXQiOjE2MDg2ODc1OTF9.YuGGc6m1ZaL7BMSGTwdS7hBt8QFIcxRn1MJ-PqjnOm9vtVUPrVsYbg0n_TcwypcqtAcuhsI3buIipFj9GJU657q3INZWcVzNzlWEzeaPPUKuoJtL2EUP6veGElKd8bAQWsg5eX1T48ff8x4CxW-s7PJ0ZLWMi2Al2TU4xbzz4wxGs6PfgD3T4UYuwnCvnC3GGRdL0htLmqc9EiGqs4M6fzu8HhrusKSRvDdbbKNBO6eELtOzRM8_YKcbBBKMGsS9gKxGnDY227_zqYsc1T1fpy7NYmz7SSxZjd4c6XEqcmItqG28L9tvELZk1HlMQvOI7_yTxW13ntCaLLKdkKnZ0A","refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJleHAiOjE2MDg5NDY3OTEsImp0aSI6IjQ3MTFjMDE0LTVmMDgtNDZhNC04Njk3LWQwNGVjMDMwMTEyMCIsImlhdCI6MTYwODY4NzU5MX0.aG7Y01RldyOxxkmaFIT04iYhvb1joSkBw0bboIDSpmE"}}% 
```

## Conclusion

We now have the ability to sign up **and** sign in users. This was quite an effort, especially since we added unit tests and spent a great deal of time on architecture.

Next time, we'll create our second middleware, which will be used to extract the user from our `idToken`. You might recall that at the very beginning of the tutorial, we created a `me` handler and API endpoint, but didn't use this in our application. I did this deliberately to show that we could test the handler in isolation od middleware and service layers.

Next time, we'll create this middleware. This can be used to verify the user, and authorize them to do things like retrieve and update their user profile details. 

Until next time, ¡chau!