diff --git a/.env b/.env
index 1f5f0f141e9c1391bcd5b0325fbb516f0fa4bbfb..f0f1fecc697b60d78a401477ee01c6c5febbc5a5 100644
--- a/.env
+++ b/.env
@@ -7,4 +7,6 @@ LOG_FLUSH_INTERVAL_MS=1000
 DB_STRING="host=localhost user=ocw password=ocw dbname=ocw-db port=5433 sslmode=disable TimeZone=Asia/Shanghai"
 SMTP_USERNAME="noreply@ocw.id"
 SMTP_SERVER=localhost
-SMTP_PORT=1025
\ No newline at end of file
+SMTP_PORT=1025
+REDIS_STRING="localhost"
+FE_BASE_URL="http://localhost:3000"
\ No newline at end of file
diff --git a/.env.docker b/.env.docker
index 11f1edfc100a7fcebca3466da30fcdd21feff42d..2f87fea919b87db0f51f52306a1a8c21e13e3aaf 100644
--- a/.env.docker
+++ b/.env.docker
@@ -1,3 +1,14 @@
 POSTGRES_USER=ocw
 POSTGRES_PASSWORD=ocw
 POSTGRES_DB=ocw-db
+ENV=DEVELOPMENT
+LISTEN_ADDR=0.0.0.0
+PORT=8080
+LOGTAIL_TOKEN=
+HTTP_TIMEOUT_SEC=2
+LOG_FLUSH_INTERVAL_MS=1000
+DB_STRING="host=database user=ocw password=ocw dbname=ocw-db port=5432 sslmode=disable TimeZone=Asia/Shanghai"
+SMTP_USERNAME="noreply@ocw.id"
+SMTP_SERVER=mailhog
+SMTP_PORT=1025
+REDIS_STRING="redis"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 7e2965e3e35f23ac242d20ffece83456a71de7ef..c5c13586f4cb91a1d501686acc635db166cd0398 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 bin/
 tmp/
 wire_gen.go
+__debug_bin*
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 65b15586a8cec0a52bff462637f75ad06310cc3e..948871652a86d5e028ef4f13a7c2571766d27ec1 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ run: dependency
 build: dependency
 	@go build -o=bin/server.app .
 
-watch:
+watch: dependency
 	@air --build.cmd="make build" --build.bin="./bin/server.app" --build.exclude_dir="bin,tmp,docs" --build.exclude_file="wire_gen.go"
 
 test: test-dependency
diff --git a/docker-compose.yml b/docker-compose.yml
index 7ccabe6b9f336b3be6a3a55f4c8d11c8c2127306..3eccf485e7a93a9172dcdfca800f8bcf2b732655 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -21,7 +21,7 @@ services:
       - .:/app
     ports:
       - 8888:8080
-    env_file: .env
+    env_file: .env.docker
     depends_on:
       - database
       - minio
diff --git a/docs/docs.go b/docs/docs.go
index 5e9e1a5761c0a19b0b6a37940422fc4f045a04b9..cf30202b3fb0d89fde8a104b485c022b647a0734 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -315,7 +315,7 @@ const docTemplate = `{
         },
         "/auth/register": {
             "post": {
-                "description": "Generate New Account as Member",
+                "description": "Do Email Verification",
                 "consumes": [
                     "application/json"
                 ],
@@ -325,7 +325,7 @@ const docTemplate = `{
                 "tags": [
                     "auth"
                 ],
-                "summary": "Register New Account",
+                "summary": "Email Verification",
                 "parameters": [
                     {
                         "description": "Register Payload",
@@ -333,7 +333,7 @@ const docTemplate = `{
                         "in": "body",
                         "required": true,
                         "schema": {
-                            "$ref": "#/definitions/register.RegisterRequestPayload"
+                            "$ref": "#/definitions/verification.VerificationRequestPayload"
                         }
                     }
                 ],
@@ -358,6 +358,104 @@ const docTemplate = `{
                     }
                 }
             }
+        },
+        "/reset/confirm": {
+            "post": {
+                "description": "Do confirmation to reset password",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "reset"
+                ],
+                "summary": "Confirm Reset Password",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Email validation token",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    },
+                    {
+                        "description": "payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/confirm.ConfirmRequestPayload"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "Login Success",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/reset/request": {
+            "post": {
+                "description": "Send Reset password token to email",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "reset"
+                ],
+                "summary": "Request Reset Password Token",
+                "parameters": [
+                    {
+                        "description": "payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/request.RequestRequestPayload"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "Login Success",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/reset/validate": {
+            "get": {
+                "description": "Send Reset password token to email",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "reset"
+                ],
+                "summary": "Request Reset Password Token",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Email validation token",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "Login Success",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
         }
     },
     "definitions": {
@@ -411,6 +509,30 @@ const docTemplate = `{
                 }
             }
         },
+        "confirm.ConfirmRequestPayload": {
+            "description": "Information that should be available when you confirm a password reset",
+            "type": "object",
+            "required": [
+                "password",
+                "password_validation"
+            ],
+            "properties": {
+                "confirmToken": {
+                    "description": "Web Token that was appended to the link",
+                    "type": "string"
+                },
+                "password": {
+                    "description": "User Password",
+                    "type": "string",
+                    "example": "secret"
+                },
+                "password_validation": {
+                    "description": "User Password Validation, must be same as user",
+                    "type": "string",
+                    "example": "secret"
+                }
+            }
+        },
         "login.LoginRequestPayload": {
             "description": "Information that should be available when do a login process",
             "type": "object",
@@ -487,6 +609,34 @@ const docTemplate = `{
                 }
             }
         },
+        "request.RequestRequestPayload": {
+            "description": "Information that should be available when password reset is requested",
+            "type": "object",
+            "required": [
+                "email"
+            ],
+            "properties": {
+                "email": {
+                    "description": "User Email",
+                    "type": "string",
+                    "example": "someone@example.com"
+                }
+            }
+        },
+        "verification.VerificationRequestPayload": {
+            "description": "Information that should be passed when request verify",
+            "type": "object",
+            "required": [
+                "email"
+            ],
+            "properties": {
+                "email": {
+                    "description": "User Email",
+                    "type": "string",
+                    "example": "someone@example.com"
+                }
+            }
+        },
         "web.BaseResponse": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.json b/docs/swagger.json
index 84edd189e1059594347807ded586f484aa7714d9..b0c36239bb54582921dae56d834e5903e59ebe55 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -307,7 +307,7 @@
         },
         "/auth/register": {
             "post": {
-                "description": "Generate New Account as Member",
+                "description": "Do Email Verification",
                 "consumes": [
                     "application/json"
                 ],
@@ -317,7 +317,7 @@
                 "tags": [
                     "auth"
                 ],
-                "summary": "Register New Account",
+                "summary": "Email Verification",
                 "parameters": [
                     {
                         "description": "Register Payload",
@@ -325,7 +325,7 @@
                         "in": "body",
                         "required": true,
                         "schema": {
-                            "$ref": "#/definitions/register.RegisterRequestPayload"
+                            "$ref": "#/definitions/verification.VerificationRequestPayload"
                         }
                     }
                 ],
@@ -350,6 +350,104 @@
                     }
                 }
             }
+        },
+        "/reset/confirm": {
+            "post": {
+                "description": "Do confirmation to reset password",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "reset"
+                ],
+                "summary": "Confirm Reset Password",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Email validation token",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    },
+                    {
+                        "description": "payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/confirm.ConfirmRequestPayload"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "Login Success",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/reset/request": {
+            "post": {
+                "description": "Send Reset password token to email",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "reset"
+                ],
+                "summary": "Request Reset Password Token",
+                "parameters": [
+                    {
+                        "description": "payload",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/request.RequestRequestPayload"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "Login Success",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/reset/validate": {
+            "get": {
+                "description": "Send Reset password token to email",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "reset"
+                ],
+                "summary": "Request Reset Password Token",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Email validation token",
+                        "name": "Authorization",
+                        "in": "header",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "Login Success",
+                        "schema": {
+                            "$ref": "#/definitions/web.BaseResponse"
+                        }
+                    }
+                }
+            }
         }
     },
     "definitions": {
@@ -403,6 +501,30 @@
                 }
             }
         },
