sqlite

package
v1.9.1 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 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