package user import ( "time" ) // JWTSecret represents a JWT secret with metadata type JWTSecret struct { Secret string IsPrimary bool CreatedAt time.Time ExpiresAt *time.Time // Optional expiration time } // JWTSecretManager manages multiple JWT secrets for rotation type JWTSecretManager struct { secrets []JWTSecret primarySecret string } // NewJWTSecretManager creates a new JWT secret manager func NewJWTSecretManager(initialSecret string) *JWTSecretManager { return &JWTSecretManager{ secrets: []JWTSecret{ { Secret: initialSecret, IsPrimary: true, CreatedAt: time.Now(), }, }, primarySecret: initialSecret, } } // AddSecret adds a new JWT secret func (m *JWTSecretManager) AddSecret(secret string, isPrimary bool, expiresIn time.Duration) { var expiresAt *time.Time if expiresIn > 0 { expirationTime := time.Now().Add(expiresIn) expiresAt = &expirationTime } // If expiresIn is 0 or negative, expiresAt remains nil (no expiration) m.secrets = append(m.secrets, JWTSecret{ Secret: secret, IsPrimary: isPrimary, CreatedAt: time.Now(), ExpiresAt: expiresAt, }) if isPrimary { m.primarySecret = secret } } // RotateToSecret rotates to a new primary secret func (m *JWTSecretManager) RotateToSecret(newSecret string) { // Mark existing primary as non-primary for i, secret := range m.secrets { if secret.IsPrimary { m.secrets[i].IsPrimary = false break } } // Add new secret as primary m.AddSecret(newSecret, true, 0) // No expiration for primary } // GetPrimarySecret returns the current primary secret func (m *JWTSecretManager) GetPrimarySecret() string { return m.primarySecret } // GetAllValidSecrets returns all valid (non-expired) secrets func (m *JWTSecretManager) GetAllValidSecrets() []JWTSecret { var validSecrets []JWTSecret now := time.Now() for _, secret := range m.secrets { if secret.ExpiresAt == nil || secret.ExpiresAt.After(now) { validSecrets = append(validSecrets, secret) } } return validSecrets } // GetSecretByIndex returns a secret by index for testing func (m *JWTSecretManager) GetSecretByIndex(index int) (string, bool) { if index < 0 || index >= len(m.secrets) { return "", false } return m.secrets[index].Secret, true } // Reset resets the secret manager to its initial state with only the primary secret // This is useful for test cleanup to ensure tests don't interfere with each other func (m *JWTSecretManager) Reset(initialSecret string) { m.secrets = []JWTSecret{ { Secret: initialSecret, IsPrimary: true, CreatedAt: time.Now(), }, } m.primarySecret = initialSecret }