Help us shape the future of Go! Take the 2026 Go Developer Survey

sqlite

package
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package sqlite implements the AuthSome store interface using grove ORM (SQLite).

Index

Constants

This section is empty.

Variables

View Source
var Migrations = migrate.NewGroup("authsome")

Migrations is the grove migration group for the AuthSome store (SQLite).

Functions

This section is empty.

Types

type APIKeyModel

type APIKeyModel struct {
	grove.BaseModel `grove:"table:authsome_api_keys,alias:ak"`

	ID              string       `grove:"id,pk"`
	AppID           string       `grove:"app_id,notnull"`
	EnvID           string       `grove:"env_id,notnull"`
	UserID          string       `grove:"user_id,notnull"`
	Name            string       `grove:"name,notnull"`
	KeyHash         string       `grove:"key_hash,notnull"`
	KeyPrefix       string       `grove:"key_prefix,notnull"`
	PublicKey       string       `grove:"public_key,notnull"`
	PublicKeyPrefix string       `grove:"public_key_prefix,notnull"`
	Scopes          string       `grove:"scopes"` // comma-separated
	ExpiresAt       sql.NullTime `grove:"expires_at"`
	LastUsedAt      sql.NullTime `grove:"last_used_at"`
	Revoked         bool         `grove:"revoked"`
	CreatedAt       time.Time    `grove:"created_at,notnull,default:now()"`
	UpdatedAt       time.Time    `grove:"updated_at,notnull,default:now()"`
}

type AppClientConfigModel

type AppClientConfigModel struct {
	grove.BaseModel `grove:"table:authsome_app_client_configs,alias:acc"`

	ID                       string          `grove:"id,pk"`
	AppID                    string          `grove:"app_id,notnull"`
	SignupEnabled            *bool           `grove:"signup_enabled"`
	PasswordEnabled          *bool           `grove:"password_enabled"`
	PasskeyEnabled           *bool           `grove:"passkey_enabled"`
	MagicLinkEnabled         *bool           `grove:"magic_link_enabled"`
	MFAEnabled               *bool           `grove:"mfa_enabled"`
	MFARequired              *bool           `grove:"mfa_required"`
	SSOEnabled               *bool           `grove:"sso_enabled"`
	SocialEnabled            *bool           `grove:"social_enabled"`
	WaitlistEnabled          *bool           `grove:"waitlist_enabled"`
	RequireEmailVerification *bool           `grove:"require_email_verification"`
	SocialProviders          json.RawMessage `grove:"social_providers"`
	MFAMethods               json.RawMessage `grove:"mfa_methods"`
	AppName                  *string         `grove:"app_name"`
	LogoURL                  *string         `grove:"logo_url"`
	CreatedAt                time.Time       `grove:"created_at,notnull"`
	UpdatedAt                time.Time       `grove:"updated_at,notnull"`
}

type AppModel

type AppModel struct {
	grove.BaseModel `grove:"table:authsome_apps,alias:a"`

	ID             string          `grove:"id,pk"`
	Name           string          `grove:"name,notnull"`
	Slug           string          `grove:"slug,notnull"`
	PublishableKey string          `grove:"publishable_key"`
	IsPlatform     bool            `grove:"is_platform"`
	Metadata       json.RawMessage `grove:"metadata,type:jsonb"`
	CreatedAt      time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt      time.Time       `grove:"updated_at,notnull,default:now()"`
}

type AppSessionConfigModel

type AppSessionConfigModel struct {
	grove.BaseModel `grove:"table:authsome_app_session_configs,alias:asc"`

	ID                     string        `grove:"id,pk"`
	AppID                  string        `grove:"app_id,notnull"`
	TokenTTLSeconds        sql.NullInt64 `grove:"token_ttl_seconds"`
	RefreshTokenTTLSeconds sql.NullInt64 `grove:"refresh_token_ttl_seconds"`
	MaxActiveSessions      sql.NullInt64 `grove:"max_active_sessions"`
	RotateRefreshToken     sql.NullBool  `grove:"rotate_refresh_token"`
	BindToIP               sql.NullBool  `grove:"bind_to_ip"`
	BindToDevice           sql.NullBool  `grove:"bind_to_device"`
	TokenFormat            string        `grove:"token_format"`
	CreatedAt              time.Time     `grove:"created_at,notnull,default:now()"`
	UpdatedAt              time.Time     `grove:"updated_at,notnull,default:now()"`
}

type BrandingConfigModel

type BrandingConfigModel struct {
	grove.BaseModel `grove:"table:authsome_branding_configs,alias:bc"`

	ID              string    `grove:"id,pk"`
	OrgID           string    `grove:"org_id,notnull"`
	AppID           string    `grove:"app_id,notnull"`
	LogoURL         string    `grove:"logo_url"`
	PrimaryColor    string    `grove:"primary_color"`
	BackgroundColor string    `grove:"background_color"`
	AccentColor     string    `grove:"accent_color"`
	FontFamily      string    `grove:"font_family"`
	CustomCSS       string    `grove:"custom_css"`
	CompanyName     string    `grove:"company_name"`
	Tagline         string    `grove:"tagline"`
	CreatedAt       time.Time `grove:"created_at,notnull"`
	UpdatedAt       time.Time `grove:"updated_at,notnull"`
}

type DelegationModel

type DelegationModel struct {
	grove.BaseModel `grove:"table:authsome_delegations,alias:dl"`

	ID            string          `grove:"id,pk"`
	AppID         string          `grove:"app_id,notnull"`
	OrgID         string          `grove:"org_id"`
	ActorKind     string          `grove:"actor_kind,notnull"`
	ActorID       string          `grove:"actor_id,notnull"`
	SubjectKind   string          `grove:"subject_kind,notnull"`
	SubjectID     string          `grove:"subject_id,notnull"`
	GrantKind     string          `grove:"grant_kind,notnull"`
	Scopes        json.RawMessage `grove:"scopes,type:jsonb"`
	GrantedByKind string          `grove:"granted_by_kind"`
	GrantedByID   string          `grove:"granted_by_id"`
	ExpiresAt     sql.NullTime    `grove:"expires_at"`
	RevokedAt     sql.NullTime    `grove:"revoked_at"`
	CreatedAt     time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt     time.Time       `grove:"updated_at,notnull,default:now()"`
}

type DeviceModel

