docs: public boolean hasValidKey(HttpRequestHeader reqHeader, JSONObject params) {
  		try {
  			String apiPath;
  			try {
  				apiPath = reqHeader.getURI().getPath();
  			} catch (URIException e) {
  				logger.error(e.getMessage(), e);
  				return false;
  			}
  			String nonceParam = reqHeader.getHeader(HttpHeader.X_ZAP_API_NONCE);
  			if (nonceParam == null && params.has(API_NONCE_PARAM)) {
  				nonceParam = params.getString(API_NONCE_PARAM);
  			}
  			
  			if (nonceParam != null) {
  				Nonce nonce = nonces.get(nonceParam);
  				if (nonce == null) {
  					logger.warn("API nonce " + nonceParam + " not found in request from " + reqHeader.getSenderAddress().getHostAddress());
  					return false;
  				} else if (nonce.isOneTime()) {
  					nonces.remove(nonceParam);
  				}
  				if (! nonce.isValid()) {
  					logger.warn("API nonce " + nonce.getNonceKey() + " expired at " + nonce.getExpires().toString() +
  							" in request from " + reqHeader.getSenderAddress().getHostAddress());
  					return false;
  				}
  				
  				if (! apiPath.equals(nonce.getApiPath())) {
  					logger.warn("API nonce path was " + nonce.getApiPath() + " but call was for " + apiPath + 
  						" in request from " + reqHeader.getSenderAddress().getHostAddress());
  					return false;
  				}
  			} else {
  				String keyParam = reqHeader.getHeader(HttpHeader.X_ZAP_API_KEY);
  				if (keyParam == null && params.has(API_KEY_PARAM)) {
  					keyParam = params.getString(API_KEY_PARAM);
  				}
  				if (!getOptionsParamApi().getKey().equals(keyParam)) {
  					logger.warn("API key incorrect or not supplied: " + keyParam +
  							" in request from " + reqHeader.getSenderAddress().getHostAddress());
  					return false;
  				}
  			}
  			
  			return true;
  		} finally {
  			synchronized (nonces) {
  				for (Entry<String, Nonce> entry : nonces.entrySet()) {
  					if (! entry.getValue().isValid()) {
  						nonces.remove(entry.getKey());
  					}
  				}
  			}
  		}
  	}