103 lines
2.9 KiB
Go
103 lines
2.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/XingfenD/yoresee_doc/internal/status"
|
|
"github.com/XingfenD/yoresee_doc/pkg/key"
|
|
"github.com/XingfenD/yoresee_doc/pkg/storage"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
func hashToken(token string) string {
|
|
sum := sha256.Sum256([]byte(token))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func StoreJWTToken(userExternalID, token string, ttl time.Duration) error {
|
|
if storage.KVS == nil {
|
|
return status.StatusRedisNotInitialized
|
|
}
|
|
if ttl <= 0 {
|
|
return status.GenErrWithCustomMsg(status.StatusInternalParamsError, "invalid jwt ttl")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
tokenHash := hashToken(token)
|
|
pipe := storage.KVS.TxPipeline()
|
|
pipe.Set(ctx, key.KeyJWTActiveToken(tokenHash), userExternalID, ttl)
|
|
pipe.SAdd(ctx, key.KeyJWTUserTokenSet(userExternalID), tokenHash)
|
|
pipe.Expire(ctx, key.KeyJWTUserTokenSet(userExternalID), ttl)
|
|
if _, err := pipe.Exec(ctx); err != nil {
|
|
return fmt.Errorf("persist jwt token failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ValidateJWTTokenInRedis(userExternalID, token string) error {
|
|
if storage.KVS == nil {
|
|
return status.StatusRedisNotInitialized
|
|
}
|
|
|
|
ctx := context.Background()
|
|
tokenHash := hashToken(token)
|
|
blacklisted, err := storage.KVS.Exists(ctx, key.KeyJWTBlacklistToken(tokenHash)).Result()
|
|
if err != nil {
|
|
return fmt.Errorf("query jwt blacklist failed: %w", err)
|
|
}
|
|
if blacklisted > 0 {
|
|
return status.GenErrWithCustomMsg(status.StatusTokenInvalid, "token is blacklisted")
|
|
}
|
|
|
|
storedUserExternalID, err := storage.KVS.Get(ctx, key.KeyJWTActiveToken(tokenHash)).Result()
|
|
if err != nil {
|
|
if err == redis.Nil {
|
|
return status.GenErrWithCustomMsg(status.StatusTokenInvalid, "token is invalid")
|
|
}
|
|
return fmt.Errorf("query active jwt token failed: %w", err)
|
|
}
|
|
if storedUserExternalID != userExternalID {
|
|
return status.GenErrWithCustomMsg(status.StatusTokenInvalid, "token user mismatch")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BlacklistUserJWTs(userExternalID string) error {
|
|
if storage.KVS == nil {
|
|
return status.StatusRedisNotInitialized
|
|
}
|
|
|
|
ctx := context.Background()
|
|
tokenHashes, err := storage.KVS.SMembers(ctx, key.KeyJWTUserTokenSet(userExternalID)).Result()
|
|
if err != nil {
|
|
return fmt.Errorf("list user jwt tokens failed: %w", err)
|
|
}
|
|
if len(tokenHashes) == 0 {
|
|
return nil
|
|
}
|
|
|
|
pipe := storage.KVS.TxPipeline()
|
|
for _, tokenHash := range tokenHashes {
|
|
activeKey := key.KeyJWTActiveToken(tokenHash)
|
|
ttlCmd := storage.KVS.TTL(ctx, activeKey)
|
|
ttl, ttlErr := ttlCmd.Result()
|
|
if ttlErr != nil && ttlErr != redis.Nil {
|
|
return fmt.Errorf("query token ttl failed: %w", ttlErr)
|
|
}
|
|
if ttl <= 0 {
|
|
ttl = 24 * time.Hour
|
|
}
|
|
pipe.Set(ctx, key.KeyJWTBlacklistToken(tokenHash), "1", ttl)
|
|
pipe.Del(ctx, activeKey)
|
|
}
|
|
pipe.Del(ctx, key.KeyJWTUserTokenSet(userExternalID))
|
|
if _, err := pipe.Exec(ctx); err != nil {
|
|
return fmt.Errorf("blacklist user jwt tokens failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|