+        "confirm.ConfirmRequestPayload": {
+            "description": "Information that should be available when you confirm a password reset",
+            "type": "object",
+            "required": [
+                "password",
+                "password_validation"
+            ],
+            "properties": {
+                "confirmToken": {
+                    "description": "Web Token that was appended to the link",
+                    "type": "string"
+                },
+                "password": {
+                    "description": "User Password",
+                    "type": "string",
+                    "example": "secret"
+                },
+                "password_validation": {
+                    "description": "User Password Validation, must be same as user",
+                    "type": "string",
+                    "example": "secret"
+                }
+            }
+        },
         "login.LoginRequestPayload": {
             "description": "Information that should be available when do a login process",
             "type": "object",
@@ -479,6 +601,34 @@
                 }
             }
         },
+        "request.RequestRequestPayload": {
+            "description": "Information that should be available when password reset is requested",
+            "type": "object",
+            "required": [
+                "email"
+            ],
+            "properties": {
+                "email": {
+                    "description": "User Email",
+                    "type": "string",
+                    "example": "someone@example.com"
+                }
+            }
+        },
+        "verification.VerificationRequestPayload": {
+            "description": "Information that should be passed when request verify",
+            "type": "object",
+            "required": [
+                "email"
+            ],
+            "properties": {
+                "email": {
+                    "description": "User Email",
+                    "type": "string",
+                    "example": "someone@example.com"
+                }
+            }
+        },
         "web.BaseResponse": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index beaf58f3d9c777673c50b54fdc87d44aa3bae145..e01ccea263c225cf596ae8d2dd0a9750a1422851 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -37,6 +37,25 @@ definitions:
     - name
     - role
     type: object
+  confirm.ConfirmRequestPayload:
+    description: Information that should be available when you confirm a password
+      reset
+    properties:
+      confirmToken:
+        description: Web Token that was appended to the link
+        type: string
+      password:
+        description: User Password
+        example: secret
+        type: string
+      password_validation:
+        description: User Password Validation, must be same as user
+        example: secret
+        type: string
+    required:
+    - password
+    - password_validation
+    type: object
   login.LoginRequestPayload:
     description: Information that should be available when do a login process
     properties:
@@ -94,6 +113,26 @@ definitions:
     - password
     - password_validation
     type: object
+  request.RequestRequestPayload:
+    description: Information that should be available when password reset is requested
+    properties:
+      email:
+        description: User Email
+        example: someone@example.com
+        type: string
+    required:
+    - email
+    type: object
+  verification.VerificationRequestPayload:
+    description: Information that should be passed when request verify
+    properties:
+      email:
+        description: User Email
+        example: someone@example.com
+        type: string
+    required:
+    - email
+    type: object
   web.BaseResponse:
     properties:
       data: {}
@@ -302,14 +341,14 @@ paths:
     post:
       consumes:
       - application/json
-      description: Generate New Account as Member
+      description: Do Email Verification
       parameters:
       - description: Register Payload
         in: body
         name: data
         required: true
         schema:
-          $ref: '#/definitions/register.RegisterRequestPayload'
+          $ref: '#/definitions/verification.VerificationRequestPayload'
       produces:
       - application/json
       responses:
@@ -325,7 +364,71 @@ paths:
           description: Internal Server Error
           schema:
             $ref: '#/definitions/web.BaseResponse'
-      summary: Register New Account
+      summary: Email Verification
       tags:
       - auth
+  /reset/confirm:
+    post:
+      description: Do confirmation to reset password
+      parameters:
+      - description: Email validation token
+        in: header
+        name: Authorization
+        required: true
+        type: string
+      - description: payload
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/confirm.ConfirmRequestPayload'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: Login Success
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: Confirm Reset Password
+      tags:
+      - reset
+  /reset/request:
+    post:
+      description: Send Reset password token to email
+      parameters:
+      - description: payload
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/request.RequestRequestPayload'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: Login Success
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: Request Reset Password Token
+      tags:
+      - reset
+  /reset/validate:
+    get:
+      description: Send Reset password token to email
+      parameters:
+      - description: Email validation token
+        in: header
+        name: Authorization
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: Login Success
+          schema:
+            $ref: '#/definitions/web.BaseResponse'
+      summary: Request Reset Password Token
+      tags:
+      - reset
 swagger: "2.0"
diff --git a/go.mod b/go.mod
index b69d320688f3db0c6bbdcc1ad19eea994fde74ea..b6cf3e2e6812ad2a15b62a2272bf6fc00fc72b76 100644
--- a/go.mod
+++ b/go.mod
@@ -19,6 +19,11 @@ require (
 	gorm.io/gorm v1.24.5
 )
 
+require (
+	github.com/cespare/xxhash/v2 v2.1.2 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+)
+
 require (
 	github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect
 	github.com/KyleBanks/depth v1.2.1 // indirect
@@ -35,6 +40,8 @@ require (
 	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-redis/redis/v8 v8.11.5
+	github.com/gomodule/redigo v1.8.9
 	github.com/jackc/pgpassfile v1.0.0 // indirect
 	github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
 	github.com/jackc/pgx/v5 v5.2.0 // indirect
diff --git a/go.sum b/go.sum
index d771a874f694fecc968e13f8658ba7de1cd1ca6f..104db32d430bfc3149b43928c0b68f3784d9056d 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
 github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
+github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
+github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cosmtrek/air v1.41.0 h1:6ck2LbcVvby6cyuwE8ruia41U2nppMZGWOpq+E/EhoU=
 github.com/cosmtrek/air v1.41.0/go.mod h1:+RBGjJt7T2f3I7td8Tvk0XsH/hZ3E1QBLfiWObICO4c=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -16,6 +18,8 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
 github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -49,8 +53,12 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
 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/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
+github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
 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/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
+github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
diff --git a/handler/auth/types.go b/handler/auth/types.go
index 71b4d1eba14c50a290f9c43565da1dd707e73f29..1b70a41ef00b627a24521bb24ab8804bfe24c640 100644
--- a/handler/auth/types.go
+++ b/handler/auth/types.go
@@ -6,4 +6,5 @@ type AuthHandler interface {
 	Login(w http.ResponseWriter, r *http.Request)
 	Register(w http.ResponseWriter, r *http.Request)
 	Refresh(w http.ResponseWriter, r *http.Request)
+	EmailVerify(w http.ResponseWriter, r *http.Request)
 }
diff --git a/handler/auth/verify.go b/handler/auth/verify.go
new file mode 100644
index 0000000000000000000000000000000000000000..6ab6b25233d8c7da2f4339abe7bd439736a979f1
--- /dev/null
+++ b/handler/auth/verify.go
@@ -0,0 +1,72 @@
+package auth
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/go-playground/validator/v10"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/verification"
+)
+
+// Index godoc
+//
+//		@Tags					auth
+//		@Summary			Email Verification
+//		@Description	Do Email Verification
+//		@Produce			json
+//		@Accept				json
+//		@Param				data body verification.VerificationRequestPayload true "Register Payload"
+//		@Success			200	{object}	web.BaseResponse
+//	  @Failure			400 {object}  web.BaseResponse
+//	  @Failure			500 {object}  web.BaseResponse
+//		@Router				/auth/register [post]
+func (a AuthHandlerImpl) EmailVerify(w http.ResponseWriter, r *http.Request) {
+	payload := verification.VerificationRequestPayload{}
+	validate := validator.New()
+
+	if r.Header.Get("Content-Type") != "application/json" {
+		payload := a.WrapperUtil.ErrorResponseWrap("this service only receive json input", nil)
+		a.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload)
+		return
+	}
+
+	if err := a.HttpUtil.ParseJson(r, &payload); err != nil {
+		payload := a.WrapperUtil.ErrorResponseWrap("invalid json input", err.Error())
+		a.HttpUtil.WriteJson(w, http.StatusUnprocessableEntity, 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
+		}
+
+		errPayload := web.NewResponseErrorFromValidator(err.(validator.ValidationErrors))
+		payload := a.WrapperUtil.ErrorResponseWrap(errPayload.Error(), errPayload)
+		a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		return
+	}
+
+	err := a.AuthService.VerifyEmail(payload)
+
+	if err != nil {
+		respErr, ok := err.(web.ResponseError)
+		if ok {
+			payload := a.WrapperUtil.ErrorResponseWrap("you have reach limit of resend verification", respErr)
+			a.HttpUtil.WriteJson(w, http.StatusBadRequest, payload)
+		} else {
+			a.Logger.Error(
+				fmt.Sprintf("[AUTH] some error happened when do email verification: %s", err.Error()),
+			)
+			payload := a.WrapperUtil.ErrorResponseWrap("internal server error", nil)
+			a.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload)
+		}
+		return
+	}
+
+	responsePayload := a.WrapperUtil.SuccessResponseWrap(nil)
+	a.HttpUtil.WriteSuccessJson(w, responsePayload)
+}
diff --git a/handler/reset/confirm.go b/handler/reset/confirm.go
index e68ba0568c58bacb594068f183d79c7d5a20f94b..1a1d33a297c1a303a2ed787b25a43f636904f8e1 100644
--- a/handler/reset/confirm.go
+++ b/handler/reset/confirm.go
@@ -10,6 +10,16 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/reset/confirm"
 )
 