type DeviceModel struct {
	grove.BaseModel `grove:"table:authsome_devices,alias:d"`

	ID          string    `grove:"id,pk"`
	UserID      string    `grove:"user_id,notnull"`
	AppID       string    `grove:"app_id,notnull"`
	EnvID       string    `grove:"env_id,notnull"`
	Name        string    `grove:"name"`
	Type        string    `grove:"type"`
	Browser     string    `grove:"browser"`
	OS          string    `grove:"os"`
	IPAddress   string    `grove:"ip_address"`
	Fingerprint string    `grove:"fingerprint"`
	Trusted     bool      `grove:"trusted"`
	LastSeenAt  time.Time `grove:"last_seen_at,notnull,default:now()"`
	CreatedAt   time.Time `grove:"created_at,notnull,default:now()"`
	UpdatedAt   time.Time `grove:"updated_at,notnull,default:now()"`
}

type EnvironmentModel

type EnvironmentModel struct {
	grove.BaseModel `grove:"table:authsome_environments,alias:env"`

	ID          string          `grove:"id,pk"`
	AppID       string          `grove:"app_id,notnull"`
	Name        string          `grove:"name,notnull"`
	Slug        string          `grove:"slug,notnull"`
	Type        string          `grove:"type,notnull"`
	IsDefault   bool            `grove:"is_default"`
	Color       string          `grove:"color"`
	Description string          `grove:"description"`
	Settings    json.RawMessage `grove:"settings,type:jsonb"`
	ClonedFrom  string          `grove:"cloned_from"`
	Metadata    json.RawMessage `grove:"metadata,type:jsonb"`
	CreatedAt   time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt   time.Time       `grove:"updated_at,notnull,default:now()"`
}

type FormConfigModel

type FormConfigModel struct {
	grove.BaseModel `grove:"table:authsome_form_configs,alias:fc"`

	ID        string          `grove:"id,pk"`
	AppID     string          `grove:"app_id,notnull"`
	FormType  string          `grove:"form_type,notnull"`
	Fields    json.RawMessage `grove:"fields"`
	Active    bool            `grove:"active"`
	Version   int             `grove:"version,notnull"`
	CreatedAt time.Time       `grove:"created_at,notnull"`
	UpdatedAt time.Time       `grove:"updated_at,notnull"`
}

type InvitationModel

type InvitationModel struct {
	grove.BaseModel `grove:"table:authsome_invitations,alias:inv"`

	ID        string         `grove:"id,pk"`
	OrgID     string         `grove:"org_id,notnull"`
	Email     string         `grove:"email,notnull"`
	Role      string         `grove:"role,notnull"`
	InviterID string         `grove:"inviter_id,notnull"`
	Status    string         `grove:"status,notnull"`
	Token     string         `grove:"token,notnull"`
	TokenHash sql.NullString `grove:"token_hash"`
	ExpiresAt time.Time      `grove:"expires_at,notnull"`
	CreatedAt time.Time      `grove:"created_at,notnull,default:now()"`
}

type KVModel

type KVModel struct {
	grove.BaseModel `grove:"table:authsome_kv,alias:kv"`

	Key       string `grove:"key,pk"`
	Value     []byte `grove:"value"`
	Counter   int64  `grove:"counter,notnull"`
	ExpiresAt int64  `grove:"expires_at,notnull"`
}

KVModel is one authsome_kv row: a caller-chosen key, an opaque value, a counter for increments, and an expiry in unix milliseconds.

type MemberModel

type MemberModel struct {
	grove.BaseModel `grove:"table:authsome_members,alias:m"`

	ID        string    `grove:"id,pk"`
	OrgID     string    `grove:"org_id,notnull"`
	UserID    string    `grove:"user_id,notnull"`
	Role      string    `grove:"role,notnull"`
	CreatedAt time.Time `grove:"created_at,notnull,default:now()"`
	UpdatedAt time.Time `grove:"updated_at,notnull,default:now()"`
}

type NotificationModel

type NotificationModel struct {
	grove.BaseModel `grove:"table:authsome_notifications,alias:n"`

	ID        string       `grove:"id,pk"`
	AppID     string       `grove:"app_id,notnull"`
	EnvID     string       `grove:"env_id,notnull"`
	UserID    string       `grove:"user_id,notnull"`
	Type      string       `grove:"type,notnull"`
	Channel   string       `grove:"channel,notnull"`
	Subject   string       `grove:"subject"`
	Body      string       `grove:"body"`
	Sent      bool         `grove:"sent"`
	SentAt    sql.NullTime `grove:"sent_at"`
	CreatedAt time.Time    `grove:"created_at,notnull,default:now()"`
}

type OrganizationModel

type OrganizationModel struct {
	grove.BaseModel `grove:"table:authsome_organizations,alias:o"`

	ID        string          `grove:"id,pk"`
	AppID     string          `grove:"app_id,notnull"`
	EnvID     string          `grove:"env_id,notnull"`
	Name      string          `grove:"name,notnull"`
	Slug      string          `grove:"slug,notnull"`
	Metadata  json.RawMessage `grove:"metadata,type:jsonb"`
	CreatedBy string          `grove:"created_by,notnull"`
	CreatedAt time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt time.Time       `grove:"updated_at,notnull,default:now()"`
}

type PasswordResetModel

type PasswordResetModel struct {
	grove.BaseModel `grove:"table:authsome_password_resets,alias:pr"`

	ID        string         `grove:"id,pk"`
	AppID     string         `grove:"app_id,notnull"`
	EnvID     string         `grove:"env_id,notnull"`
	UserID    string         `grove:"user_id,notnull"`
	Token     string         `grove:"token,notnull"`
	TokenHash sql.NullString `grove:"token_hash"`
	ExpiresAt time.Time      `grove:"expires_at,notnull"`
	Consumed  bool           `grove:"consumed"`
	CreatedAt time.Time      `grove:"created_at,notnull,default:now()"`
}

type RevokedRefreshTokenModel

type RevokedRefreshTokenModel struct {
	grove.BaseModel `grove:"table:authsome_revoked_refresh_tokens,alias:rrt"`

	TokenHash string    `grove:"token_hash,pk"`
	FamilyID  string    `grove:"family_id,notnull"`
	RevokedAt time.Time `grove:"revoked_at,notnull"`
	Reason    string    `grove:"reason"`
}

RevokedRefreshTokenModel is the on-disk representation used for refresh-token replay detection. token_hash is the SHA-256 hex of a previously-rotated or replay-flagged refresh token.

type ServiceAccountModel

