package auth import ( "context" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "time" "github.com/kindredsystems/silo/internal/db" ) const ( tokenPrefixStr = "silo_" tokenRawBytes = 32 // 32 random bytes = 64 hex chars ) // GenerateToken creates a new API token. Returns the raw token string // (shown once to the user) and the persisted token info. func (s *Service) GenerateToken(ctx context.Context, userID, name string, scopes []string, expiresAt *time.Time) (string, *db.TokenInfo, error) { rawBytes := make([]byte, tokenRawBytes) if _, err := rand.Read(rawBytes); err != nil { return "", nil, fmt.Errorf("generating random bytes: %w", err) } rawToken := tokenPrefixStr + hex.EncodeToString(rawBytes) hash := sha256.Sum256([]byte(rawToken)) tokenHash := hex.EncodeToString(hash[:]) // Prefix for display: "silo_" + first 8 hex chars displayPrefix := rawToken[:len(tokenPrefixStr)+8] info, err := s.tokens.Create(ctx, userID, name, tokenHash, displayPrefix, scopes, expiresAt) if err != nil { return "", nil, err } return rawToken, info, nil } // ListTokens returns all tokens for a user. func (s *Service) ListTokens(ctx context.Context, userID string) ([]*db.TokenInfo, error) { return s.tokens.ListByUser(ctx, userID) } // RevokeToken deletes a token by ID, ensuring it belongs to the given user. func (s *Service) RevokeToken(ctx context.Context, userID, tokenID string) error { return s.tokens.Delete(ctx, userID, tokenID) }