From a19046838ae4a2697ddbf4930314ff2d79dcf3a5 Mon Sep 17 00:00:00 2001
From: bayusamudra5502 <bayusamudra.55.02.com@gmail.com>
Date: Tue, 14 Feb 2023 22:03:25 +0700
Subject: [PATCH] feat: finishing login

---
 docs/docs.go                       | 149 +++++++++++++++++++++++++++++
 docs/swagger.json                  | 149 +++++++++++++++++++++++++++++
 docs/swagger.yaml                  |  95 ++++++++++++++++++
 go.mod                             |  12 ++-
 go.sum                             |  16 ++++
 handler/auth/handler.go            |  13 +++
 handler/auth/login.go              |  58 +++++++++++
 handler/auth/refresh.go            |  57 +++++++++++
 handler/auth/types.go              |   8 ++
 handler/common/home.go             |   1 +
 handler/di.go                      |   5 +
 model/domain/user/user.go          |   2 +-
 model/web/auth/login/request.go    |  10 +-
 model/web/auth/login/response.go   |   7 +-
 model/web/auth/refresh/request.go  |   5 +
 model/web/auth/refresh/response.go |   8 ++
 model/web/auth/token/token.go      |   2 +-
 repository/user/type.go            |   8 +-
 repository/user/user.go            |  24 +++--
 routes/auth/route.go               |  17 ++++
 routes/common/route.go             |   6 --
 routes/di.go                       |   2 +
 routes/routes.go                   |   2 +
 service/auth/impl.go               |  15 +++
 service/auth/login.go              |  74 ++++++++++++++
 service/auth/refresh.go            |  30 ++++++
 service/auth/type.go               |   8 +-
 service/di.go                      |   7 ++
 utils/base64/impl.go               |  13 +++
 utils/base64/type.go               |   6 ++
 utils/di.go                        |  15 +++
 utils/env/env.go                   |   8 ++
 utils/password/impl.go             |  35 +++++++
 utils/password/type.go             |   6 ++
 utils/token/impl.go                |  55 +++++++++++
 utils/token/type.go                |  10 ++
 36 files changed, 907 insertions(+), 31 deletions(-)
 create mode 100644 handler/auth/handler.go
 create mode 100644 handler/auth/login.go
 create mode 100644 handler/auth/refresh.go
 create mode 100644 handler/auth/types.go
 create mode 100644 model/web/auth/refresh/request.go
 create mode 100644 model/web/auth/refresh/response.go
 create mode 100644 routes/auth/route.go
 create mode 100644 service/auth/impl.go
 create mode 100644 service/auth/login.go
 create mode 100644 service/auth/refresh.go
 create mode 100644 utils/base64/impl.go
 create mode 100644 utils/base64/type.go
 create mode 100644 utils/password/impl.go
 create mode 100644 utils/password/type.go
 create mode 100644 utils/token/impl.go
 create mode 100644 utils/token/type.go

diff --git a/docs/docs.go b/docs/docs.go
index d9cd11b..a98b111 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -21,6 +21,9 @@ const docTemplate = `{
                 "produces": [
                     "application/json"
                 ],
+                "tags": [
+                    "common"
+                ],
                 "summary": "Index page",
                 "responses": {
                     "200": {
@@ -31,9 +34,155 @@ const docTemplate = `{
                     }
                 }
             }