type ServiceAccountModel struct {
	grove.BaseModel `grove:"table:authsome_service_accounts,alias:sa"`

	ID    string `grove:"id,pk"`
	AppID string `grove:"app_id,notnull"`
	EnvID string `grove:"env_id"`
	OrgID string `grove:"org_id"`
	// Kind is empty on no row this store writes, but stays nullable-tolerant
	// so a row inserted by hand without it still reads as a service account.
	Kind        string          `grove:"kind"`
	Name        string          `grove:"name,notnull"`
	Description string          `grove:"description"`
	Scopes      json.RawMessage `grove:"scopes,type:jsonb"`
	OwnerUserID string          `grove:"owner_user_id"`
	ParentID    string          `grove:"parent_id"`
	ExpiresAt   sql.NullTime    `grove:"expires_at"`
	Active      bool            `grove:"active,notnull"`
	CreatedAt   time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt   time.Time       `grove:"updated_at,notnull,default:now()"`
}

type SessionModel

type SessionModel struct {
	grove.BaseModel `grove:"table:authsome_sessions,alias:s"`

	ID    string `grove:"id,pk"`
	AppID string `grove:"app_id,notnull"`
	EnvID string `grove:"env_id,notnull"`
	// UserID is empty for a service-account session, which has no user behind
	// it. That is the same sentinel-empty convention every other optional id
	// column here uses (org_id, device_id, family_id, impersonated_by).
	UserID string `grove:"user_id,notnull"`
	// PrincipalKind and ServiceAccountID carry which kind of principal owns the
	// session. Empty PrincipalKind means "user" for rows written before these
	// columns existed, so neither direction normalizes it — inventing "user" on
	// read would make a legacy row indistinguishable from one deliberately
	// stamped, and this value is branched on to make authorization decisions.
	PrincipalKind    string `grove:"principal_kind"`
	ServiceAccountID string `grove:"service_account_id"`
	// AgentID and GrantID carry the agent principal and the grant that
	// authorized it, mapped the same way as ServiceAccountID above.
	AgentID  string `grove:"agent_id"`
	GrantID  string `grove:"grant_id"`
	OrgID    string `grove:"org_id"`
	FamilyID string `grove:"family_id"`
	// Token and RefreshToken are empty on every row written since
	// hash_session_tokens; they hold plaintext only on rows older than that
	// migration, which the first lookup rewrites. TokenHash and
	// RefreshTokenHash are NULL on those legacy rows so the unique index
	// skips them.
	Token            string         `grove:"token,notnull"`
	RefreshToken     string         `grove:"refresh_token,notnull"`
	TokenHash        sql.NullString `grove:"token_hash"`
	RefreshTokenHash sql.NullString `grove:"refresh_token_hash"`
	IPAddress        string         `grove:"ip_address"`
	UserAgent        string         `grove:"user_agent"`
	DeviceID         string         `grove:"device_id"`
	ImpersonatedBy   string         `grove:"impersonated_by"`
	// Roles is JSON rather than the comma-separated form APIKeyModel.Scopes
	// uses. A slug containing a comma would split into two role names nobody
	// was ever granted, and these strings are read back as an authorization
	// decision, so the encoding must not be able to invent a member.
	Roles json.RawMessage `grove:"roles,type:jsonb"`
	// Audience is JSON for the same reason Roles is. A resource identifier is
	// a URI and may contain a comma, and these strings are compared to decide
	// whether a token is allowed to authenticate at all.
	Audience json.RawMessage `grove:"audience,type:jsonb"`
	// Scopes is JSON for the same reason Roles is: these strings are read back
	// as an authorization decision, so the encoding must not be able to invent
	// or merge a member.
	Scopes json.RawMessage `grove:"scopes,type:jsonb"`
	// Actors is JSON for the same reason Roles is: a chain element carries a
	// kind and an id, and both are compared to make authorization decisions,
	// so the encoding must not be able to invent or merge a member.
	Actors                json.RawMessage `grove:"actors,type:jsonb"`
	ActorGrant            string          `grove:"actor_grant"`
	DelegationID          string          `grove:"delegation_id"`
	LastActivityAt        time.Time       `grove:"last_activity_at"`
	ExpiresAt             time.Time       `grove:"expires_at,notnull"`
	RefreshTokenExpiresAt time.Time       `grove:"refresh_token_expires_at,notnull"`
	CreatedAt             time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt             time.Time       `grove:"updated_at,notnull,default:now()"`
	DPoPJKT               string          `grove:"dpop_jkt"`
	ClientID              string          `grove:"client_id"`
}

type SettingModel

type SettingModel struct {
	grove.BaseModel `grove:"table:authsome_settings,alias:st"`

	ID        string          `grove:"id,pk"`
	Key       string          `grove:"key,notnull"`
	Value     json.RawMessage `grove:"value"`
	Scope     string          `grove:"scope,notnull"`
	ScopeID   string          `grove:"scope_id,notnull"`
	AppID     string          `grove:"app_id"`
	OrgID     string          `grove:"org_id"`
	Enforced  bool            `grove:"enforced"`
	Namespace string          `grove:"namespace"`
	Version   int64           `grove:"version"`
	UpdatedBy string          `grove:"updated_by"`
	CreatedAt time.Time       `grove:"created_at,notnull"`
	UpdatedAt time.Time       `grove:"updated_at,notnull"`
}

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store implements store.Store using grove ORM (SQLite).

func New

func New(db *grove.DB) *Store

New creates a new SQLite-backed store.

func (*Store) AddUserEmail

func (s *Store) AddUserEmail(ctx context.Context, e *user.UserEmail) error

func (*Store) BatchResolve

func (s *Store) BatchResolve(ctx context.Context, keys []string, opts settings.ResolveOpts) (map[string][]*settings.Setting, error)

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection.

func (*Store) ConsumePasswordReset

func (s *Store) ConsumePasswordReset(ctx context.Context, token string) error

func (*Store) ConsumeVerification

func (s *Store) ConsumeVerification(ctx context.Context, token string) error

func (*Store) CreateAPIKey

func (s *Store) CreateAPIKey(ctx context.Context, k *apikey.APIKey) error

func (*Store) CreateApp

func (s *Store) CreateApp(ctx context.Context, a *app.App) error

func (*Store) CreateDelegation

func (s *Store) CreateDelegation(ctx context.Context, d *principal.Delegation) error

CreateDelegation stores a new grant, refusing one that would duplicate a grant already live for the same (app, actor, subject, kind).

