Documentation
¶
Overview ¶
Package gorm provides GORM-based implementations of oneauth store interfaces. It supports any database that GORM supports (PostgreSQL, MySQL, SQLite, etc.) and is suitable for production deployments requiring relational database storage.
Database Schema ¶
The package auto-migrates the following tables:
- users: User accounts
- identities: Email/phone identities linked to users
- channels: Authentication channels (local, google, github, etc.)
- auth_tokens: Verification and password reset tokens
- refresh_tokens: Long-lived refresh tokens for API access
- api_keys: Long-lived API keys for programmatic access
Usage ¶
db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})
userStore := gormstore.NewUserStore(db)
refreshTokenStore := gormstore.NewRefreshTokenStore(db)
apiKeyStore := gormstore.NewAPIKeyStore(db)
Index ¶
- func AutoMigrate(db *gorm.DB) error
- type APIKeyModel
- type APIKeyStore
- func (s *APIKeyStore) CreateAPIKey(userID, name string, scopes []string, expiresAt *time.Time) (string, *oa.APIKey, error)
- func (s *APIKeyStore) GetAPIKeyByID(keyID string) (*oa.APIKey, error)
- func (s *APIKeyStore) ListUserAPIKeys(userID string) ([]*oa.APIKey, error)
- func (s *APIKeyStore) RevokeAPIKey(keyID string) error
- func (s *APIKeyStore) UpdateAPIKeyLastUsed(keyID string) error
- func (s *APIKeyStore) ValidateAPIKey(fullKey string) (*oa.APIKey, error)
- type AuthTokenModel
- type ChannelModel
- type ChannelStore
- type GORMUser
- type IdentityModel
- type IdentityStore
- func (s *IdentityStore) GetIdentity(identityType, identityValue string, createIfMissing bool) (*oa.Identity, bool, error)
- func (s *IdentityStore) GetUserIdentities(userId string) ([]*oa.Identity, error)
- func (s *IdentityStore) MarkIdentityVerified(identityType, identityValue string) error
- func (s *IdentityStore) SaveIdentity(identity *oa.Identity) error
- func (s *IdentityStore) SetUserForIdentity(identityType, identityValue string, newUserId string) error
- type JSONMap
- type RefreshTokenModel
- type RefreshTokenStore
- func (s *RefreshTokenStore) CleanupExpiredTokens() error
- func (s *RefreshTokenStore) CreateRefreshToken(userID, clientID string, deviceInfo map[string]any, scopes []string) (*oa.RefreshToken, error)
- func (s *RefreshTokenStore) GetRefreshToken(token string) (*oa.RefreshToken, error)
- func (s *RefreshTokenStore) GetUserTokens(userID string) ([]*oa.RefreshToken, error)
- func (s *RefreshTokenStore) RevokeRefreshToken(token string) error
- func (s *RefreshTokenStore) RevokeTokenFamily(family string) error
- func (s *RefreshTokenStore) RevokeUserTokens(userID string) error
- func (s *RefreshTokenStore) RotateRefreshToken(oldToken string) (*oa.RefreshToken, error)
- type StringSlice
- type TokenStore
- func (s *TokenStore) CreateToken(userID, email string, tokenType oa.TokenType, expiryDuration time.Duration) (*oa.AuthToken, error)
- func (s *TokenStore) DeleteToken(token string) error
- func (s *TokenStore) DeleteUserTokens(userID string, tokenType oa.TokenType) error
- func (s *TokenStore) GetToken(token string) (*oa.AuthToken, error)
- type UserModel
- type UserStore
- type UsernameModel
- type UsernameStore
- func (s *UsernameStore) ChangeUsername(oldUsername, newUsername, userID string) error
- func (s *UsernameStore) GetUserByUsername(username string) (string, error)
- func (s *UsernameStore) ReleaseUsername(username string) error
- func (s *UsernameStore) ReserveUsername(username string, userID string) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AutoMigrate ¶
AutoMigrate runs database migrations for all oneauth tables
Types ¶
type APIKeyModel ¶
type APIKeyModel struct {
KeyID string `gorm:"primaryKey;size:64"`
KeyHash string `gorm:"size:128"`
UserID string `gorm:"size:64;index"`
Name string `gorm:"size:255"`
Scopes StringSlice `gorm:"type:jsonb"`
CreatedAt time.Time `gorm:"autoCreateTime"`
ExpiresAt *time.Time
LastUsedAt time.Time
RevokedAt *time.Time
Revoked bool `gorm:"default:false;index"`
}
APIKeyModel is the GORM model for API keys
func APIKeyToModel ¶
func APIKeyToModel(k *oa.APIKey) *APIKeyModel
func (APIKeyModel) TableName ¶
func (APIKeyModel) TableName() string
func (*APIKeyModel) ToAPIKey ¶
func (m *APIKeyModel) ToAPIKey() *oa.APIKey
type APIKeyStore ¶
type APIKeyStore struct {
// contains filtered or unexported fields
}
APIKeyStore implements oa.APIKeyStore using GORM
func NewAPIKeyStore ¶
func NewAPIKeyStore(db *gorm.DB) *APIKeyStore
func (*APIKeyStore) CreateAPIKey ¶
func (*APIKeyStore) GetAPIKeyByID ¶
func (s *APIKeyStore) GetAPIKeyByID(keyID string) (*oa.APIKey, error)
func (*APIKeyStore) ListUserAPIKeys ¶
func (s *APIKeyStore) ListUserAPIKeys(userID string) ([]*oa.APIKey, error)
func (*APIKeyStore) RevokeAPIKey ¶
func (s *APIKeyStore) RevokeAPIKey(keyID string) error
func (*APIKeyStore) UpdateAPIKeyLastUsed ¶
func (s *APIKeyStore) UpdateAPIKeyLastUsed(keyID string) error
func (*APIKeyStore) ValidateAPIKey ¶
func (s *APIKeyStore) ValidateAPIKey(fullKey string) (*oa.APIKey, error)
type AuthTokenModel ¶
type AuthTokenModel struct {
Token string `gorm:"primaryKey;size:128"`
Type oa.TokenType `gorm:"size:32;index"`
UserID string `gorm:"size:64;index"`
Email string `gorm:"size:255"`
CreatedAt time.Time `gorm:"autoCreateTime"`
ExpiresAt time.Time `gorm:"index"`
}
AuthTokenModel is the GORM model for verification/reset tokens
func AuthTokenToModel ¶
func AuthTokenToModel(t *oa.AuthToken) *AuthTokenModel
func (AuthTokenModel) TableName ¶
func (AuthTokenModel) TableName() string
func (*AuthTokenModel) ToAuthToken ¶
func (m *AuthTokenModel) ToAuthToken() *oa.AuthToken
type ChannelModel ¶
type ChannelModel struct {
Provider string `gorm:"primaryKey;size:32"`
IdentityKey string `gorm:"primaryKey;size:320"`
Credentials JSONMap `gorm:"type:jsonb"`
Profile JSONMap `gorm:"type:jsonb"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
ChannelModel is the GORM model for authentication channels
func ChannelToModel ¶
func ChannelToModel(c *oa.Channel) *ChannelModel
func (ChannelModel) TableName ¶
func (ChannelModel) TableName() string
func (*ChannelModel) ToChannel ¶
func (m *ChannelModel) ToChannel() *oa.Channel
type ChannelStore ¶
type ChannelStore struct {
// contains filtered or unexported fields
}
ChannelStore implements oa.ChannelStore using GORM
func NewChannelStore ¶
func NewChannelStore(db *gorm.DB) *ChannelStore
func (*ChannelStore) GetChannel ¶
func (*ChannelStore) GetChannelsByIdentity ¶
func (s *ChannelStore) GetChannelsByIdentity(identityKey string) ([]*oa.Channel, error)
func (*ChannelStore) SaveChannel ¶
func (s *ChannelStore) SaveChannel(channel *oa.Channel) error
type GORMUser ¶
type GORMUser struct {
// contains filtered or unexported fields
}
GORMUser implements the oa.User interface
type IdentityModel ¶
type IdentityModel struct {
Type string `gorm:"primaryKey;size:32"`
Value string `gorm:"primaryKey;size:255"`
UserID string `gorm:"size:64;index"`
Verified bool `gorm:"default:false"`
CreatedAt time.Time `gorm:"autoCreateTime"`
}
IdentityModel is the GORM model for identities
func IdentityToModel ¶
func IdentityToModel(i *oa.Identity) *IdentityModel
func (IdentityModel) TableName ¶
func (IdentityModel) TableName() string
func (*IdentityModel) ToIdentity ¶
func (m *IdentityModel) ToIdentity() *oa.Identity
type IdentityStore ¶
type IdentityStore struct {
// contains filtered or unexported fields
}
IdentityStore implements oa.IdentityStore using GORM
func NewIdentityStore ¶
func NewIdentityStore(db *gorm.DB) *IdentityStore
func (*IdentityStore) GetIdentity ¶
func (*IdentityStore) GetUserIdentities ¶
func (s *IdentityStore) GetUserIdentities(userId string) ([]*oa.Identity, error)
func (*IdentityStore) MarkIdentityVerified ¶
func (s *IdentityStore) MarkIdentityVerified(identityType, identityValue string) error
func (*IdentityStore) SaveIdentity ¶
func (s *IdentityStore) SaveIdentity(identity *oa.Identity) error
func (*IdentityStore) SetUserForIdentity ¶
func (s *IdentityStore) SetUserForIdentity(identityType, identityValue string, newUserId string) error
type RefreshTokenModel ¶
type RefreshTokenModel struct {
TokenHash string `gorm:"primaryKey;size:64"`
Token string `gorm:"-"` // Not stored, only used in memory
UserID string `gorm:"size:64;index"`
ClientID string `gorm:"size:64"`
DeviceInfo JSONMap `gorm:"type:jsonb"`
Family string `gorm:"size:32;index"`
Generation int `gorm:"default:1"`
Scopes StringSlice `gorm:"type:jsonb"`
CreatedAt time.Time `gorm:"autoCreateTime"`
ExpiresAt time.Time `gorm:"index"`
LastUsedAt time.Time
RevokedAt *time.Time
Revoked bool `gorm:"default:false;index"`
}
RefreshTokenModel is the GORM model for refresh tokens
func RefreshTokenToModel ¶
func RefreshTokenToModel(t *oa.RefreshToken) *RefreshTokenModel
func (RefreshTokenModel) TableName ¶
func (RefreshTokenModel) TableName() string
func (*RefreshTokenModel) ToRefreshToken ¶
func (m *RefreshTokenModel) ToRefreshToken() *oa.RefreshToken
type RefreshTokenStore ¶
type RefreshTokenStore struct {
// contains filtered or unexported fields
}
RefreshTokenStore implements oa.RefreshTokenStore using GORM
func NewRefreshTokenStore ¶
func NewRefreshTokenStore(db *gorm.DB) *RefreshTokenStore
func (*RefreshTokenStore) CleanupExpiredTokens ¶
func (s *RefreshTokenStore) CleanupExpiredTokens() error
func (*RefreshTokenStore) CreateRefreshToken ¶
func (s *RefreshTokenStore) CreateRefreshToken(userID, clientID string, deviceInfo map[string]any, scopes []string) (*oa.RefreshToken, error)
func (*RefreshTokenStore) GetRefreshToken ¶
func (s *RefreshTokenStore) GetRefreshToken(token string) (*oa.RefreshToken, error)
func (*RefreshTokenStore) GetUserTokens ¶
func (s *RefreshTokenStore) GetUserTokens(userID string) ([]*oa.RefreshToken, error)
func (*RefreshTokenStore) RevokeRefreshToken ¶
func (s *RefreshTokenStore) RevokeRefreshToken(token string) error
func (*RefreshTokenStore) RevokeTokenFamily ¶
func (s *RefreshTokenStore) RevokeTokenFamily(family string) error
func (*RefreshTokenStore) RevokeUserTokens ¶
func (s *RefreshTokenStore) RevokeUserTokens(userID string) error
func (*RefreshTokenStore) RotateRefreshToken ¶
func (s *RefreshTokenStore) RotateRefreshToken(oldToken string) (*oa.RefreshToken, error)
type StringSlice ¶
type StringSlice []string
StringSlice is a helper type for storing string slices in GORM
func (*StringSlice) Scan ¶
func (s *StringSlice) Scan(value any) error
type TokenStore ¶
type TokenStore struct {
// contains filtered or unexported fields
}
TokenStore implements oa.TokenStore using GORM
func NewTokenStore ¶
func NewTokenStore(db *gorm.DB) *TokenStore
func (*TokenStore) CreateToken ¶
func (*TokenStore) DeleteToken ¶
func (s *TokenStore) DeleteToken(token string) error
func (*TokenStore) DeleteUserTokens ¶
func (s *TokenStore) DeleteUserTokens(userID string, tokenType oa.TokenType) error
type UserModel ¶
type UserModel struct {
ID string `gorm:"primaryKey;size:64"`
IsActive bool `gorm:"default:true"`
Profile JSONMap `gorm:"type:jsonb"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
UserModel is the GORM model for users
type UserStore ¶
type UserStore struct {
// contains filtered or unexported fields
}
UserStore implements oa.UserStore using GORM
func NewUserStore ¶
func (*UserStore) CreateUser ¶
type UsernameModel ¶ added in v0.0.26
type UsernameModel struct {
// NormalizedUsername is the lowercase version (primary key for case-insensitive lookup)
NormalizedUsername string `gorm:"primaryKey;size:64"`
Username string `gorm:"size:64;not null"` // Original case-preserved
UserID string `gorm:"size:64;index;not null"`
Version int `gorm:"default:1"` // For optimistic concurrency control
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
UsernameModel is the GORM model for username -> userID mapping Used for enforcing username uniqueness and enabling username-based login
func (UsernameModel) TableName ¶ added in v0.0.26
func (UsernameModel) TableName() string
type UsernameStore ¶ added in v0.0.26
type UsernameStore struct {
// contains filtered or unexported fields
}
UsernameStore implements oa.UsernameStore using GORM with optimistic concurrency.
Purpose ¶
Provides username uniqueness enforcement and username-based login lookup. This is optional - only configure it if your app needs:
- Username uniqueness (prevent two users from having same username)
- Username-based login (login with "johndoe" instead of email)
Concurrency Model ¶
Uses optimistic locking with version numbers. Updates use "WHERE version = ?" clauses to detect concurrent modifications. If a conflict occurs, the operation returns an error and can be retried.
Setup ¶
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
gorm.AutoMigrate(db) // Creates usernames table
usernameStore := gorm.NewUsernameStore(db)
// Use with LocalAuth
localAuth := &oneauth.LocalAuth{
UsernameStore: usernameStore,
SignupPolicy: &oneauth.SignupPolicy{
RequireUsername: true,
EnforceUsernameUnique: true,
},
}
func NewUsernameStore ¶ added in v0.0.26
func NewUsernameStore(db *gorm.DB) *UsernameStore
NewUsernameStore creates a new GORM-backed UsernameStore
func (*UsernameStore) ChangeUsername ¶ added in v0.0.26
func (s *UsernameStore) ChangeUsername(oldUsername, newUsername, userID string) error
ChangeUsername atomically changes a username using optimistic concurrency. Returns error if new username is already taken or concurrent modification detected.
Usage ¶
Called from a "Change Username" profile page handler.
Concurrency ¶
Uses version check to detect concurrent modifications. If another process modifies the username between read and update, returns error for retry.
func (*UsernameStore) GetUserByUsername ¶ added in v0.0.26
func (s *UsernameStore) GetUserByUsername(username string) (string, error)
GetUserByUsername looks up a userID by username (case-insensitive).
Usage ¶
Called by NewCredentialsValidatorWithUsername during login when user enters a username instead of email.
func (*UsernameStore) ReleaseUsername ¶ added in v0.0.26
func (s *UsernameStore) ReleaseUsername(username string) error
ReleaseUsername removes a username reservation.
When to Use ¶
Call this when:
- User deletes their account
- Admin removes a username (e.g., for policy violations)
func (*UsernameStore) ReserveUsername ¶ added in v0.0.26
func (s *UsernameStore) ReserveUsername(username string, userID string) error
ReserveUsername reserves a username for a user. Returns error if username is already taken by a different user.
Concurrency ¶
Uses database unique constraint on primary key. Concurrent inserts for the same username will have one succeed and one fail with a constraint violation.