+        },
+        "/auth/login": {
+            "post": {
+                "description": "Login and generate new pair of token",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "auth"
+                ],
+                "summary": "Login",
+                "parameters": [
+                    {
+                        "description": "Login payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/login.LoginRequestPayload"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/login.LoginResponsePayload"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    },
+                    "403": {
+                        "description": "Forbidden",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/auth/refresh": {
+            "post": {
+                "description": "Generate new access token",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "auth"
+                ],
+                "summary": "Refresh Token",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Refresh token",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/refresh.RefreshResponsePayload"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    },
+                    "403": {
+                        "description": "Forbidden",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
         }
     },
     "definitions": {
+        "login.LoginRequestPayload": {
+            "description": "Information that should be available when do a login process",
+            "type": "object",
+            "required": [
+                "email",
+                "password"
+            ],
+            "properties": {
+                "email": {
+                    "description": "User Email",
+                    "type": "string",
+                    "example": "someone@example.com"
+                },
+                "password": {
+                    "description": "User Password",
+                    "type": "string",
+                    "example": "secret"
+                }
+            }
+        },
+        "login.LoginResponsePayload": {
+            "description": "Login response when process success",
+            "type": "object",
+            "properties": {
+                "access_token": {
+                    "description": "Token that used to access the resources",
+                    "type": "string"
+                },
+                "refresh_token": {
+                    "description": "Token that used to generate new access token",
+                    "type": "string"
+                }
+            }
+        },
+        "refresh.RefreshResponsePayload": {
+            "description": "Refresh endpoint response when process success",
+            "type": "object",
+            "properties": {
+                "access_token": {
+                    "description": "Token that used to access the resources",
+                    "type": "string"
+                }
+            }
+        },
         "web.BaseResponse": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.json b/docs/swagger.json
index 7b049fa..442fa43 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -13,6 +13,9 @@
                 "produces": [
                     "application/json"
                 ],
+                "tags": [
+                    "common"
+                ],
                 "summary": "Index page",
                 "responses": {
                     "200": {
@@ -23,9 +26,155 @@
                     }
                 }
             }
+        },
+        "/auth/login": {
+            "post": {
+                "description": "Login and generate new pair of token",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "auth"
+                ],
+                "summary": "Login",
+                "parameters": [
+                    {
+                        "description": "Login payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/login.LoginRequestPayload"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/login.LoginResponsePayload"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    },
+                    "403": {
+                        "description": "Forbidden",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/auth/refresh": {
+            "post": {
+                "description": "Generate new access token",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "auth"
+                ],
+                "summary": "Refresh Token",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Refresh token",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/web.BaseResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/refresh.RefreshResponsePayload"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    },
+                    "403": {
+                        "description": "Forbidden",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
         }
     },
     "definitions": {
+        "login.LoginRequestPayload": {
+            "description": "Information that should be available when do a login process",
+            "type": "object",
+            "required": [
+                "email",
+                "password"
+            ],
+            "properties": {
+                "email": {
+                    "description": "User Email",
+                    "type": "string",
+                    "example": "someone@example.com"
+                },
+                "password": {
+                    "description": "User Password",
+                    "type": "string",
+                    "example": "secret"
+                }
+            }
+        },
+        "login.LoginResponsePayload": {
+            "description": "Login response when process success",
+            "type": "object",
+            "properties": {
+                "access_token": {
+                    "description": "Token that used to access the resources",
+                    "type": "string"
+                },
+                "refresh_token": {
+                    "description": "Token that used to generate new access token",
+                    "type": "string"
+                }
+            }
+        },
+        "refresh.RefreshResponsePayload": {
+            "description": "Refresh endpoint response when process success",
+            "type": "object",
+            "properties": {
+                "access_token": {
+                    "description": "Token that used to access the resources",
+                    "type": "string"
+                }
+            }
+        },
         "web.BaseResponse": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 7183dda..f21c315 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -1,4 +1,36 @@
 definitions:
+  login.LoginRequestPayload:
+    description: Information that should be available when do a login process
+    properties:
+      email:
+        description: User Email
+        example: someone@example.com
+        type: string
+      password:
+        description: User Password
+        example: secret
+        type: string
+    required:
+    - email
+    - password
+    type: object
+  login.LoginResponsePayload:
+    description: Login response when process success
+    properties:
+      access_token:
+        description: Token that used to access the resources
+        type: string
+      refresh_token:
+        description: Token that used to generate new access token
+        type: string
+    type: object
+  refresh.RefreshResponsePayload:
+    description: Refresh endpoint response when process success
+    properties:
+      access_token:
+        description: Token that used to access the resources
+        type: string
+    type: object
   web.BaseResponse:
     properties:
       data: {}
@@ -27,4 +59,67 @@ paths:
           schema:
             $ref: '#/definitions/web.BaseResponse'
       summary: Index page
+      tags:
+      - common
+  /auth/login:
+    post:
+      consumes:
+      - application/json
+      description: Login and generate new pair of token
+      parameters:
+      - description: Login payload
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/login.LoginRequestPayload'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  $ref: '#/definitions/login.LoginResponsePayload'
+              type: object
+        "403":
+          description: Forbidden
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: Login
+      tags:
+      - auth
+  /auth/refresh:
+    post:
+      consumes:
+      - application/json
+      description: Generate new access token
+      parameters:
+      - description: Refresh token
+        in: header
+        name: Authorization
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/web.BaseResponse'
+            - properties:
+                data:
+                  $ref: '#/definitions/refresh.RefreshResponsePayload'
+              type: object
+        "403":
+          description: Forbidden
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: Refresh Token
+      tags:
+      - auth
 swagger: "2.0"
diff --git a/go.mod b/go.mod
index 9ab9120..e4ec072 100644
--- a/go.mod
+++ b/go.mod
@@ -24,6 +24,9 @@ require (
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
 	github.com/go-openapi/spec v0.20.8 // indirect
 	github.com/go-openapi/swag v0.22.3 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.11.2 // indirect
 	github.com/golang-jwt/jwt/v4 v4.4.3 // indirect
 	github.com/jackc/pgpassfile v1.0.0 // indirect
 	github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
@@ -31,14 +34,15 @@ require (
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
+	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/stretchr/objx v0.5.0 // indirect
 	github.com/swaggo/files v1.0.0 // indirect
-	golang.org/x/crypto v0.5.0 // indirect
-	golang.org/x/net v0.5.0 // indirect
-	golang.org/x/sys v0.4.0 // indirect
-	golang.org/x/text v0.6.0 // indirect
+	golang.org/x/crypto v0.6.0 // indirect
+	golang.org/x/net v0.6.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
+	golang.org/x/text v0.7.0 // indirect
 	golang.org/x/tools v0.5.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index a4c86f1..db1952c 100644
--- a/go.sum
+++ b/go.sum
@@ -35,6 +35,12 @@ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyr
 github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
+github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
 github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
 github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -66,6 +72,8 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
@@ -104,6 +112,8 @@ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
 golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
 golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
+golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -116,6 +126,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
 golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -132,6 +144,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
 golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -146,6 +160,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
 golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
diff --git a/handler/auth/handler.go b/handler/auth/handler.go
new file mode 100644
index 0000000..6a8f65b
--- /dev/null
+++ b/handler/auth/handler.go
@@ -0,0 +1,13 @@
+package auth
+
+import (
+	"gitlab.informatika.org/ocw/ocw-backend/service/auth"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/httputil"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/wrapper"
+)
+
+type AuthHandlerImpl struct {
+	auth.AuthService
+	httputil.HttpUtil
+	wrapper.WrapperUtil
+}
diff --git a/handler/auth/login.go b/handler/auth/login.go
new file mode 100644
index 0000000..ee72964
--- /dev/null
+++ b/handler/auth/login.go
@@ -0,0 +1,58 @@
+package auth
+
+import (
+	"net/http"
+
+	"github.com/go-playground/validator/v10"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/login"
+)
+
+// Index godoc
+//
+//		@Tags					auth
+//		@Summary			Login
+//		@Description	Login and generate new pair of token
+//		@Produce			json
+//		@Accept				json
+//		@Param				data body login.LoginRequestPayload true "Login payload"
+//		@Success			200	{object}	web.BaseResponse{data=login.LoginResponsePayload}
+//	  @Failure			403 {object}  web.BaseResponse
+//		@Router				/auth/login [post]
+func (a AuthHandlerImpl) Login(w http.ResponseWriter, r *http.Request) {
+	payload := login.LoginRequestPayload{}
+	validate := validator.New()
+
+	if err := a.HttpUtil.ParseJson(r, &payload); err != nil {
+		payload := a.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	if err := validate.Struct(payload); err != nil {
+		if _, ok := err.(*validator.InvalidValidationError); ok {
+			payload := a.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+			a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+			return
+		}
+
+		errList := []string{}
+		for _, err := range err.(validator.ValidationErrors) {
+			errList = append(errList, err.Error())
+		}
+
+		payload := a.WrapperUtil.ErrorResponseWrap("input validation error", errList)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	response, err := a.AuthService.Login(payload)
+
+	if err != nil {
+		payload := a.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	responsePayload := a.WrapperUtil.SuccessResponseWrap(response)
+	a.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
diff --git a/handler/auth/refresh.go b/handler/auth/refresh.go
new file mode 100644
index 0000000..bf8fda2
--- /dev/null
+++ b/handler/auth/refresh.go
@@ -0,0 +1,57 @@
+package auth
+
+import (
+	"net/http"
+	"strings"
+
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/refresh"
+)
+
+// Index godoc
+//
+//		@Tags					auth
+//		@Summary			Refresh Token
+//		@Description	Generate new access token
+//		@Produce			json
+//		@Accept				json
+//		@Param				Authorization header string true "Refresh token"
+//		@Success			200	{object}	web.BaseResponse{data=refresh.RefreshResponsePayload}
+//	  @Failure			403 {object}  web.BaseResponse
+//		@Router				/auth/refresh [post]
+func (a AuthHandlerImpl) Refresh(w http.ResponseWriter, r *http.Request) {
+	payload := refresh.RefreshRequestPayload{}
+
+	refreshTokenHeader := r.Header.Get("Authorization")
+
+	if refreshTokenHeader == "" {
+		payload := a.WrapperUtil.ErrorResponseWrap("token is required", nil)
+		a.HttpUtil.WriteJson(w, http.StatusForbidden, payload)
+		return
+	}
+
+	token := strings.Split(refreshTokenHeader, " ")
+
+	if len(token) != 2 {
+		payload := a.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	if token[0] != "Bearer" {
+		payload := a.WrapperUtil.ErrorResponseWrap("invalid token", nil)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	payload.RefreshToken = token[1]
+	response, err := a.AuthService.Refresh(payload)
+
+	if err != nil {
+		payload := a.WrapperUtil.ErrorResponseWrap(err.Error(), nil)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	responsePayload := a.WrapperUtil.SuccessResponseWrap(response)
+	a.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
diff --git a/handler/auth/types.go b/handler/auth/types.go
new file mode 100644
index 0000000..49aa52c
--- /dev/null
+++ b/handler/auth/types.go
@@ -0,0 +1,8 @@
+package auth
+
+import "net/http"
+
+type AuthHandler interface {
+	Login(w http.ResponseWriter, r *http.Request)
+	Refresh(w http.ResponseWriter, r *http.Request)
+}
diff --git a/handler/common/home.go b/handler/common/home.go
index 81fd72f..7266a95 100644
--- a/handler/common/home.go
+++ b/handler/common/home.go
@@ -6,6 +6,7 @@ import (
 
 // Index godoc
 //
+//	@Tags					common
 //	@Summary			Index page
 //	@Description	Give server index page response
 //	@Produce			json
diff --git a/handler/di.go b/handler/di.go
index 7c3cb64..b07a554 100644
--- a/handler/di.go
+++ b/handler/di.go
@@ -2,6 +2,7 @@ package handler
 
 import (
 	"github.com/google/wire"
+	"gitlab.informatika.org/ocw/ocw-backend/handler/auth"
 	"gitlab.informatika.org/ocw/ocw-backend/handler/common"
 	"gitlab.informatika.org/ocw/ocw-backend/handler/swagger"
 )
@@ -14,4 +15,8 @@ var HandlerSet = wire.NewSet(
 	// Swagger
 	wire.Struct(new(swagger.SwaggerHandlerImpl), "*"),
 	wire.Bind(new(swagger.SwaggerHandler), new(*swagger.SwaggerHandlerImpl)),
+
+	// Auth
+	wire.Struct(new(auth.AuthHandlerImpl), "*"),
+	wire.Bind(new(auth.AuthHandler), new(*auth.AuthHandlerImpl)),
 )
diff --git a/model/domain/user/user.go b/model/domain/user/user.go
index ad30a66..62d5e1e 100644
--- a/model/domain/user/user.go
+++ b/model/domain/user/user.go
@@ -1,7 +1,7 @@
 package user
 
 type User struct {
-	Username    string `gorm:"primaryKey"`
+	Email       string `gorm:"primaryKey"`
 	Password    string
 	Name        string
 	Role        UserRole `gorm:"type:user_role"`
diff --git a/model/web/auth/login/request.go b/model/web/auth/login/request.go
index cdd602c..716c505 100644
--- a/model/web/auth/login/request.go
+++ b/model/web/auth/login/request.go
@@ -1,7 +1,11 @@
 package login
 
+// Login Request Payload
+// @Description Information that should be available when do a login process
 type LoginRequestPayload struct {
-	Email        string `json:"email"`
-	Password     string `json:"password"`
-	CaptchaToken string `json:"token"`
+	// User Email
+	Email string `json:"email" validate:"required,email" example:"someone@example.com"`
+
+	// User Password
+	Password string `json:"password" validate:"required" example:"secret"`
 }
diff --git a/model/web/auth/login/response.go b/model/web/auth/login/response.go
index 4c77fe4..8cb68ca 100644
--- a/model/web/auth/login/response.go
+++ b/model/web/auth/login/response.go
@@ -1,6 +1,11 @@
 package login
 
+// Login Response Payload
+// @Description Login response when process success
 type LoginResponsePayload struct {
+	// Token that used to generate new access token
 	RefreshToken string `json:"refresh_token"`
-	AccessToken  string `json:"access_token"`
+
+	// Token that used to access the resources
+	AccessToken string `json:"access_token"`
 }
diff --git a/model/web/auth/refresh/request.go b/model/web/auth/refresh/request.go
new file mode 100644
index 0000000..4fd9c44
--- /dev/null
+++ b/model/web/auth/refresh/request.go
@@ -0,0 +1,5 @@
+package refresh
+
+type RefreshRequestPayload struct {
+	RefreshToken string
+}
diff --git a/model/web/auth/refresh/response.go b/model/web/auth/refresh/response.go
new file mode 100644
index 0000000..f349ba6
--- /dev/null
+++ b/model/web/auth/refresh/response.go
@@ -0,0 +1,8 @@
+package refresh
+
+// Refresh Response Payload
+// @Description Refresh endpoint response when process success
+type RefreshResponsePayload struct {
+	// Token that used to access the resources
+	AccessToken string `json:"access_token"`
+}
diff --git a/model/web/auth/token/token.go b/model/web/auth/token/token.go
index b17d002..497287e 100644
--- a/model/web/auth/token/token.go
+++ b/model/web/auth/token/token.go
@@ -6,7 +6,7 @@ import (
 )
 
 type UserClaim struct {
-	jwt.StandardClaims
+	jwt.RegisteredClaims
 	Name  string        `json:"name"`
 	Email string        `json:"email"`
 	Role  user.UserRole `json:"role"`
diff --git a/repository/user/type.go b/repository/user/type.go
index f181046..047ee69 100644
--- a/repository/user/type.go
+++ b/repository/user/type.go
@@ -5,8 +5,8 @@ import (
 )
 
 type UserRepository interface {
-	Add(user user.User)
-	Get(username string) user.User
-	Update(user user.User)
-	Delete(username string)
+	Add(user user.User) error
+	Get(username string) (*user.User, error)
+	Update(user user.User) error
+	Delete(username string) error
 }
diff --git a/repository/user/user.go b/repository/user/user.go
index 2287a31..881a4c6 100644
--- a/repository/user/user.go
+++ b/repository/user/user.go
@@ -16,21 +16,25 @@ func New(
 	return &UserRepositoryImpl{db.Connect()}
 }
 
-func (repo UserRepositoryImpl) Add(user user.User) {
-	repo.db.Create(user)
+func (repo UserRepositoryImpl) Add(user user.User) error {
+	return repo.db.Create(user).Error
 }
 
-func (repo UserRepositoryImpl) Get(username string) user.User {
-	result := user.User{}
-	repo.db.First(&result, "username = ?", username)
+func (repo UserRepositoryImpl) Get(username string) (*user.User, error) {
+	result := &user.User{}
+	err := repo.db.Where("username = ?", username).First(result).Error
 
-	return result
+	if err != nil {
+		return nil, err
+	}
+
+	return result, nil
 }
 
-func (repo UserRepositoryImpl) Update(user user.User) {
-	repo.db.Save(user)
+func (repo UserRepositoryImpl) Update(user user.User) error {
+	return repo.db.Save(user).Error
 }
 
-func (repo UserRepositoryImpl) Delete(username string) {
-	repo.db.Delete(&user.User{}, "username = ?", username)
+func (repo UserRepositoryImpl) Delete(username string) error {
+	return repo.db.Where("username = ?", username).Delete(&user.User{}).Error
 }
diff --git a/routes/auth/route.go b/routes/auth/route.go
new file mode 100644
index 0000000..55377de
--- /dev/null
+++ b/routes/auth/route.go
@@ -0,0 +1,17 @@
+package auth
+
+import (
+	"github.com/go-chi/chi/v5"
+	"gitlab.informatika.org/ocw/ocw-backend/handler/auth"
+)
+
+type AuthRoutes struct {
+	auth.AuthHandler
+}
+
+func (ar AuthRoutes) Register(r chi.Router) {
+	r.Route("/auth", func(r chi.Router) {
+		r.Post("/login", ar.AuthHandler.Login)
+		r.Post("/refresh", ar.AuthHandler.Refresh)
+	})
+}
diff --git a/routes/common/route.go b/routes/common/route.go
index a04fc14..47e768a 100644
--- a/routes/common/route.go
+++ b/routes/common/route.go
@@ -4,17 +4,11 @@ import (
 	"net/http"
 
 	"github.com/go-chi/chi/v5"
-	"gitlab.informatika.org/ocw/ocw-backend/service/common"
-	"gitlab.informatika.org/ocw/ocw-backend/utils/httputil"
-	"gitlab.informatika.org/ocw/ocw-backend/utils/wrapper"
 
 	commonHandler "gitlab.informatika.org/ocw/ocw-backend/handler/common"
 )
 
 type CommonRoutes struct {
-	common.CommonService
-	httputil.HttpUtil
-	wrapper.WrapperUtil
 	commonHandler.CommonHandler
 }
 
diff --git a/routes/di.go b/routes/di.go
index 93a65f0..ad350ce 100644
--- a/routes/di.go
+++ b/routes/di.go
@@ -2,6 +2,7 @@ package routes
 
 import (
 	"github.com/google/wire"
+	"gitlab.informatika.org/ocw/ocw-backend/routes/auth"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/common"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/swagger"
 )
@@ -9,6 +10,7 @@ import (
 var routesCollectionSet = wire.NewSet(
 	wire.Struct(new(common.CommonRoutes), "*"),
 	wire.Struct(new(swagger.SwaggerRoutes), "*"),
+	wire.Struct(new(auth.AuthRoutes), "*"),
 )
 
 var RoutesSet = wire.NewSet(
diff --git a/routes/routes.go b/routes/routes.go
index 6e37d83..c824d2d 100644
--- a/routes/routes.go
+++ b/routes/routes.go
@@ -1,6 +1,7 @@
 package routes
 
 import (
+	"gitlab.informatika.org/ocw/ocw-backend/routes/auth"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/common"
 	"gitlab.informatika.org/ocw/ocw-backend/routes/swagger"
 
@@ -11,6 +12,7 @@ type AppRouter struct {
 	// Routes
 	swagger.SwaggerRoutes
 	common.CommonRoutes
+	auth.AuthRoutes
 
 	// Utility
 	Logger logger.Logger
diff --git a/service/auth/impl.go b/service/auth/impl.go
new file mode 100644
index 0000000..d8ddef7
--- /dev/null
+++ b/service/auth/impl.go
@@ -0,0 +1,15 @@
+package auth
+
+import (
+	"gitlab.informatika.org/ocw/ocw-backend/repository/user"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/password"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/token"
+)
+
+type AuthServiceImpl struct {
+	user.UserRepository
+	password.PasswordUtil
+	env.Environment
+	token.TokenUtil
+}
diff --git a/service/auth/login.go b/service/auth/login.go
new file mode 100644
index 0000000..85d6de9
--- /dev/null
+++ b/service/auth/login.go
@@ -0,0 +1,74 @@
+package auth
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	"github.com/golang-jwt/jwt/v4"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/login"
+	tokenModel "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
+	"gorm.io/gorm"
+)
+
+func (auth AuthServiceImpl) Login(payload login.LoginRequestPayload) (*login.LoginResponsePayload, error) {
+	user, err := auth.Get(payload.Email)
+
+	if err != nil {
+		var errorObj error
+
+		switch {
+		case errors.Is(err, gorm.ErrRecordNotFound):
+			errorObj = fmt.Errorf("username or password combination not found")
+		default:
+			errorObj = err
+		}
+
+		return nil, errorObj
+	}
+
+	if err := auth.Check(payload.Password, user.Password); err != nil {
+		return nil, err
+	}
+
+	refreshClaim := tokenModel.UserClaim{
+		Name:  user.Name,
+		Email: user.Email,
+		Role:  user.Role,
+		Type:  tokenModel.Refresh,
+		RegisteredClaims: jwt.RegisteredClaims{
+			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(auth.TokenRefreshExpired) * time.Millisecond)),
+			Issuer:    auth.TokenIssuer,
+			IssuedAt:  jwt.NewNumericDate(time.Now()),
+		},
+	}
+
+	accessClaim := tokenModel.UserClaim{
+		Name:  user.Name,
+		Email: user.Email,
+		Role:  user.Role,
+		Type:  tokenModel.Access,
+		RegisteredClaims: jwt.RegisteredClaims{
+			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(auth.TokenAccessExpired) * time.Millisecond)),
+			Issuer:    auth.TokenIssuer,
+			IssuedAt:  jwt.NewNumericDate(time.Now()),
+		},
+	}
+
+	refreshToken, err := auth.TokenUtil.Generate(refreshClaim)
+
+	if err != nil {
+		return nil, err
+	}
+
+	accessToken, err := auth.TokenUtil.Generate(accessClaim)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &login.LoginResponsePayload{
+		RefreshToken: refreshToken,
+		AccessToken:  accessToken,
+	}, nil
+}
diff --git a/service/auth/refresh.go b/service/auth/refresh.go
new file mode 100644
index 0000000..70aecd6
--- /dev/null
+++ b/service/auth/refresh.go
@@ -0,0 +1,30 @@
+package auth
+
+import (
+	"time"
+
+	"github.com/golang-jwt/jwt/v4"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/refresh"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
+)
+
+func (auth AuthServiceImpl) Refresh(payload refresh.RefreshRequestPayload) (*refresh.RefreshResponsePayload, error) {
+	claim, err := auth.TokenUtil.Validate(payload.RefreshToken, token.Refresh)
+
+	if err != nil {
+		return nil, err
+	}
+
+	claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Duration(auth.TokenAccessExpired) * time.Millisecond))
+	claim.Type = token.Access
+
+	newToken, err := auth.TokenUtil.Generate(*claim)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &refresh.RefreshResponsePayload{
+		AccessToken: newToken,
+	}, nil
+}
diff --git a/service/auth/type.go b/service/auth/type.go
index 59d4ebd..87a4615 100644
--- a/service/auth/type.go
+++ b/service/auth/type.go
@@ -1,5 +1,11 @@
 package auth
 
+import (
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/login"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/refresh"
+)
+
 type AuthService interface {
-	Login(email string, password string)
+	Login(payload login.LoginRequestPayload) (*login.LoginResponsePayload, error)
+	Refresh(payload refresh.RefreshRequestPayload) (*refresh.RefreshResponsePayload, error)
 }
diff --git a/service/di.go b/service/di.go
index aafd073..b2eb205 100644
--- a/service/di.go
+++ b/service/di.go
@@ -2,6 +2,7 @@ package service
 
 import (
 	"github.com/google/wire"
+	"gitlab.informatika.org/ocw/ocw-backend/service/auth"
 	"gitlab.informatika.org/ocw/ocw-backend/service/common"
 	"gitlab.informatika.org/ocw/ocw-backend/service/logger"
 	"gitlab.informatika.org/ocw/ocw-backend/service/logger/hooks"
@@ -20,6 +21,12 @@ var ServiceTestSet = wire.NewSet(
 		reporter.New,
 		wire.Bind(new(reporter.Reporter), new(*reporter.LogtailReporter)),
 	),
+
+	// auth service
+	wire.NewSet(
+		wire.Struct(new(auth.AuthServiceImpl)),
+		wire.Bind(new(auth.AuthService), new(*auth.AuthServiceImpl)),
+	),
 )
 
 var ServiceSet = wire.NewSet(
diff --git a/utils/base64/impl.go b/utils/base64/impl.go
new file mode 100644
index 0000000..9783d90
--- /dev/null
+++ b/utils/base64/impl.go
@@ -0,0 +1,13 @@
+package base64
+
+import "encoding/base64"
+
+type Base64UtilImpl struct{}
+
+func (Base64UtilImpl) Encode(input []byte) string {
+	return base64.StdEncoding.EncodeToString(input)
+}
+
+func (Base64UtilImpl) Decode(input string) ([]byte, error) {
+	return base64.StdEncoding.DecodeString(input)
+}
diff --git a/utils/base64/type.go b/utils/base64/type.go
new file mode 100644
index 0000000..568b601
--- /dev/null
+++ b/utils/base64/type.go
@@ -0,0 +1,6 @@
+package base64
+
+type Base64Util interface {
+	Encode(input []byte) string
+	Decode(input string) ([]byte, error)
+}
diff --git a/utils/di.go b/utils/di.go
index 9e4afce..5b8ac4d 100644
--- a/utils/di.go
+++ b/utils/di.go
@@ -3,11 +3,14 @@ package utils
 import (
 	"github.com/google/wire"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/app"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/base64"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/db"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/httputil"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/log"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/password"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/res"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/token"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/wrapper"
 )
 
@@ -32,6 +35,18 @@ var UtilSetTest = wire.NewSet(
 	wire.Struct(new(wrapper.WrapperUtilImpl), "*"),
 	wire.Bind(new(wrapper.WrapperUtil), new(*wrapper.WrapperUtilImpl)),
 
+	// Base64 Utility
+	wire.Struct(new(base64.Base64UtilImpl), "*"),
+	wire.Bind(new(base64.Base64Util), new(*base64.Base64UtilImpl)),
+
+	// Password utility
+	wire.Struct(new(password.PasswordUtilImpl), "*"),
+	wire.Bind(new(password.PasswordUtil), new(*password.PasswordUtilImpl)),
+
+	// Token utility
+	wire.Struct(new(token.TokenUtilImpl), "*"),
+	wire.Bind(new(token.TokenUtil), new(*token.TokenUtilImpl)),
+
 	// app
 	app.New,
 	wire.Bind(new(app.Server), new(*app.HttpServer)),
diff --git a/utils/env/env.go b/utils/env/env.go
index df6fcbb..1112fb6 100644
--- a/utils/env/env.go
+++ b/utils/env/env.go
@@ -19,6 +19,14 @@ type Environment struct {
 
 	UseReporter        bool   `env:"USE_REPORTER" envDefault:"true"`
 	DatabaseConnection string `env:"DB_STRING"`
+
+	PasswordCost int `env:"PASSWORD_COST" envDefault:"10"`
+
+	TokenMethod         string `env:"TOKEN_SIGNING_METHOD" envDefault:"hs512"`
+	TokenSecret         string `env:"TOKEN_SECRET"`
+	TokenRefreshExpired int64  `env:"TOKEN_REFRESH_EXPIRED_MS" envDefault:"86400000"`
+	TokenAccessExpired  int64  `env:"TOKEN_ACCESS_EXPIRED_MS" envDefault:"300000"`
+	TokenIssuer         string `env:"TOKEN_ISSUER" envDefault:"ocw"`
 }
 
 func New() (*Environment, error) {
diff --git a/utils/password/impl.go b/utils/password/impl.go
new file mode 100644
index 0000000..f9cb7ab
--- /dev/null
+++ b/utils/password/impl.go
@@ -0,0 +1,35 @@
+package password
+
+import (
+	"fmt"
+
+	"gitlab.informatika.org/ocw/ocw-backend/utils/base64"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
+	"golang.org/x/crypto/bcrypt"
+)
+
+type PasswordUtilImpl struct {
+	env.Environment
+	base64.Base64Util
+}
+
+func (e PasswordUtilImpl) Hash(password string) (string, error) {
+	hash, err := bcrypt.GenerateFromPassword([]byte(password), e.Environment.PasswordCost)
+	return e.Base64Util.Encode(hash), err
+}
+
+func (e PasswordUtilImpl) Check(password string, hashedPassword string) error {
+	hash, err := e.Base64Util.Decode(hashedPassword)
+
+	if err != nil {
+		return err
+	}
+
+	err = bcrypt.CompareHashAndPassword(hash, []byte(password))
+
+	if err != nil {
+		return fmt.Errorf("username or password combination is not found")
+	}
+
+	return nil
+}
diff --git a/utils/password/type.go b/utils/password/type.go
new file mode 100644
index 0000000..026a583
--- /dev/null
+++ b/utils/password/type.go
@@ -0,0 +1,6 @@
+package password
+
+type PasswordUtil interface {
+	Hash(password string) (string, error)
+	Check(password string, hashedPassword string) error
+}
diff --git a/utils/token/impl.go b/utils/token/impl.go
new file mode 100644
index 0000000..9dde3aa
--- /dev/null
+++ b/utils/token/impl.go
@@ -0,0 +1,55 @@
+package token
+
+import (
+	"fmt"
+
+	"github.com/golang-jwt/jwt/v4"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
+)
+
+type TokenUtilImpl struct {
+	env.Environment
+}
+
+func (t TokenUtilImpl) Method() jwt.SigningMethod {
+	switch t.TokenMethod {
+	case "hs256":
+		return jwt.SigningMethodHS256
+	default:
+		return jwt.SigningMethodHS512
+	}
+}
+
+func (tu TokenUtilImpl) Validate(tokenString string, tokenType token.TokenType) (*token.UserClaim, error) {
+	jwtData, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
+		if method, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, fmt.Errorf("invalid signing method")
+		} else if method != tu.Method() {
+			return nil, fmt.Errorf("invalid signing method")
+		}
+
+		return tu.Method(), nil
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	claims := jwtData.Claims.(*token.UserClaim)
+
+	if claims.Type != tokenType {
+		return claims, fmt.Errorf("token type is not valid")
+	}
+
+	return claims, nil
+}
+
+func (t TokenUtilImpl) Generate(claim token.UserClaim) (string, error) {
+	token := jwt.NewWithClaims(
+		jwt.SigningMethodHS512,
+		claim,
+	)
+
+	return token.SignedString([]byte(t.TokenSecret))
+}
diff --git a/utils/token/type.go b/utils/token/type.go
new file mode 100644
index 0000000..cd6c27a
--- /dev/null
+++ b/utils/token/type.go
@@ -0,0 +1,10 @@
+package token
+
+import (
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
+)
+
+type TokenUtil interface {
+	Validate(jwt string, tokenType token.TokenType) (*token.UserClaim, error)
+	Generate(claim token.UserClaim) (string, error)
+}
-- 
GitLab