func (store *SessionStore) loadSessionFromString(value string) (*sessions.SessionState, error) {
	ticket, err := decodeTicket(store.CookieOptions.CookieName, value)
	if err != nil {
		return nil, err
	}
	result, err := store.Client.Get(ticket.asHandle(store.CookieOptions.CookieName)).Result()
	if err != nil {
		return nil, err
	}
	resultBytes := []byte(result)
	block, err := aes.NewCipher(ticket.Secret)
	if err != nil {
		return nil, err
	}
	stream := cipher.NewCFBDecrypter(block, ticket.Secret)
	stream.XORKeyStream(resultBytes, resultBytes)
	session, err := sessions.DecodeSessionState(string(resultBytes), store.CookieCipher)
	if err != nil {
		return nil, err
	}
	return session, nil
}
loadSessionFromString loads the session based on the ticket value
package redis
import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
	"github.com/go-redis/redis"
	"github.com/pusher/oauth2_proxy/pkg/apis/options"
	"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
	"github.com/pusher/oauth2_proxy/pkg/cookies"
	"github.com/pusher/oauth2_proxy/pkg/encryption"
	"github.com/pusher/oauth2_proxy/pkg/logger"
)
type TicketData struct {
	TicketID string
	Secret   []byte
}
type SessionStore struct {
	CookieCipher  *encryption.Cipher
	CookieOptions *options.CookieOptions
	Client        *redis.Client
}
func NewRedisSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessions.SessionStore, error) {
	client, err := newRedisClient(opts.RedisStoreOptions)
	if err != nil {
		return nil, fmt.Errorf("error constructing redis client: %v", err)
	}
	rs := &SessionStore{
		Client:        client,
		CookieCipher:  opts.Cipher,
		CookieOptions: cookieOpts,
	}
	return rs, nil
}
func newRedisClient(opts options.RedisStoreOptions) (*redis.Client, error) {
	if opts.UseSentinel {
		client := redis.NewFailoverClient(&redis.FailoverOptions{
			MasterName:    opts.SentinelMasterName,
			SentinelAddrs: opts.SentinelConnectionURLs,
		})
		return client, nil
	}
	opt, err := redis.ParseURL(opts.RedisConnectionURL)
	if err != nil {
		return nil, fmt.Errorf("unable to parse redis url: %s", err)
	}
	if opts.RedisInsecureTLS != false {
		opt.TLSConfig.InsecureSkipVerify = true
	}
	if opts.RedisCAPath != "" {
		rootCAs, err := x509.SystemCertPool()
		if err != nil {
			logger.Printf("failed to load system cert pool for redis connection, falling back to empty cert pool")
		}
		if rootCAs == nil {
			rootCAs = x509.NewCertPool()
		}
		certs, err := ioutil.ReadFile(opts.RedisCAPath)
		if err != nil {
			return nil, fmt.Errorf("failed to load %q, %v", opts.RedisCAPath, err)
		}
		if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
			logger.Printf("no certs appended, using system certs only")
		}
		opt.TLSConfig.RootCAs = rootCAs
	}
	client := redis.NewClient(opt)
	return client, nil
}
func (store *SessionStore) Save(rw http.ResponseWriter, req *http.Request, s *sessions.SessionState) error {
	if s.CreatedAt.IsZero() {
		s.CreatedAt = time.Now()
	}
	requestCookie, _ := req.Cookie(store.CookieOptions.CookieName)
	value, err := s.EncodeSessionState(store.CookieCipher)
	if err != nil {
		return err
	}
	ticketString, err := store.storeValue(value, store.CookieOptions.CookieExpire, requestCookie)
	if err != nil {
		return err
	}
	ticketCookie := store.makeCookie(
		req,
		ticketString,
		store.CookieOptions.CookieExpire,
		s.CreatedAt,
	)
	http.SetCookie(rw, ticketCookie)
	return nil
}
func (store *SessionStore) Load(req *http.Request) (*sessions.SessionState, error) {
	requestCookie, err := req.Cookie(store.CookieOptions.CookieName)
	if err != nil {
		return nil, fmt.Errorf("error loading session: %s", err)
	}
	val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.CookieSecret, store.CookieOptions.CookieExpire)
	if !ok {
		return nil, fmt.Errorf("Cookie Signature not valid")
	}
	session, err := store.loadSessionFromString(val)
	if err != nil {
		return nil, fmt.Errorf("error loading session: %s", err)
	}
	return session, nil
}