func (w *WebStrategy) handleRefreshTokens(sessionID string, session *authserver.TokenResponse, c client.Client, rules []policiesV1.Rule) (*authnz.HandleAuthnZResponse, error) {
	if session.RefreshToken == "" {
		zap.L().Debug("Refresh token not provided", zap.String("client_name", c.Name()))
		return nil, nil
	}
	userInfoEndpoint := c.AuthorizationServer().UserInfoEndpoint()
	keySet := c.AuthorizationServer().KeySet()
	if tokens, err := c.RefreshToken(session.RefreshToken); err != nil {
		zap.L().Info("Could not retrieve tokens using the refresh token", zap.String("client_name", c.Name()), zap.Error(err))
		return nil, nil
	} else if validationErr := w.tokenUtil.Validate(tokens.AccessToken, validator.Access, keySet, rules, userInfoEndpoint); validationErr != nil {
		zap.L().Debug("Could not validate Access tokens. Beginning a new session.", zap.String("client_name", c.Name()), zap.String("session_id", sessionID), zap.Error(validationErr))
		return nil, nil
	} else if validationErr := w.tokenUtil.Validate(tokens.IdentityToken, validator.ID, keySet, rules, userInfoEndpoint); validationErr != nil {
		zap.L().Debug("Could not validate Id tokens. Beginning a new session.", zap.String("client_name", c.Name()), zap.String("session_id", sessionID), zap.Error(validationErr))
		return nil, nil
	} else {
		zap.L().Debug("Updated tokens using refresh token", zap.String("client_name", c.Name()), zap.String("session_id", sessionID))
		cookie := generateSessionIDCookie(c, &sessionID)
		w.tokenCache.Store(cookie.Value, tokens)
		return &authnz.HandleAuthnZResponse{
			Result: &v1beta1.CheckResult{Status: status.WithMessage(rpc.OK, "User is authenticated")},
			Output: &authnz.OutputMsg{
				Authorization: strings.Join([]string{bearer, tokens.AccessToken, tokens.IdentityToken}, " "),
				SessionCookie: cookie.String(),
			},
		}, nil
	}
}
handleRefreshTokens attempts to update an expired session using the refresh token flow
package webstrategy
import (
	"errors"
	"net/http"
	"strings"
	"sync"
	"time"
	"k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"
	"github.com/gogo/googleapis/google/rpc"
	"github.com/gogo/protobuf/types"
	"github.com/gorilla/securecookie"
	"go.uber.org/zap"
	"gopkg.in/mgo.v2/bson"
	"istio.io/api/mixer/adapter/model/v1beta1"
	policy "istio.io/api/policy/v1beta1"
	"istio.io/istio/mixer/pkg/status"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/authserver"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/client"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/config"
	oAuthError "github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/errors"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/networking"
	policiesV1 "github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/pkg/apis/policies/v1"
	adapterPolicy "github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/policy"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/policy/engine"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/strategy"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/adapter/validator"
	"github.com/ibm-cloud-security/app-identity-and-access-adapter/config/template"
)
const (
	bearer           = "Bearer"
	logoutEndpoint   = "/oidc/logout"
	callbackEndpoint = "/oidc/callback"
	location         = "location"
	setCookie        = "Set-Cookie"
	sessionCookie    = "oidc-cookie"
	hashKey          = "HASH_KEY"
	blockKey         = "BLOCK_KEY"
	defaultNamespace = "istio-system"
	defaultKeySecret = "appidentityandaccessadapter-cookie-sig-enc-keys"
)
type WebStrategy struct {
	ctx        *config.Config
	tokenUtil  validator.TokenValidator
	kubeClient kubernetes.Interface
	mutex      *sync.Mutex
	encrpytor  Encryptor
	tokenCache *sync.Map
}
type Encryptor interface {
	Encode(name string, value interface{}) (string, error)
	Decode(name, value string, dst interface{}) error
}
type OidcCookie struct {
	Value      string
	Expiration time.Time
}
func New(ctx *config.Config, kubeClient kubernetes.Interface) strategy.Strategy {
	w := &WebStrategy{
		ctx:        ctx,
		tokenUtil:  validator.NewTokenValidator(adapterPolicy.OIDC),
		kubeClient: kubeClient,
		mutex:      &sync.Mutex{},
		tokenCache: new(sync.Map),
	}
	_, err := w.getSecureCookie()
	if err != nil {
		zap.L().Warn("Could not sync signing / encryption secrets, will retry later", zap.Error(err))
	}
	return w
}
func (w *WebStrategy) HandleAuthnZRequest(r *authnz.HandleAuthnZRequest, action *engine.Action) (*authnz.HandleAuthnZResponse, error) {
	if strings.HasSuffix(r.Instance.Request.Path, logoutEndpoint) {
		zap.L().Debug("Received logout request.", zap.String("client_name", action.Client.Name()))
		return w.handleLogout(r, action)
	}
	if strings.HasSuffix(r.Instance.Request.Path, callbackEndpoint) {
		if r.Instance.Request.Params.Error != "" {
			zap.L().Debug("An error occurred during authentication", zap.String("error_query_param", r.Instance.Request.Params.Error))
			return w.handleErrorCallback(errors.New(r.Instance.Request.Params.Error))
		} else if r.Instance.Request.Params.Code != "" {
			zap.L().Debug("Received authorization code")
			return w.handleAuthorizationCodeCallback(r.Instance.Request.Params.Code, r.Instance.Request, action)
		} else {
			zap.L().Debug("Unexpected response on callback endpoint /oidc/callback. Triggering re-authentication.")
			return w.handleAuthorizationCodeFlow(r.Instance.Request, action)
		}
	}
	res, err := w.isAuthorized(r.Instance.Request.Headers.Cookies, action)
	if res != nil || err != nil {
		return res, err
	}
	zap.L().Debug("Handling new user authentication")
	return w.handleAuthorizationCodeFlow(r.Instance.Request, action)
}
func (w *WebStrategy) isAuthorized(cookies string, action *engine.Action) (*authnz.HandleAuthnZResponse, error) {
	if action.Client == nil {
		zap.L().Warn("Internal server error: OIDC client not provided")
		return nil, errors.New("invalid OIDC configuration")
	}
	header := http.Header{}
	header.Add("Cookie", cookies)
	request := http.Request{Header: header}
	sessionCookie, err := request.Cookie(buildTokenCookieName(sessionCookie, action.Client))
	if err != nil {
		zap.L().Debug("Current session does not exist.", zap.String("client_name", action.Client.Name()))
		return nil, nil
	}
	var session *authserver.TokenResponse
	if storedSession, ok := w.tokenCache.Load(sessionCookie.Value); !ok {
		zap.L().Debug("Tokens not found in cache.", zap.String("client_name", action.Client.Name()))
		return nil, nil
	} else if session, ok = storedSession.(*authserver.TokenResponse); !ok {
		zap.L().Debug("Tokens not found in cache.", zap.String("client_name", action.Client.Name()))
		return nil, nil
	}
	zap.L().Debug("Found active session", zap.String("client_name", action.Client.Name()), zap.String("session_id", sessionCookie.Value))
	userInfoEndpoint := action.Client.AuthorizationServer().UserInfoEndpoint()
	keySet := action.Client.AuthorizationServer().KeySet()
	handleTokenValidationError := func(validationErr *oAuthError.OAuthError) (*authnz.HandleAuthnZResponse, error) {
		if validationErr.Msg == oAuthError.ExpiredTokenError().Msg {
			zap.L().Debug("Tokens have expired", zap.String("client_name", action.Client.Name()))
			return w.handleRefreshTokens(sessionCookie.Value, session, action.Client, action.Rules)
		}
		zap.L().Debug("Tokens are invalid - starting a new session", zap.String("client_name", action.Client.Name()), zap.String("session_id", sessionCookie.Value), zap.Error(validationErr))
		w.tokenCache.Delete(sessionCookie.Value)
		return nil, nil
	}
	if validationErr := w.tokenUtil.Validate(session.AccessToken, validator.Access, keySet, action.Rules, userInfoEndpoint); validationErr != nil {
		return handleTokenValidationError(validationErr)
	}
	if validationErr := w.tokenUtil.Validate(session.IdentityToken, validator.ID, keySet, action.Rules, userInfoEndpoint); validationErr != nil {
		return handleTokenValidationError(validationErr)
	}
	zap.L().Debug("User is currently authenticated")
	return &authnz.HandleAuthnZResponse{
		Result: &v1beta1.CheckResult{Status: status.WithMessage(rpc.OK, "User is authenticated")},
		Output: &authnz.OutputMsg{
			Authorization: strings.Join([]string{bearer, session.AccessToken, session.IdentityToken}, " "),
		},
	}, nil
}