func (j *Service) Set(w http.ResponseWriter, claims Claims) (Claims, error) {
	if claims.ExpiresAt == 0 {
		claims.ExpiresAt = time.Now().Add(j.TokenDuration).Unix()
	}
	if claims.Issuer == "" {
		claims.Issuer = j.Issuer
	}
	if !j.DisableIAT {
		claims.IssuedAt = time.Now().Unix()
	}
	tokenString, err := j.Token(claims)
	if err != nil {
		return Claims{}, errors.Wrap(err, "failed to make token token")
	}
	if j.SendJWTHeader {
		w.Header().Set(j.JWTHeaderKey, tokenString)
		return claims, nil
	}
	cookieExpiration := 0  
	if !claims.SessionOnly && claims.Handshake == nil {
		cookieExpiration = int(j.CookieDuration.Seconds())
	}
	jwtCookie := http.Cookie{Name: j.JWTCookieName, Value: tokenString, HttpOnly: true, Path: "/", Domain: j.JWTCookieDomain,
		MaxAge: cookieExpiration, Secure: j.SecureCookies, SameSite: j.SameSite}
	http.SetCookie(w, &jwtCookie)
	xsrfCookie := http.Cookie{Name: j.XSRFCookieName, Value: claims.Id, HttpOnly: false, Path: "/", Domain: j.JWTCookieDomain,
		MaxAge: cookieExpiration, Secure: j.SecureCookies, SameSite: j.SameSite}
	http.SetCookie(w, &xsrfCookie)
	return claims, nil
}
Set creates token cookie with xsrf cookie and put it to ResponseWriter
accepts claims and sets expiration if none defined. permanent flag means long-living cookie,
false makes it session only.
package token
import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"
	"github.com/golang-jwt/jwt"
	"github.com/pkg/errors"
)
type Service struct {
	Opts
}
type Claims struct {
	jwt.StandardClaims
	User        *User      `json:"user,omitempty"`  
	SessionOnly bool       `json:"sess_only,omitempty"`
	Handshake   *Handshake `json:"handshake,omitempty"`  
	NoAva       bool       `json:"no-ava,omitempty"`     
}
type Handshake struct {
	State string `json:"state,omitempty"`
	From  string `json:"from,omitempty"`
	ID    string `json:"id,omitempty"`
}
const (
	defaultJWTCookieName   = "JWT"
	defaultJWTCookieDomain = ""
	defaultJWTHeaderKey    = "X-JWT"
	defaultXSRFCookieName  = "XSRF-TOKEN"
	defaultXSRFHeaderKey   = "X-XSRF-TOKEN"
	defaultIssuer = "go-pkgz/auth"
	defaultTokenDuration  = time.Minute * 15
	defaultCookieDuration = time.Hour * 24 * 31
	defaultTokenQuery = "token"
)
type Opts struct {
	SecretReader   Secret
	ClaimsUpd      ClaimsUpdater
	SecureCookies  bool
	TokenDuration  time.Duration
	CookieDuration time.Duration
	DisableXSRF    bool
	DisableIAT     bool  
	JWTCookieName   string
	JWTCookieDomain string
	JWTHeaderKey    string
	XSRFCookieName  string
	XSRFHeaderKey   string
	JWTQuery        string
	AudienceReader  Audience       
	Issuer          string         
	AudSecrets      bool           
	SendJWTHeader   bool           
	SameSite        http.SameSite  
}
func NewService(opts Opts) *Service {
	res := Service{Opts: opts}
	setDefault := func(fld *string, def string) {
		if *fld == "" {
			*fld = def
		}
	}
	setDefault(&res.JWTCookieName, defaultJWTCookieName)
	setDefault(&res.JWTHeaderKey, defaultJWTHeaderKey)
	setDefault(&res.XSRFCookieName, defaultXSRFCookieName)
	setDefault(&res.XSRFHeaderKey, defaultXSRFHeaderKey)
	setDefault(&res.JWTQuery, defaultTokenQuery)
	setDefault(&res.Issuer, defaultIssuer)
	setDefault(&res.JWTCookieDomain, defaultJWTCookieDomain)
	if opts.TokenDuration == 0 {
		res.TokenDuration = defaultTokenDuration
	}
	if opts.CookieDuration == 0 {
		res.CookieDuration = defaultCookieDuration
	}
	return &res
}
func (j *Service) Token(claims Claims) (string, error) {
	if j.ClaimsUpd != nil {
		claims = j.ClaimsUpd.Update(claims)
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	if j.SecretReader == nil {
		return "", errors.New("secret reader not defined")
	}
	if err := j.checkAuds(&claims, j.AudienceReader); err != nil {
		return "", errors.Wrap(err, "aud rejected")
	}
	secret, err := j.SecretReader.Get(claims.Audience)  
	if err != nil {
		return "", errors.Wrap(err, "can't get secret")
	}
	tokenString, err := token.SignedString([]byte(secret))
	if err != nil {
		return "", errors.Wrap(err, "can't sign token")
	}
	return tokenString, nil
}
func (j *Service) Parse(tokenString string) (Claims, error) {
	parser := jwt.Parser{SkipClaimsValidation: true}  
	if j.SecretReader == nil {
		return Claims{}, errors.New("secret reader not defined")
	}
	aud := "ignore"
	if j.AudSecrets {
		var err error
		aud, err = j.aud(tokenString)
		if err != nil {
			return Claims{}, errors.New("can't retrieve audience from the token")
		}
	}
	secret, err := j.SecretReader.Get(aud)
	if err != nil {
		return Claims{}, errors.Wrap(err, "can't get secret")
	}
	token, err := parser.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return []byte(secret), nil
	})
	if err != nil {
		return Claims{}, errors.Wrap(err, "can't parse token")
	}
	claims, ok := token.Claims.(*Claims)
	if !ok {
		return Claims{}, errors.New("invalid token")
	}
	if err = j.checkAuds(claims, j.AudienceReader); err != nil {
		return Claims{}, errors.Wrap(err, "aud rejected")
	}
	return *claims, j.validate(claims)
}
func (j *Service) aud(tokenString string) (string, error) {
	parser := jwt.Parser{}
	token, _, err := parser.ParseUnverified(tokenString, &Claims{})
	if err != nil {
		return "", errors.Wrap(err, "can't pre-parse token")
	}
	claims, ok := token.Claims.(*Claims)
	if !ok {
		return "", errors.New("invalid token")
	}
	if strings.TrimSpace(claims.Audience) == "" {
		return "", errors.New("empty aud")
	}
	return claims.Audience, nil
}
func (j *Service) validate(claims *Claims) error {
	cerr := claims.Valid()
	if cerr == nil {
		return nil
	}
	if e, ok := cerr.(*jwt.ValidationError); ok {
		if e.Errors == jwt.ValidationErrorExpired {
			return nil  
		}
	}
	return cerr
}