authproviders

package
v0.5.9 Latest Latest
Warning

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

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package authproviders persists federated authentication provider configurations (OIDC, SAML) to the database. Each row is one IdP configuration — multiple OIDC providers are supported, each rendered as a separate button on the login page.

The package mirrors the pattern established by pkg/logsinks: a Registry maps each provider type to a typed field schema, a decode function, and a build function. The field schema drives the frontend's dynamic form. Secrets (OIDC client secret, SAML signing key PEM) are redacted in read responses and merged on edit.

SAML signing keys can be auto-generated (self-signed RSA 2048-bit keypair) when the operator doesn't provide their own PEM — no files on disk needed.

Index

Constants

View Source
const SourceDB = "db"

SourceDB marks a row that has been edited or created through the API.

View Source
const SourceService = "service"

SourceService marks a row seeded from the resolved service configuration.

View Source
const SourceYAML = "yaml"

SourceYAML is retained for backwards compatibility.

Variables

View Source
var (
	ErrProviderNotFound      = errors.New("auth provider not found")
	ErrInvalidProviderType   = errors.New("invalid auth provider type")
	ErrInvalidProviderConfig = errors.New("invalid auth provider configuration")
)
View Source
var Registry = map[string]ProviderSpec{
	config.AuthOIDC: {
		Type:         config.AuthOIDC,
		Description:  "OpenID Connect (OAuth2 + OIDC Core). Multi-provider: one button per enabled row.",
		HasSecret:    true,
		SecretFields: []string{"ClientSecret"},
		Fields: []FieldSpec{
			{Name: "IssuerURL", Label: "Issuer URL", Type: FieldString, Required: true, Placeholder: "https://keycloak.example.com/realms/myrealm", Help: "OIDC issuer URL. The provider does discovery against /.well-known/openid-configuration."},
			{Name: "ClientID", Label: "Client ID", Type: FieldString, Required: true, Placeholder: "osctrl-client"},
			{Name: "ClientSecret", Label: "Client secret", Type: FieldPassword, Secret: true, Placeholder: "prefer env vars/secrets in prod", Help: "OIDC client secret. Required unless UsePKCE is true."},
			{Name: "RedirectURL", Label: "Redirect URL", Type: FieldString, Required: true, Placeholder: "https://osctrl.example.com:8444/api/v1/auth/oidc/{id}/callback", Help: "Callback URL registered with the IdP. {id} is replaced with the provider row ID at runtime."},
			{Name: "Scopes", Label: "Scopes", Type: FieldString, Placeholder: "openid,profile,email", Help: "Comma-separated. 'openid' is always prepended if absent."},
			{Name: "UsernameClaim", Label: "Username claim", Type: FieldSelect, Options: []string{"preferred_username", "email", "sub"}, Default: "preferred_username", Help: "OIDC claim used as the AdminUser.Username."},
			{Name: "GroupsClaim", Label: "Groups claim", Type: FieldString, Placeholder: "groups", Help: "OIDC claim consulted for group membership."},
			{Name: "RequiredGroups", Label: "Required groups", Type: FieldString, Placeholder: "osctrl-admins", Help: "Comma-separated. At least one must be present for login to succeed. Empty disables the gate."},
			{Name: "JITProvision", Label: "JIT provision", Type: FieldBoolean, Default: false, Help: "Auto-create an AdminUser on first successful login."},
			{Name: "UsePKCE", Label: "Use PKCE", Type: FieldBoolean, Default: false, Help: "Enable PKCE (RFC 7636). Recommended for public clients."},
		},
		Decode: decodeOIDCConfig,
		Build:  buildOIDCProvider,
	},
	config.AuthSAML: {
		Type:         config.AuthSAML,
		Description:  "SAML 2.0 Web Browser SSO. SP signing keys auto-generated when not provided.",
		HasSecret:    true,
		SecretFields: []string{"SigningKeyPEM"},
		Fields: []FieldSpec{
			{Name: "IDPMetadataURL", Label: "IdP metadata URL", Type: FieldString, Placeholder: "https://idp.example.com/metadata", Help: "IdP's published SAML metadata document URL. One of URL or XML below is required."},
			{Name: "IDPMetadataXML", Label: "IdP metadata XML", Type: FieldText, Placeholder: "<EntityDescriptor ...>", Help: "Inline IdP metadata XML (air-gapped deployments). Mutually exclusive with URL."},
			{Name: "EntityID", Label: "Entity ID", Type: FieldString, Required: true, Placeholder: "https://osctrl.example.com:8444/api/v1/auth/saml/{id}/metadata", Help: "SP entity identifier. {id} is replaced at runtime."},
			{Name: "ACSURL", Label: "ACS URL", Type: FieldString, Required: true, Placeholder: "https://osctrl.example.com:8444/api/v1/auth/saml/{id}/acs", Help: "Assertion Consumer Service URL. {id} is replaced at runtime."},
			{Name: "UsernameAttribute", Label: "Username attribute", Type: FieldString, Placeholder: "", Help: "SAML attribute whose value becomes the username. Empty = use NameID."},
			{Name: "GroupsAttribute", Label: "Groups attribute", Type: FieldString, Placeholder: "groups", Help: "SAML attribute carrying group memberships."},
			{Name: "RequiredGroups", Label: "Required groups", Type: FieldString, Placeholder: "osctrl-admins", Help: "Comma-separated. Empty disables the gate."},
			{Name: "JITProvision", Label: "JIT provision", Type: FieldBoolean, Default: false, Help: "Auto-create an AdminUser on first successful login."},
			{Name: "SigningCertPEM", Label: "SP signing cert (PEM)", Type: FieldText, Placeholder: "-----BEGIN CERTIFICATE-----", Help: "Optional. Leave empty to auto-generate a self-signed keypair."},
			{Name: "SigningKeyPEM", Label: "SP signing key (PEM)", Type: FieldPassword, Secret: true, Placeholder: "-----BEGIN RSA PRIVATE KEY-----", Help: "Optional. Leave empty to auto-generate."},
			{Name: "ForceAuthn", Label: "Force Authn", Type: FieldBoolean, Default: true, Help: "Re-prompt for credentials even if IdP SSO cookie is alive."},
			{Name: "RequireAssertionSigned", Label: "Require signed assertion", Type: FieldBoolean, Default: true, Help: "Must be true for production (threat S2 defense)."},
			{Name: "ReplayWindow", Label: "Replay window (min)", Type: FieldInteger, Default: 5, Help: "Maximum clock skew on NotBefore/NotOnOrAfter checks."},
		},
		Decode: decodeSAMLConfig,
		Build:  buildSAMLProvider,
	},
}

