func (u *User) ValidPassword(password string) bool {
	return heat.Compare(u.PasswordHash, password) == nil
}
ValidPassword implements the flame.ResourceOwner interface.
package flame
import (
	"time"
	"github.com/256dpi/oauth2/v2"
	"github.com/256dpi/fire/coal"
	"github.com/256dpi/fire/heat"
	"github.com/256dpi/fire/stick"
)
type TokenType string
const (
	AccessToken TokenType = "access"
	RefreshToken TokenType = "refresh"
	AuthorizationCode TokenType = "code"
)
type TokenData struct {
	Type TokenType
	Scope oauth2.Scope
	ExpiresAt time.Time
	RedirectURI string
	Client        Client
	ResourceOwner ResourceOwner
	ClientID        coal.ID
	ResourceOwnerID *coal.ID
}
type GenericToken interface {
	coal.Model
	GetTokenData() TokenData
	SetTokenData(TokenData)
}
type Token struct {
	coal.Base   `json:"-" bson:",inline" coal:"tokens:tokens"`
	Type        TokenType `json:"type"`
	Scope       []string  `json:"scope"`
	ExpiresAt   time.Time `json:"expires-at" bson:"expires_at"`
	RedirectURI string    `json:"redirect-uri" bson:"redirect_uri"`
	Application coal.ID   `json:"-" bson:"application_id" coal:"application:applications"`
	User        *coal.ID  `json:"-" bson:"user_id" coal:"user:users"`
}
func AddTokenIndexes(catalog *coal.Catalog, removeAfter time.Duration) {
	catalog.AddIndex(&Token{}, false, 0, "Type")
	catalog.AddIndex(&Token{}, false, 0, "Application")
	catalog.AddIndex(&Token{}, false, 0, "User")
	if removeAfter > 0 {
		catalog.AddIndex(&Token{}, false, removeAfter, "ExpiresAt")
	}
}
func (t *Token) GetTokenData() TokenData {
	return TokenData{
		Type:            t.Type,
		Scope:           t.Scope,
		ExpiresAt:       t.ExpiresAt,
		RedirectURI:     t.RedirectURI,
		ClientID:        t.Application,
		ResourceOwnerID: t.User,
	}
}
func (t *Token) SetTokenData(data TokenData) {
	t.Type = data.Type
	t.Scope = data.Scope
	t.ExpiresAt = data.ExpiresAt
	t.RedirectURI = data.RedirectURI
	t.Application = data.Client.ID()
	if data.ResourceOwner != nil {
		t.User = coal.P(data.ResourceOwner.ID())
	}
}
func (t *Token) Validate() error {
	return stick.Validate(t, func(v *stick.Validator) {
		v.Value("Type", false, stick.IsNotZero, stick.IsValidUTF8)
		v.Items("Scope", stick.IsNotZero, stick.IsValidUTF8)
		v.Value("ExpiresAt", false, stick.IsNotZero)
		v.Value("RedirectURI", false, stick.IsValidUTF8)
		v.Value("Application", false, stick.IsNotZero)
		v.Value("User", true, stick.IsNotZero)
	})
}
type Client interface {
	coal.Model
	IsConfidential() bool
	ValidRedirectURI(string) bool
	ValidSecret(string) bool
}
type Application struct {
	coal.Base    `json:"-" bson:",inline" coal:"applications"`
	Name         string   `json:"name"`
	Key          string   `json:"key" coal:"flame-client-id"`
	Secret       string   `json:"secret,omitempty" bson:"-"`
	SecretHash   []byte   `json:"-" bson:"secret"`
	RedirectURIs []string `json:"redirect-uris" bson:"redirect_uris"`
}
func AddApplicationIndexes(catalog *coal.Catalog) {
	catalog.AddIndex(&Application{}, true, 0, "Key")
}
func (a *Application) IsConfidential() bool {
	return len(a.SecretHash) > 0
}
func (a *Application) ValidRedirectURI(uri string) bool {
	return stick.Contains(a.RedirectURIs, uri)
}
func (a *Application) ValidSecret(secret string) bool {
	return heat.Compare(a.SecretHash, secret) == nil
}
func (a *Application) Validate() error {
	err := a.HashSecret()
	if err != nil {
		return err
	}
	return stick.Validate(a, func(v *stick.Validator) {
		v.Value("Name", false, stick.IsNotZero, stick.IsValidUTF8)
		v.Value("Key", false, stick.IsNotZero, stick.IsValidUTF8)
		v.Items("RedirectURIs", stick.IsNotZero, stick.IsValidUTF8)
	})
}
func (a *Application) HashSecret() error {
	if len(a.Secret) == 0 {
		return nil
	}
	hash, err := heat.Hash(a.Secret)
	if err != nil {
		return err
	}
	a.SecretHash = hash
	a.Secret = ""
	return nil
}
type ResourceOwner interface {
	coal.Model
	ValidPassword(string) bool
}
type User struct {
	coal.Base    `json:"-" bson:",inline" coal:"users"`
	Name         string `json:"name"`
	Email        string `json:"email" coal:"flame-resource-owner-id"`
	Password     string `json:"password,omitempty" bson:"-"`
	PasswordHash []byte `json:"-" bson:"password"`
}
func AddUserIndexes(catalog *coal.Catalog) {
	catalog.AddIndex(&User{}, true, 0, "Email")
}