Documentation
¶
Overview ¶
contract.go: Wires the sso plugin into the forge-dashboard contract surface via plugin.ContractContributor.
Package sso provides enterprise single sign-on authentication for AuthSome.
The SSO plugin enables organizations to authenticate users through SAML 2.0 and OpenID Connect (OIDC) identity providers. SSO connections are scoped per organization, allowing each tenant to configure its own identity provider. The plugin manages the full SSO flow including metadata exchange, assertion validation, and automatic user provisioning.
Usage:
eng, _ := authsome.NewEngine(
authsome.WithStore(store),
authsome.WithPlugin(sso.New(sso.Config{
DefaultRedirectURL: "https://example.com/dashboard",
})),
)
Package sso provides enterprise Single Sign-On (SSO) authentication via SAML 2.0 and OpenID Connect (OIDC) protocols.
Usage:
p := sso.New(sso.Config{
Providers: []sso.Provider{
sso.NewOIDCProvider(sso.OIDCConfig{
Name: "okta",
Issuer: "https://mycompany.okta.com",
ClientID: "client-id",
ClientSecret: "client-secret",
RedirectURL: "https://app.com/api/auth/sso/okta/callback",
}),
},
})
Index ¶
- Variables
- type AdminConnectionPathRequest
- type AdminCreateConnectionRequest
- type AdminCreateConnectionResponse
- type AdminDeleteConnectionResponse
- type AdminListConnectionsRequest
- type AdminListConnectionsResponse
- type AdminUpdateConnectionRequest
- type CallbackRequest
- type CallbackResponse
- type Config
- type Connection
- type CreateConnectionInput
- type EncryptedStore
- func (s *EncryptedStore) CreateConnection(ctx context.Context, c *Connection) error
- func (s *EncryptedStore) CreateIdentity(ctx context.Context, ident *Identity) error
- func (s *EncryptedStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
- func (s *EncryptedStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
- func (s *EncryptedStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
- func (s *EncryptedStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
- func (s *EncryptedStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
- func (s *EncryptedStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
- func (s *EncryptedStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
- func (s *EncryptedStore) UpdateConnection(ctx context.Context, c *Connection) error
- type ExchangeRequest
- type Identity
- type LoginByDomainRequest
- type LoginRequest
- type LoginResponse
- type MemoryStore
- func (s *MemoryStore) CreateConnection(_ context.Context, c *Connection) error
- func (s *MemoryStore) CreateIdentity(_ context.Context, ident *Identity) error
- func (s *MemoryStore) DeleteConnection(_ context.Context, connID id.SSOConnectionID) error
- func (s *MemoryStore) GetConnection(_ context.Context, connID id.SSOConnectionID) (*Connection, error)
- func (s *MemoryStore) GetConnectionByDomain(_ context.Context, appID id.AppID, domain string) (*Connection, error)
- func (s *MemoryStore) GetConnectionByDomainAndOrg(_ context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
- func (s *MemoryStore) GetConnectionByProvider(_ context.Context, appID id.AppID, provider string) (*Connection, error)
- func (s *MemoryStore) GetIdentity(_ context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
- func (s *MemoryStore) ListConnections(_ context.Context, appID id.AppID) ([]*Connection, error)
- func (s *MemoryStore) UpdateConnection(_ context.Context, c *Connection) error
- type MongoStore
- func (s *MongoStore) CreateConnection(ctx context.Context, c *Connection) error
- func (s *MongoStore) CreateIdentity(ctx context.Context, ident *Identity) error
- func (s *MongoStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
- func (s *MongoStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
- func (s *MongoStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
- func (s *MongoStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
- func (s *MongoStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
- func (s *MongoStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
- func (s *MongoStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
- func (s *MongoStore) UpdateConnection(ctx context.Context, c *Connection) error
- type OIDCConfig
- type Plugin
- func (p *Plugin) Connections() []string
- func (p *Plugin) CreateConnection(ctx context.Context, in CreateConnectionInput) (*Connection, error)
- func (p *Plugin) DeclareSettings(m *settings.Manager) error
- func (p *Plugin) MigrationGroups(driverName string) []*migrate.Group
- func (p *Plugin) Name() string
- func (p *Plugin) OnBeforeSignIn(ctx context.Context, req *account.SignInRequest) error
- func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error
- func (p *Plugin) ProviderNames() []string
- func (p *Plugin) RegisterContract(d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, ...) error
- func (p *Plugin) RegisterRoutes(router forge.Router) error
- func (p *Plugin) SSOStore() Store
- func (p *Plugin) SetAppID(appID string)
- func (p *Plugin) SetSSOStore(s Store)
- func (p *Plugin) SetStore(s store.Store)
- type PostgresStore
- func (s *PostgresStore) CreateConnection(ctx context.Context, c *Connection) error
- func (s *PostgresStore) CreateIdentity(ctx context.Context, ident *Identity) error
- func (s *PostgresStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
- func (s *PostgresStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
- func (s *PostgresStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
- func (s *PostgresStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
- func (s *PostgresStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
- func (s *PostgresStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
- func (s *PostgresStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
- func (s *PostgresStore) UpdateConnection(ctx context.Context, c *Connection) error
- type Provider
- type SAMLConfig
- type SAMLMetadataProvider
- type SqliteStore
- func (s *SqliteStore) CreateConnection(ctx context.Context, c *Connection) error
- func (s *SqliteStore) CreateIdentity(ctx context.Context, ident *Identity) error
- func (s *SqliteStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
- func (s *SqliteStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
- func (s *SqliteStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
- func (s *SqliteStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
- func (s *SqliteStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
- func (s *SqliteStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
- func (s *SqliteStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
- func (s *SqliteStore) UpdateConnection(ctx context.Context, c *Connection) error
- type Store
- type User
Constants ¶
This section is empty.
Variables ¶
var ( // SettingSessionTokenTTLSeconds controls the session token lifetime for SSO sign-in. SettingSessionTokenTTLSeconds = settings.Define("sso.session_token_ttl_seconds", 3600, settings.WithDisplayName("Session Token TTL (seconds)"), settings.WithDescription("Lifetime of sessions created via SSO sign-in in seconds"), settings.WithCategory("SSO"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldNumber), settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(300), Max: intPtr(86400)}), settings.WithHelpText("How long sessions created via SSO remain valid. Default: 3600 (1 hour)"), settings.WithOrder(10), ) // SettingSessionRefreshTTLSeconds controls the refresh token lifetime for SSO sessions. SettingSessionRefreshTTLSeconds = settings.Define("sso.session_refresh_ttl_seconds", 2592000, settings.WithDisplayName("Refresh Token TTL (seconds)"), settings.WithDescription("Lifetime of refresh tokens for SSO sessions in seconds"), settings.WithCategory("SSO"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldNumber), settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(3600), Max: intPtr(7776000)}), settings.WithHelpText("How long refresh tokens remain valid. Default: 2592000 (30 days)"), settings.WithOrder(20), ) )
var ErrConnectionNotFound = errors.New("sso: connection not found")
ErrConnectionNotFound is returned when an SSO connection is not found.
var ErrIdentityExists = errors.New("sso: identity already bound")
ErrIdentityExists is returned when a connection already binds the subject to a user.
var ErrIdentityNotFound = errors.New("sso: identity not found")
ErrIdentityNotFound is returned when a connection has never bound the presented subject to a user.
var PostgresMigrations = migrate.NewGroup("authsome-sso", migrate.DependsOn("authsome"))
PostgresMigrations is the postgres migration group for the SSO plugin.
var SqliteMigrations = migrate.NewGroup("authsome-sso", migrate.DependsOn("authsome"))
SqliteMigrations is the SQLite migration group for the SSO plugin.
Functions ¶
This section is empty.
Types ¶
type AdminConnectionPathRequest ¶ added in v1.9.0
type AdminConnectionPathRequest struct {
ConnectionID string `path:"connectionId" description:"Connection identifier"`
}
AdminConnectionPathRequest binds :connectionId for GET / DELETE.
type AdminCreateConnectionRequest ¶ added in v1.9.0
type AdminCreateConnectionRequest struct {
AppID string `json:"app_id,omitempty" description:"Application ID; optional, must be the caller's own app"`
OrgID string `json:"org_id,omitempty" description:"Optional Org scope inside the App"`
Provider string `json:"provider" description:"Stable name for this IdP (e.g. 'studio', 'okta')"`
Protocol string `json:"protocol" description:"oidc or saml"`
Domain string `json:"domain" description:"Email-domain or IdP host this connection covers"`
Issuer string `json:"issuer,omitempty" description:"OIDC issuer URL (required for oidc)"`
ClientID string `json:"client_id,omitempty" description:"OIDC client ID"`
ClientSecret string `json:"client_secret,omitempty" description:"OIDC client secret (omit for public flows)"`
MetadataURL string `json:"metadata_url,omitempty" description:"SAML metadata URL (one IdP source for saml)"`
// SAML IdP configuration. Supply exactly one IdP source: metadata_url,
// idp_metadata_xml, or idp_sso_url + idp_certificate.
IDPMetadataXML string `json:"idp_metadata_xml,omitempty" description:"Pasted SAML IdP metadata XML"`
IDPSSOURL string `json:"idp_sso_url,omitempty" description:"SAML IdP SSO (redirect) URL"`
IDPCertificate string `json:"idp_certificate,omitempty" description:"SAML IdP signing certificate (PEM)"`
EntityID string `json:"entity_id,omitempty" description:"SP EntityID override; defaults to the metadata URL"`
ACSURL string `json:"acs_url,omitempty" description:"SP ACS URL override; defaults to the canonical /acs route"`
SignRequests bool `json:"sign_requests,omitempty" description:"Sign outbound AuthnRequests with the SP key"`
AttributeMappings map[string]string `json:"attribute_mappings,omitempty" description:"SAML attribute name → user field (email|first_name|last_name|groups)"`
AllowedDomains []string `` /* 141-byte string literal not displayed */
TrustedFederation bool `` /* 136-byte string literal not displayed */
}
AdminCreateConnectionRequest is the body for POST /v1/admin/sso/connections. Caller specifies the target App + the IdP details. Domain is required and must be unique within the App so the dispatch path (`/v1/sso/:provider/login`) can resolve the right connection.
type AdminCreateConnectionResponse ¶ added in v1.9.0
type AdminCreateConnectionResponse struct {
ID string `json:"id"`
AppID string `json:"app_id"`
Provider string `json:"provider"`
Protocol string `json:"protocol"`
Domain string `json:"domain"`
Active bool `json:"active"`
}
AdminCreateConnectionResponse is the response from POST /v1/admin/sso/connections.
type AdminDeleteConnectionResponse ¶ added in v1.9.0
type AdminDeleteConnectionResponse struct {
Status string `json:"status"`
}
AdminDeleteConnectionResponse mirrors the StatusResponse shape used elsewhere in the admin surface so the SDK can model deletes uniformly.
type AdminListConnectionsRequest ¶ added in v1.9.0
type AdminListConnectionsRequest struct {
AppID string `query:"app_id,omitempty" description:"App identifier; optional, must be the caller's own app"`
}
AdminListConnectionsRequest binds the query for GET /v1/admin/sso/connections.
type AdminListConnectionsResponse ¶ added in v1.9.0
type AdminListConnectionsResponse struct {
Connections []*Connection `json:"connections"`
}
AdminListConnectionsResponse is the listing response. Returns [] instead of a paged envelope because per-app connection counts are expected to stay small (a few IdPs at most).
type AdminUpdateConnectionRequest ¶ added in v1.9.0
type AdminUpdateConnectionRequest struct {
ConnectionID string `path:"connectionId" description:"Connection identifier"`
Provider string `json:"provider,omitempty" description:"Stable provider name"`
Domain string `json:"domain,omitempty" description:"Email-domain or IdP host"`
Issuer string `json:"issuer,omitempty" description:"OIDC issuer URL"`
ClientID string `json:"client_id,omitempty" description:"OIDC client ID"`
ClientSecret string `json:"client_secret,omitempty" description:"OIDC client secret; pass empty string to leave unchanged"`
MetadataURL string `json:"metadata_url,omitempty" description:"SAML metadata URL"`
// SAML fields. Empty strings leave the stored value unchanged; send
// attribute_mappings/sign_requests to replace them wholesale.
IDPMetadataXML string `json:"idp_metadata_xml,omitempty" description:"Pasted SAML IdP metadata XML"`
IDPSSOURL string `json:"idp_sso_url,omitempty" description:"SAML IdP SSO URL"`
IDPCertificate string `json:"idp_certificate,omitempty" description:"SAML IdP signing certificate (PEM)"`
EntityID string `json:"entity_id,omitempty" description:"SP EntityID override"`
ACSURL string `json:"acs_url,omitempty" description:"SP ACS URL override"`
SignRequests *bool `json:"sign_requests,omitempty" description:"Toggle signing outbound AuthnRequests"`
AttributeMappings map[string]string `json:"attribute_mappings,omitempty" description:"Replace the SAML attribute → user field map"`
AllowedDomains []string `json:"allowed_domains,omitempty" description:"Replace the extra email domains this connection may assert"`
TrustedFederation *bool `json:"trusted_federation,omitempty" description:"Adopt the IdP subject as the local user id for new users"`
Active *bool `json:"active,omitempty" description:"Activate/deactivate the connection without deleting it"`
}
AdminUpdateConnectionRequest binds the body for PUT /connections/:id. Empty/zero values are treated as "leave unchanged" so callers can PATCH a single field without re-sending the entire connection.
type CallbackRequest ¶ added in v1.9.0
type CallbackRequest struct {
Provider string `path:"provider"`
State string `json:"state" query:"state,omitempty"`
Code string `json:"code" query:"code,omitempty"`
Error string `json:"error,omitempty" query:"error,omitempty"`
}
CallbackRequest contains the parameters for the OIDC callback.
type CallbackResponse ¶ added in v1.9.0
type CallbackResponse struct {
User any `json:"user"`
SessionToken string `json:"session_token"`
RefreshToken string `json:"refresh_token"`
ExpiresAt any `json:"expires_at"`
Provider string `json:"provider"`
IsNewUser bool `json:"is_new_user"`
// contains filtered or unexported fields
}
CallbackResponse is returned on successful SSO authentication.
type Config ¶
type Config struct {
// Providers is the list of configured SSO providers.
Providers []Provider
// SessionTokenTTL is the lifetime of sessions created via SSO sign-in (default: 1 hour).
SessionTokenTTL time.Duration
// SessionRefreshTTL is the lifetime of refresh tokens (default: 30 days).
SessionRefreshTTL time.Duration
// PublicBaseURL is the externally-reachable base URL of this SSO service
// (e.g. "https://auth.muono.cloud"). SAML SP EntityID / ACS URL derive from
// it when a connection doesn't override them. Required for SAML.
PublicBaseURL string
// AllowedReturnOrigins is the allowlist of scheme://host[:port] origins the
// browser SSO landing may redirect back to after ACS. Guards against open
// redirect. localhost/127.0.0.1 are always allowed (dev). The first entry is
// used to derive a default `/sso/callback` landing when none is supplied.
AllowedReturnOrigins []string
}
Config configures the SSO plugin.
type Connection ¶ added in v1.9.0
type Connection struct {
ID id.SSOConnectionID `json:"id"`
AppID id.AppID `json:"app_id"`
EnvID string `json:"env_id,omitempty"`
OrgID id.OrgID `json:"org_id,omitempty"`
Provider string `json:"provider"`
Protocol string `json:"protocol"`
Domain string `json:"domain"`
// DisplayName is an optional admin-set label for the connection (e.g. "Okta",
// "Google Workspace"), so multiple connections for the same domain can be told
// apart. Cosmetic — never used for routing.
DisplayName string `json:"display_name,omitempty"`
MetadataURL string `json:"metadata_url,omitempty"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"-"`
Issuer string `json:"issuer,omitempty"`
Active bool `json:"active"`
// Enforced requires users on this connection's domain to sign in via SSO —
// password login is blocked for them (workspace owners/admins excepted). See
// the plugin's OnBeforeSignIn.
Enforced bool `json:"enforced"`
// AllowedDomains lists email domains, besides Domain, that this
// connection may assert. A login whose email is outside Domain and this
// list is refused before any account is touched.
AllowedDomains []string `json:"allowed_domains,omitempty"`
// TrustedFederation adopts the identity provider's subject as the local
// user id when a user is first created through this connection. Off by
// default: an IdP that can pick subjects can otherwise pick local ids.
TrustedFederation bool `json:"trusted_federation,omitempty"`
// SAML-specific configuration. Populated only for SAML connections.
IDPMetadataXML string `json:"idp_metadata_xml,omitempty"`
IDPSSOURL string `json:"idp_sso_url,omitempty"`
IDPCertificate string `json:"idp_certificate,omitempty"`
EntityID string `json:"entity_id,omitempty"`
ACSURL string `json:"acs_url,omitempty"`
SPCertificate string `json:"sp_certificate,omitempty"`
SPPrivateKey string `json:"-"` // secret — never serialized
SignRequests bool `json:"sign_requests,omitempty"`
AttributeMappings map[string]string `json:"attribute_mappings,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (*Connection) AllowsEmailDomain ¶ added in v1.9.0
func (c *Connection) AllowsEmailDomain(email string) bool
AllowsEmailDomain reports whether an email the provider asserted may sign in through this connection: its domain must be the connection's own or one of the listed allowed domains. A connection with neither configured accepts any domain, which is the shape code-configured providers have.
type CreateConnectionInput ¶ added in v1.9.0
type CreateConnectionInput struct {
AppID id.AppID
OrgID id.OrgID // zero value = app-wide (no org scope)
Provider string
Protocol string // "oidc" | "saml"
Domain string
// OIDC
Issuer string
ClientID string
ClientSecret string
// SAML — supply one IdP source: MetadataURL, IDPMetadataXML, or
// IDPSSOURL + IDPCertificate.
MetadataURL string
IDPMetadataXML string
IDPSSOURL string
IDPCertificate string
EntityID string
ACSURL string
SignRequests bool
AttributeMappings map[string]string
// Enforced requires users on this domain to sign in via SSO (password login
// blocked). Usually toggled on later via UpdateConnection, not at create.
Enforced bool
// DisplayName is an optional admin-set label for the connection (cosmetic).
DisplayName string
// AllowedDomains and TrustedFederation confine and shape the identities
// this connection may assert; see Connection.
AllowedDomains []string
TrustedFederation bool
}
CreateConnectionInput describes an SSO connection to provision. It is the transport-agnostic form of AdminCreateConnectionRequest so host apps that expose their own tenant-scoped admin surface (instead of the platform-admin HTTP route) can create connections after their own authorization check.
type EncryptedStore ¶ added in v1.9.0
type EncryptedStore struct {
// contains filtered or unexported fields
}
EncryptedStore wraps a Store, transparently encrypting the sensitive SSO connection secrets at rest — the OIDC ClientSecret and the SAML SP private key. Reads decrypt; bridge.AESGCMEncryptor tags its output with a version prefix and returns un-prefixed values unchanged on Decrypt, so pre-existing plaintext rows keep working and re-saving a connection upgrades it in place.
func NewEncryptedStore ¶ added in v1.9.0
func NewEncryptedStore(inner Store, enc bridge.Encryptor) *EncryptedStore
NewEncryptedStore wraps inner with secret encryption. A nil encryptor falls back to bridge.NoopEncryptor (no-op passthrough, e.g. dev without a key). Pass an AES-GCM encryptor (engine.TokenEncryptor()) in production.
func (*EncryptedStore) CreateConnection ¶ added in v1.9.0
func (s *EncryptedStore) CreateConnection(ctx context.Context, c *Connection) error
func (*EncryptedStore) CreateIdentity ¶ added in v1.9.0
func (s *EncryptedStore) CreateIdentity(ctx context.Context, ident *Identity) error
CreateIdentity delegates: identity bindings hold no secret.
func (*EncryptedStore) DeleteConnection ¶ added in v1.9.0
func (s *EncryptedStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
func (*EncryptedStore) GetConnection ¶ added in v1.9.0
func (s *EncryptedStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
func (*EncryptedStore) GetConnectionByDomain ¶ added in v1.9.0
func (s *EncryptedStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
func (*EncryptedStore) GetConnectionByDomainAndOrg ¶ added in v1.9.0
func (s *EncryptedStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
func (*EncryptedStore) GetConnectionByProvider ¶ added in v1.9.0
func (s *EncryptedStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
func (*EncryptedStore) GetIdentity ¶ added in v1.9.0
func (s *EncryptedStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
GetIdentity delegates: identity bindings hold no secret.
func (*EncryptedStore) ListConnections ¶ added in v1.9.0
func (s *EncryptedStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
func (*EncryptedStore) UpdateConnection ¶ added in v1.9.0
func (s *EncryptedStore) UpdateConnection(ctx context.Context, c *Connection) error
type ExchangeRequest ¶ added in v1.9.0
type ExchangeRequest struct {
Code string `json:"code" description:"One-time code issued by the ACS redirect"`
}
ExchangeRequest is the body for POST /v1/sso/exchange.
type Identity ¶ added in v1.9.0
type Identity struct {
ConnectionID id.SSOConnectionID `json:"connection_id"`
Subject string `json:"subject"`
UserID id.UserID `json:"user_id"`
CreatedAt time.Time `json:"created_at"`
}
Identity binds an identity provider subject to a local user for one connection. A later login by the same subject lands on this user whatever email the provider asserts, and a login by a subject the connection has never seen falls back to email matching inside the connection's domains.
type LoginByDomainRequest ¶ added in v1.9.0
type LoginByDomainRequest struct {
Email string `json:"email" description:"Work email; its domain routes to the matching SSO connection"`
ConnectionID string `` /* 143-byte string literal not displayed */
ReturnURL string `json:"return_url,omitempty" description:"Frontend URL to land on after login (must be allowlisted)"`
}
LoginByDomainRequest carries an email whose domain selects the IdP connection. ConnectionID pins the exact connection (multi-tenant SSO: the same domain can belong to several orgs, so home-realm discovery resolves the specific connection and passes its id here). When empty, the domain resolves the connection app-wide (single-tenant / legacy).
type LoginRequest ¶ added in v1.9.0
type LoginRequest struct {
Provider string `path:"provider"`
ReturnURL string `` /* 128-byte string literal not displayed */
}
LoginRequest contains the path parameter for starting SSO.
type LoginResponse ¶ added in v1.9.0
LoginResponse is returned when the SSO flow is initiated.
type MemoryStore ¶ added in v1.9.0
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory Store for testing.
func NewMemoryStore ¶ added in v1.9.0
func NewMemoryStore() *MemoryStore
NewMemoryStore creates a new in-memory SSO connection store.
func (*MemoryStore) CreateConnection ¶ added in v1.9.0
func (s *MemoryStore) CreateConnection(_ context.Context, c *Connection) error
func (*MemoryStore) CreateIdentity ¶ added in v1.9.0
func (s *MemoryStore) CreateIdentity(_ context.Context, ident *Identity) error
func (*MemoryStore) DeleteConnection ¶ added in v1.9.0
func (s *MemoryStore) DeleteConnection(_ context.Context, connID id.SSOConnectionID) error
func (*MemoryStore) GetConnection ¶ added in v1.9.0
func (s *MemoryStore) GetConnection(_ context.Context, connID id.SSOConnectionID) (*Connection, error)
func (*MemoryStore) GetConnectionByDomain ¶ added in v1.9.0
func (s *MemoryStore) GetConnectionByDomain(_ context.Context, appID id.AppID, domain string) (*Connection, error)
func (*MemoryStore) GetConnectionByDomainAndOrg ¶ added in v1.9.0
func (s *MemoryStore) GetConnectionByDomainAndOrg(_ context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
func (*MemoryStore) GetConnectionByProvider ¶ added in v1.9.0
func (s *MemoryStore) GetConnectionByProvider(_ context.Context, appID id.AppID, provider string) (*Connection, error)
func (*MemoryStore) GetIdentity ¶ added in v1.9.0
func (s *MemoryStore) GetIdentity(_ context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
func (*MemoryStore) ListConnections ¶ added in v1.9.0
func (s *MemoryStore) ListConnections(_ context.Context, appID id.AppID) ([]*Connection, error)
func (*MemoryStore) UpdateConnection ¶ added in v1.9.0
func (s *MemoryStore) UpdateConnection(_ context.Context, c *Connection) error
type MongoStore ¶ added in v1.9.0
type MongoStore struct {
// contains filtered or unexported fields
}
MongoStore implements sso.Store using the Grove MongoDB driver.
func NewMongoStore ¶ added in v1.9.0
func NewMongoStore(db *grove.DB) *MongoStore
NewMongoStore creates a new MongoDB-backed SSO connection store.
func (*MongoStore) CreateConnection ¶ added in v1.9.0
func (s *MongoStore) CreateConnection(ctx context.Context, c *Connection) error
func (*MongoStore) CreateIdentity ¶ added in v1.9.0
func (s *MongoStore) CreateIdentity(ctx context.Context, ident *Identity) error
func (*MongoStore) DeleteConnection ¶ added in v1.9.0
func (s *MongoStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
func (*MongoStore) GetConnection ¶ added in v1.9.0
func (s *MongoStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
func (*MongoStore) GetConnectionByDomain ¶ added in v1.9.0
func (s *MongoStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
func (*MongoStore) GetConnectionByDomainAndOrg ¶ added in v1.9.0
func (s *MongoStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
func (*MongoStore) GetConnectionByProvider ¶ added in v1.9.0
func (s *MongoStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
func (*MongoStore) GetIdentity ¶ added in v1.9.0
func (s *MongoStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
func (*MongoStore) ListConnections ¶ added in v1.9.0
func (s *MongoStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
func (*MongoStore) UpdateConnection ¶ added in v1.9.0
func (s *MongoStore) UpdateConnection(ctx context.Context, c *Connection) error
type OIDCConfig ¶ added in v1.9.0
type OIDCConfig struct {
// Name is the provider's stable name, used in routes and state.
Name string
// Issuer is the OpenID provider's issuer URL. Discovery is fetched from
// Issuer + "/.well-known/openid-configuration".
Issuer string
// ClientID and ClientSecret are the relying party credentials.
ClientID string
ClientSecret string
// RedirectURL is the callback registered with the provider.
RedirectURL string
// Scopes defaults to openid, profile and email.
Scopes []string
// HTTPClient overrides the client used for discovery, the token exchange
// and userinfo. Nil selects the hardened client, which refuses private
// addresses and plain http; tests point it at a local server.
HTTPClient *http.Client
// contains filtered or unexported fields
}
OIDCConfig configures an OpenID Connect SSO provider.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the SSO authentication plugin.
func (*Plugin) Connections ¶ added in v1.9.0
Connections returns the list of configured SSO connection names for client config.
func (*Plugin) CreateConnection ¶ added in v1.9.0
func (p *Plugin) CreateConnection(ctx context.Context, in CreateConnectionInput) (*Connection, error)
CreateConnection provisions an SSO connection: it resolves the app's default environment (env_id is NOT NULL), validates the protocol-specific fields, and for SAML derives the SP EntityID/ACS URL from PublicBaseURL and generates a self-signed SP keypair when none is supplied. This is the shared core behind handleAdminCreateConnection; host apps call it directly behind their own authorization instead of the platform-admin HTTP route.
func (*Plugin) DeclareSettings ¶ added in v1.9.0
DeclareSettings implements plugin.SettingsProvider.
func (*Plugin) MigrationGroups ¶ added in v1.9.0
MigrationGroups implements plugin.MigrationProvider.
func (*Plugin) OnBeforeSignIn ¶ added in v1.9.0
OnBeforeSignIn enforces SSO: when a user's email domain has an active + enforced SSO connection, password sign-in is vetoed — they must use SSO. Workspace owners/admins are exempt (break-glass, so a misconfigured IdP can't lock out the people who administer it). Non-email sign-ins and domains without an enforced connection pass through untouched.
func (*Plugin) OnInit ¶ added in v1.9.0
OnInit captures the store reference and bridges from the engine.
func (*Plugin) ProviderNames ¶ added in v1.9.0
Providers returns the list of configured provider names.
func (*Plugin) RegisterContract ¶ added in v1.9.0
func (p *Plugin) RegisterContract( d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, engine plugin.Engine, ) error
func (*Plugin) RegisterRoutes ¶
RegisterRoutes registers SSO HTTP endpoints on a forge.Router.
func (*Plugin) SSOStore ¶ added in v1.9.0
SSOStore returns the connection store so an embedding host app can read/manage connections directly (e.g. list by domain, toggle active) behind its own authorization. Returns nil before OnInit has wired a store.
func (*Plugin) SetSSOStore ¶ added in v1.9.0
SetSSOStore sets the SSO connection store.
type PostgresStore ¶ added in v1.9.0
type PostgresStore struct {
// contains filtered or unexported fields
}
PostgresStore implements sso.Store using the Grove ORM with PostgreSQL.
func NewPostgresStore ¶ added in v1.9.0
func NewPostgresStore(db *grove.DB) *PostgresStore
NewPostgresStore creates a new PostgreSQL-backed SSO connection store.
func (*PostgresStore) CreateConnection ¶ added in v1.9.0
func (s *PostgresStore) CreateConnection(ctx context.Context, c *Connection) error
func (*PostgresStore) CreateIdentity ¶ added in v1.9.0
func (s *PostgresStore) CreateIdentity(ctx context.Context, ident *Identity) error
func (*PostgresStore) DeleteConnection ¶ added in v1.9.0
func (s *PostgresStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
func (*PostgresStore) GetConnection ¶ added in v1.9.0
func (s *PostgresStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
func (*PostgresStore) GetConnectionByDomain ¶ added in v1.9.0
func (s *PostgresStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
func (*PostgresStore) GetConnectionByDomainAndOrg ¶ added in v1.9.0
func (s *PostgresStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
func (*PostgresStore) GetConnectionByProvider ¶ added in v1.9.0
func (s *PostgresStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
func (*PostgresStore) GetIdentity ¶ added in v1.9.0
func (s *PostgresStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
func (*PostgresStore) ListConnections ¶ added in v1.9.0
func (s *PostgresStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
func (*PostgresStore) UpdateConnection ¶ added in v1.9.0
func (s *PostgresStore) UpdateConnection(ctx context.Context, c *Connection) error
type Provider ¶ added in v1.9.0
type Provider interface {
// Name returns the unique identifier for this provider (e.g. "okta", "azure-ad").
Name() string
// Protocol returns the SSO protocol ("oidc" or "saml").
Protocol() string
// LoginURL returns the URL to redirect the user to for authentication.
LoginURL(state string) (string, error)
// HandleCallback processes the callback from the identity provider
// and returns the authenticated user's identity.
HandleCallback(ctx context.Context, params map[string]string) (*User, error)
}
Provider is the interface for SSO identity providers.
func NewOIDCProvider ¶ added in v1.9.0
func NewOIDCProvider(cfg OIDCConfig) Provider
NewOIDCProvider creates an OIDC SSO provider. It uses OIDC discovery to find the authorization, token and userinfo endpoints and falls back to the conventional paths under the issuer when discovery is unavailable.
func NewSAMLProvider ¶ added in v1.9.0
func NewSAMLProvider(cfg SAMLConfig) (Provider, error)
NewSAMLProvider builds a SAML provider from cfg. It resolves the IdP metadata/cert up front so a misconfigured connection fails fast rather than at first login.
type SAMLConfig ¶ added in v1.9.0
type SAMLConfig struct {
// Name is the unique provider identifier (e.g. "okta-saml").
Name string
// IdP description — resolved in this order: pasted metadata XML, a
// metadata URL to fetch, or an explicit SSO URL + signing certificate.
IDPMetadataXML string
MetadataURL string
IDPSSOURL string
IDPCertificatePEM string
// Service Provider identity.
EntityID string // SP entity ID (assertion audience)
ACSURL string // SP Assertion Consumer Service URL
SPCertificatePEM string // SP signing cert (PEM); optional
SPPrivateKeyPEM string // SP signing key (PEM); optional, enables signed AuthnRequests
SignRequests bool
// AllowIDPInitiated permits unsolicited assertions (no prior AuthnRequest).
AllowIDPInitiated bool
// AttributeMap maps SAML attribute names to user fields
// ("email" | "first_name" | "last_name" | "groups"). Empty uses defaults.
AttributeMap map[string]string
// contains filtered or unexported fields
}
SAMLConfig configures a SAML 2.0 identity provider connection.
type SAMLMetadataProvider ¶ added in v1.9.0
SAMLMetadataProvider is implemented by SAML providers that can emit SP metadata XML for IdP configuration.
type SqliteStore ¶ added in v1.9.0
type SqliteStore struct {
// contains filtered or unexported fields
}
SqliteStore implements sso.Store using the Grove ORM with SQLite.
func NewSqliteStore ¶ added in v1.9.0
func NewSqliteStore(db *grove.DB) *SqliteStore
NewSqliteStore creates a new SQLite-backed SSO connection store.
func (*SqliteStore) CreateConnection ¶ added in v1.9.0
func (s *SqliteStore) CreateConnection(ctx context.Context, c *Connection) error
func (*SqliteStore) CreateIdentity ¶ added in v1.9.0
func (s *SqliteStore) CreateIdentity(ctx context.Context, ident *Identity) error
func (*SqliteStore) DeleteConnection ¶ added in v1.9.0
func (s *SqliteStore) DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
func (*SqliteStore) GetConnection ¶ added in v1.9.0
func (s *SqliteStore) GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
func (*SqliteStore) GetConnectionByDomain ¶ added in v1.9.0
func (s *SqliteStore) GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
func (*SqliteStore) GetConnectionByDomainAndOrg ¶ added in v1.9.0
func (s *SqliteStore) GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
func (*SqliteStore) GetConnectionByProvider ¶ added in v1.9.0
func (s *SqliteStore) GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
func (*SqliteStore) GetIdentity ¶ added in v1.9.0
func (s *SqliteStore) GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
func (*SqliteStore) ListConnections ¶ added in v1.9.0
func (s *SqliteStore) ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
func (*SqliteStore) UpdateConnection ¶ added in v1.9.0
func (s *SqliteStore) UpdateConnection(ctx context.Context, c *Connection) error
type Store ¶ added in v1.9.0
type Store interface {
CreateConnection(ctx context.Context, c *Connection) error
GetConnection(ctx context.Context, connID id.SSOConnectionID) (*Connection, error)
GetConnectionByDomain(ctx context.Context, appID id.AppID, domain string) (*Connection, error)
// GetConnectionByDomainAndOrg resolves the active connection for a domain
// within a specific org (workspace). Multi-tenant SSO allows the same domain
// in several orgs, so lookups that must land on one workspace use this.
GetConnectionByDomainAndOrg(ctx context.Context, appID id.AppID, orgID id.OrgID, domain string) (*Connection, error)
GetConnectionByProvider(ctx context.Context, appID id.AppID, provider string) (*Connection, error)
ListConnections(ctx context.Context, appID id.AppID) ([]*Connection, error)
UpdateConnection(ctx context.Context, c *Connection) error
DeleteConnection(ctx context.Context, connID id.SSOConnectionID) error
// GetIdentity resolves the user bound to a provider subject on one
// connection. CreateIdentity records the binding and fails on a subject
// the connection already holds.
GetIdentity(ctx context.Context, connID id.SSOConnectionID, subject string) (*Identity, error)
CreateIdentity(ctx context.Context, ident *Identity) error
}
Store persists SSO connections.
type User ¶ added in v1.9.0
type User struct {
ProviderUserID string `json:"provider_user_id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Groups []string `json:"groups,omitempty"`
Attributes map[string]string `json:"attributes,omitempty"`
// EmailVerified is the provider's own statement about the address, when
// it made one: OIDC's email_verified claim. Nil means the provider said
// nothing; an explicit false refuses the login.
EmailVerified *bool `json:"email_verified,omitempty"`
}
User represents the identity returned by an SSO provider.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package contract wires the sso plugin's deep-link settings page into the forge-dashboard contract registry.
|
Package contract wires the sso plugin's deep-link settings page into the forge-dashboard contract registry. |
|
Package ssotest provides a backend-agnostic conformance suite for the sso.Store interface.
|
Package ssotest provides a backend-agnostic conformance suite for the sso.Store interface. |