Skip to content

Commit 20eee13

Browse files
committed
feat: add refresh token endpoint and related configurations
New Feature: -Implemented the RefreshToken method in the TokenUsecase to handle token refreshing. -The method retrieves the refresh token from the HTTP cookie, validates it, and generates a new access and refresh token pair. Details: -Extracts the refresh token from the cookie using c.Cookie. -Validates the refresh token and extracts claims using the GetClaims method. -Converts roles from []interface{} to []string for proper type handling. -Generates a new token pair using the GenerateToken method. Reason for Addition: -To provide functionality for refreshing expired access tokens while maintaining security through refresh tokens. -This is a critical feature for session management in the application. Benefits: -Enables secure token lifecycle management. -Improves user experience by allowing seamless token refresh without requiring re-login.
1 parent 0dda9bd commit 20eee13

File tree

12 files changed

+205
-16
lines changed

12 files changed

+205
-16
lines changed

src/api/handler/user.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,27 @@ import (
77
"github.com/naeemaei/golang-clean-web-api/api/dto"
88
"github.com/naeemaei/golang-clean-web-api/api/helper"
99
"github.com/naeemaei/golang-clean-web-api/config"
10+
"github.com/naeemaei/golang-clean-web-api/constant"
1011
"github.com/naeemaei/golang-clean-web-api/dependency"
1112
"github.com/naeemaei/golang-clean-web-api/usecase"
1213
)
1314

1415
type UsersHandler struct {
15-
usecase *usecase.UserUsecase
16-
otpUsecase *usecase.OtpUsecase
16+
usecase *usecase.UserUsecase
17+
otpUsecase *usecase.OtpUsecase
18+
tokenUsecase *usecase.TokenUsecase
19+
config *config.Config
1720
}
1821

1922
func NewUserHandler(cfg *config.Config) *UsersHandler {
23+
tokenUsecase := usecase.NewTokenUsecase(cfg)
2024
usecase := usecase.NewUserUsecase(cfg, dependency.GetUserRepository(cfg))
21-
return &UsersHandler{usecase: usecase}
25+
26+
return &UsersHandler{
27+
usecase: usecase,
28+
tokenUsecase: tokenUsecase,
29+
config: cfg,
30+
}
2231
}
2332

2433
// LoginByUsername godoc
@@ -47,6 +56,9 @@ func (h *UsersHandler) LoginByUsername(c *gin.Context) {
4756
return
4857
}
4958

59+
// Set the refresh token in a cookie
60+
c.SetCookie(constant.RefreshTokenCookieName, token.RefreshToken, int(h.config.JWT.RefreshTokenExpireDuration*60), "/", h.config.Server.Domin, true, true)
61+
5062
c.JSON(http.StatusCreated, helper.GenerateBaseResponse(token, true, helper.Success))
5163
}
5264

@@ -105,6 +117,9 @@ func (h *UsersHandler) RegisterLoginByMobileNumber(c *gin.Context) {
105117
return
106118
}
107119

120+
// Set the refresh token in a cookie
121+
c.SetCookie(constant.RefreshTokenCookieName, token.RefreshToken, int(h.config.JWT.RefreshTokenExpireDuration*60), "/", h.config.Server.Domin, true, true)
122+
108123
c.JSON(http.StatusCreated, helper.GenerateBaseResponse(token, true, helper.Success))
109124
}
110125

@@ -136,3 +151,25 @@ func (h *UsersHandler) SendOtp(c *gin.Context) {
136151
// TODO: Call internal SMS service
137152
c.JSON(http.StatusCreated, helper.GenerateBaseResponse(nil, true, helper.Success))
138153
}
154+
155+
// RefreshToken godoc
156+
// @Summary RefreshToken
157+
// @Description RefreshToken
158+
// @Tags Users
159+
// @Accept json
160+
// @Produce json
161+
// @Success 200 {object} helper.BaseHttpResponse "Success"
162+
// @Failure 400 {object} helper.BaseHttpResponse "Failed"
163+
// @Failure 401 {object} helper.BaseHttpResponse "Failed"
164+
// @Router /v1/users/refresh-token [get]
165+
func (h *UsersHandler) RefreshToken(c *gin.Context) {
166+
token, err := h.tokenUsecase.RefreshToken(c)
167+
if err != nil {
168+
c.AbortWithStatusJSON(helper.TranslateErrorToStatusCode(err),
169+
helper.GenerateBaseResponseWithError(nil, false, helper.InternalError, err))
170+
return
171+
}
172+
// Set the refresh token in a cookie
173+
c.SetCookie(constant.RefreshTokenCookieName, token.RefreshToken, int(h.config.JWT.RefreshTokenExpireDuration*60), "/", h.config.Server.Domin, true, true)
174+
c.JSON(http.StatusOK, helper.GenerateBaseResponse(token, true, helper.Success))
175+
}

src/api/router/users.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ func User(router *gin.RouterGroup, cfg *config.Config) {
1414
router.POST("/login-by-username", h.LoginByUsername)
1515
router.POST("/register-by-username", h.RegisterByUsername)
1616
router.POST("/login-by-mobile", h.RegisterLoginByMobileNumber)
17+
router.GET("/refresh-token", h.RefreshToken)
1718
}

