func (a *accessService) StartAccessUpdater(ctx context.Context) <-chan error {
	glg.Info("Starting access token updater")
	ech := make(chan error, 100)
	go func() {
		defer close(ech)
		ticker := time.NewTicker(a.refreshPeriod)
		for {
			select {
			case <-ctx.Done():
				glg.Info("Stopping access token updater")
				ticker.Stop()
				ech <- ctx.Err()
				return
			case <-ticker.C:
				for err := range a.RefreshAccessTokenCache(ctx) {
					ech <- errors.Wrap(err, "error update access token")
				}
			}
		}
	}()
	a.tokenCache.StartExpired(ctx, cachePurgePeriod)
	a.tokenCache.EnableExpiredHook().SetExpiredHook(func(ctx context.Context, k string) {
		glg.Warnf("the following cache is expired, key: %v", k)
	})
	return ech
}
StartAccessUpdater returns AccessService.
This function will periodically refresh the access token.
package service
import (
	"bytes"
	"context"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
	"github.com/kpango/fastime"
	"github.com/kpango/gache"
	"github.com/kpango/glg"
	"github.com/kpango/ntokend"
	"github.com/pkg/errors"
	"github.com/yahoojapan/athenz-client-sidecar/v2/config"
	"golang.org/x/sync/singleflight"
)
type AccessService interface {
	StartAccessUpdater(context.Context) <-chan error
	RefreshAccessTokenCache(ctx context.Context) <-chan error
	GetAccessProvider() AccessProvider
}
type accessService struct {
	cfg                   config.AccessToken
	token                 ntokend.TokenProvider
	athenzURL             string
	athenzPrincipleHeader string
	tokenCache            gache.Gache
	group                 singleflight.Group
	expiry                time.Duration
	httpClient            atomic.Value
	rootCAs               *x509.CertPool
	certPath              string
	certKeyPath           string
	refreshPeriod    time.Duration
	errRetryMaxCount int
	errRetryInterval time.Duration
}
type accessCacheData struct {
	token             *AccessTokenResponse
	domain            string
	role              string
	proxyForPrincipal string
	expiresIn         int64
}
type AccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	TokenType string `json:"token_type"`
	ExpiresIn int64 `json:"expires_in,omitempty"`
	Scope string `json:"scope,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken string `json:"id_token,omitempty"`
}
type AccessProvider func(ctx context.Context, domain string, role string, proxyForPrincipal string, expiresIn int64) (*AccessTokenResponse, error)
var (
	ErrAccessTokenRequestFailed = errors.New("Failed to fetch AccessToken")
)
const (
	scopeSeparator = " "
)
func NewAccessService(cfg config.AccessToken, token ntokend.TokenProvider) (AccessService, error) {
	var (
		err              error
		exp              = defaultExpiry
		refreshPeriod    = defaultRefreshPeriod
		errRetryInterval = defaultErrRetryInterval
	)
	if !cfg.Enable {
		return nil, ErrDisabled
	}
	if cfg.Expiry != "" {
		if exp, err = time.ParseDuration(cfg.Expiry); err != nil {
			return nil, errors.Wrap(ErrInvalidSetting, "Expiry: "+err.Error())
		}
	}
	if cfg.RefreshPeriod != "" {
		if refreshPeriod, err = time.ParseDuration(cfg.RefreshPeriod); err != nil {
			return nil, errors.Wrap(ErrInvalidSetting, "RefreshPeriod: "+err.Error())
		}
	}
	if cfg.Retry.Delay != "" {
		if errRetryInterval, err = time.ParseDuration(cfg.Retry.Delay); err != nil {
			return nil, errors.Wrap(ErrInvalidSetting, "ErrRetryInterval: "+err.Error())
		}
	}
	if exp != 0 && refreshPeriod > exp {
		return nil, errors.Wrap(ErrInvalidSetting, "refresh period > token expiry time")
	}
	errRetryMaxCount := defaultErrRetryMaxCount
	if cfg.Retry.Attempts > 0 {
		errRetryMaxCount = cfg.Retry.Attempts
	} else if cfg.Retry.Attempts != 0 {
		return nil, errors.Wrap(ErrInvalidSetting, "ErrRetryMaxCount < 0")
	}
	if token == nil && cfg.CertPath == "" {
		return nil, errors.Wrap(ErrInvalidSetting, "Neither NToken nor client certificate is set.")
	}
	var cp *x509.CertPool
	if cfg.AthenzCAPath != "" {
		var err error
		caPath := config.GetActualValue(cfg.AthenzCAPath)
		_, err = os.Stat(caPath)
		if os.IsNotExist(err) {
			return nil, errors.Wrap(ErrInvalidSetting, "Athenz CA not exist")
		}
		cp, err = NewX509CertPool(caPath)
		if err != nil {
			return nil, errors.Wrap(ErrInvalidSetting, err.Error())
		}
	}
	certPath := cfg.CertPath
	certKeyPath := cfg.CertKeyPath
	if token != nil {
		certPath = ""
		certKeyPath = ""
	}
	tlsConfig, err := NewTLSClientConfig(cp, certPath, certKeyPath)
	if err != nil {
		return nil, errors.Wrap(ErrInvalidSetting, err.Error())
	}
	var httpClient atomic.Value
	httpClient.Store(&http.Client{
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
		},
	})
	return &accessService{
		cfg:                   cfg,
		token:                 token,
		athenzURL:             cfg.AthenzURL,
		athenzPrincipleHeader: cfg.PrincipalAuthHeader,
		tokenCache:            gache.New(),
		expiry:                exp,
		httpClient:            httpClient,
		rootCAs:               cp,
		certPath:              certPath,
		certKeyPath:           certKeyPath,
		refreshPeriod:         refreshPeriod,
		errRetryMaxCount:      errRetryMaxCount,
		errRetryInterval:      errRetryInterval,
	}, nil
}