func Validate(token string, signingKey []byte) (*Claims, *session.Session, error) {
	parsed, err := jwt.ParseWithClaims(token, &Claims{},
		func(token *jwt.Token) (interface{}, error) {
			return signingKey, nil
		}, jwt.WithAudience(Audience))
	if err != nil {
		return nil, nil, err
	}
	c := parsed.Claims.(*Claims)
	ret := c.Message
	if c.ExpiresAt != nil {
		ret.ExpiresAt = timestamppb.New(c.ExpiresAt.Time)
	}
	if c.ID != "" {
		u, err := uuid.Parse(c.ID)
		if err != nil {
			return nil, nil, err
		}
		ret.ID = &session.ID{Data: u[:]}
	}
	if c.Subject != "" {
		u, err := uuid.Parse(c.Subject)
		if err != nil {
			return nil, nil, err
		}
		ret.PrincipalId = &principal.ID{Data: u[:]}
	}
	if c.Issuer != "" && ret.GetScope().GetOnLocation() != nil {
		u, err := uuid.Parse(c.Issuer)
		if err != nil {
			return nil, nil, err
		}
		ret.GetScope().GetOnLocation().TenantId = &tenant.ID{Data: u[:]}
	}
	return c, ret, nil
}
Validate will validate the token and expand its contents into a
Session. Not all fields of the Session will be populated.
package claims
import (
	"time"
	"github.com/bobvawter/cacheroach/api/principal"
	"github.com/bobvawter/cacheroach/api/session"
	"github.com/bobvawter/cacheroach/api/tenant"
	"github.com/bobvawter/cacheroach/api/token"
	"github.com/dgrijalva/jwt-go/v4"
	"github.com/google/uuid"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
)
const Audience = "cacheroach"
type Claims struct {
	Message *session.Session `json:"msg,omitempty"`
	jwt.StandardClaims
}
func Sign(now time.Time, sn *session.Session, key []byte) (*Claims, *token.Token, error) {
	cl := &Claims{
		Message: &session.Session{
			Capabilities: sn.Capabilities,
			Name:         sn.Name,
			Scope:        sn.Scope,
		},
		StandardClaims: jwt.StandardClaims{
			Audience: jwt.ClaimStrings{Audience},
			IssuedAt: &jwt.Time{Time: now},
		},
	}
	if sn.ExpiresAt.GetSeconds() > 0 {
		cl.ExpiresAt = &jwt.Time{Time: sn.ExpiresAt.AsTime()}
	}
	if !sn.PrincipalId.Zero() {
		cl.Subject = sn.PrincipalId.AsUUID().String()
	}
	if !sn.ID.Zero() {
		cl.ID = sn.ID.AsUUID().String()
	}
	if tnt := cl.Message.GetScope().GetOnLocation().GetTenantId(); tnt != nil {
		cl.Message.Scope = proto.Clone(cl.Message.Scope).(*session.Scope)
		cl.Issuer = tnt.AsUUID().String()
		cl.Message.Scope.GetOnLocation().TenantId = nil
	}
	tkn := jwt.NewWithClaims(jwt.SigningMethodHS256, cl)
	signed, err := tkn.SignedString(key)
	if err != nil {
		return nil, nil, err
	}
	return cl, &token.Token{Jwt: signed}, nil
}