Files
back-hokm/internal/auth/jwt.go
T
2026-06-15 16:53:25 +03:30

91 lines
2.4 KiB
Go

package auth
import (
"context"
"errors"
"net/http"
"strconv"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
)
type ctxKey string
const userIDKey ctxKey = "uid"
// JWT صدور و اعتبارسنجی توکن stateless.
type JWT struct {
secret []byte
ttl time.Duration
}
func NewJWT(secret string, ttl time.Duration) *JWT {
return &JWT{secret: []byte(secret), ttl: ttl}
}
// Issue توکن برای کاربر صادر می‌کند. ability مثل "user" یا "admin".
func (j *JWT) Issue(userID int64, ability string) (string, error) {
claims := jwt.MapClaims{
"sub": strconv.FormatInt(userID, 10),
"abl": ability,
"exp": time.Now().Add(j.ttl).Unix(),
"iat": time.Now().Unix(),
}
tok := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return tok.SignedString(j.secret)
}
func (j *JWT) parse(tokenStr string) (int64, error) {
tok, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return j.secret, nil
})
if err != nil || !tok.Valid {
return 0, errors.New("invalid token")
}
claims, ok := tok.Claims.(jwt.MapClaims)
if !ok {
return 0, errors.New("invalid claims")
}
sub, _ := claims["sub"].(string)
id, err := strconv.ParseInt(sub, 10, 64)
if err != nil {
return 0, errors.New("invalid sub")
}
return id, nil
}
// Verify توکن را اعتبارسنجی کرده و شناسه کاربر را برمی‌گرداند (برای WebSocket).
func (j *JWT) Verify(token string) (int64, error) {
return j.parse(token)
}
// Middleware احراز هویت Bearer JWT.
func (j *JWT) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h := r.Header.Get("Authorization")
tokenStr := strings.TrimPrefix(h, "Bearer ")
if tokenStr == h || tokenStr == "" {
http.Error(w, `{"message":"unauthorized"}`, http.StatusUnauthorized)
return
}
id, err := j.parse(tokenStr)
if err != nil {
http.Error(w, `{"message":"unauthorized"}`, http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), userIDKey, id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// UserID شناسه کاربر را از context (پس از Middleware) برمی‌گرداند.
func UserID(ctx context.Context) (int64, bool) {
id, ok := ctx.Value(userIDKey).(int64)
return id, ok
}