The liveness check runs here, in Go, and not in the index predicate. Live means principal.Delegation.IsActive: not revoked AND not past its expiry. A partial index cannot express the second half, because sqlite requires an index predicate to be deterministic and datetime('now') is not, so the index (see the delegation_live_index_excludes_expired migration) now covers only grants that never expire, and this is where the rest is enforced.

This is a read followed by a write rather than one atomic statement, so two concurrent creates for the same tuple can both land. That is a deliberate trade against the bug it replaces: filtering on revoked_at alone meant an impersonation grant that merely lapsed blocked every later impersonation of that user by that admin forever, because nothing but StopImpersonation ever writes revoked_at.

func (*Store) CreateDevice

func (s *Store) CreateDevice(ctx context.Context, d *device.Device) error

func (*Store) CreateEnvironment

func (s *Store) CreateEnvironment(ctx context.Context, e *environment.Environment) error

func (*Store) CreateFormConfig

func (s *Store) CreateFormConfig(ctx context.Context, fc *formconfig.FormConfig) error

func (*Store) CreateInvitation

func (s *Store) CreateInvitation(ctx context.Context, inv *organization.Invitation) error

func (*Store) CreateMember

func (s *Store) CreateMember(ctx context.Context, m *organization.Member) error

func (*Store) CreateNotification

func (s *Store) CreateNotification(ctx context.Context, n *notification.Notification) error

func (*Store) CreateOrganization

func (s *Store) CreateOrganization(ctx context.Context, o *organization.Organization) error

func (*Store) CreatePasswordReset

func (s *Store) CreatePasswordReset(ctx context.Context, pr *account.PasswordReset) error

func (*Store) CreateServiceAccount

func (s *Store) CreateServiceAccount(ctx context.Context, svc *serviceaccount.ServiceAccount) error

func (*Store) CreateSession

func (s *Store) CreateSession(ctx context.Context, sess *session.Session) error

func (*Store) CreateTeam

func (s *Store) CreateTeam(ctx context.Context, t *organization.Team) error

func (*Store) CreateUser

func (s *Store) CreateUser(ctx context.Context, u *user.User) error

func (*Store) CreateUserWithPrimaryEmail

func (s *Store) CreateUserWithPrimaryEmail(ctx context.Context, u *user.User, primary *user.UserEmail) error

func (*Store) CreateVerification

func (s *Store) CreateVerification(ctx context.Context, v *account.Verification) error

func (*Store) CreateWebhook

func (s *Store) CreateWebhook(ctx context.Context, w *webhook.Webhook) error

func (*Store) DB

func (s *Store) DB() *grove.DB

DB returns the underlying grove.DB for advanced use cases.

func (*Store) DeleteAPIKey

func (s *Store) DeleteAPIKey(ctx context.Context, keyID id.APIKeyID) error

func (*Store) DeleteApp

func (s *Store) DeleteApp(ctx context.Context, appID id.AppID) error

DeleteApp removes the app and every child record scoped to it. The sqlite schema declares no app_id foreign keys, so the cascade is performed explicitly inside a single transaction to keep this backend consistent with PostgreSQL (which relies on ON DELETE CASCADE). Child rows are hard-deleted: once the owning app is gone there is nothing to restore them under. Plugin- owned tables (passkey, oauth connections, oauth2 clients, ...) live in their own stores and are not handled here.

func (*Store) DeleteAppClientConfig

func (s *Store) DeleteAppClientConfig(ctx context.Context, appID id.AppID) error

func (*Store) DeleteAppSessionConfig

func (s *Store) DeleteAppSessionConfig(ctx context.Context, appID id.AppID) error

func (*Store) DeleteBranding

func (s *Store) DeleteBranding(ctx context.Context, orgID id.OrgID) error

func (*Store) DeleteDevice

func (s *Store) DeleteDevice(ctx context.Context, deviceID id.DeviceID) error

func (*Store) DeleteEnvironment

func (s *Store) DeleteEnvironment(ctx context.Context, envID id.EnvironmentID) error

func (*Store) DeleteExpiredPasswordResets

func (s *Store) DeleteExpiredPasswordResets(ctx context.Context, before time.Time, batch int) (int64, error)

DeleteExpiredPasswordResets implements store.Retention.

func (*Store) DeleteExpiredRevokedRefreshTokens

func (s *Store) DeleteExpiredRevokedRefreshTokens(ctx context.Context, before time.Time, batch int) (int64, error)

DeleteExpiredRevokedRefreshTokens implements store.Retention.

func (*Store) DeleteExpiredSessions

func (s *Store) DeleteExpiredSessions(ctx context.Context, before time.Time, batch int) (int64, error)

DeleteExpiredSessions implements store.Retention.

func (*Store) DeleteExpiredVerifications

func (s *Store) DeleteExpiredVerifications(ctx context.Context, before time.Time, batch int) (int64, error)

DeleteExpiredVerifications implements store.Retention.

func (*Store) DeleteFormConfig

func (s *Store) DeleteFormConfig(ctx context.Context, appID id.AppID, formType string) error

func (*Store) DeleteInvitation

func (s *Store) DeleteInvitation(ctx context.Context, invID id.InvitationID) error

func (*Store) DeleteMember

func (s *Store) DeleteMember(ctx context.Context, memberID id.MemberID) error

func (*Store) DeleteOrganization

func (s *Store) DeleteOrganization(ctx context.Context, orgID id.OrgID) error

func (*Store) DeleteOrganizationCascade

func (s *Store) DeleteOrganizationCascade(ctx context.Context, orgID id.OrgID) error

DeleteOrganizationCascade deletes the organization and all of its dependent rows (members, teams, invitations) inside a single SqliteTx. On any error the entire cascade rolls back; the orgs row is the last delete so a failure mid-cascade leaves the org row visible (callers can retry).

func (*Store) DeleteServiceAccount

func (s *Store) DeleteServiceAccount(ctx context.Context, svcID id.ServiceAccountID) error

func (*Store) DeleteSession

func (s *Store) DeleteSession(ctx context.Context, sessionID id.SessionID) error

func (*Store) DeleteSessionsByGrant

func (s *Store) DeleteSessionsByGrant(ctx context.Context, grantID id.AgentGrantID) error

DeleteSessionsByGrant deletes every session issued under grantID, backed by idx_authsome_sessions_grant_id.

func (*Store) DeleteSetting

