func (t *ticket) makeCipher() (encryption.Cipher, error) {
	c, err := encryption.NewGCMCipher(t.secret)
	if err != nil {
		return nil, fmt.Errorf("failed to make an AES-GCM cipher from the ticket secret: %v", err)
	}
	return c, nil
}
makeCipher makes a AES-GCM cipher out of the ticket's secret
package persistence
import (
	"crypto/aes"
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
)
type saveFunc func(string, []byte, time.Duration) error
type loadFunc func(string) ([]byte, error)
type clearFunc func(string) error
type initLockFunc func(string) sessions.Lock
type ticket struct {
	id      string
	secret  []byte
	options *options.Cookie
}
func newTicket(cookieOpts *options.Cookie) (*ticket, error) {
	rawID := make([]byte, 16)
	if _, err := io.ReadFull(rand.Reader, rawID); err != nil {
		return nil, fmt.Errorf("failed to create new ticket ID: %v", err)
	}
	ticketID := fmt.Sprintf("%s-%s", cookieOpts.Name, hex.EncodeToString(rawID))
	secret := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(rand.Reader, secret); err != nil {
		return nil, fmt.Errorf("failed to create encryption secret: %v", err)
	}
	return &ticket{
		id:      ticketID,
		secret:  secret,
		options: cookieOpts,
	}, nil
}
func (t *ticket) encodeTicket() string {
	return fmt.Sprintf("%s.%s", t.id, base64.RawURLEncoding.EncodeToString(t.secret))
}
func decodeTicket(encTicket string, cookieOpts *options.Cookie) (*ticket, error) {
	ticketParts := strings.Split(encTicket, ".")
	if len(ticketParts) != 2 {
		return nil, errors.New("failed to decode ticket")
	}
	ticketID, secretBase64 := ticketParts[0], ticketParts[1]
	secret, err := base64.RawURLEncoding.DecodeString(secretBase64)
	if err != nil {
		return nil, fmt.Errorf("failed to decode encryption secret: %v", err)
	}
	return &ticket{
		id:      ticketID,
		secret:  secret,
		options: cookieOpts,
	}, nil
}
func decodeTicketFromRequest(req *http.Request, cookieOpts *options.Cookie) (*ticket, error) {
	requestCookie, err := req.Cookie(cookieOpts.Name)
	if err != nil {
		return nil, err
	}
	val, _, ok := encryption.Validate(requestCookie, cookieOpts.Secret, cookieOpts.Expire)
	if !ok {
		return nil, fmt.Errorf("session ticket cookie failed validation: %v", err)
	}
	return decodeTicket(string(val), cookieOpts)
}
func (t *ticket) saveSession(s *sessions.SessionState, saver saveFunc) error {
	c, err := t.makeCipher()
	if err != nil {
		return err
	}
	ciphertext, err := s.EncodeSessionState(c, false)
	if err != nil {
		return fmt.Errorf("failed to encode the session state with the ticket: %v", err)
	}
	return saver(t.id, ciphertext, t.options.Expire)
}
func (t *ticket) loadSession(loader loadFunc, initLock initLockFunc) (*sessions.SessionState, error) {
	ciphertext, err := loader(t.id)
	if err != nil {
		return nil, fmt.Errorf("failed to load the session state with the ticket: %v", err)
	}
	c, err := t.makeCipher()
	if err != nil {
		return nil, err
	}
	sessionState, err := sessions.DecodeSessionState(ciphertext, c, false)
	if err != nil {
		return nil, err
	}
	lock := initLock(t.id)
	sessionState.Lock = lock
	return sessionState, nil
}
func (t *ticket) clearSession(clearer clearFunc) error {
	return clearer(t.id)
}
func (t *ticket) setCookie(rw http.ResponseWriter, req *http.Request, s *sessions.SessionState) error {
	ticketCookie, err := t.makeCookie(
		req,
		t.encodeTicket(),
		t.options.Expire,
		*s.CreatedAt,
	)
	if err != nil {
		return err
	}
	http.SetCookie(rw, ticketCookie)
	return nil
}
func (t *ticket) clearCookie(rw http.ResponseWriter, req *http.Request) {
	http.SetCookie(rw, cookies.MakeCookieFromOptions(
		req,
		t.options.Name,
		"",
		t.options,
		time.Hour*-1,
		time.Now(),
	))
}
func (t *ticket) makeCookie(req *http.Request, value string, expires time.Duration, now time.Time) (*http.Cookie, error) {
	if value != "" {
		var err error
		value, err = encryption.SignedValue(t.options.Secret, t.options.Name, []byte(value), now)
		if err != nil {
			return nil, err
		}
	}
	return cookies.MakeCookieFromOptions(
		req,
		t.options.Name,
		value,
		t.options,
		expires,
		now,
	), nil
}