Registry maps each provider type to its ProviderSpec.

Functions

func MergeSecrets

func MergeSecrets(typ, prevJSON, newJSON string) (string, error)

MergeSecrets replaces "***" placeholders with values from prev.

func RedactedConfig

func RedactedConfig(typ, cfgJSON string) string

RedactedConfig replaces secret fields with "***" for the provider Type.

func SupportedTypes

func SupportedTypes() []string

SupportedTypes returns the Registry keys in sorted order.

func ValidateProvider

func ValidateProvider(name, typ, cfgJSON string) error

func ValidateType

func ValidateType(typ string) bool

ValidateType reports whether the provider type is registered.

Types

type AuthProvider

type AuthProvider struct {
	gorm.Model
	Name    string `gorm:"uniqueIndex"`
	Type    string `gorm:"index"` // "oidc" or "saml"
	Enabled bool
	Config  string `gorm:"type:text"` // JSON blob
	Source  string // "service" (seeded) or "db" (edited)
	Info    string
}

AuthProvider stores one IdP configuration. Config is a JSON-encoded blob whose shape is determined by Type and validated against the Registry. Multiple rows of the same Type are allowed — the login page renders one button per enabled row.

type AuthProviderManager

type AuthProviderManager struct {
	DB *gorm.DB
}

