package api import ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" ) type renewAccessRequest struct { RefreshToken string `json:"refresh_token" binding:"required"` } type renewAccessResponse struct { AccesToken string `json:"access_token"` AccessTokenExpiresAt time.Time `json:"access_token_expires_at"` } func (server *Server) renewAccessToken(ctx *gin.Context) { var req renewAccessRequest if err := ctx.ShouldBindJSON(&req); err != nil { ctx.JSON(http.StatusBadRequest, ErrorResponse(err, "")) return } refreshPayload, err := server.TokenMaker.VerifyToken(req.RefreshToken) if err != nil { ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "")) return } session, err := server.Store.GetSession(ctx, int32(refreshPayload.UserID)) if err != nil { if err == pgx.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "")) return } if session.IsBlocked { err := fmt.Errorf("blocked session") ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "")) return } if session.Username != refreshPayload.Username { err := fmt.Errorf("incorrect session user") ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "")) return } if session.RefreshToken != req.RefreshToken { err := fmt.Errorf("mismatched session token") ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "")) return } if time.Now().After(refreshPayload.ExpiredAt) { err := fmt.Errorf("expired session") ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "")) return } user, err := server.Store.GetUser(ctx, refreshPayload.Username) if err != nil { if err == pgx.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "")) return } accessToken, accessPayload, err := server.TokenMaker.CreateToken( refreshPayload.Username, int(user.ID), server.Config.TokenDuration, ) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "")) return } res := renewAccessResponse{ AccesToken: accessToken, AccessTokenExpiresAt: accessPayload.ExpiredAt, } ctx.JSON(http.StatusOK, res) }