func (s *Store) DeleteSetting(ctx context.Context, key string, scope settings.Scope, scopeID string) error

func (*Store) DeleteSettingsByNamespace

func (s *Store) DeleteSettingsByNamespace(ctx context.Context, namespace string) error

func (*Store) DeleteTeam

func (s *Store) DeleteTeam(ctx context.Context, teamID id.TeamID) error

func (*Store) DeleteUser

func (s *Store) DeleteUser(ctx context.Context, userID id.UserID) error

func (*Store) DeleteUserEmail

func (s *Store) DeleteUserEmail(ctx context.Context, userID id.UserID, email string) error

func (*Store) DeleteUserSessions

func (s *Store) DeleteUserSessions(ctx context.Context, userID id.UserID) error

func (*Store) DeleteWebhook

func (s *Store) DeleteWebhook(ctx context.Context, webhookID id.WebhookID) error

func (*Store) FindActiveDelegation

func (s *Store) FindActiveDelegation(
	ctx context.Context, appID id.AppID, actor, subject principal.Ref, grantKind principal.GrantKind,
) (*principal.Delegation, error)

FindActiveDelegation resolves the live grant letting actor act for subject.

Revocation is filtered in SQL; expiry is filtered in Go. That split is not a style choice, it is what this backend actually supports.

A bare `expires_at >= ?` here matched every row whatever its expiry, and an expired grant went on authenticating. The column is declared TIMESTAMP, which sqlite gives NUMERIC affinity, but the driver writes an ISO-8601 string that sqlite cannot coerce to a number, so the stored value settles as TEXT while the bound time.Time parameter arrives as a number. Sqlite then compares across storage classes by its own type ordering, where every TEXT value outranks every numeric one, and the predicate is constant-true.

Wrapping both sides in julianday() does not rescue it: julianday() applied to a numeric argument reads that number as a Julian day count rather than as an instant, so the bound parameter comes back astronomically large and the predicate flips to constant-false. Constant-false is worse than constant-true, because it fails closed on grants that are perfectly live.

Filtering in Go removes the guesswork. principal.Delegation.IsActive is the definition every other consumer already uses, boundary convention included: a grant expiring at exactly this instant is still active. The row count this walks is bounded by the live-grant uniqueness constraint on (app, actor, subject, kind), so this is a handful of rows at most even on the authentication path.

func (*Store) FindByPrefix

func (s *Store) FindByPrefix(ctx context.Context, prefix string) (*apikey.APIKey, error)

func (*Store) GetAPIKey

func (s *Store) GetAPIKey(ctx context.Context, keyID id.APIKeyID) (*apikey.APIKey, error)

func (*Store) GetAPIKeyByPrefix

func (s *Store) GetAPIKeyByPrefix(ctx context.Context, appID id.AppID, prefix string) (*apikey.APIKey, error)

func (*Store) GetAPIKeyByPublicKey

func (s *Store) GetAPIKeyByPublicKey(ctx context.Context, appID id.AppID, publicKey string) (*apikey.APIKey, error)

func (*Store) GetActiveEmailVerification

func (s *Store) GetActiveEmailVerification(ctx context.Context, userID id.UserID) (*account.Verification, error)

func (*Store) GetApp

func (s *Store) GetApp(ctx context.Context, appID id.AppID) (*app.App, error)

func (*Store) GetAppByPublishableKey

func (s *Store) GetAppByPublishableKey(ctx context.Context, key string) (*app.App, error)

func (*Store) GetAppBySlug

func (s *Store) GetAppBySlug(ctx context.Context, slug string) (*app.App, error)

func (*Store) GetAppClientConfig

func (s *Store) GetAppClientConfig(ctx context.Context, appID id.AppID) (*appclientconfig.Config, error)

func (*Store) GetAppSessionConfig

func (s *Store) GetAppSessionConfig(ctx context.Context, appID id.AppID) (*appsessionconfig.Config, error)

func (*Store) GetBranding

func (s *Store) GetBranding(ctx context.Context, orgID id.OrgID) (*formconfig.BrandingConfig, error)

func (*Store) GetDefaultEnvironment

func (s *Store) GetDefaultEnvironment(ctx context.Context, appID id.AppID) (*environment.Environment, error)

func (*Store) GetDelegation

func (s *Store) GetDelegation(ctx context.Context, delID id.DelegationID) (*principal.Delegation, error)

func (*Store) GetDevice

func (s *Store) GetDevice(ctx context.Context, deviceID id.DeviceID) (*device.Device, error)

func (*Store) GetDeviceByFingerprint

func (s *Store) GetDeviceByFingerprint(ctx context.Context, userID id.UserID, fingerprint string) (*device.Device, error)

func (*Store) GetEnvironment

func (s *Store) GetEnvironment(ctx context.Context, envID id.EnvironmentID) (*environment.Environment, error)

func (*Store) GetEnvironmentBySlug

func (s *Store) GetEnvironmentBySlug(ctx context.Context, appID id.AppID, slug string) (*environment.Environment, error)

func (*Store) GetFormConfig

func (s *Store) GetFormConfig(ctx context.Context, appID id.AppID, formType string) (*formconfig.FormConfig, error)

func (*Store) GetInvitation

func (s *Store) GetInvitation(ctx context.Context, invID id.InvitationID) (*organization.Invitation, error)

func (*Store) GetInvitationByToken

func (s *Store) GetInvitationByToken(ctx context.Context, token string) (*organization.Invitation, error)

func (*Store) GetMember

func (s *Store) GetMember(ctx context.Context, memberID id.MemberID) (*organization.Member, error)

func (*Store) GetMemberByUserAndOrg

func (s *Store) GetMemberByUserAndOrg(ctx context.Context, userID id.UserID, orgID id.OrgID) (*organization.Member, error)

func (*Store) GetNotification

func (s *Store) GetNotification(ctx context.Context, notifID id.NotificationID) (*notification.Notification, error)

func (*Store) GetOrganization

func (s *Store) GetOrganization(ctx context.Context, orgID id.OrgID) (*organization.Organization, error)

func (*Store) GetOrganizationBySlug

func (s *Store) GetOrganizationBySlug(ctx context.Context, appID id.AppID, slug string) (*organization.Organization, error)

func (*Store) GetPasswordReset

func (s *Store) GetPasswordReset(ctx context.Context, token string) (*account.PasswordReset, error)

func (*Store) GetPlatformApp

func (s *Store) GetPlatformApp(ctx context.Context) (*app.App, error)

