func (m MemStorer) NewFromOAuth2(_ context.Context, provider string, details map[string]string) (authboss.OAuth2User, error) {
	switch provider {
	case "google":
		email := details[aboauth.OAuth2Email]
		var user *User
		if u, ok := m.Users[email]; ok {
			user = &u
		} else {
			user = &User{}
		}
		user.Name = "Unknown"
		user.Email = details[aboauth.OAuth2Email]
		user.OAuth2UID = details[aboauth.OAuth2UID]
		user.Confirmed = true
		return user, nil
	}
	return nil, errors.Errorf("unknown provider %s", provider)
}
NewFromOAuth2 creates an oauth2 user (but not in the database, just a blank one to be saved later)
package main
import (
	"context"
	"time"
	"github.com/davecgh/go-spew/spew"
	"github.com/pkg/errors"
	"github.com/volatiletech/authboss/v3"
	aboauth "github.com/volatiletech/authboss/v3/oauth2"
	"github.com/volatiletech/authboss/v3/otp/twofactor/sms2fa"
	"github.com/volatiletech/authboss/v3/otp/twofactor/totp2fa"
)
type User struct {
	ID int
	Name string
	Email    string
	Password string
	ConfirmSelector string
	ConfirmVerifier string
	Confirmed       bool
	AttemptCount int
	LastAttempt  time.Time
	Locked       time.Time
	RecoverSelector    string
	RecoverVerifier    string
	RecoverTokenExpiry time.Time
	OAuth2UID          string
	OAuth2Provider     string
	OAuth2AccessToken  string
	OAuth2RefreshToken string
	OAuth2Expiry       time.Time
	TOTPSecretKey      string
	SMSPhoneNumber     string
	SMSSeedPhoneNumber string
	RecoveryCodes      string
}
var (
	assertUser   = &User{}
	assertStorer = &MemStorer{}
	_ authboss.User            = assertUser
	_ authboss.AuthableUser    = assertUser
	_ authboss.ConfirmableUser = assertUser
	_ authboss.LockableUser    = assertUser
	_ authboss.RecoverableUser = assertUser
	_ authboss.ArbitraryUser   = assertUser
	_ totp2fa.User = assertUser
	_ sms2fa.User  = assertUser
	_ authboss.CreatingServerStorer    = assertStorer
	_ authboss.ConfirmingServerStorer  = assertStorer
	_ authboss.RecoveringServerStorer  = assertStorer
	_ authboss.RememberingServerStorer = assertStorer
)
func (u *User) PutPID(pid string) { u.Email = pid }
func (u *User) PutPassword(password string) { u.Password = password }
func (u *User) PutEmail(email string) { u.Email = email }
func (u *User) PutConfirmed(confirmed bool) { u.Confirmed = confirmed }
func (u *User) PutConfirmSelector(confirmSelector string) { u.ConfirmSelector = confirmSelector }
func (u *User) PutConfirmVerifier(confirmVerifier string) { u.ConfirmVerifier = confirmVerifier }
func (u *User) PutLocked(locked time.Time) { u.Locked = locked }
func (u *User) PutAttemptCount(attempts int) { u.AttemptCount = attempts }
func (u *User) PutLastAttempt(last time.Time) { u.LastAttempt = last }
func (u *User) PutRecoverSelector(token string) { u.RecoverSelector = token }
func (u *User) PutRecoverVerifier(token string) { u.RecoverVerifier = token }
func (u *User) PutRecoverExpiry(expiry time.Time) { u.RecoverTokenExpiry = expiry }
func (u *User) PutTOTPSecretKey(key string) { u.TOTPSecretKey = key }
func (u *User) PutSMSPhoneNumber(key string) { u.SMSPhoneNumber = key }
func (u *User) PutRecoveryCodes(key string) { u.RecoveryCodes = key }
func (u *User) PutOAuth2UID(uid string) { u.OAuth2UID = uid }
func (u *User) PutOAuth2Provider(provider string) { u.OAuth2Provider = provider }
func (u *User) PutOAuth2AccessToken(token string) { u.OAuth2AccessToken = token }
func (u *User) PutOAuth2RefreshToken(refreshToken string) { u.OAuth2RefreshToken = refreshToken }
func (u *User) PutOAuth2Expiry(expiry time.Time) { u.OAuth2Expiry = expiry }
func (u *User) PutArbitrary(values map[string]string) {
	if n, ok := values["name"]; ok {
		u.Name = n
	}
}
func (u User) GetPID() string { return u.Email }
func (u User) GetPassword() string { return u.Password }
func (u User) GetEmail() string { return u.Email }
func (u User) GetConfirmed() bool { return u.Confirmed }
func (u User) GetConfirmSelector() string { return u.ConfirmSelector }
func (u User) GetConfirmVerifier() string { return u.ConfirmVerifier }
func (u User) GetLocked() time.Time { return u.Locked }
func (u User) GetAttemptCount() int { return u.AttemptCount }
func (u User) GetLastAttempt() time.Time { return u.LastAttempt }
func (u User) GetRecoverSelector() string { return u.RecoverSelector }
func (u User) GetRecoverVerifier() string { return u.RecoverVerifier }
func (u User) GetRecoverExpiry() time.Time { return u.RecoverTokenExpiry }
func (u User) GetTOTPSecretKey() string { return u.TOTPSecretKey }
func (u User) GetSMSPhoneNumber() string { return u.SMSPhoneNumber }
func (u User) GetSMSPhoneNumberSeed() string { return u.SMSSeedPhoneNumber }
func (u User) GetRecoveryCodes() string { return u.RecoveryCodes }
func (u User) IsOAuth2User() bool { return len(u.OAuth2UID) != 0 }
func (u User) GetOAuth2UID() (uid string) { return u.OAuth2UID }
func (u User) GetOAuth2Provider() (provider string) { return u.OAuth2Provider }
func (u User) GetOAuth2AccessToken() (token string) { return u.OAuth2AccessToken }
func (u User) GetOAuth2RefreshToken() (refreshToken string) { return u.OAuth2RefreshToken }
func (u User) GetOAuth2Expiry() (expiry time.Time) { return u.OAuth2Expiry }
func (u User) GetArbitrary() map[string]string {
	return map[string]string{
		"name": u.Name,
	}
}
type MemStorer struct {
	Users  map[string]User
	Tokens map[string][]string
}
func NewMemStorer() *MemStorer {
	return &MemStorer{
		Users: map[string]User{
			"rick@councilofricks.com": {
				ID:                 1,
				Name:               "Rick",
				Password:           "$2a$10$XtW/BrS5HeYIuOCXYe8DFuInetDMdaarMUJEOg/VA/JAIDgw3l4aG",  
				Email:              "rick@councilofricks.com",
				Confirmed:          true,
				SMSSeedPhoneNumber: "(777)-123-4567",
			},
		},
		Tokens: make(map[string][]string),
	}
}
func (m MemStorer) Save(_ context.Context, user authboss.User) error {
	u := user.(*User)
	m.Users[u.Email] = *u
	debugln("Saved user:", u.Name)
	return nil
}
func (m MemStorer) Load(_ context.Context, key string) (user authboss.User, err error) {
	provider, uid, err := authboss.ParseOAuth2PID(key)
	if err == nil {
		for _, u := range m.Users {
			if u.OAuth2Provider == provider && u.OAuth2UID == uid {
				debugln("Loaded OAuth2 user:", u.Email)
				return &u, nil
			}
		}
		return nil, authboss.ErrUserNotFound
	}
	u, ok := m.Users[key]
	if !ok {
		return nil, authboss.ErrUserNotFound
	}
	debugln("Loaded user:", u.Name)
	return &u, nil
}
func (m MemStorer) New(_ context.Context) authboss.User {
	return &User{}
}
func (m MemStorer) Create(_ context.Context, user authboss.User) error {
	u := user.(*User)
	if _, ok := m.Users[u.Email]; ok {
		return authboss.ErrUserFound
	}
	debugln("Created new user:", u.Name)
	m.Users[u.Email] = *u
	return nil
}
func (m MemStorer) LoadByConfirmSelector(_ context.Context, selector string) (user authboss.ConfirmableUser, err error) {
	for _, v := range m.Users {
		if v.ConfirmSelector == selector {
			debugln("Loaded user by confirm selector:", selector, v.Name)
			return &v, nil
		}
	}
	return nil, authboss.ErrUserNotFound
}
func (m MemStorer) LoadByRecoverSelector(_ context.Context, selector string) (user authboss.RecoverableUser, err error) {
	for _, v := range m.Users {
		if v.RecoverSelector == selector {
			debugln("Loaded user by recover selector:", selector, v.Name)
			return &v, nil
		}
	}
	return nil, authboss.ErrUserNotFound
}
func (m MemStorer) AddRememberToken(_ context.Context, pid, token string) error {
	m.Tokens[pid] = append(m.Tokens[pid], token)
	debugf("Adding rm token to %s: %s\n", pid, token)
	spew.Dump(m.Tokens)
	return nil
}
func (m MemStorer) DelRememberTokens(_ context.Context, pid string) error {
	delete(m.Tokens, pid)
	debugln("Deleting rm tokens from:", pid)
	spew.Dump(m.Tokens)
	return nil
}
func (m MemStorer) UseRememberToken(_ context.Context, pid, token string) error {
	tokens, ok := m.Tokens[pid]
	if !ok {
		debugln("Failed to find rm tokens for:", pid)
		return authboss.ErrTokenNotFound
	}
	for i, tok := range tokens {
		if tok == token {
			tokens[len(tokens)-1] = tokens[i]
			m.Tokens[pid] = tokens[:len(tokens)-1]
			debugf("Used remember for %s: %s\n", pid, token)
			return nil
		}
	}
	return authboss.ErrTokenNotFound
}