+// Index godoc
+//
+//	@Tags					reset
+//	@Summary			Confirm Reset Password
+//	@Description	Do confirmation to reset password
+//	@Produce			json
+//	@Param			  Authorization header string true "Email validation token"
+//	@Param				data body confirm.ConfirmRequestPayload true "payload"
+//	@Success			200	{object}	web.BaseResponse "Login Success"
+//	@Router				/reset/confirm [post]
 func (rs ResetHandlerImpl) Confirm(w http.ResponseWriter, r *http.Request) {
 	payload := confirm.ConfirmRequestPayload{}
 	validate := validator.New()
@@ -88,4 +98,4 @@ func (rs ResetHandlerImpl) Confirm(w http.ResponseWriter, r *http.Request) {
 
 	responsePayload := rs.WrapperUtil.SuccessResponseWrap(nil)
 	rs.HttpUtil.WriteSuccessJson(w, responsePayload)
-}
\ No newline at end of file
+}
diff --git a/handler/reset/request.go b/handler/reset/request.go
index f2dc0d450e58ca4c26d33f574a403e3e3269c221..e118a0261b97d7bd0dbec2cde86644293d786313 100644
--- a/handler/reset/request.go
+++ b/handler/reset/request.go
@@ -10,6 +10,15 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/reset/request"
 )
 
+// Index godoc
+//
+//	@Tags					reset
+//	@Summary			Request Reset Password Token
+//	@Description	Send Reset password token to email
+//	@Produce			json
+//	@Param				data body request.RequestRequestPayload true "payload"
+//	@Success			200	{object}	web.BaseResponse "Login Success"
+//	@Router				/reset/request [post]
 func (rs ResetHandlerImpl) Request(w http.ResponseWriter, r *http.Request) {
 	payload := request.RequestRequestPayload{}
 	validate := validator.New()
@@ -62,4 +71,4 @@ func (rs ResetHandlerImpl) Request(w http.ResponseWriter, r *http.Request) {
 
 	responsePayload := rs.WrapperUtil.SuccessResponseWrap(nil)
 	rs.HttpUtil.WriteSuccessJson(w, responsePayload)
-}
\ No newline at end of file
+}
diff --git a/handler/reset/validate.go b/handler/reset/validate.go
index 19d48dbfdd2e9cf42e5c874779a1f03c8db100f9..1bfa4dec36c2c1f3f2d2ff94110c80070906c3db 100644
--- a/handler/reset/validate.go
+++ b/handler/reset/validate.go
@@ -9,6 +9,15 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/reset/validate"
 )
 
+// Index godoc
+//
+//	@Tags					reset
+//	@Summary			Request Reset Password Token
+//	@Description	Send Reset password token to email
+//	@Produce			json
+//	@Param			  Authorization header string true "Email validation token"
+//	@Success			200	{object}	web.BaseResponse "Login Success"
+//	@Router				/reset/validate [get]
 func (rs ResetHandlerImpl) Validate(w http.ResponseWriter, r *http.Request) {
 	payload := validate.ValidateRequestPayload{}
 	validateTokenHeader := r.Header.Get("Authorization")
@@ -54,4 +63,4 @@ func (rs ResetHandlerImpl) Validate(w http.ResponseWriter, r *http.Request) {
 	responsePayload := rs.WrapperUtil.SuccessResponseWrap(nil)
 	rs.HttpUtil.WriteSuccessJson(w, responsePayload)
 
-}
\ No newline at end of file
+}
diff --git a/model/domain/cache/cache.go b/model/domain/cache/cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc0d3361389b8dc39be807a61dbe54d890fa5577
--- /dev/null
+++ b/model/domain/cache/cache.go
@@ -0,0 +1,60 @@
+package cache
+
+import "fmt"
+
+type String struct {
+	Key             Key
+	Value           string
+	ExpiryInMinutes int
+}
+
+type Hash struct {
+	Key             Key
+	Values          []Value
+	ExpiryInMinutes int
+}
+
+type Key struct {
+	Hash string
+	Id   string
+}
+
+type Value struct {
+	Field string
+	Store string
+}
+
+func (c *Hash) AppendValue(value Value) *Hash {
+	c.Values = append(c.Values, value)
+	return c
+}
+
+func NewKey(hash string, id string) *Key {
+	return &Key{hash, id}
+}
+
+func (k Key) String() string {
+	return fmt.Sprintf("%s:%s", k.Hash, k.Id)
+}
+
+func NewValue(field string, store string) *Value {
+	return &Value{field, store}
+}
+
+func NewString(key Key, value string, expiryInMinutes int) *String {
+	return &String{key, value, expiryInMinutes}
+}
+
+func NewHash(key Key, initValue Value, expiryInMinutes int) *Hash {
+	return &Hash{key, []Value{initValue}, expiryInMinutes}
+}
+
+func (c *Hash) Slice() []interface{} {
+	slice := make([]interface{}, len(c.Values)*2+1)
+	slice[0] = c.Key.String()
+	for i := range make([]int, len(c.Values)) {
+		slice[i*2+1] = c.Values[i].Field
+		slice[i*2+2] = c.Values[i].Store
+	}
+	return slice
+}
diff --git a/model/domain/user/verification.go b/model/domain/user/verification.go
new file mode 100644
index 0000000000000000000000000000000000000000..1d0d04925c079c1f18de8fdb125595aa6ee937f7
--- /dev/null
+++ b/model/domain/user/verification.go
@@ -0,0 +1,7 @@
+package user
+
+type EmailData struct {
+	Email   string
+	BaseUrl string
+	Token   string
+}
diff --git a/model/web/auth/verification/request.go b/model/web/auth/verification/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..52b9cbc8b19133a25aca771ca96f4633e07deafa
--- /dev/null
+++ b/model/web/auth/verification/request.go
@@ -0,0 +1,8 @@
+package verification
+
+// Email Verification Request Payload
+// @Description Information that should be passed when request verify
+type VerificationRequestPayload struct {
+	// User Email
+	Email string `json:"email" validate:"required,email" example:"someone@example.com"`
+}
diff --git a/provider/di.go b/provider/di.go
index 51e100b166816bee6b5d9c23147f9470831a1b9f..e4e73e3c160c589defec01bf93bee94ab81d93bc 100644
--- a/provider/di.go
+++ b/provider/di.go
@@ -5,6 +5,7 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/provider/db"
 	"gitlab.informatika.org/ocw/ocw-backend/provider/mail"
 	"gitlab.informatika.org/ocw/ocw-backend/provider/mail/smtp"
