mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(auth): implement master key authentication system with child key issuance
Add admin and master authentication layers with JWT support. Replace direct key creation with hierarchical master/child key system. Update database schema to support master accounts with configurable limits and epoch-based key revocation. Add health check endpoint with system status monitoring. BREAKING CHANGE: Removed direct POST /keys endpoint in favor of master-based key issuance through /v1/tokens. Database migration requires dropping old User table and creating Master table with new relationships.
This commit is contained in:
69
internal/service/token.go
Normal file
69
internal/service/token.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/ez-api/ez-api/internal/util"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type TokenService struct {
|
||||
rdb *redis.Client
|
||||
}
|
||||
|
||||
func NewTokenService(rdb *redis.Client) *TokenService {
|
||||
return &TokenService{rdb: rdb}
|
||||
}
|
||||
|
||||
type TokenInfo struct {
|
||||
MasterID uint
|
||||
IssuedAtEpoch int64
|
||||
Status string
|
||||
Group string
|
||||
}
|
||||
|
||||
// ValidateToken checks a child key against Redis for validity.
|
||||
// This is designed to be called by the data plane (balancer).
|
||||
func (s *TokenService) ValidateToken(ctx context.Context, token string) (*TokenInfo, error) {
|
||||
tokenHash := util.HashToken(token)
|
||||
tokenKey := fmt.Sprintf("auth:token:%s", tokenHash)
|
||||
|
||||
// 1. Get token metadata from Redis
|
||||
tokenData, err := s.rdb.HGetAll(ctx, tokenKey).Result()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get token data: %w", err)
|
||||
}
|
||||
if len(tokenData) == 0 {
|
||||
return nil, errors.New("token not found")
|
||||
}
|
||||
|
||||
if tokenData["status"] != "active" {
|
||||
return nil, errors.New("token is not active")
|
||||
}
|
||||
|
||||
masterID, _ := strconv.ParseUint(tokenData["master_id"], 10, 64)
|
||||
issuedAtEpoch, _ := strconv.ParseInt(tokenData["issued_at_epoch"], 10, 64)
|
||||
|
||||
// 2. Get master metadata from Redis
|
||||
masterKey := fmt.Sprintf("auth:master:%d", masterID)
|
||||
masterEpochStr, err := s.rdb.HGet(ctx, masterKey, "epoch").Result()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get master epoch: %w", err)
|
||||
}
|
||||
masterEpoch, _ := strconv.ParseInt(masterEpochStr, 10, 64)
|
||||
|
||||
// 3. Core Epoch Validation
|
||||
if issuedAtEpoch < masterEpoch {
|
||||
return nil, errors.New("token revoked due to master key rotation")
|
||||
}
|
||||
|
||||
return &TokenInfo{
|
||||
MasterID: uint(masterID),
|
||||
IssuedAtEpoch: issuedAtEpoch,
|
||||
Status: tokenData["status"],
|
||||
Group: tokenData["group"],
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user