07 - Completing Signup Handler in Gin - Token Creation
Tags: go, testing, authentication, tutorial
Published: 2020-11-05 01:58:22

Last time, we started building a handler for signing up a user with an email address and password. We learned how to validate and bind incoming JSON on an HTTP request body to a struct, and then how to call the `UserService` for signing up a user.

![Accessing Token Service](https://dev-to-uploads.s3.amazonaws.com/i/lh0zyhtib8kaodcx8yrj.png)

In this tutorial, we'll complete this handler by having it reach out to a `TokenService` which creates an access token and a refresh token. These tokens will be created, or derived, from the `User` we signed up in the last tutorial. These tokens are what we'll use to authorize users to the eventual memorization app we plan to build. In a real-word scenario, these tokens could be used to access various applications or micro-services in a company or organization. 

At the end of this section, your folder structure should be as follows. If at any point you are confused about the file structure or code, go to [Github repository](https://github.com/JacobSNGoodwin/memrizr) and checkout the branch for the previous lesson!

![Folder Structure](https://dev-to-uploads.s3.amazonaws.com/i/zs3pcyuhso49q7iu4q2c.PNG)

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

> I want to clarify that we'll definitely go into our auth flow later. For now we're going to focus on the handler logic. I've added some resources at the end of the article that I used in deciding how to store tokens in this app. This is merely a part of the auth flow decision process, but I hope you'll find the info useful.

## Add Tokens Model

We need to create a model for the structure of the aforementioned tokens that we'll send to users as JSON. Recall that we create these models in our _model_ layer, and that these models can be used across, and passed between, application layers. Right now we have a single `User` model. 

Let's add a model for our access and refresh tokens in `~/model/token_pair.go`. In this app, the ID token will also serve as an *access token* (for those of you that are thinking ahead). The tokens we ultimately serialize and send to the user as JSON with the keys in the struct tags are represented as strings. 

```go
// TokenPair used for returning pairs of id and refresh tokens
type TokenPair struct {
	IDToken      string `json:"idToken"`
	RefreshToken string `json:"refreshToken"`
}
```

## Create TokenService Interface

With the `TokenPair` model created, we now define our expectations for the TokenService. Let's do this by adding this `TokenService` to `~/model/interfaces.go`, and add our first method for creating tokens from a `User`. 

```go
// TokenService defines methods the handler layer expects to interact
// with in regards to producing JWTs as string
type TokenService interface {
	NewPairFromUser(ctx context.Context, u *User, prevTokenID string) (*TokenPair, error)
}
```

The method accepts a `Context` from the handler layer, a `User`, and a `prevTokenID`. When we sign up a user, we'll be passing an empty string to `prevTokenID`. We'll eventually create a refresh token handler which will call this method with a non-empty `prevTokenID`.

## Add TokenService to Handler Struct

We also need to make sure we have access to this `TokenService` in our handler. Inside of `~/handler/handler.go`, let's add it to our `Handler` and `Config` structs. We also need to make sure to "instantiate" our handler inside of `NewHandler` with the `TokenService` passed from the config. 

```go
// Handler struct holds required services for handler to function
type Handler struct {
	UserService  model.UserService
	TokenService model.TokenService
}

// Config will hold services that will eventually be injected into this
// handler layer on handler initialization
type Config struct {
	R            *gin.Engine
	UserService  model.UserService
	TokenService model.TokenService
}

// NewHandler initializes the handler with required injected services along with http routes
// Does not return as it deals directly with a reference to the gin Engine
func NewHandler(c *Config) {
	// Create a handler (which will later have injected services)
	h := &Handler{
		UserService:  c.UserService,
		TokenService: c.TokenService,
  }
  
  ...
```

## Create Tokens in Signup

Let's call this method back inside of `~/handler/signup.go`. I've also included some of the code from the last tutorial where we called `UserService.Signup`.

```go
...
u := &model.User{
		Email:    req.Email,
		Password: req.Password,
	}

	err := h.UserService.Signup(c, u)

	if err != nil {
		log.Printf("Failed to sign up user: %v\n", err.Error())
		c.JSON(apperrors.Status(err), gin.H{
			"error": err,
		})
		return
	}

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

	if err != nil {
		log.Printf("Failed to create tokens for user: %v\n", err.Error())

		// may eventually implement rollback logic here
		// meaning, if we fail to create tokens after creating a user,
		// we make sure to clear/delete the created user in the database

		c.JSON(apperrors.Status(err), gin.H{
			"error": err,
		})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"tokens": tokens,
	})
```

We pass the reference to the successfully created `User` to the `NewPairFromUser` method.  If there is an error, we're return this error as JSON. If the token creation is successful, we'll send the pair as JSON according to the struct tags in `model.TokenPair`.

## Mock TokenService.NewPairFromUser Method

Before we can test the token-creation portion of `Signup`, we need to create a mock implementation of `NewPairFromUser`. As always, take a look at previous lectures and the [testify documentation](https://github.com/stretchr/testify) to learn more!

Let's create a new file, `~/model/mocks/token_service.go`, remembering to include appropriate imports for your module.

```go
// MockTokenService is a mock type for model.TokenService
type MockTokenService struct {
	mock.Mock
}

// NewPairFromUser mocks concrete NewPairFromUser
func (m *MockTokenService) NewPairFromUser(ctx context.Context, u *model.User, prevTokenID string) (*model.TokenPair, error) {
	ret := m.Called(ctx, u, prevTokenID)

	// first value passed to "Return"
	var r0 *model.TokenPair
	if ret.Get(0) != nil {
		// we can just return this if we know we won't be passing function to "Return"
		r0 = ret.Get(0).(*model.TokenPair)
	}

	var r1 error

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

	return r0, r1
}
```

## Complete Tests of Signup Handler

With the mock created, we're now ready to add at least two more test cases.

1. Successful token creation upon calling `NewPairFromUser`
2. An error returned upon calling `NewPairFromUser`

For both of these cases, we'll need to create a mock for both the UserService and TokenService. Calling `UserService.Signup` should not return an error for both of these tests, but calling `TokenService.NewPairFromUser` should return a different result for the two cases.

### Successful Token Creation

Let's note the important aspects of this test.

1. We create a user, `u`, which will be created when calling `mockUserService.Signup`. We also create a `mockTokenResponse` to return when calling `mockTokenService.NewPairFromUser`.
2. We create the mock services mentioned in 1, and define responses for when their methods are called. As this is the success case, we return `mockTokenResponse, nil` when calling `NewPairFromUser`.
3. We setup our handler, remembering to inject `mockTokenService` into the `NewHandler` factory.
4. We setup our request body with valid email and password as in previous tests.
5. We send the POST request with `reqBody` to `/signup`, and define our expectations. We expect the methods on both services to be called, and we expect to receive an HTTP status code of 201, indicating a resource has been created. We also check the response body of the returned JSON.

```go
t.Run("Successful Token Creation", func(t *testing.T) {
		u := &model.User{
			Email:    "bob@bob.com",
			Password: "avalidpassword",
		}

		mockTokenResp := &model.TokenPair{
			IDToken:      "idToken",
			RefreshToken: "refreshToken",
		}

		mockUserService := new(mocks.MockUserService)
		mockTokenService := new(mocks.MockTokenService)

		mockUserService.
			On("Signup", mock.AnythingOfType("*gin.Context"), u).
			Return(nil)
		mockTokenService.
			On("NewPairFromUser", mock.AnythingOfType("*gin.Context"), u, "").
			Return(mockTokenResp, nil)

		// a response recorder for getting written http response
		rr := httptest.NewRecorder()

		// don't need a middleware as we don't yet have authorized user
		router := gin.Default()

		NewHandler(&Config{
			R:            router,
			UserService:  mockUserService,
			TokenService: mockTokenService,
		})

		// create a request body with empty email and password
		reqBody, err := json.Marshal(gin.H{
			"email":    u.Email,
			"password": u.Password,
		})
		assert.NoError(t, err)

		// use bytes.NewBuffer to create a reader
		request, err := http.NewRequest(http.MethodPost, "/signup", bytes.NewBuffer(reqBody))
		assert.NoError(t, err)

		request.Header.Set("Content-Type", "application/json")

		router.ServeHTTP(rr, request)

		respBody, err := json.Marshal(gin.H{
			"tokens": mockTokenResp,
		})
		assert.NoError(t, err)

		assert.Equal(t, http.StatusCreated, rr.Code)
		assert.Equal(t, respBody, rr.Body.Bytes())

		mockUserService.AssertExpectations(t)
		mockTokenService.AssertExpectations(t)
	})
```

### Error Token Creation

Much of this test is the same as the last, with the following changes.

1. We replace `mockTokenResponse` with `mockErrorResponse`, and change the response of `mockTokenService.NewPairFromUser` to `nil, mockErrorResponse`. 
2. We change the expectations for the handler's JSON response. We expect the body to have an `error` key containing `mockErrorResponse`, and we expect the status code to be that contained in the `mockErrorResponse`, which for this test we created using one of our custom `appErrors` for an internal, http status code 500, error.

```go
t.Run("Failed Token Creation", func(t *testing.T) {
		u := &model.User{
			Email:    "bob@bob.com",
			Password: "avalidpassword",
		}

		mockErrorResponse := apperrors.NewInternal()

		mockUserService := new(mocks.MockUserService)
		mockTokenService := new(mocks.MockTokenService)

		mockUserService.
			On("Signup", mock.AnythingOfType("*gin.Context"), u).
			Return(nil)
		mockTokenService.
			On("NewPairFromUser", mock.AnythingOfType("*gin.Context"), u, "").
			Return(nil, mockErrorResponse)

		// a response recorder for getting written http response
		rr := httptest.NewRecorder()

		// don't need a middleware as we don't yet have authorized user
		router := gin.Default()

		NewHandler(&Config{
			R:            router,
			UserService:  mockUserService,
			TokenService: mockTokenService,
		})

		// create a request body with empty email and password
		reqBody, err := json.Marshal(gin.H{
			"email":    u.Email,
			"password": u.Password,
		})
		assert.NoError(t, err)

		// use bytes.NewBuffer to create a reader
		request, err := http.NewRequest(http.MethodPost, "/signup", bytes.NewBuffer(reqBody))
		assert.NoError(t, err)

		request.Header.Set("Content-Type", "application/json")

		router.ServeHTTP(rr, request)

		respBody, err := json.Marshal(gin.H{
			"error": mockErrorResponse,
		})
		assert.NoError(t, err)

		assert.Equal(t, mockErrorResponse.Status(), rr.Code)
		assert.Equal(t, respBody, rr.Body.Bytes())

		mockUserService.AssertExpectations(t)
		mockTokenService.AssertExpectations(t)
	})
```

## Conclusion

Thanks for joining me again! I hope you've learned something today. If you didn't, you wouldn't be the first person of superior intelligence and knowledge I've encountered, and I'm fine with that.😁 But also thanks for reading this far!

Next time, we'll move onto the service layer, and eventually to the repository layer with the hope that we'll soon(ish) be able to signup users and store them in an actual database by making requests with an HTTP client. 

¡Hasta pronto¡

## Bonus: Auth Information 

For those of you with some experience creating auth flows, I'm including some links here for you that address the complexity of storing "tokens" (in whatever form and via whatever storage mechanism). I do this so that you'll understand that I did not choose blindly the auth mechanism for this project. You may disagree with my approach, but I still encourage you to read the resources below to refine your views (whether I sway you, or push you to more vehemently disagree, I just hope you also lose as much sleep as I did mulling this over).

[Please Stop Using Local Storage](https://dev.to/rdegges/please-stop-using-local-storage-1i04) - Note, I highly recommend reading the comments by [Jon Gross-Dubois](https://dev.to/jondubois), if not all of the comments.

[Academind Explanation](https://academind.com/learn/javascript/localstorage-vs-cookies-xss/#but-samesite-helps)

[Auth0](https://auth0.com/docs/tokens/token-storage) - Note that their JS/SPA APIs basically have the option of storing tokens in memory or in local storage, though they give a scary warning about using local storage. And this is a company of people that sit around (though standing desks are cool) thinking about this way more than most of us.

[SPA Best Practices](https://stackoverflow.com/questions/20963273/spa-best-practices-for-authentication-and-session-management) - Cookie session ID's are basically just a "token."

Of course, you could also go read 50 page theses written by some of the finest cryptography academicians in unreadable plaintext, which basically tell you you're screwed, all the while providing no workable solution for the "normies" like you and me. (On a hopeful note, I did see some promising **implementations** coming down the road. 🤞🏼)

Does your head hurt yet? Anyhow, if you lose sleep over this, I hope you'll find comfort in knowing that I, your fellow developer, have also lost sleep over this.