@@ -93,7 +93,7 @@ type loginUserRequest struct {
9393 Password string `json:"password"`
9494}
9595
96- type loginUserResponse struct {
96+ type tokenResponse struct {
9797 AccessToken string `json:"access_token"`
9898 ExpiresIn int `json:"expires_in"`
9999}
@@ -113,13 +113,13 @@ func (h *UserHandler) LoginUser(w http.ResponseWriter, r *http.Request) {
113113
114114 accessToken , err := h .tokenService .GenerateAccessToken (user .ID )
115115 if err != nil {
116- WriteJSONError (w , http .StatusInternalServerError , "server_error " , "failed to generate access token" )
116+ WriteJSONError (w , http .StatusInternalServerError , "internal_error " , "failed to generate access token" )
117117 return
118118 }
119119
120120 refreshToken , err := h .tokenService .CreateRefreshToken (r .Context (), user .ID )
121121 if err != nil {
122- WriteJSONError (w , http .StatusInternalServerError , "server_error " , "failed to create refresh token" )
122+ WriteJSONError (w , http .StatusInternalServerError , "internal_error " , "failed to create refresh token" )
123123 return
124124 }
125125
@@ -138,7 +138,45 @@ func (h *UserHandler) LoginUser(w http.ResponseWriter, r *http.Request) {
138138 }
139139 http .SetCookie (w , cookie )
140140
141- resp := & loginUserResponse {
141+ resp := tokenResponse {
142+ AccessToken : accessToken ,
143+ ExpiresIn : int (h .cfg .AccessTokenTTL .Seconds ()),
144+ }
145+
146+ WriteJSON (w , http .StatusOK , resp )
147+ }
148+
149+ func (h * UserHandler ) RefreshAccessToken (w http.ResponseWriter , r * http.Request ) {
150+ cookie , err := r .Cookie ("refresh_token" )
151+ if err != nil {
152+ WriteJSONError (w , http .StatusUnauthorized , "invalid_request" , "refresh token missing" )
153+ return
154+ }
155+
156+ refreshTok := cookie .Value
157+
158+ accessToken , refreshToken , err := h .tokenService .RotateTokens (r .Context (), refreshTok )
159+ if err != nil {
160+ WriteJSONError (w , http .StatusUnauthorized , "invalid_grant" , "invalid or expired refresh token" )
161+ return
162+ }
163+
164+ cookie = & http.Cookie {
165+ Name : "refresh_token" ,
166+ Value : refreshToken .Plaintext ,
167+ Path : "/" ,
168+ HttpOnly : true ,
169+ Expires : refreshToken .ExpiresAt ,
170+ SameSite : http .SameSiteLaxMode ,
171+ }
172+ if h .cfg .Env == "production" {
173+ cookie .Secure = true
174+ } else {
175+ cookie .Secure = false
176+ }
177+ http .SetCookie (w , cookie )
178+
179+ resp := tokenResponse {
142180 AccessToken : accessToken ,
143181 ExpiresIn : int (h .cfg .AccessTokenTTL .Seconds ()),
144182 }
0 commit comments