func (f ClaimsUpdFunc) Update(claims Claims) Claims {
	return f(claims)
}
Update calls f(id)
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
}
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
}
func (j *Service) Get(r *http.Request) (Claims, string, error) {
	fromCookie := false
	tokenString := ""
	if tkQuery := r.URL.Query().Get(j.JWTQuery); tkQuery != "" {
		tokenString = tkQuery
	}
	if tokenHeader := r.Header.Get(j.JWTHeaderKey); tokenHeader != "" && tokenString == "" {
		tokenString = tokenHeader
	}
	if tokenString == "" {
		fromCookie = true
		jc, err := r.Cookie(j.JWTCookieName)
		if err != nil {
			return Claims{}, "", errors.Wrap(err, "token cookie was not presented")
		}
		tokenString = jc.Value
	}
	claims, err := j.Parse(tokenString)
	if err != nil {
		return Claims{}, "", errors.Wrap(err, "failed to get token")
	}
	if claims.User != nil {
		claims.User.Audience = claims.Audience
	}
	if !fromCookie && j.IsExpired(claims) {
		return Claims{}, "", errors.New("token expired")
	}
	if j.DisableXSRF {
		return claims, tokenString, nil
	}
	if fromCookie && claims.User != nil {
		xsrf := r.Header.Get(j.XSRFHeaderKey)
		if claims.Id != xsrf {
			return Claims{}, "", errors.New("xsrf mismatch")
		}
	}
	return claims, tokenString, nil
}
func (j *Service) IsExpired(claims Claims) bool {
	return !claims.VerifyExpiresAt(time.Now().Unix(), true)
}
func (j *Service) Reset(w http.ResponseWriter) {
	jwtCookie := http.Cookie{Name: j.JWTCookieName, Value: "", HttpOnly: false, Path: "/", Domain: j.JWTCookieDomain,
		MaxAge: -1, Expires: time.Unix(0, 0), Secure: j.SecureCookies, SameSite: j.SameSite}
	http.SetCookie(w, &jwtCookie)
	xsrfCookie := http.Cookie{Name: j.XSRFCookieName, Value: "", HttpOnly: false, Path: "/", Domain: j.JWTCookieDomain,
		MaxAge: -1, Expires: time.Unix(0, 0), Secure: j.SecureCookies, SameSite: j.SameSite}
	http.SetCookie(w, &xsrfCookie)
}
func (j *Service) checkAuds(claims *Claims, audReader Audience) error {
	if audReader == nil {  
		return nil
	}
	auds, err := audReader.Get()
	if err != nil {
		return errors.Wrap(err, "failed to get auds")
	}
	for _, a := range auds {
		if strings.EqualFold(a, claims.Audience) {
			return nil
		}
	}
	return errors.Errorf("aud %q not allowed", claims.Audience)
}
func (c Claims) String() string {
	b, err := json.Marshal(c)
	if err != nil {
		return fmt.Sprintf("%+v %+v", c.StandardClaims, c.User)
	}
	return string(b)
}
type Secret interface {
	Get(aud string) (string, error)  
}
type SecretFunc func(aud string) (string, error)
func (f SecretFunc) Get(aud string) (string, error) {
	return f(aud)
}
type ClaimsUpdater interface {
	Update(claims Claims) Claims
}
type ClaimsUpdFunc func(claims Claims) Claims