         throw DeserializationError.invalidURLScheme
     }
 
-    var queryDictionary = Dictionary<String, String>()
-    URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.forEach { item in
-        // TODO: guard against duplicate query items for the same key.
-        queryDictionary[item.name] = item.value
-    }
+    let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems ?? []
 
     let factor: Generator.Factor
     switch url.host {
     case .some(kFactorCounterKey):
-        let counterValue = try queryDictionary[kQueryCounterKey].map(parseCounterValue) ?? defaultCounter
+        let counterValue = try queryItems.value(for: kQueryCounterKey).map(parseCounterValue) ?? defaultCounter
         factor = .counter(counterValue)
     case .some(kFactorTimerKey):
-        let period = try queryDictionary[kQueryPeriodKey].map(parseTimerPeriod) ?? defaultPeriod
+        let period = try queryItems.value(for: kQueryPeriodKey).map(parseTimerPeriod) ?? defaultPeriod
         factor = .timer(period: period)
     case let .some(rawValue):
         throw DeserializationError.invalidFactor(rawValue)

        throw DeserializationError.missingFactor
    }

    let algorithm = try queryDictionary[kQueryAlgorithmKey].map(algorithmFromString) ?? defaultAlgorithm
    let digits = try queryDictionary[kQueryDigitsKey].map(parseDigits) ?? defaultDigits
    guard let secret = try externalSecret ?? queryDictionary[kQuerySecretKey].map(parseSecret) else {
        throw DeserializationError.missingSecret
    }
    guard let generator = Generator(factor: factor, secret: secret, algorithm: algorithm, digits: digits) else {

    }
    return digits
}

extension Array where Element == URLQueryItem {
    func value(for name: String) throws -> String? {
        let matchingQueryItems = self.filter({
            $0.name == name
        })
        guard matchingQueryItems.count <= 1 else {
            throw DeserializationError.duplicateQueryItem(name)
        }
        return matchingQueryItems.first?.value
    }
}
