OAuth providers

Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
Ronni Skansing
2025-11-20 17:14:49 +01:00
parent cff927d477
commit f6eb87fa2b
31 changed files with 2627 additions and 60 deletions

View File

@@ -25,6 +25,10 @@ type APISender struct {
CustomField3 string
CustomField4 string
// oauth provider for token-based authentication
OAuthProviderID *uuid.UUID `gorm:"type:uuid;index;"`
OAuthProvider *OAuthProvider `gorm:"foreignKey:OAuthProviderID"`
// Request fields
RequestMethod string
RequestURL string
@@ -38,6 +42,13 @@ type APISender struct {
}
func (e *APISender) Migrate(db *gorm.DB) error {
// add o_auth_provider_id column if it doesn't exist
if !db.Migrator().HasColumn(&APISender{}, "o_auth_provider_id") {
if err := db.Migrator().AddColumn(&APISender{}, "OAuthProviderID"); err != nil {
return err
}
}
// SQLITE
// ensure name + null company id is unique
return UniqueIndexNameAndNullCompanyID(db, "api_senders")

View File

@@ -0,0 +1,55 @@
package database
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
const (
OAUTH_PROVIDER_TABLE = "oauth_providers"
)
// OAuthProvider is the gorm data model for oauth providers
type OAuthProvider struct {
ID uuid.UUID `gorm:"primary_key;not null;unique;type:uuid"`
CreatedAt *time.Time `gorm:"not null;index;"`
UpdatedAt *time.Time `gorm:"not null;index;"`
Name string `gorm:"not null;uniqueIndex:idx_oauth_providers_unique_name_and_company_id;"`
// oauth endpoints (user configurable)
AuthURL string `gorm:"not null;type:varchar(512);"`
TokenURL string `gorm:"not null;type:varchar(512);"`
Scopes string `gorm:"not null;type:varchar(512);"`
// user's oauth app credentials (stored as plain text like smtp passwords)
ClientID string `gorm:"not null;type:varchar(255);"`
ClientSecret string `gorm:"not null;type:varchar(255);"`
// current token state (stored as plain text)
AccessToken string `gorm:"type:varchar(4096);"`
RefreshToken string `gorm:"type:varchar(4096);"`
TokenExpiresAt *time.Time `gorm:"index;"`
// authorization metadata
AuthorizedEmail string `gorm:"type:varchar(255);"`
AuthorizedAt *time.Time `gorm:"index;"`
// status
IsAuthorized bool `gorm:"not null;default:false;"`
// can belong-to
CompanyID *uuid.UUID `gorm:"uniqueIndex:idx_oauth_providers_unique_name_and_company_id;"`
Company *Company `gorm:"foreignkey:CompanyID;"`
}
func (o *OAuthProvider) Migrate(db *gorm.DB) error {
// ensure name + company id is unique
return UniqueIndexNameAndNullCompanyID(db, "oauth_providers")
}
func (OAuthProvider) TableName() string {
return OAUTH_PROVIDER_TABLE
}

View File

@@ -0,0 +1,36 @@
package database
import (
"time"
"github.com/google/uuid"
)
const (
OAUTH_STATE_TABLE = "oauth_states"
)
// OAuthState stores temporary state tokens for oauth flows
// used for csrf protection
type OAuthState struct {
ID uuid.UUID `gorm:"primary_key;not null;unique;type:uuid"`
CreatedAt *time.Time `gorm:"not null;index;"`
// the state token sent to oauth provider (random cryptographic token)
StateToken string `gorm:"not null;uniqueIndex;type:varchar(255);"`
// the oauth provider this state is for
OAuthProviderID uuid.UUID `gorm:"not null;index;type:uuid"`
OAuthProvider *OAuthProvider `gorm:"foreignkey:OAuthProviderID;"`
// expiration (state tokens expire after 10 minutes)
ExpiresAt *time.Time `gorm:"not null;index;"`
// whether this state token has been used (prevent replay attacks)
Used bool `gorm:"not null;default:false;index;"`
UsedAt *time.Time `gorm:"index;"`
}
func (OAuthState) TableName() string {
return OAUTH_STATE_TABLE
}