init
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user