AuthProviderManager manages the auth_providers table.

func NewAuthProviderManager

func NewAuthProviderManager(backend *gorm.DB) *AuthProviderManager

NewAuthProviderManager initializes the manager and auto-migrates.

func (*AuthProviderManager) BuildProviders

func (m *AuthProviderManager) BuildProviders(ctx context.Context) ([]ProviderEntry, error)

BuildProviders reads all enabled rows, builds live auth.Provider instances, and returns them. Fail-fast: if any enabled provider's Build fails, the error is returned so the caller can log.Fatal.

func (*AuthProviderManager) Create

func (m *AuthProviderManager) Create(name, typ string, enabled bool, cfgJSON string, info string) (AuthProvider, error)

Create inserts a new provider row.

func (*AuthProviderManager) Delete

func (m *AuthProviderManager) Delete(id uint) error

Delete removes a provider by ID.

func (*AuthProviderManager) Get

Get retrieves one provider by ID.

func (*AuthProviderManager) List

func (m *AuthProviderManager) List() ([]AuthProvider, error)

List returns all providers.

func (*AuthProviderManager) ListEnabled

func (m *AuthProviderManager) ListEnabled() ([]AuthProvider, error)

ListEnabled returns all enabled providers.

func (*AuthProviderManager) PersistGeneratedSAMLKeys

func (m *AuthProviderManager) PersistGeneratedSAMLKeys(row AuthProvider, certPEM, keyPEM string) error

PersistGeneratedSAMLKeys checks if a SAML provider row has auto-generated signing keys (both SigningCertPEM and SigningKeyPEM empty). After Build, the SAML provider has generated keys in memory. This function marshals the generated PEM back into the config JSON so the keys are stable across reloads. It only writes when the row's Source is NOT "db" (seed rows get the keys pinned; operator-edited rows keep whatever the operator set).

func (*AuthProviderManager) RevertToService

func (m *AuthProviderManager) RevertToService(id uint) error

RevertToService flips a row's Source from "db" back to "service".

func (*AuthProviderManager) Seed

Seed translates the resolved service configuration (OIDC and SAML sections) into AuthProvider rows using create-if-missing semantics.

func (*AuthProviderManager) Update

func (m *AuthProviderManager) Update(id uint, name, typ string, enabled bool, cfgJSON string, info string) (AuthProvider, error)

Update replaces an existing provider row's mutable fields.

type FieldSpec

type FieldSpec struct {
	Name        string
	Label       string
	Type        FieldType
	Required    bool
	Secret      bool
	Placeholder string
	Help        string
	Options     []string
	Default     any
}

FieldSpec describes one configurable field of a provider type.

type FieldType

type FieldType string

FieldType is the input kind the frontend should render.

const (
	FieldString   FieldType = "string"
	FieldPassword FieldType = "password"
	FieldBoolean  FieldType = "boolean"
	FieldSelect   FieldType = "select"
	FieldInteger  FieldType = "integer"
	FieldText     FieldType = "text" // multiline
)

type ProviderEntry

type ProviderEntry struct {
	ID           uint
	Name         string
	Type         string
	Provider     auth.Provider
	JITProvision bool
	ClientID     string // OIDC only
	LogoutURL    string // SAML only
}

ProviderEntry is one built provider with its metadata.

type ProviderSpec

type ProviderSpec struct {
	Type         string
	Description  string
	HasSecret    bool
	SecretFields []string
	Fields       []FieldSpec
	// Decode unmarshals raw JSON Config into the typed config struct.
	Decode func(json.RawMessage) (any, error)
	// Build instantiates an auth.Provider from a decoded config.
	// The context is used for IdP discovery/metadata fetch.
	Build func(any, context.Context) (auth.Provider, error)
}

ProviderSpec describes one supported provider type in the Registry.

Jump to

Keyboard shortcuts

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