func (*Store) GetPrincipal

func (s *Store) GetPrincipal(ctx context.Context, ref principal.Ref) (*principal.Principal, error)

GetPrincipal resolves any principal by ref. A user ref is assembled from authsome_users; every other kind comes from authsome_service_accounts, looked up by ID alone: the ref's Kind only routes which table to read, matching how the memory backend resolves a principal.

func (*Store) GetRevokedRefreshTokenFamily

func (s *Store) GetRevokedRefreshTokenFamily(ctx context.Context, tokenHash string) (id.SessionFamilyID, error)

GetRevokedRefreshTokenFamily returns the family bound to a revoked token, or store.ErrNotFound if the hash is unknown.

func (*Store) GetServiceAccount

func (s *Store) GetServiceAccount(ctx context.Context, svcID id.ServiceAccountID) (*serviceaccount.ServiceAccount, error)

func (*Store) GetSession

func (s *Store) GetSession(ctx context.Context, sessionID id.SessionID) (*session.Session, error)

func (*Store) GetSessionByRefreshToken

func (s *Store) GetSessionByRefreshToken(ctx context.Context, refreshToken string) (*session.Session, error)

func (*Store) GetSessionByToken

func (s *Store) GetSessionByToken(ctx context.Context, token string) (*session.Session, error)

func (*Store) GetSetting

func (s *Store) GetSetting(ctx context.Context, key string, scope settings.Scope, scopeID string) (*settings.Setting, error)

func (*Store) GetTeam

func (s *Store) GetTeam(ctx context.Context, teamID id.TeamID) (*organization.Team, error)

func (*Store) GetUser

func (s *Store) GetUser(ctx context.Context, userID id.UserID) (*user.User, error)

func (*Store) GetUserByAnyEmail

func (s *Store) GetUserByAnyEmail(ctx context.Context, appID id.AppID, envID id.EnvironmentID, email string) (*user.User, error)

func (*Store) GetUserByEmail

func (s *Store) GetUserByEmail(ctx context.Context, appID id.AppID, envID id.EnvironmentID, email string) (*user.User, error)

func (*Store) GetUserByPhone

func (s *Store) GetUserByPhone(ctx context.Context, appID id.AppID, envID id.EnvironmentID, phone string) (*user.User, error)

func (*Store) GetUserByUsername

func (s *Store) GetUserByUsername(ctx context.Context, appID id.AppID, envID id.EnvironmentID, username string) (*user.User, error)

func (*Store) GetUserEmailRecord

func (s *Store) GetUserEmailRecord(ctx context.Context, appID id.AppID, envID id.EnvironmentID, email string) (*user.UserEmail, error)

func (*Store) GetUserEmails

func (s *Store) GetUserEmails(ctx context.Context, userID id.UserID) ([]*user.UserEmail, error)

func (*Store) GetVerification

func (s *Store) GetVerification(ctx context.Context, token string) (*account.Verification, error)

func (*Store) GetWebhook

func (s *Store) GetWebhook(ctx context.Context, webhookID id.WebhookID) (*webhook.Webhook, error)

func (*Store) HashLegacyTokens

func (s *Store) HashLegacyTokens(ctx context.Context, batch int) (int64, error)

HashLegacyTokens implements store.LegacyTokenHasher. Each table is swept with a bounded select of rows whose hash is NULL and whose plaintext is present, then rewritten row by row: the digest is computed here, not in SQL, so the two SQL backends and mongo convert identically. A row with neither plaintext nor hash is unusable and left alone.

func (*Store) IsRefreshTokenRevoked

func (s *Store) IsRefreshTokenRevoked(ctx context.Context, tokenHash string) (bool, error)

IsRefreshTokenRevoked reports whether tokenHash has been revoked.

func (*Store) KVCounter

func (s *Store) KVCounter(ctx context.Context, key string) (int64, error)

KVCounter implements store.KV.

func (*Store) KVDelete

func (s *Store) KVDelete(ctx context.Context, key string) error

KVDelete implements store.KV.

func (*Store) KVDeleteExpired

func (s *Store) KVDeleteExpired(ctx context.Context, now time.Time) (int64, error)

KVDeleteExpired implements store.KV.

func (*Store) KVGet

func (s *Store) KVGet(ctx context.Context, key string) ([]byte, error)

KVGet implements store.KV.

func (*Store) KVIncrement

func (s *Store) KVIncrement(ctx context.Context, key string, ttl time.Duration) (int64, error)

KVIncrement implements store.KV.

func (*Store) KVSet

func (s *Store) KVSet(ctx context.Context, key string, value []byte, ttl time.Duration) error

KVSet implements store.KV.

func (*Store) KVSetNX

func (s *Store) KVSetNX(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, error)

KVSetNX implements store.KV.

func (*Store) ListAPIKeysByApp

func (s *Store) ListAPIKeysByApp(ctx context.Context, appID id.AppID) ([]*apikey.APIKey, error)

func (*Store) ListAPIKeysByUser

func (s *Store) ListAPIKeysByUser(ctx context.Context, appID id.AppID, userID id.UserID) ([]*apikey.APIKey, error)

func (*Store) ListApps

func (s *Store) ListApps(ctx context.Context) ([]*app.App, error)

func (*Store) ListDelegations

func (s *Store) ListDelegations(ctx context.Context, q *principal.DelegationQuery) ([]*principal.Delegation, error)

func (*Store) ListDevices

func (s *Store) ListDevices(ctx context.Context, limit int) ([]*device.Device, error)

func (*Store) ListEnvironments

func (s *Store) ListEnvironments(ctx context.Context, appID id.AppID) ([]*environment.Environment, error)

func (*Store) ListFormConfigs

func (s *Store) ListFormConfigs(ctx context.Context, appID id.AppID) ([]*formconfig.FormConfig, error)

func (*Store) ListInvitations

func (s *Store) ListInvitations(ctx context.Context, orgID id.OrgID) ([]*organization.Invitation, error)

func (*Store) ListInvitationsPage

func (s *Store) ListInvitationsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Invitation], error)

func (*Store) ListMembers

func (s *Store) ListMembers(ctx context.Context, orgID id.OrgID) ([]*organization.Member, error)

func (*Store) ListMembersPage

func (s *Store) ListMembersPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Member], error)

func (*Store) ListOrganizations

func (s *Store) ListOrganizations(ctx context.Context, appID id.AppID) ([]*organization.Organization, error)

func (*Store) ListOrganizationsPage