src/config/config-development.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ server:
22
internalPort: 5005
33
externalPort: 5005
44
runMode: debug
5+
domin: localhost
56
logger:
67
filePath: ../logs/
78
encoding: json

src/config/config-docker.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ server:
22
internalPort: 5000
33
externalPort: 0
44
runMode: release
5+
domin: localhost
56
logger:
67
filePath: /app/logs/
78
encoding: json

src/config/config-production.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ server:
22
internalPort: 5010
33
externalPort: 5010
44
runMode: release
5+
domin: localhost
56
logger:
67
filePath: logs/
78
encoding: json

src/config/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ type Config struct {
2121
}
2222

2323
type ServerConfig struct {
24-
InternalPort string
25-
ExternalPort string
26-
RunMode string
24+
InternalPort string
25+
ExternalPort string
26+
RunMode string
27+
Domin string
2728
}
2829

2930
type LoggerConfig struct {
@@ -93,10 +94,10 @@ func GetConfig() *Config {
9394

9495
cfg, err := ParseConfig(v)
9596
envPort := os.Getenv("PORT")
96-
if envPort != ""{
97+
if envPort != "" {
9798
cfg.Server.ExternalPort = envPort
9899
log.Printf("Set external port from environment -> %s", cfg.Server.ExternalPort)
99-
}else{
100+
} else {
100101
cfg.Server.ExternalPort = cfg.Server.InternalPort
101102
log.Printf("Set external port from environment -> %s", cfg.Server.ExternalPort)
102103
}

src/constant/constanst.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ const (
1717
MobileNumberKey string = "MobileNumber"
1818
RolesKey string = "Roles"
1919
ExpireTimeKey string = "Exp"
20+
21+
// JWT
22+
RefreshTokenCookieName string = "refresh_token"
2023
)

src/docs/docs.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4661,6 +4661,41 @@ const docTemplate = `{
46614661
}
46624662
}
46634663
},
4664+
"/v1/users/refresh-token": {
4665+
"get": {
4666+
"description": "RefreshToken",
4667+
"consumes": [
4668+
"application/json"
4669+
],
4670+
"produces": [
4671+
"application/json"
4672+
],
4673+
"tags": [
4674+
"Users"
4675+
],
4676+
"summary": "RefreshToken",
4677+
"responses": {
4678+
"200": {
4679+
"description": "Success",
4680+
"schema": {
4681+
"$ref": "#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse"
4682+
}
4683+
},
4684+
"400": {
4685+
"description": "Failed",
4686+
"schema": {
4687+
"$ref": "#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse"
4688+
}
4689+
},
4690+
"401": {
4691+
"description": "Failed",
4692+
"schema": {
4693+
"$ref": "#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse"
4694+
}
4695+
}
4696+
}
4697+
}
4698+
},
46644699
"/v1/users/register-by-username": {
46654700
"post": {
46664701
"description": "RegisterByUsername",

src/docs/swagger.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4650,6 +4650,41 @@
46504650
}
46514651
}
46524652
},
4653+
"/v1/users/refresh-token": {
4654+
"get": {
4655+
"description": "RefreshToken",
4656+
"consumes": [
4657+
"application/json"
4658+
],
4659+
"produces": [
4660+
"application/json"
4661+
],
4662+
"tags": [
4663+
"Users"
4664+
],
4665+
"summary": "RefreshToken",
4666+
"responses": {
4667+
"200": {
4668+
"description": "Success",
4669+
"schema": {
4670+
"$ref": "#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse"
4671+
}
4672+
},
4673+
"400": {
4674+
"description": "Failed",
4675+
"schema": {
4676+
"$ref": "#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse"
4677+
}
4678+
},
4679+
"401": {
4680+
"description": "Failed",
4681+
"schema": {
4682+
"$ref": "#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse"
4683+
}
4684+
}
4685+
}
4686+
}
4687+
},
46534688
"/v1/users/register-by-username": {
46544689
"post": {
46554690
"description": "RegisterByUsername",

src/docs/swagger.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3821,6 +3821,29 @@ paths:
38213821
summary: LoginByUsername
38223822
tags:
38233823
- Users
3824+
/v1/users/refresh-token:
3825+
get:
3826+
consumes:
3827+
- application/json
3828+
description: RefreshToken
3829+
produces:
3830+
- application/json
3831+
responses:
3832+
"200":
3833+
description: Success
3834+
schema:
3835+
$ref: '#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse'
3836+
"400":
3837+
description: Failed
3838+
schema:
3839+
$ref: '#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse'
3840+
"401":
3841+
description: Failed
3842+
schema:
3843+
$ref: '#/definitions/github_com_naeemaei_golang-clean-web-api_api_helper.BaseHttpResponse'
3844+
summary: RefreshToken
3845+
tags:
3846+
- Users
38243847
/v1/users/register-by-username:
38253848
post:
38263849
consumes:

0 commit comments

Comments
 (0)