Ginを利用して新規登録、ログイン機能をJWT認証で実装（ログイン編）

# 概要

今回は下記の続きでログイン機能の実装を行なっていきます。

https://qiita.com/soicchi/items/968568c839c182098a48

**バージョン等の情報**は上記の記事に記載されているので

もし知りたい方がいらっしゃれば、前回の記事を閲覧いただけると幸いです。

# ログイン処理の実装

それではさっそく実装を行なっていきます。

まずは前回同様モデルの構築から行なっていきます。

## モデルの構築

[前回解説](https://qiita.com/soicchi/items/968568c839c182098a48)の際に使用した`pkg/models/user.go`に追記していきます。

```pkg/models/user.go
package models

import (
	"github.com/go-ozzo/ozzo-validation"
	"github.com/go-ozzo/ozzo-validation/is"
	"gorm.io/gorm"

	"github.com/soicchi/chatapp_backend/pkg/utils"
)

type User struct {
	gorm.Model
	Name     string `gorm:"size:255;not null"`
	Email    string `gorm:"size:255;not null;unique"`
	Password string `gorm:"size:255;not null"`
}

type SignUpInput struct {
	Name     string `json:"name" binding:"required"`
	Email    string `json:"email" binding:"required"`
	Password string `json:"password" binding:"required"`
}

type LoginInput struct {
	Email    string `json:"email" binding:"required"`
	Password string `json:"password" binding:"required"`
}

func (u *User) Create(db *gorm.DB) (User, error) {
	user := User{
		Name:     u.Name,
		Email:    u.Email,
		Password: utils.Encrypt(u.Password),
	}
	result := db.Create(&user)

	return user, result.Error
}

// ここから追記
func FindUserByEmail(db *gorm.DB, email string) (User, error) {
	var user User
	result := db.Where("email = ?", email).First(&user)

	return user, result.Error
}

func (u *User) VerifyPassword(inputPassword string) bool {
	return u.Password == utils.Encrypt(inputPassword)
}
```

ログインする際は**メールアドレス**と**パスワード**を用いるので、

メールアドレスでユーザーを検索できるように`FindUserByEmail()`を定義しています。

また、`VerifyPassword()`はログインの際のパスワードをコントローラー側で

検証するために定義しています。

さらに前回同様ログインの際にバインドさせるための**構造体**（`LoginInput`）を定義しています。

## コントローラーの定義

続いてコントローラーの定義を行なっていきます。

こちらもモデル同様に追記していきます。

```pkg/controllers/auth.go
func (handler *Handler) LoginHandler(context *gin.Context) {
	var loginInput models.LoginInput
	if err := context.ShouldBind(&loginInput); err != nil {
        context.JSON(http.StatusBadRequest, gin.H{
			"error":   err.Error(),
			"message": "Invalid request body",
		})
		return
    }

	user, err := models.FindUserByEmail(handler.DB, loginInput.Email)
	if err != nil {
		context.JSON(http.StatusBadRequest, gin.H{
			"error":   err.Error(),
			"message": "Failed to find user",
		})
		return
	}

    // 入力されたパスワードとIDから取得したパスワードが等しいかを検証
	if !user.VerifyPassword(loginInput.Password) {
		context.JSON(http.StatusUnauthorized, gin.H{
			"message": "Password is invalid",
		})
		return
	}

	context.JSON(http.StatusOK, gin.H{
		"message": "Successfully logged in",
	})
}
```

## routerの設定

最後に`router`の設定を行います。

```pkg/router/auth.go
package router

import (
	"github.com/gin-gonic/gin"

	"github.com/soicchi/chatapp_backend/pkg/controllers"
)

func addAuthRoutes(routerGroup *gin.RouterGroup, handler *controllers.Handler) {
	auth := routerGroup.Group("/auth")
	{
		auth.POST("/signup", handler.SignUpHandler)
        // 追記
		auth.POST("/login", handler.LoginHandler)
	}
}
```

これでログインのための一通りの実装は終わりました。

# 検証

では、実際に立ち上げてログインしてみましょう。

下記コマンドでコンテナを立ち上げます。

```shell
docker compose up
```

下記のようになれば立ち上げの成功です。

```shell
api    | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
api    | 
api    | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
api    |  - using env:	export GIN_MODE=release
api    |  - using code:	gin.SetMode(gin.ReleaseMode)
api    | 
api    | [GIN-debug] POST   /api/v1/auth/signup       --> app/pkg/controllers.(*Handler).SignUpHandler-fm (3 handlers)
api    | [GIN-debug] POST   /api/v1/auth/login        --> app/pkg/controllers.(*Handler).LoginHandler-fm (3 handlers)
api    | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
api    | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
api    | [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
api    | [GIN-debug] Listening and serving HTTP on :8080

```

[前回](https://qiita.com/soicchi/items/968568c839c182098a48)登録したユーザー情報は下記なので

そのユーザーIDとパスワードを使用してログインしてみます。

**前回登録したユーザー情報**
```json
{"name":"sample-test", "email":"sample@sample.com", "password":"Test1234"}
```

下記コマンドを入力してリクエストを送ってみます。

```shell
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8080/api/v1/auth/login -d '{"email":"sample@sample.com", "password":"Test1234"}'
```

正常に動作していれば下記のようにレスポンスが返ってくると思います。

```shell
{"message":"Successfully logged in"}
```

# まとめ

いかがだったでしょうか。

ログイン機能の実装自体はそれほど難しくはなかったと思います。

しかし、私が記載した方法よりももっといい方法があるかもしれません。

もしそういった意見がありましたら是非コメントしていただけると幸いです。

次回はいよいよJWT認証機能を説明します！

最後まで読んでいただきありがとうございました！

https://qiita.com/soicchi/items/98839a786cc385d92b9a