func (s *Store) ListOrganizationsPage(ctx context.Context, appID id.AppID, opts page.Opts) (page.Page[*organization.Organization], error)

func (*Store) ListPrincipals

func (s *Store) ListPrincipals(ctx context.Context, q *principal.Query) ([]*principal.Principal, error)

ListPrincipals returns principals matching q. Only non-human principals are stored in authsome_service_accounts, so this never returns users.

func (*Store) ListServiceAccounts

func (s *Store) ListServiceAccounts(ctx context.Context, q *serviceaccount.Query) (*serviceaccount.List, error)

func (*Store) ListSessions

func (s *Store) ListSessions(ctx context.Context, limit int) ([]*session.Session, error)

func (*Store) ListSettings

func (s *Store) ListSettings(ctx context.Context, opts settings.ListOpts) ([]*settings.Setting, error)

func (*Store) ListTeams

func (s *Store) ListTeams(ctx context.Context, orgID id.OrgID) ([]*organization.Team, error)

func (*Store) ListTeamsPage

func (s *Store) ListTeamsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Team], error)

func (*Store) ListUserDevices

func (s *Store) ListUserDevices(ctx context.Context, userID id.UserID) ([]*device.Device, error)

func (*Store) ListUserNotifications

func (s *Store) ListUserNotifications(ctx context.Context, userID id.UserID) ([]*notification.Notification, error)

func (*Store) ListUserOrganizations

func (s *Store) ListUserOrganizations(ctx context.Context, userID id.UserID) ([]*organization.Organization, error)

func (*Store) ListUserOrganizationsPage

func (s *Store) ListUserOrganizationsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*organization.Organization], error)

func (*Store) ListUserSessions

func (s *Store) ListUserSessions(ctx context.Context, userID id.UserID) ([]*session.Session, error)

func (*Store) ListUserSessionsPage

func (s *Store) ListUserSessionsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*session.Session], error)

func (*Store) ListUsers

func (s *Store) ListUsers(ctx context.Context, q *user.Query) (*user.List, error)

func (*Store) ListWebhooks

func (s *Store) ListWebhooks(ctx context.Context, appID id.AppID) ([]*webhook.Webhook, error)

func (*Store) MarkRefreshTokenReplayed

func (s *Store) MarkRefreshTokenReplayed(ctx context.Context, tokenHash string) (bool, error)

MarkRefreshTokenReplayed flips an already-revoked token's reason to "replay_detected", returning whether this call made the change. Atomic conditional UPDATE — the engine's once-per-family storm guard.

func (*Store) MarkRefreshTokenRevoked

func (s *Store) MarkRefreshTokenRevoked(ctx context.Context, tokenHash string, familyID id.SessionFamilyID, reason string) error

MarkRefreshTokenRevoked records tokenHash as revoked. Idempotent.

func (*Store) MarkSent

func (s *Store) MarkSent(ctx context.Context, notifID id.NotificationID) error

func (*Store) MarkUserEmailVerified

func (s *Store) MarkUserEmailVerified(ctx context.Context, userID id.UserID, email string) error

func (*Store) Migrate

func (s *Store) Migrate(ctx context.Context, extraGroups ...*migrate.Group) error

Migrate runs all registered migrations via the grove orchestrator.

func (*Store) Ping

func (s *Store) Ping(ctx context.Context) error

Ping checks database connectivity.

func (*Store) ResolveSettings

func (s *Store) ResolveSettings(ctx context.Context, key string, opts settings.ResolveOpts) ([]*settings.Setting, error)

func (*Store) RevokeDelegation

func (s *Store) RevokeDelegation(ctx context.Context, delID id.DelegationID, at time.Time) error

RevokeDelegation marks a grant revoked. The revoked_at guard makes a repeat call a no-op rather than moving the timestamp, so a retried revocation cannot rewrite when the revocation actually happened.

func (*Store) RevokeRefreshTokenFamily

func (s *Store) RevokeRefreshTokenFamily(ctx context.Context, familyID id.SessionFamilyID, reason string) error

RevokeRefreshTokenFamily revokes every active session sharing familyID, recording each refresh-token hash as revoked. Runs inside a SqliteTx.

func (*Store) RotateSession

func (s *Store) RotateSession(ctx context.Context, sess *session.Session, expectedTokenHash string) (bool, error)

func (*Store) SaveBranding

func (s *Store) SaveBranding(ctx context.Context, b *formconfig.BrandingConfig) error

func (*Store) SeedLegacyInvitation

func (s *Store) SeedLegacyInvitation(ctx context.Context, inv *organization.Invitation) error

func (*Store) SeedLegacyPasswordReset

func (s *Store) SeedLegacyPasswordReset(ctx context.Context, pr *account.PasswordReset) error

func (*Store) SeedLegacySession

func (s *Store) SeedLegacySession(ctx context.Context, sess *session.Session) error

SeedLegacySession writes sess exactly as a release before token hashing did: plaintext tokens and NULL hashes. It exists so the conformance suite can prove the upgrade path and is refused outside `go test`.

func (*Store) SeedLegacyVerification

func (s *Store) SeedLegacyVerification(ctx context.Context, v *account.Verification) error

SeedLegacyVerification, SeedLegacyPasswordReset and SeedLegacyInvitation write rows the way releases before token hashing did: plaintext and a NULL hash. They exist for the conformance suite and are refused outside `go test`.

func (*Store) SetAppClientConfig

func (s *Store) SetAppClientConfig(ctx context.Context, cfg *appclientconfig.Config) error

func (*Store) SetAppSessionConfig

func (s *Store) SetAppSessionConfig(ctx context.Context, cfg *appsessionconfig.Config) error

func (*Store) SetDefaultEnvironment

func (s *Store) SetDefaultEnvironment(ctx context.Context, appID id.AppID, envID id.EnvironmentID) error

func (*Store) SetPrimaryEmail

func (s *Store) SetPrimaryEmail(ctx context.Context, userID id.UserID, email string) error

func (*Store) SetSetting

func (s *Store) SetSetting(ctx context.Context, st *settings.Setting) error

func (*Store) TouchAPIKey

func (s *Store) TouchAPIKey(ctx context.Context, keyID id.APIKeyID, at time.Time) error

TouchAPIKey writes last_used_at alone.

func (*Store) TouchSession

func (s *Store) TouchSession(ctx context.Context, sessionID id.SessionID, lastActivityAt, expiresAt time.Time) error

