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 }