+	"gitlab.informatika.org/ocw/ocw-backend/provider/redis"
 )
 
 var ProviderTestSet = wire.NewSet(
@@ -14,6 +15,10 @@ var ProviderTestSet = wire.NewSet(
 
 	wire.Bind(new(mail.MailQueue), new(*mail.MailQueueImpl)),
 	wire.Bind(new(mail.MailProvider), new(*smtp.SmtpMailProvider)),
+
+	// Redis utility
+	wire.Bind(new(redis.Redis), new(*redis.RedisImpl)),
+	redis.NewRedisEnv,
 )
 
 var ProviderSet = wire.NewSet(
diff --git a/provider/mail/smtp/smtp.go b/provider/mail/smtp/smtp.go
index dd2873a17083a7381fa3009679552d7dbd121590..40053da847885d83306541305c303d3aed8b0568 100644
--- a/provider/mail/smtp/smtp.go
+++ b/provider/mail/smtp/smtp.go
@@ -30,6 +30,7 @@ func (s SmtpMailProvider) Send(to []string, subject string, message string) erro
 	payload := fmt.Sprintf(
 		"To: %s\r\n"+
 			"Subject: %s\r\n"+
+			"Content-Type: text/html; charset=UTF-8\r\n"+
 			"\r\n%s\r\n",
 		to, subject, message,
 	)
diff --git a/provider/redis/cache.go b/provider/redis/cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..9d656ae2093191e41cdd8666e7f20f5b2c185a92
--- /dev/null
+++ b/provider/redis/cache.go
@@ -0,0 +1,84 @@
+package redis
+
+import (
+	"fmt"
+	"os"
+	"runtime/debug"
+	"strings"
+	"time"
+
+	"github.com/gomodule/redigo/redis"
+	"gitlab.informatika.org/ocw/ocw-backend/service/logger"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
+)
+
+type RedisImpl struct {
+	pool *redis.Pool
+}
+
+func resolver(log logger.Logger) {
+	if rec := recover(); rec != nil {
+		log.Error("Some panic occured when processing request:")
+		log.Error(fmt.Sprint(rec))
+		log.Error("")
+
+		log.Error("Stack Trace:")
+		stacks := strings.Split(string(debug.Stack()), "\n")
+
+		for _, val := range stacks {
+			log.Error(val)
+		}
+
+		os.Exit(-1)
+	}
+}
+
+func NewRedisEnv(
+	env *env.Environment,
+	log logger.Logger,
+) (*RedisImpl, error) {
+	return &RedisImpl{
+		&redis.Pool{
+			MaxIdle:     3,
+			IdleTimeout: 240 * time.Second,
+			Dial: func() (redis.Conn, error) {
+				defer resolver(log)
+				conn, err := redis.Dial("tcp", env.RedisConnection+":"+env.RedisPort)
+
+				if err != nil {
+					log.Warning("failed connect to redis server: tcp " + env.RedisConnection + ":" + env.RedisPort)
+					log.Warning(err.Error())
+
+					return nil, err
+				}
+
+				if env.RedisUseAuth {
+					if _, err := conn.Do("AUTH", env.RedisUsername, env.RedisPassword); err != nil {
+						conn.Close()
+
+						log.Warning("failed connect to redis server: authentication failed")
+
+						return nil, err
+					}
+				}
+
+				return conn, err
+			},
+			TestOnBorrow: func(c redis.Conn, t time.Time) error {
+				if time.Since(t) < time.Minute {
+					return nil
+				}
+				_, err := c.Do("PING")
+
+				if err != nil {
+					log.Warning("redis server is down")
+				}
+
+				return err
+			},
+		}}, nil
+}
+
+func (r RedisImpl) Pool() *redis.Pool {
+	return r.pool
+}
diff --git a/provider/redis/type.go b/provider/redis/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..c2b4e2c661a7ce9851e8963e107c7fd063f04c26
--- /dev/null
+++ b/provider/redis/type.go
@@ -0,0 +1,7 @@
+package redis
+
+import "github.com/gomodule/redigo/redis"
+
+type Redis interface {
+	Pool() *redis.Pool
+}
\ No newline at end of file
diff --git a/repository/cache/cache.go b/repository/cache/cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..7f554ed0dfe7148112096c8e58426fddc44fa900
--- /dev/null
+++ b/repository/cache/cache.go
@@ -0,0 +1,107 @@
+package cache
+
+import (
+	"github.com/gomodule/redigo/redis"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/cache"
+	rd "gitlab.informatika.org/ocw/ocw-backend/provider/redis"
+)
+
+type CacheRepositoryImpl struct {
+	pool *redis.Pool
+}
+
+func New(
+	cache rd.Redis,
+) *CacheRepositoryImpl {
+	return &CacheRepositoryImpl{cache.Pool()}
+}
+
+func (c CacheRepositoryImpl) Get(key cache.Key) (string, error) {
+	conn := c.pool.Get()
+	defer conn.Close()
+
+	value, err := redis.String(conn.Do("GET", key))
+
+	if err != nil {
+		return "", err
+	}
+
+	return value, nil
+}
+
+func (c CacheRepositoryImpl) Delete(key string) error {
+	conn := c.pool.Get()
+	defer conn.Close()
+
+	_, err := conn.Do("DEL", key)
+	return err
+}
+
+func (c CacheRepositoryImpl) Set(str cache.String) error {
+	conn := c.pool.Get()
+	defer conn.Close()
+
+	_, err := conn.Do("SET", str.Key, str.Value)
+
+	if err != nil {
+		return err
+	}
+
+	if str.ExpiryInMinutes > 0 {
+		_, err = conn.Do("EXPIRE", str.Key, str.ExpiryInMinutes*60)
+
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (c CacheRepositoryImpl) HGet(cache cache.Hash, field string) (string, error) {
+	conn := c.pool.Get()
+	defer conn.Close()
+
+	value, err := redis.String(conn.Do("HGET", cache.Key.String(), field))
+
+	if err != nil {
+		return "", err
+	}
+
+	return value, nil
+}
+
+func (c CacheRepositoryImpl) HGetAll(cache cache.Hash) (map[string]string, error) {
+	conn := c.pool.Get()
+	defer conn.Close()
+
+	value, err := redis.StringMap(conn.Do("HGETALL", cache.Key.String()))
+
+	if err != nil {
+		return nil, err
+	}
+
+	return value, nil
+}
+
+func (c CacheRepositoryImpl) HSet(cache cache.Hash) error {
+	conn := c.pool.Get()
+	defer conn.Close()
+
+	slice := cache.Slice()
+	_, err := conn.Do("HSET", slice...)
+
+	if err != nil {
+		return err
+	}
+
+	if cache.ExpiryInMinutes > 0 {
+		_, err = conn.Do("EXPIRE", cache.Key, cache.ExpiryInMinutes*60)
+
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/repository/cache/type.go b/repository/cache/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..dca193bc9959ce6ecde1cb35e75e021e7a20d382
--- /dev/null
+++ b/repository/cache/type.go
@@ -0,0 +1,14 @@
+package cache
+
+import (
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/cache"
+)
+
+type CacheRepository interface {
+	Get(key cache.Key) (string, error)
+	Set(str cache.String) error
+	Delete(key string) error
+	HGet(cache cache.Hash, field string) (string, error)
+	HGetAll(cache cache.Hash) (map[string]string, error)
+	HSet(cache cache.Hash) error
+}
diff --git a/repository/di.go b/repository/di.go
index ccec9594f14c22bb4d6444b2edbb26f54b8a90f3..3a1d5b9646290496eabde8604276da5c39f15332 100644
--- a/repository/di.go
+++ b/repository/di.go
@@ -3,12 +3,17 @@ package repository
 import (
 	"github.com/google/wire"
 	"gitlab.informatika.org/ocw/ocw-backend/repository/user"
+	"gitlab.informatika.org/ocw/ocw-backend/repository/cache"
 )
 
 var RepositoryBasicSet = wire.NewSet(
 	// User Repository
 	user.New,
 	wire.Bind(new(user.UserRepository), new(*user.UserRepositoryImpl)),
+
+	// Cache Repository
+	cache.New,
+	wire.Bind(new(cache.CacheRepository), new(*cache.CacheRepositoryImpl)),
 )
 
 var RepositorySet = wire.NewSet(
diff --git a/routes/auth/route.go b/routes/auth/route.go
index d7ea811b69fd604111f9a013b7821a10ef005619..02205f37e1215d7ff63512613a7a7117f05edb7c 100644
--- a/routes/auth/route.go
+++ b/routes/auth/route.go
@@ -14,5 +14,6 @@ func (ar AuthRoutes) Register(r chi.Router) {
 		r.Post("/login", ar.AuthHandler.Login)
 		r.Post("/refresh", ar.AuthHandler.Refresh)
 		r.Post("/register", ar.AuthHandler.Register)
+		r.Post("/verify", ar.AuthHandler.EmailVerify)
 	})
 }
diff --git a/routes/reset/route.go b/routes/reset/route.go
index 46b32e0f9f389bfe8f3ff730d2c683225dfcee91..1d4522b1e57b4c91d47007d84013d03abd0419cb 100644
--- a/routes/reset/route.go
+++ b/routes/reset/route.go
@@ -13,6 +13,6 @@ func (rr ResetRoutes) Register(r chi.Router) {
 	r.Route("/reset", func(r chi.Router) {
 		r.Post("/request", rr.ResetHandler.Request)
 		r.Post("/confirm", rr.ResetHandler.Confirm)
-		r.Post("/validate", rr.ResetHandler.Validate)
+		r.Get("/validate", rr.ResetHandler.Validate)
 	})
 }
diff --git a/service/auth/register.go b/service/auth/register.go
index 6eb423b39900bb2502234c2558b0414a4e50f10c..b7941c50ba99c941bd7810ad82f3c3862f9ef26c 100644
--- a/service/auth/register.go
+++ b/service/auth/register.go
@@ -6,9 +6,15 @@ import (
 )
 
 func (auth AuthServiceImpl) Register(payload register.RegisterRequestPayload) error {
-	err := auth.UserRepository.Add(user.User{
+	hashedPassword, err := auth.Hash(payload.Password)
+
+	if err != nil {
+		return err
+	}
+
+	err = auth.UserRepository.Add(user.User{
 		Email:       payload.Email,
-		Password:    payload.Password,
+		Password:    hashedPassword,
 		Name:        payload.Name,
 		Role:        user.Student,
 		IsActivated: false,
diff --git a/service/auth/type.go b/service/auth/type.go
index 2503726fe0562e9b2b26544dabae60017d13c386..937521d8a9aacc0e95cfd1061e6cbc627b545686 100644
--- a/service/auth/type.go
+++ b/service/auth/type.go
@@ -4,10 +4,12 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/login"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/refresh"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/register"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/verification"
 )
 
 type AuthService interface {
 	Login(payload login.LoginRequestPayload) (*login.LoginResponsePayload, error)
 	Refresh(payload refresh.RefreshRequestPayload) (*refresh.RefreshResponsePayload, error)
 	Register(payload register.RegisterRequestPayload) error
+	VerifyEmail(payload verification.VerificationRequestPayload) error
 }
diff --git a/service/auth/verify.go b/service/auth/verify.go
new file mode 100644
index 0000000000000000000000000000000000000000..a8462cec12a42a999b2910ef831768c8f6f4826d
--- /dev/null
+++ b/service/auth/verify.go
@@ -0,0 +1,7 @@
+package auth
+
+import "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/verification"
+
+func (auth AuthServiceImpl) VerifyEmail(payload verification.VerificationRequestPayload) error {
+	return nil
+}
diff --git a/service/reset/confirm.go b/service/reset/confirm.go
index 426af52f5514eec16fd0a6c4553bd817974177fd..66a42c1eef7843a9888fb9e4a5c0d34c17d9d86b 100644
--- a/service/reset/confirm.go
+++ b/service/reset/confirm.go
@@ -1,11 +1,47 @@
 package reset
 
 import (
-	// "gitlab.informatika.org/ocw/ocw-backend/model/domain/user"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/cache"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/reset/confirm"
 )
 
 func (rs ResetServiceImpl) Confirm(payload confirm.ConfirmRequestPayload) error {
-	// TODO replace dummy
+	// Double Layered Security
+	// Validate Token
+	_, err := rs.TokenUtil.Validate(payload.ConfirmToken, token.Access)
+
+	if err != nil {
+		return web.NewResponseErrorFromError(err, web.TokenError)
+	}
+
+	// Check if Token is Cached
+	email, err := rs.CacheRepository.Get(*cache.NewKey(rs.RedisPrefixKey+"resetPassword", payload.ConfirmToken))
+
+	if err != nil {
+		return web.NewResponseErrorFromError(err, web.LinkNotAvailable)
+	}
+
+	// Reset the password
+	user, err := rs.UserRepository.Get(email)
+
+	if err != nil {
+		return err
+	}
+
+	hashedPassword, err := rs.Hash(payload.Password)
+
+	if err != nil {
+		return err
+	}
+
+	user.Password = hashedPassword
+	err = rs.UserRepository.Update(*user)
+
+	if err != nil {
+		return err
+	}
+
 	return nil
-}
\ No newline at end of file
+}
diff --git a/service/reset/impl.go b/service/reset/impl.go
index b9986e2509f4af7f8c0deb9053bf8d30640e4168..37c8ec7a35f7fef60c68047fc38946c5b16bfe13 100644
--- a/service/reset/impl.go
+++ b/service/reset/impl.go
@@ -1,17 +1,25 @@
 package reset
 
 import (
+	"gitlab.informatika.org/ocw/ocw-backend/provider/mail"
+	"gitlab.informatika.org/ocw/ocw-backend/repository/cache"
 	"gitlab.informatika.org/ocw/ocw-backend/repository/user"
+	"gitlab.informatika.org/ocw/ocw-backend/service/logger"
 	"gitlab.informatika.org/ocw/ocw-backend/service/verification"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/password"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/template"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/token"
 )
 
 type ResetServiceImpl struct {
 	user.UserRepository
+	cache.CacheRepository
 	password.PasswordUtil
 	*env.Environment
 	token.TokenUtil
 	verification.VerificationService
+	logger.Logger
+	mail.MailQueue
+	template.TemplateWritterBuilder
 }
diff --git a/service/reset/mail.go b/service/reset/mail.go
new file mode 100644
index 0000000000000000000000000000000000000000..7de41926aa002e9e4fd233804ed82c056777aa44
--- /dev/null
+++ b/service/reset/mail.go
@@ -0,0 +1,7 @@
+package reset
+
+type mailPayload struct {
+	BaseUrl string
+	Email   string
+	Token   string
+}
diff --git a/service/reset/request.go b/service/reset/request.go
index 05584e6a9bb3dcf0a7a78c4ec5e12e1c4502732d..0e386525cee3b36e95c0734ae3a9016218802068 100644
--- a/service/reset/request.go
+++ b/service/reset/request.go
@@ -1,11 +1,83 @@
 package reset
 
 import (
-	// "gitlab.informatika.org/ocw/ocw-backend/model/domain/user"
+	"errors"
+	"time"
+
+	"github.com/golang-jwt/jwt/v4"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	tokenModel "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
+	"gitlab.informatika.org/ocw/ocw-backend/provider/mail"
+	"gorm.io/gorm"
+
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/cache"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/reset/request"
 )
 
 func (rs ResetServiceImpl) Request(payload request.RequestRequestPayload) error {
-	// TODO replace dummy
+	// Fetch user data from email
+	user, err := rs.UserRepository.Get(payload.Email)
+
+	if err != nil {
+		var errorObj error
+
+		switch {
+		case errors.Is(err, gorm.ErrRecordNotFound):
+			errorObj = web.NewResponseError("Email was not found", web.EmailNotExist)
+		default:
+			errorObj = err
+		}
+
+		return errorObj
+	}
+
+	if !user.IsActivated {
+		return web.NewResponseError("user is not activated yet", web.InactiveUser)
+	}
+
+	// Mint JWT Token for 30 minutes
+	resetClaim := 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(rs.TokenAccessExpired*6) * time.Millisecond)),
+			Issuer:    rs.TokenIssuer,
+			IssuedAt:  jwt.NewNumericDate(time.Now()),
+		},
+	}
+
+	resetToken, err := rs.TokenUtil.Generate(resetClaim, rs.TokenUtil.DefaultMethod())
+	if err != nil {
+		return err
+	}
+
+	// Cache Website on Redis, TTL 30 mins
+	rs.CacheRepository.Set(*cache.NewString(*cache.NewKey(rs.RedisPrefixKey+"resetPassword", resetToken), payload.Email, 30))
+
+	// TODO: Send Reset Email
+	mailBuilder, err := rs.TemplateWritterBuilder.Get("reset-password.format.html")
+
+	if err != nil {
+		return err
+	}
+
+	mailData, err := mailBuilder.Write(&mailPayload{
+		BaseUrl: rs.FrontendBaseURL + rs.ResetPasswordPath,
+		Email:   user.Email,
+		Token:   resetToken,
+	})
+
+	if err != nil {
+		return err
+	}
+
+	rs.MailQueue.Send(mail.Mail{
+		To:      []string{user.Email},
+		Subject: "Reset Password",
+		Message: mailData,
+	})
+
 	return nil
-}
\ No newline at end of file
+}
diff --git a/service/reset/validate.go b/service/reset/validate.go
index 7cd09fe9bc83b23a2e4d0bbc0abf95fe1758b5cc..f1ae7028d7e0da998768720f97ff43adbf9f2c98 100644
--- a/service/reset/validate.go
+++ b/service/reset/validate.go
@@ -1,11 +1,27 @@
 package reset
 
 import (
-	// "gitlab.informatika.org/ocw/ocw-backend/model/domain/user"
+	"gitlab.informatika.org/ocw/ocw-backend/model/domain/cache"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web"
+	"gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token"
 	"gitlab.informatika.org/ocw/ocw-backend/model/web/reset/validate"
 )
 
 func (rs ResetServiceImpl) Validate(payload validate.ValidateRequestPayload) error {
-	// TODO replace dummy
+	// Double Layered Security
+	// Validate Token
+	_, err := rs.TokenUtil.Validate(payload.ValidateToken, token.Access)
+
+	if err != nil {
+		return web.NewResponseErrorFromError(err, web.TokenError)
+	}
+
+	// Check if Token is Cached
+	_, err = rs.CacheRepository.Get(*cache.NewKey(rs.RedisPrefixKey+"resetPassword", payload.ValidateToken))
+
+	if err != nil {
+		return web.NewResponseErrorFromError(err, web.LinkNotAvailable)
+	}
+
 	return nil
-}
\ No newline at end of file
+}
diff --git a/test/di.go b/test/di.go
index 9ce05c76ea2a7f5f97645eb7bde26d66fdb121f0..6e9f9ff3ac420b22ee453b42ef68835662842c43 100644
--- a/test/di.go
+++ b/test/di.go
@@ -18,6 +18,7 @@ import (
 	"gitlab.informatika.org/ocw/ocw-backend/utils"
 
 	"gitlab.informatika.org/ocw/ocw-backend/utils/env"
+	"gitlab.informatika.org/ocw/ocw-backend/utils/template"
 )
 
 func CreateServer(logger logger.Logger, envTest *env.Environment) (*ApiTestPack, error) {
@@ -37,3 +38,11 @@ func CreateServer(logger logger.Logger, envTest *env.Environment) (*ApiTestPack,
 
 	return nil, nil
 }
+
+func CreateTemplateBuilder() template.TemplateWritterBuilder {
+	wire.Build(
+		utils.UtilSetTest,
+	)
+
+	return nil
+}
diff --git a/test/utils/template/template_test.go b/test/utils/template/template_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a6e783ba29460022a978183bf452afba00c86c54
--- /dev/null
+++ b/test/utils/template/template_test.go
@@ -0,0 +1,66 @@
+package template
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"gitlab.informatika.org/ocw/ocw-backend/test"
+)
+
+type testTemplate struct {
+	Name string
+	Url  string
+}
+
+func TestTemplate(t *testing.T) {
+	builder := test.CreateTemplateBuilder()
+	template, err := builder.Get("test.html")
+
+	assert.Nil(t, err)
+
+	if err != nil {
+		return
+	}
+
+	t.Run("BuilderSuccess", func(t *testing.T) {
+		data := &testTemplate{
+			Name: "Bayu",
+			Url:  "Hehe",
+		}
+
+		result, err := template.Write(data)
+
+		assert.Nil(t, err)
+		assert.Equal(t, result, "<h1>Hello, Bayu. This is your Hehe</h1>")
+	})
+
+	t.Run("ShouldBeReuseObject", func(t *testing.T) {
+		templateInner, _ := builder.Get("test.html")
+
+		assert.Equal(t, template, templateInner)
+	})
+
+	t.Run("HtmlShouldBeParsed", func(t *testing.T) {
+		data := &testTemplate{
+			Name: "<script>alert('hayoloh')</script>",
+			Url:  "Hehe",
+		}
+
+		result, err := template.Write(data)
+
+		assert.Nil(t, err)
+		assert.NotEqual(t, result, "<h1>Hello, <script>alert('hayoloh')</script>. This is your Hehe</h1>")
+	})
+
+	t.Run("URLShouldBeParsed", func(t *testing.T) {
+		data := &testTemplate{
+			Name: "Bayu",
+			Url:  "http://localhost:8080/?q=' AND 10 AND \"",
+		}
+
+		result, err := template.Write(data)
+
+		assert.Nil(t, err)
+		assert.Equal(t, result, "<h1>Hello, Bayu. This is your http://localhost:8080/?q=&#39; AND 10 AND &#34;</h1>")
+	})
+}
diff --git a/utils/di.go b/utils/di.go
index d6b65b8b8f725bf8c3da03f3ab776ef8aca15e90..3987842c6ebf7c6b1103f237a4c80160b5fd4da2 100644
--- a/utils/di.go
+++ b/utils/di.go
@@ -8,6 +8,7 @@ import (
 	"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/template"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/token"
 	"gitlab.informatika.org/ocw/ocw-backend/utils/wrapper"
 )
@@ -40,6 +41,10 @@ var UtilSetTest = wire.NewSet(
 	// Token utility
 	wire.Struct(new(token.TokenUtilImpl), "*"),
 	wire.Bind(new(token.TokenUtil), new(*token.TokenUtilImpl)),
+
+	// Template Writter
+	template.NewBuilder,
+	wire.Bind(new(template.TemplateWritterBuilder), new(*template.TemplateWritterBuilderImpl)),
 )
 
 var UtilSet = wire.NewSet(
diff --git a/utils/env/env.go b/utils/env/env.go
index a55416e52bf462568666b3b4de1afc886d97fa44..26bd1d30315d75f149114037acbdac94e11e9e3b 100644
--- a/utils/env/env.go
+++ b/utils/env/env.go
@@ -36,6 +36,17 @@ type Environment struct {
 	SmtpPassword string `env:"SMTP_PASSWORD"`
 	SmtpServer   string `env:"SMTP_SERVER"`
 	SmtpPort     int    `env:"SMTP_PORT" envDefault:"25"`
+
+	FrontendBaseURL       string `env:"FE_BASE_URL"`
+	ResetPasswordPath     string `env:"RESET_PASSWORD_PATH" envDefault:"/reset"`
+	EmailVerificationPath string `env:"EMAIL_VERIFICATION_PATH" envDefault:"/verification"`
+
+	RedisConnection string `env:"REDIS_STRING"`
+	RedisPort       string `env:"REDIS_PORT" envDefault:"6379"`
+	RedisUsername   string `env:"REDIS_USERNAME"`
+	RedisPassword   string `env:"REDIS_PASSWORD"`
+	RedisUseAuth    bool   `env:"REDIS_USE_AUTH" envDefault:"false"`
+	RedisPrefixKey  string `env:"REDIS_PREFIX_KEY" envDefault:"app:"`
 }
 
 func New() (*Environment, error) {
diff --git a/utils/res/data/email-verification.format.html b/utils/res/data/email-verification.format.html
new file mode 100644
index 0000000000000000000000000000000000000000..a4a763e42aa997d4024843a5b3302be342e61a66
--- /dev/null
+++ b/utils/res/data/email-verification.format.html
@@ -0,0 +1,298 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+
+<head>
+  <!-- Compiled with Bootstrap Email version: 1.3.1 -->
+  <meta http-equiv="x-ua-compatible" content="ie=edge">
+  <meta name="x-apple-disable-message-reformatting">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <style type="text/css">
+    body,
+    table,
+    td {
+      font-family: Helvetica, Arial, sans-serif !important
+    }
+
+    .ExternalClass {
+      width: 100%
+    }
+
+    .ExternalClass,
+    .ExternalClass p,
+    .ExternalClass span,
+    .ExternalClass font,
+    .ExternalClass td,
+    .ExternalClass div {
+      line-height: 150%
+    }
+
+    a {
+      text-decoration: none
+    }
+
+    * {
+      color: inherit
+    }
+
+    a[x-apple-data-detectors],
+    u+#body a,
+    #MessageViewBody a {
+      color: inherit;
+      text-decoration: none;
+      font-size: inherit;
+      font-family: inherit;
+      font-weight: inherit;
+      line-height: inherit
+    }
+
+    img {
+      -ms-interpolation-mode: bicubic
+    }
+
+    table:not([class^=s-]) {
+      font-family: Helvetica, Arial, sans-serif;
+      mso-table-lspace: 0pt;
+      mso-table-rspace: 0pt;
+      border-spacing: 0px;
+      border-collapse: collapse
+    }
+
+    table:not([class^=s-]) td {
+      border-spacing: 0px;
+      border-collapse: collapse
+    }
+
+    @media screen and (max-width: 600px) {
+
+      .w-full,
+      .w-full>tbody>tr>td {
+        width: 100% !important
+      }
+
+      *[class*=s-lg-]>tbody>tr>td {
+        font-size: 0 !important;
+        line-height: 0 !important;
+        height: 0 !important
+      }
+
+      .s-2>tbody>tr>td {
+        font-size: 8px !important;
+        line-height: 8px !important;
+        height: 8px !important
+      }
+
+      .s-5>tbody>tr>td {
+        font-size: 20px !important;
+        line-height: 20px !important;
+        height: 20px !important
+      }
+
+      .s-10>tbody>tr>td {
+        font-size: 40px !important;
+        line-height: 40px !important;
+        height: 40px !important
+      }
+    }
+  </style>
+</head>
+
+<body class="bg-light"
+  style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;"
+  bgcolor="#f7fafc">
+  <table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0"
+    style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;"
+    bgcolor="#f7fafc">
+    <tbody>
+      <tr>
+        <td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
+          <table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
+            <tbody>
+              <tr>
+                <td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
+                  <!--[if (gte mso 9)|(IE)]>
+                      <table align="center" role="presentation">
+                        <tbody>
+                          <tr>
+                            <td width="600">
+                    <![endif]-->
+                  <table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                    style="width: 100%; max-width: 600px; margin: 0 auto;">
+                    <tbody>
+                      <tr>
+                        <td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
+                          <table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                            style="width: 100%;" width="100%">
+                            <tbody>
+                              <tr>
+                                <td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;"
+                                  align="left" width="100%" height="40">
+                                  &#160;
+                                </td>
+                              </tr>
+                            </tbody>
+                          </table>
+                          <table class="card" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                            style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;"
+                            bgcolor="#ffffff">
+                            <tbody>
+                              <tr>
+                                <td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left"
+                                  bgcolor="#ffffff">
+                                  <table class="card-body" role="presentation" border="0" cellpadding="0"
+                                    cellspacing="0" style="width: 100%;">
+                                    <tbody>
+                                      <tr>
+                                        <td
+                                          style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 20px;"
+                                          align="left">
+                                          <h1 class="h3"
+                                            style="padding-top: 0; padding-bottom: 0; font-weight: 500; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;"
+                                            align="left">Email Verifikasi</h1>
+                                          <table class="s-2 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 8px; font-size: 8px; width: 100%; height: 8px; margin: 0;"
+                                                  align="left" width="100%" height="8">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="hr" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;"
+                                                  align="left">
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <div class="space-y-3">
+                                            <p class="text-gray-700"
+                                              style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;"
+                                              align="left">
+                                              Anda telah melakukan pendaftaran pada OCW ITB menggunakan email {{ .Email
+                                              }}. Untuk menyelesaikan proses
+                                              pendaftaran, anda dapat menekan tombol dibawah ini.
+                                            </p>
+                                          </div>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="hr" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;"
+                                                  align="left">
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="btn btn-primary" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0"
+                                            style="border-radius: 6px; border-collapse: separate !important;">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 24px; font-size: 16px; border-radius: 6px; margin: 0;"
+                                                  align="center" bgcolor="#0d6efd">
+                                                  <a href="{{ .BaseUrl }}/?token={{ .Token }}" target="_blank"
+                                                    style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: normal; white-space: nowrap; background-color: #0d6efd; padding: 8px 12px; border: 1px solid #0d6efd;">Reset
+                                                    Password</a>
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                        </td>
+                                      </tr>
+                                    </tbody>
+                                  </table>
+                                </td>
+                              </tr>
+                            </tbody>
+                          </table>
+                          <table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                            style="width: 100%;" width="100%">
+                            <tbody>
+                              <tr>
+                                <td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;"
+                                  align="left" width="100%" height="40">
+                                  &#160;
+                                </td>
+                              </tr>
+                            </tbody>
+                          </table>
+                        </td>
+                      </tr>
+                    </tbody>
+                  </table>
+                  <!--[if (gte mso 9)|(IE)]>
+                    </td>
+                  </tr>
+                </tbody>
+              </table>
+                    <![endif]-->
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/utils/res/data/reset-password.format.html b/utils/res/data/reset-password.format.html
new file mode 100644
index 0000000000000000000000000000000000000000..80ec4d4f8b7de170e4d8388e23008efc4449cb19
--- /dev/null
+++ b/utils/res/data/reset-password.format.html
@@ -0,0 +1,297 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+
+<head>
+  <!-- Compiled with Bootstrap Email version: 1.3.1 -->
+  <meta http-equiv="x-ua-compatible" content="ie=edge">
+  <meta name="x-apple-disable-message-reformatting">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <style type="text/css">
+    body,
+    table,
+    td {
+      font-family: Helvetica, Arial, sans-serif !important
+    }
+
+    .ExternalClass {
+      width: 100%
+    }
+
+    .ExternalClass,
+    .ExternalClass p,
+    .ExternalClass span,
+    .ExternalClass font,
+    .ExternalClass td,
+    .ExternalClass div {
+      line-height: 150%
+    }
+
+    a {
+      text-decoration: none
+    }
+
+    * {
+      color: inherit
+    }
+
+    a[x-apple-data-detectors],
+    u+#body a,
+    #MessageViewBody a {
+      color: inherit;
+      text-decoration: none;
+      font-size: inherit;
+      font-family: inherit;
+      font-weight: inherit;
+      line-height: inherit
+    }
+
+    img {
+      -ms-interpolation-mode: bicubic
+    }
+
+    table:not([class^=s-]) {
+      font-family: Helvetica, Arial, sans-serif;
+      mso-table-lspace: 0pt;
+      mso-table-rspace: 0pt;
+      border-spacing: 0px;
+      border-collapse: collapse
+    }
+
+    table:not([class^=s-]) td {
+      border-spacing: 0px;
+      border-collapse: collapse
+    }
+
+    @media screen and (max-width: 600px) {
+
+      .w-full,
+      .w-full>tbody>tr>td {
+        width: 100% !important
+      }
+
+      *[class*=s-lg-]>tbody>tr>td {
+        font-size: 0 !important;
+        line-height: 0 !important;
+        height: 0 !important
+      }
+
+      .s-2>tbody>tr>td {
+        font-size: 8px !important;
+        line-height: 8px !important;
+        height: 8px !important
+      }
+
+      .s-5>tbody>tr>td {
+        font-size: 20px !important;
+        line-height: 20px !important;
+        height: 20px !important
+      }
+
+      .s-10>tbody>tr>td {
+        font-size: 40px !important;
+        line-height: 40px !important;
+        height: 40px !important
+      }
+    }
+  </style>
+</head>
+
+<body class="bg-light"
+  style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;"
+  bgcolor="#f7fafc">
+  <table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0"
+    style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;"
+    bgcolor="#f7fafc">
+    <tbody>
+      <tr>
+        <td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
+          <table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
+            <tbody>
+              <tr>
+                <td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
+                  <!--[if (gte mso 9)|(IE)]>
+                      <table align="center" role="presentation">
+                        <tbody>
+                          <tr>
+                            <td width="600">
+                    <![endif]-->
+                  <table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                    style="width: 100%; max-width: 600px; margin: 0 auto;">
+                    <tbody>
+                      <tr>
+                        <td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
+                          <table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                            style="width: 100%;" width="100%">
+                            <tbody>
+                              <tr>
+                                <td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;"
+                                  align="left" width="100%" height="40">
+                                  &#160;
+                                </td>
+                              </tr>
+                            </tbody>
+                          </table>
+                          <table class="card" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                            style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;"
+                            bgcolor="#ffffff">
+                            <tbody>
+                              <tr>
+                                <td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left"
+                                  bgcolor="#ffffff">
+                                  <table class="card-body" role="presentation" border="0" cellpadding="0"
+                                    cellspacing="0" style="width: 100%;">
+                                    <tbody>
+                                      <tr>
+                                        <td
+                                          style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 20px;"
+                                          align="left">
+                                          <h1 class="h3"
+                                            style="padding-top: 0; padding-bottom: 0; font-weight: 500; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;"
+                                            align="left">Reset Password</h1>
+                                          <table class="s-2 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 8px; font-size: 8px; width: 100%; height: 8px; margin: 0;"
+                                                  align="left" width="100%" height="8">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="hr" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;"
+                                                  align="left">
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <div class="space-y-3">
+                                            <p class="text-gray-700"
+                                              style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;"
+                                              align="left">
+                                              Anda telah mengirimkan reset password untuk email {{ .Email }}. Untuk
+                                              melakukan reset password tersebut, anda dapat menekan tombol dibawah ini.
+                                            </p>
+                                          </div>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="hr" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;"
+                                                  align="left">
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="s-5 w-full" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0" style="width: 100%;" width="100%">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;"
+                                                  align="left" width="100%" height="20">
+                                                  &#160;
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                          <table class="btn btn-primary" role="presentation" border="0" cellpadding="0"
+                                            cellspacing="0"
+                                            style="border-radius: 6px; border-collapse: separate !important;">
+                                            <tbody>
+                                              <tr>
+                                                <td
+                                                  style="line-height: 24px; font-size: 16px; border-radius: 6px; margin: 0;"
+                                                  align="center" bgcolor="#0d6efd">
+                                                  <a href="{{ .BaseUrl }}/?token={{ .Token }}" target="_blank"
+                                                    style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: normal; white-space: nowrap; background-color: #0d6efd; padding: 8px 12px; border: 1px solid #0d6efd;">Reset
+                                                    Password</a>
+                                                </td>
+                                              </tr>
+                                            </tbody>
+                                          </table>
+                                        </td>
+                                      </tr>
+                                    </tbody>
+                                  </table>
+                                </td>
+                              </tr>
+                            </tbody>
+                          </table>
+                          <table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0"
+                            style="width: 100%;" width="100%">
+                            <tbody>
+                              <tr>
+                                <td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;"
+                                  align="left" width="100%" height="40">
+                                  &#160;
+                                </td>
+                              </tr>
+                            </tbody>
+                          </table>
+                        </td>
+                      </tr>
+                    </tbody>
+                  </table>
+                  <!--[if (gte mso 9)|(IE)]>
+                    </td>
+                  </tr>
+                </tbody>
+              </table>
+                    <![endif]-->
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/utils/res/data/test.html b/utils/res/data/test.html
new file mode 100644
index 0000000000000000000000000000000000000000..8d432ede97e9a3ddad9b5fcbbfefaece69f7fdb4
--- /dev/null
+++ b/utils/res/data/test.html
@@ -0,0 +1 @@
+<h1>Hello, {{ .Name }}. This is your {{ .Url }}</h1>
\ No newline at end of file
diff --git a/utils/res/embed.go b/utils/res/embed.go
index 598ad06c4a4d25269165e6676bfb2d127df69fd9..19f072892de5c8faf0a23d205dc00caaa5be0425 100644
--- a/utils/res/embed.go
+++ b/utils/res/embed.go
@@ -2,6 +2,7 @@ package res
 
 import (
 	"embed"
+	"io/fs"
 )
 
 //go:embed data/*
@@ -13,6 +14,9 @@ func (EmbedResources) GetBytesResource(path string) ([]byte, error) {
 	return data.ReadFile("data/" + path)
 }
 
+func (EmbedResources) GetFile(path string) (fs.File, error) {
+	return data.Open("data/" + path)
+}
 
 func (EmbedResources) GetStringResource(path string) (string, error) {
 	content, err := data.ReadFile("data/" + path)
@@ -22,4 +26,4 @@ func (EmbedResources) GetStringResource(path string) (string, error) {
 	}
 
 	return string(content), nil
-}
\ No newline at end of file
+}
diff --git a/utils/res/res.go b/utils/res/res.go
index 387823d3a50c02d2a0f8630ae5e0c273ec5d29b1..f37288d570af4d2cf684bfb634769048d5001290 100644
--- a/utils/res/res.go
+++ b/utils/res/res.go
@@ -1,6 +1,9 @@
 package res
 
+import "io/fs"
+
 type Resource interface {
 	GetBytesResource(path string) ([]byte, error)
 	GetStringResource(path string) (string, error)
+	GetFile(path string) (fs.File, error)
 }
diff --git a/utils/template/builder.go b/utils/template/builder.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2317d5d72e83ff925ca019b8bc80cb860d10e44
--- /dev/null
+++ b/utils/template/builder.go
@@ -0,0 +1,5 @@
+package template
+
+type TemplateWritterBuilder interface {
+	Get(templatePath string) (TemplateWritter, error)
+}
diff --git a/utils/template/builder_impl.go b/utils/template/builder_impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..db5386d28fa19e33eeaedf8319ef5dd343e4cbcd
--- /dev/null
+++ b/utils/template/builder_impl.go
@@ -0,0 +1,30 @@
+package template
+
+import "gitlab.informatika.org/ocw/ocw-backend/utils/res"
+
+type TemplateWritterBuilderImpl struct {
+	res          res.Resource
+	templatePool map[string]TemplateWritter
+}
+
+func NewBuilder(res res.Resource) *TemplateWritterBuilderImpl {
+	return &TemplateWritterBuilderImpl{
+		res:          res,
+		templatePool: map[string]TemplateWritter{},
+	}
+}
+
+func (t *TemplateWritterBuilderImpl) Get(templatePath string) (TemplateWritter, error) {
+	if t.templatePool[templatePath] == nil {
+		template, err := NewTemplateWritterImpl(t.res, templatePath)
+
+		if err != nil {
+			return nil, err
+		}
+
+		t.templatePool[templatePath] = template
+		return template, nil
+	}
+
+	return t.templatePool[templatePath], nil
+}
diff --git a/utils/template/impl.go b/utils/template/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..36574a484ca5eb9ec816b5de635051ae2af4929d
--- /dev/null
+++ b/utils/template/impl.go
@@ -0,0 +1,39 @@
+package template
+
+import (
+	"bytes"
+	"html/template"
+
+	"gitlab.informatika.org/ocw/ocw-backend/utils/res"
+)
+
+type TemplateWritterImpl struct {
+	template *template.Template
+}
+
+func NewTemplateWritterImpl(res res.Resource, templatePath string) (*TemplateWritterImpl, error) {
+	file, err := res.GetStringResource(templatePath)
+
+	if err != nil {
+		return nil, err
+	}
+
+	templateData, err := template.New(templatePath).Parse(file)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &TemplateWritterImpl{templateData}, nil
+}
+
+func (m TemplateWritterImpl) Write(data interface{}) (string, error) {
+	buffer := bytes.NewBuffer([]byte{})
+	err := m.template.Execute(buffer, data)
+
+	if err != nil {
+		return "", err
+	}
+
+	return buffer.String(), nil
+}
diff --git a/utils/template/type.go b/utils/template/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..bd38384b0e32e125f951cd891d9040c8c817c84b
--- /dev/null
+++ b/utils/template/type.go
@@ -0,0 +1,5 @@
+package template
+
+type TemplateWritter interface {
+	Write(data interface{}) (string, error)
+}