func (*Store) UpdateAPIKey

func (s *Store) UpdateAPIKey(ctx context.Context, k *apikey.APIKey) error

func (*Store) UpdateApp

func (s *Store) UpdateApp(ctx context.Context, a *app.App) error

func (*Store) UpdateDevice

func (s *Store) UpdateDevice(ctx context.Context, d *device.Device) error

func (*Store) UpdateEnvironment

func (s *Store) UpdateEnvironment(ctx context.Context, e *environment.Environment) error

func (*Store) UpdateFormConfig

func (s *Store) UpdateFormConfig(ctx context.Context, fc *formconfig.FormConfig) error

func (*Store) UpdateInvitation

func (s *Store) UpdateInvitation(ctx context.Context, inv *organization.Invitation) error

func (*Store) UpdateMember

func (s *Store) UpdateMember(ctx context.Context, mem *organization.Member) error

func (*Store) UpdateOrganization

func (s *Store) UpdateOrganization(ctx context.Context, o *organization.Organization) error

func (*Store) UpdateServiceAccount

func (s *Store) UpdateServiceAccount(ctx context.Context, svc *serviceaccount.ServiceAccount) error

func (*Store) UpdateSession

func (s *Store) UpdateSession(ctx context.Context, sess *session.Session) error

func (*Store) UpdateTeam

func (s *Store) UpdateTeam(ctx context.Context, t *organization.Team) error

func (*Store) UpdateUser

func (s *Store) UpdateUser(ctx context.Context, u *user.User) error

func (*Store) UpdateVerification

func (s *Store) UpdateVerification(ctx context.Context, v *account.Verification) error

func (*Store) UpdateWebhook

func (s *Store) UpdateWebhook(ctx context.Context, w *webhook.Webhook) error

func (*Store) WithTx

func (s *Store) WithTx(_ context.Context, fn func(tx organization.Store) error) error

WithTx is a best-effort no-op wrapper for now. Real backend transactions require plumbing driver.Tx through every Store method, which is a multi- day refactor deferred to a follow-up PR. Callers that need true atomic cascade semantics should use the dedicated DeleteOrganizationCascade method below — it uses SqliteTx natively without requiring the full every-method-takes-a-tx refactor.

type TeamModel

type TeamModel struct {
	grove.BaseModel `grove:"table:authsome_teams,alias:t"`

	ID        string    `grove:"id,pk"`
	OrgID     string    `grove:"org_id,notnull"`
	Name      string    `grove:"name,notnull"`
	Slug      string    `grove:"slug,notnull"`
	CreatedAt time.Time `grove:"created_at,notnull,default:now()"`
	UpdatedAt time.Time `grove:"updated_at,notnull,default:now()"`
}

type UserEmailModel

type UserEmailModel struct {
	grove.BaseModel `grove:"table:authsome_user_emails,alias:ue"`

	ID        string       `grove:"id,pk"`
	UserID    string       `grove:"user_id,notnull"`
	AppID     string       `grove:"app_id,notnull"`
	EnvID     string       `grove:"env_id,notnull"`
	Email     string       `grove:"email,notnull"`
	Verified  bool         `grove:"verified"`
	IsPrimary bool         `grove:"is_primary"`
	Source    string       `grove:"source"`
	CreatedAt time.Time    `grove:"created_at,notnull,default:now()"`
	UpdatedAt time.Time    `grove:"updated_at,notnull,default:now()"`
	DeletedAt sql.NullTime `grove:"deleted_at"`
}

type UserModel

type UserModel struct {
	grove.BaseModel `grove:"table:authsome_users,alias:u"`

	ID              string          `grove:"id,pk"`
	AppID           string          `grove:"app_id,notnull"`
	EnvID           string          `grove:"env_id,notnull"`
	Email           string          `grove:"email,notnull"`
	EmailVerified   bool            `grove:"email_verified"`
	FirstName       string          `grove:"first_name"`
	LastName        string          `grove:"last_name"`
	Image           string          `grove:"image"`
	Username        string          `grove:"username"`
	DisplayUsername string          `grove:"display_username"`
	Phone           string          `grove:"phone"`
	PhoneVerified   bool            `grove:"phone_verified"`
	PasswordHash    string          `grove:"password_hash"`
	Banned          bool            `grove:"banned"`
	BanReason       string          `grove:"ban_reason"`
	BanExpires      sql.NullTime    `grove:"ban_expires"`
	Metadata        json.RawMessage `grove:"metadata,type:jsonb"`
	CreatedAt       time.Time       `grove:"created_at,notnull,default:now()"`
	UpdatedAt       time.Time       `grove:"updated_at,notnull,default:now()"`
	DeletedAt       sql.NullTime    `grove:"deleted_at"`
}

type VerificationModel

type VerificationModel struct {
	grove.BaseModel `grove:"table:authsome_verifications,alias:v"`

	ID     string `grove:"id,pk"`
	AppID  string `grove:"app_id,notnull"`
	EnvID  string `grove:"env_id,notnull"`
	UserID string `grove:"user_id,notnull"`
	Token  string `grove:"token,notnull"`
	// TokenHash is store.HashToken of the code; Token is empty on rows
	// written since hash_credential_tokens and NULL here marks a legacy row.
	TokenHash sql.NullString `grove:"token_hash"`
	Type      string         `grove:"type,notnull"`
	Attempts  int            `grove:"attempts,notnull,default:0"`
	ExpiresAt time.Time      `grove:"expires_at,notnull"`
	Consumed  bool           `grove:"consumed"`
	CreatedAt time.Time      `grove:"created_at,notnull,default:now()"`
}

type WebhookModel

type WebhookModel struct {
	grove.BaseModel `grove:"table:authsome_webhooks,alias:wh"`

	ID     string          `grove:"id,pk"`
	AppID  string          `grove:"app_id,notnull"`
	EnvID  string          `grove:"env_id,notnull"`
	URL    string          `grove:"url,notnull"`
	Events json.RawMessage `grove:"events,type:jsonb"`
	// Secret is empty once the row has a relay endpoint; see webhook.Webhook.
	Secret          string    `grove:"secret"`
	SecretHash      string    `grove:"secret_hash"`
	RelayEndpointID string    `grove:"relay_endpoint_id"`
	Active          bool      `grove:"active"`
	CreatedAt       time.Time `grove:"created_at,notnull,default:now()"`
	UpdatedAt       time.Time `grove:"updated_at,notnull,default:now()"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL