// Polls the Revocation Provider for new revocations and adds them to the revocation cache; handles the Force Refresh
// condition (e.g. refresh cache from a specific timestamp); expires revocations older than the
// REVOCATION_CACHE_TTL envionment variable.
func (crp *CachingRevokeProvider) RefreshRevocations() {
	ts := crp.cache.GetLastTS()
	if ts == 0 {
		ts = int(time.Now().Add(-1 * options.AppSettings.RevocationCacheTTL).Unix())
	}
	ts = ts - int(options.AppSettings.RevocationRefreshTolerance.Seconds())

	log.Printf("Checking for new revocations since %d...", ts)

	resp, err := breaker.Get("refreshRevocations", crp.url+"?from="+strconv.Itoa(ts))
	if err != nil {
		log.Println("Failed to get revocations. " + err.Error())
		return
	}

	if resp.StatusCode != http.StatusOK {
		log.Printf("Failed to get revocations. Server returned status %s.", resp.Status)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	jr := &jsonRevoke{}
	if err := json.Unmarshal(body, &jr); err != nil {
		log.Println("Failed to unmarshall revocation data. " + err.Error())
		return
	}

	if jr.Meta.RefreshTimestamp != 0 {
		r := crp.cache.Get(REVOCATION_TYPE_FORCEREFRESH)
		if r == nil || (r.(*Revocation).Data["revoked_at"] != jr.Meta.RefreshTimestamp) {
			log.Printf("Force refreshing cache from %d...", jr.Meta.RefreshFrom)
			crp.cache.ForceRefresh(jr.Meta.RefreshFrom)
			rev := &Revocation{}
			d := make(map[string]interface{})
			rev.Type = REVOCATION_TYPE_FORCEREFRESH
			d["refresh_from"] = jr.Meta.RefreshFrom
			d["revoked_at"] = jr.Meta.RefreshTimestamp
			rev.Data = d
			crp.cache.Add(rev)
		}

	}

	if len(jr.Revs) > 0 {
		log.Printf("Received %d new revocations", len(jr.Revs))
	}

	for _, j := range jr.Revs {
		r, err := j.toRevocation()
		if err == nil {
			crp.cache.Add(r)
		}
	}

	crp.cache.Expire()

}
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
func (kl *cachingOpenIDProviderLoader) loadConfiguration() (*configuration, error) {
	resp, err := breaker.Get("loadConfiguration", kl.url)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errInvalidResponseStatusCode
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	config := new(configuration)
	err = json.Unmarshal(body, config)
	return config, err
}
// Example: https://www.googleapis.com/oauth2/v3/certs
func (kl *cachingOpenIDProviderLoader) refreshKeys() {
	log.Println("Refreshing keys..")

	log.Println("Loading configuration..")
	c, err := kl.loadConfiguration()
	if err != nil {
		log.Printf("Failed to get configuration from %q. %s\n", kl.url, err)
		return
	}

	log.Println("Configuration loaded successfully, loading JWKS..")
	resp, err := breaker.Get("loadKeys", c.JwksURI)
	if err != nil {
		log.Println("Failed to get JWKS from ", c.JwksURI)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read JWKS response body from %q: %v\n", c.JwksURI, err)
		return
	}

	log.Println("JWKS loaded successfully, parsing JWKS..")
	jwks := new(jwk.JSONWebKeySet)
	if err = json.Unmarshal(body, jwks); err != nil {
		log.Println("Failed to parse JWKS: ", err)
		return
	}

	// safety first: only remove public keys if our newly
	// received list contains at least one public key!
	// (we don't want our tokeninfo to run out of public keys
	// just because somebody cleared the provider database)
	numKeys := len(jwks.Keys)
	if numKeys < 1 {
		log.Println("No JWKS currently in the OpenID provider")
		if c, ok := metrics.DefaultRegistry.GetOrRegister(metricsNoKeysError, metrics.NewCounter).(metrics.Counter); ok {
			c.Inc(1)
		}
		return
	}

	if g, ok := metrics.DefaultRegistry.GetOrRegister(metricsNumKeys, metrics.NewGauge).(metrics.Gauge); ok {
		g.Update(int64(numKeys))
	}

	newKeys := jwks.ToMap()
	for kid, k := range newKeys {
		key := k.(jwk.JSONWebKey)
		existing := kl.keyCache.Get(kid)
		if existing == nil {
			log.Printf("Received new public key %q (%s)\n", kid, key.Algorithm)
		} else if !reflect.DeepEqual(existing, key) {
			// this is potentially dangerous: the key contents changed..
			// (but maybe the key wasn't used for signing yet, so it might be ok)
			log.Printf("Received a replacement public key for existing key %q (%s)", kid, key.Algorithm)
		}
	}

	log.Printf("Resetting key cache with %d key(s)..", numKeys)
	kl.keyCache.Reset(newKeys)
	log.Println("Refresh done..")
}