This commit is contained in:
“xHuPo” 2025-06-17 14:46:09 +08:00
parent 01b8951dd5
commit 10ebc59ffb
17 changed files with 1087 additions and 238 deletions

View file

@ -96,6 +96,28 @@ func GetUserIDFromContext(ctx context.Context) (string, error) {
return userID, nil
}
// ValidateToken validates a JWT token and returns the claims
func ValidateToken(tokenString string, signingKey string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
// Validate signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(signingKey), nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}
// RequireAuth is a middleware that ensures a valid JWT token is present
func RequireAuth(signingKey string, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

73
auth/refresh.go Normal file
View file

@ -0,0 +1,73 @@
package auth
import (
"encoding/json"
"net/http"
"time"
"github.com/golang-jwt/jwt/v5"
)
// RefreshRequest represents the request body for token refresh
type RefreshRequest struct {
RefreshToken string `json:"refresh_token"`
}
// RefreshResponse represents the response body for token refresh
type RefreshResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
// HandleRefresh handles the token refresh request
func HandleRefresh(w http.ResponseWriter, r *http.Request, signingKey string, accessExpiry, refreshExpiry time.Duration) {
// Only accept POST requests
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Parse request body
var req RefreshRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
// Validate refresh token
claims := &Claims{}
token, err := jwt.ParseWithClaims(req.RefreshToken, claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrSignatureInvalid
}
return []byte(signingKey), nil
})
if err != nil || !token.Valid {
http.Error(w, "Invalid refresh token", http.StatusUnauthorized)
return
}
// Generate new access token
accessToken, err := GenerateToken(claims.UserID, signingKey, accessExpiry)
if err != nil {
http.Error(w, "Failed to generate access token", http.StatusInternalServerError)
return
}
// Generate new refresh token
refreshToken, err := GenerateToken(claims.UserID, signingKey, refreshExpiry)
if err != nil {
http.Error(w, "Failed to generate refresh token", http.StatusInternalServerError)
return
}
// Return new tokens
resp := RefreshResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}