samlidp

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

README

samlidp

github.com/ubgo/auth/plugin/samlidp makes the app a SAML 2.0 identity provider (IdP): other services, SAML service providers (SPs), send their users here to sign in and receive a signed assertion naming the user. It is the SAML sibling of plugin/oauthprovider and the mirror of plugin/sso, which makes the app a service provider of someone else's IdP.

It is its own module, because it brings an XML-signature library (goxmldsig, the library plugin/sso already uses) that apps which are not an IdP should not inherit. It adds no table: pending requests and request-ID claims are verification rows.

The guide is docs/plugins/samlidp.md; the runnable recipe is examples/saml-idp.

Wiring

idp := samlidp.New(
    samlidp.Issuer("https://id.example.com"),
    samlidp.LoginPage("/login"),
    samlidp.SigningKey(key, cert), // RSA or ECDSA crypto.Signer, KMS handles included
    samlidp.ServiceProviders(samlidp.ServiceProvider{
        EntityID:   "https://wiki.example.com/saml/metadata",
        ACSURLs:    []string{"https://wiki.example.com/saml/acs"},
        Attributes: samlidp.DefaultAttributes,
    }),
)
a, err := auth.New(auth.WithDB(db, dialect), auth.WithBaseURL("https://id.example.com"), auth.WithPlugins(signIn, idp))
mux.Handle(samlidp.DefaultBasePath+"/", idp.Handler())

Endpoints

Path (under the issuer) Method What it does
/saml/metadata GET, HEAD IdP metadata: entity ID, signing certificate, NameID formats, SSO service in both bindings
/saml/sso GET (HTTP-Redirect), POST (HTTP-POST) Accepts an AuthnRequest, claims its ID, stores it and redirects to /saml/continue
/saml/continue?request=… GET Signed out: redirects to LoginPage?return_to=<this URL> under a new handle. Signed in: answers the auto-posting form with the signed response
/saml/initiate POST (sp, acs_url, RelayState) IdP-initiated sign-on, only to an SP with AllowIdPInitiated; needs the session and passes the origin check

Each is a self-served operation (samlidp.sso, samlidp.continue, samlidp.initiate), so it runs the whole pipeline: rate limits (DefaultRateLimits, per IP), hooks and tracing. The Go API is SSO, Continue, Initiate and Metadata.

Options

Option Default Notes
Issuer(url) required https, or http on a loopback host
LoginPage(url) required same-site path or trusted origin; gets return_to and, for ForceAuthn, prompt=login
SigningKey(signer, cert) required key must match the certificate; RSA signs RSA-SHA256, ECDSA ECDSA-SHA256
ServiceProviders(sps...) checked by auth.New
WithServiceProviderStore(store) looked up after the static list; each answer checked
EntityID(id) metadata URL
BasePath(path) /saml
ReturnParam(name) return_to
AssertionTTL(d) 5 min assertion and SubjectConfirmationData NotOnOrAfter
RequestMaxAge(d) 5 min oldest accepted IssueInstant
ClockSkew(d) 1 min future tolerance, and NotBefore set back
LoginTTL(d) 15 min how long a request waits for sign-in
MaxMessageSize(n) 64 KiB after base64 and inflate
WantAuthnRequestsSigned() off every SP must sign; advertised in metadata
Access(fn) allow all the authorization seam: may this user sign in to this SP
WithRequestIDGuard(g) verification rows needed when the verification store cannot refuse duplicates
RateLimit(endpoint, limit, window) DefaultRateLimits 0 removes a limit

ServiceProvider fields: EntityID, ACSURLs, Certificates, RequireSignedRequests, NameIDFormat (persistent by default; email, transient, unspecified), NameID func, Attributes func (DefaultAttributes releases the verified email and the name), AllowIdPInitiated, UnsignedResponse.

Error codes

samlidp.invalid_request, unknown_service_provider, acs_not_registered, unsupported_binding, wrong_destination, request_expired, request_replayed, signature_required, invalid_signature, unsupported_signature_algorithm, invalid_name_id_policy, request_not_found, idp_initiated_disabled, name_id_unavailable, access_denied, relay_state_too_long, message_too_large, missing_request. Every one is rendered as a page by Handler and never sent to an ACS URL. Configuration errors from auth.New: ErrNoIssuer, ErrNoLoginPage, ErrNoSigningKey, ErrBadSP, ErrBadTTL, ErrBadBasePath, ErrNoExclusive.

Security rules, each pinned by a test

  • An AuthnRequest from an unregistered issuer is refused; the SP is found by the unverified Issuer only to choose the certificates, and every field is then read from the verified element.
  • AssertionConsumerServiceURL must equal a registered URL exactly; an index must be in range.
  • The request ID is claimed once (auth.ExclusiveVerificationStore, checked by Init); IssueInstant must be within RequestMaxAge and ClockSkew; the continue handle is single use.
  • A signed request must name this IdP's SSO URL as Destination; a POST signature is checked by goxmldsig (signature wrapping refused), a redirect signature over the raw query octets; SHA-1 is refused.
  • Every response and assertion is signed; the assertion's only Audience is the SP's entity ID, Recipient and Destination the ACS URL, InResponseTo the request ID.
  • A signed-out browser, or a session still waiting for its second factor, is sent to LoginPage and never gets an assertion; IsPassive answers NoPassive instead.

Not implemented yet

Single logout (no SingleLogoutService in the metadata; an SP's logout does not end the IdP session), encrypted assertions, signed metadata, the Artifact binding and the SOAP ECP profile.

Documentation

Overview

Package samlidp makes the app a SAML 2.0 identity provider: other services (SAML service providers, "SPs") send their users here to sign in, and receive a signed assertion naming the user. It is the SAML sibling of plugin/oauthprovider, and the mirror of plugin/sso, which makes the app a service provider of someone else's IdP.

It is opt-in and its own module. The protocol endpoints (metadata, the single sign-on service in the HTTP-Redirect and HTTP-POST bindings, the continue step after sign-in, and IdP-initiated sign-on) are served by Plugin.Handler; each is a self-served auth operation underneath (auth.OpInfo.SelfServed), so it runs the same pipeline as every other operation: rate limits, hooks, tracing.

Security rules, each pinned by a test: an AuthnRequest from an unknown issuer is refused; its AssertionConsumerServiceURL must equal a registered one exactly, and an error is never sent to an unregistered URL; its ID is single use (claimed in a store that refuses duplicates) and its IssueInstant must be recent; a signed request is verified against the SP's registered certificate and only the verified element is read (the defence against XML signature wrapping); a request whose SP requires signing is refused unsigned. Every assertion is signed (RSA-SHA256 by default), names the SP's entity ID as its only audience, the ACS URL as its recipient, the request's ID as InResponseTo, and expires after AssertionTTL. A browser that is not signed in is sent to LoginPage and never gets an assertion.

Single logout (SLO) is not implemented yet: an SP's logout does not end the IdP session, and the metadata advertises no SingleLogoutService.

Index

Constants

View Source
const (
	BindingRedirect = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
	BindingPOST     = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
)

SAML bindings.

View Source
const (
	AlgRSASHA256   = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
	AlgRSASHA512   = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
	AlgECDSASHA256 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
)

Signature algorithms (XML Signature, RFC 6931) accepted on a request and used on a response. SHA-1 is not accepted.

View Source
const (
	ParamSAMLRequest  = "SAMLRequest"
	ParamSAMLResponse = "SAMLResponse"
	ParamRelayState   = "RelayState"
	ParamSigAlg       = "SigAlg"
	ParamSignature    = "Signature"
)

Binding parameter names (SAML bindings 3.4.4 and 3.5.4).

View Source
const (
	StatusSuccess   = "urn:oasis:names:tc:SAML:2.0:status:Success"
	StatusResponder = "urn:oasis:names:tc:SAML:2.0:status:Responder"
	StatusNoPassive = "urn:oasis:names:tc:SAML:2.0:status:NoPassive"
)

Status codes (SAML core 3.2.2.2).

View Source
const (
	EndpointSSO      auth.EndpointID = "samlidp.sso"
	EndpointContinue auth.EndpointID = "samlidp.continue"
	EndpointInitiate auth.EndpointID = "samlidp.initiate"
)

Operation ids. All are self-served: Handler serves them, no transport mounts a JSON copy.

View Source
const (
	// PurposeRequestID reserves an AuthnRequest's ID so it is answered at
	// most once; the token is the SP's entity ID and the request ID.
	PurposeRequestID auth.Purpose = "samlidp.request_id"
	// PurposePending is an accepted AuthnRequest waiting for the user to
	// sign in; the token is the handle in the continue URL, Meta the
	// request. It is spent when the assertion is issued, and rotated while
	// the browser is still signed out.
	PurposePending auth.Purpose = "samlidp.pending"
)

Verification purposes this plugin files rows under.

View Source
const (
	// DefaultBasePath is where Handler serves the endpoints under the
	// issuer: <issuer>/saml/metadata, /saml/sso, /saml/continue,
	// /saml/initiate.
	//ds:def id=samlidp-defaultbasepath-q4m8zt2k owner=@khanakia stability=stable
	DefaultBasePath = "/saml"
	// DefaultAssertionTTL is how long an assertion is valid (Conditions and
	// SubjectConfirmationData NotOnOrAfter). Five minutes is what most IdPs
	// use: the browser posts it to the SP within seconds.
	//ds:def id=samlidp-defaultassertionttl-w7n3kp5x owner=@khanakia stability=stable
	DefaultAssertionTTL = 5 * time.Minute
	// DefaultRequestMaxAge is how old an AuthnRequest's IssueInstant may be
	// when it arrives. An SP builds the request just before redirecting.
	//ds:def id=samlidp-defaultrequestmaxage-h2v9qm4r owner=@khanakia stability=stable
	DefaultRequestMaxAge = 5 * time.Minute
	// DefaultClockSkew is the tolerance for an SP clock ahead of ours, and
	// how far NotBefore is set back for an SP clock behind ours.
	//ds:def id=samlidp-defaultclockskew-z5k8wn3t owner=@khanakia stability=stable
	DefaultClockSkew = time.Minute
	// DefaultLoginTTL is how long an accepted request waits for the user to
	// sign in before the continue link stops working.
	//ds:def id=samlidp-defaultloginttl-p3x7mq9v owner=@khanakia stability=stable
	DefaultLoginTTL = 15 * time.Minute
	// DefaultReturnParam is the query parameter LoginPage receives the
	// continue URL in.
	DefaultReturnParam = "return_to"
	// DefaultMaxMessageSize bounds an AuthnRequest after decoding and
	// inflating; real ones are a few kilobytes.
	DefaultMaxMessageSize = 64 << 10
	// MaxRelayState bounds the RelayState stored with a pending request.
	// SAML bindings 3.4.3 limits it to 80 bytes; real SPs send more, so the
	// bound is generous but finite.
	MaxRelayState = 1024
)

Defaults. Each has an option.

View Source
const (
	ParamPrompt = "prompt"
	PromptLogin = "login"
)

ParamPrompt is added to the LoginPage URL as prompt=login when the SP asked for ForceAuthn and the current session is older than the request: the page must make the user authenticate again even though a session exists.

View Source
const (
	NameIDUnspecified = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
	NameIDEmail       = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
	NameIDPersistent  = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
	NameIDTransient   = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
)

NameID formats (SAML core 8.3). The SP's NameIDFormat picks which one its assertions carry.

View Source
const (
	AttrNameFormatBasic       = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
	AttrNameFormatURI         = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
	AttrNameFormatUnspecified = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"
)

Attribute name formats.

View Source
const (

	// ContentTypeMetadata is the media type of SAML metadata (SAML
	// metadata 4.1.1).
	ContentTypeMetadata = "application/samlmetadata+xml"
)

HTTP constants.

View Source
const PluginName = "samlidp"

PluginName is the plugin's permanent identity.

Variables

View Source
var (
	ErrInvalidRequest      = auth.NewError("samlidp.invalid_request", http.StatusBadRequest, "The SAML request is malformed.")
	ErrUnknownSP           = auth.NewError("samlidp.unknown_service_provider", http.StatusBadRequest, "The service provider is not registered.")
	ErrACSNotRegistered    = auth.NewError("samlidp.acs_not_registered", http.StatusBadRequest, "The assertion consumer service URL is not registered for this service provider.")
	ErrUnsupportedBinding  = auth.NewError("samlidp.unsupported_binding", http.StatusBadRequest, "Only the HTTP-POST binding is supported for the response.")
	ErrWrongDestination    = auth.NewError("samlidp.wrong_destination", http.StatusBadRequest, "The request is addressed to another endpoint.")
	ErrRequestExpired      = auth.NewError("samlidp.request_expired", http.StatusBadRequest, "The SAML request is too old or issued in the future.")
	ErrRequestReplayed     = auth.NewError("samlidp.request_replayed", http.StatusBadRequest, "The SAML request was already used.")
	ErrSignatureRequired   = auth.NewError("samlidp.signature_required", http.StatusBadRequest, "This service provider must sign its requests.")
	ErrInvalidSignature    = auth.NewError("samlidp.invalid_signature", http.StatusBadRequest, "The request signature does not verify.")
	ErrNameIDPolicy        = auth.NewError("samlidp.invalid_name_id_policy", http.StatusBadRequest, "The requested NameID format is not the one configured for this service provider.")
	ErrRequestNotFound     = auth.NewError("samlidp.request_not_found", http.StatusBadRequest, "The sign-in request is unknown, used or expired. Start again from the application.")
	ErrIdPInitiatedOff     = auth.NewError("samlidp.idp_initiated_disabled", http.StatusForbidden, "IdP-initiated sign-on is not enabled for this service provider.")
	ErrNameIDUnavailable   = auth.NewError("samlidp.name_id_unavailable", http.StatusForbidden, "Your account has no verified value for the identifier this service needs.")
	ErrAccessDenied        = auth.NewError("samlidp.access_denied", http.StatusForbidden, "You may not sign in to this service.")
	ErrRelayStateTooLong   = auth.NewError("samlidp.relay_state_too_long", http.StatusBadRequest, "The RelayState is too long.")
	ErrMessageTooLarge     = auth.NewError("samlidp.message_too_large", http.StatusRequestEntityTooLarge, "The SAML message is too large.")
	ErrUnsupportedSigAlg   = auth.NewError("samlidp.unsupported_signature_algorithm", http.StatusBadRequest, "The signature algorithm is not accepted.")
	ErrMissingRequestParam = auth.NewError("samlidp.missing_request", http.StatusBadRequest, "SAMLRequest is missing.")
)

Errors. Every one is shown to the user (Handler renders an error page) and never sent to an ACS URL, so an attacker cannot use the IdP to post to a URL of their choosing.

View Source
var (
	ErrNoIssuer     = errors.New("samlidp: Issuer is required: an absolute https URL (http only for a loopback host) with no query or fragment")
	ErrNoLoginPage  = errors.New("samlidp: LoginPage is required: where a browser that is not signed in is sent")
	ErrNoSigningKey = errors.New("samlidp: SigningKey is required: an RSA or ECDSA crypto.Signer and its certificate, whose public key must match")
	ErrBadSP        = errors.New("samlidp: invalid service provider")
	ErrBadTTL       = errors.New("samlidp: every lifetime is positive")
	ErrBadBasePath  = errors.New("samlidp: BasePath must start with / and not end with one")
	ErrNoExclusive  = errors.New("samlidp: the verification store does not refuse duplicate tokens (auth.ExclusiveVerificationStore), so a request ID cannot be claimed once; give its storage SetIfAbsent or pass samlidp.WithRequestIDGuard")
)

Configuration errors, returned by auth.New.

View Source
var DefaultRateLimits = map[auth.EndpointID]int{
	EndpointSSO:      30,
	EndpointContinue: 60,
	EndpointInitiate: 30,
}

DefaultRateLimits are the calls per client IP per minute each endpoint accepts unless RateLimit changes it. The single sign-on service writes a row per accepted request, so it is bounded even though every request must come from a registered SP; continue allows the extra hops of a login.

NameIDFormats is every format the IdP can issue, in the order the metadata lists them.

Functions

This section is empty.

Types

type AccessFunc

type AccessFunc func(ctx context.Context, u auth.User, sp ServiceProvider) error

AccessFunc decides whether a signed-in user may sign in to sp: a nil answer allows, an error refuses (ErrAccessDenied unless it is an *auth.Error of its own). It is the seam for any authorization model (roles, groups, an organization's app assignments, a policy engine).

type Attribute

type Attribute struct {
	Name         string
	NameFormat   string // AttrNameFormatBasic when empty
	FriendlyName string
	Values       []string
}

Attribute is one SAML attribute an assertion releases to an SP.

func DefaultAttributes

func DefaultAttributes(_ context.Context, _ ServiceProvider, s Subject) ([]Attribute, error)

DefaultAttributes releases "email" (when verified) and "name" in the basic name format, the attribute names most SPs map by default.

type AttributesFunc

type AttributesFunc func(ctx context.Context, sp ServiceProvider, s Subject) ([]Attribute, error)

AttributesFunc builds the attributes an SP receives. It runs on every assertion; an error refuses the sign-in.

type ContinueRequest

type ContinueRequest struct {
	Request string `json:"request"`
}

ContinueRequest names a pending request by the handle in the continue URL.

type InitiateRequest

type InitiateRequest struct {
	// ServiceProvider is the SP's entity ID.
	ServiceProvider string `json:"sp"`
	// ACSURL picks a registered ACS URL; the first when empty.
	ACSURL     string `json:"acs_url,omitempty"`
	RelayState string `json:"relay_state,omitempty"`
}

InitiateRequest starts IdP-initiated sign-on to an SP.

type NameIDFunc

type NameIDFunc func(ctx context.Context, sp ServiceProvider, s Subject) (string, error)

NameIDFunc replaces the NameID value an SP receives. It returns the value for sp.NameIDFormat; "" refuses the sign-in with ErrNameIDUnavailable.

type Option

type Option func(*Plugin)

Option configures the plugin; pass options to New.

func Access

func Access(fn AccessFunc) Option

Access decides per user and SP whether an assertion may be issued; see AccessFunc. Without it every signed-in user may sign in to every registered SP.

func AssertionTTL

func AssertionTTL(d time.Duration) Option

AssertionTTL overrides DefaultAssertionTTL.

func BasePath

func BasePath(path string) Option

BasePath moves the endpoints from DefaultBasePath under the issuer.

func ClockSkew

func ClockSkew(d time.Duration) Option

ClockSkew overrides DefaultClockSkew.

func EntityID

func EntityID(id string) Option

EntityID replaces the IdP's SAML entity ID, which defaults to the metadata URL (<issuer><base>/metadata). Set it when SPs were configured with another value; it is the Issuer of every response.

func Issuer

func Issuer(iss string) Option

Issuer is the app's public origin the endpoints live under, such as "https://id.example.com": an absolute https URL (http only for a loopback host) with no query or fragment. Required.

func LoginPage

func LoginPage(u string) Option

LoginPage is where a browser that is not signed in is sent, with the continue URL in the ReturnParam query parameter (and prompt=login when the SP asked for ForceAuthn). After signing the user in, the page sends the browser to that URL. Required: a same-site path or a URL on a trusted origin.

func LoginTTL

func LoginTTL(d time.Duration) Option

LoginTTL overrides DefaultLoginTTL.

func MaxMessageSize

func MaxMessageSize(n int) Option

MaxMessageSize overrides DefaultMaxMessageSize.

func RateLimit

func RateLimit(endpoint auth.EndpointID, limit int, window time.Duration) Option

RateLimit replaces an endpoint's default limit per client IP; a limit of 0 removes it (for an app that limits in front of the library).

func RequestMaxAge

func RequestMaxAge(d time.Duration) Option

RequestMaxAge overrides DefaultRequestMaxAge.

func ReturnParam

func ReturnParam(name string) Option

ReturnParam renames the query parameter LoginPage receives the continue URL in (DefaultReturnParam).

func ServiceProviders

func ServiceProviders(sps ...ServiceProvider) Option

ServiceProviders registers SPs in code. They are checked by auth.New.

func SigningKey

func SigningKey(key crypto.Signer, cert *x509.Certificate) Option

SigningKey is the key that signs every response and assertion, and its certificate, published in the metadata for SPs to trust. key is any crypto.Signer (an *rsa.PrivateKey, or a KMS or HSM handle) whose public key is cert's. RSA signs with RSA-SHA256, ECDSA with ECDSA-SHA256. Required.

func WantAuthnRequestsSigned

func WantAuthnRequestsSigned() Option

WantAuthnRequestsSigned requires every SP to sign its AuthnRequests and says so in the metadata. Every SP then needs a Certificate. A single SP can require it with ServiceProvider.RequireSignedRequests.

func WithRequestIDGuard

func WithRequestIDGuard(g RequestIDGuard) Option

WithRequestIDGuard replaces the default single-use claim of request IDs (a verification row that the store refuses to insert twice). Use it when the verification store cannot refuse duplicates.

func WithServiceProviderStore

func WithServiceProviderStore(store ServiceProviderStore) Option

WithServiceProviderStore looks SPs up in store (a database table, an admin UI's registry, a config service) instead of, or after, the ones ServiceProviders registered. Each SP it returns is checked on every lookup, as auth.New checks static ones.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin is the SAML identity provider. Build it with New.

func New

func New(opts ...Option) *Plugin

New builds the identity provider.

func (*Plugin) Continue

func (p *Plugin) Continue(ctx context.Context, in ContinueRequest) (Result, error)

Continue answers a pending request for the caller's session: the login redirect while signed out, the response form once signed in.

func (*Plugin) ContinueURL

func (p *Plugin) ContinueURL(handle string) string

ContinueURL is the continue step for a pending request's handle.

func (*Plugin) Handler

func (p *Plugin) Handler(opts ...authhttp.Option) http.Handler

Handler serves the endpoints at their paths under the issuer: the metadata, the single sign-on service (GET for HTTP-Redirect, POST for HTTP-POST), the continue step and IdP-initiated sign-on. opts are the authhttp options the app's auth handler uses (cookie prefix, trusted proxies), so the continue step reads the same session. Errors are shown as a page and never sent to an SP. An unknown path answers 404.

func (*Plugin) IdPEntityID

func (p *Plugin) IdPEntityID() string

IdPEntityID is the entity ID the IdP signs as.

func (*Plugin) Init

func (p *Plugin) Init(a *auth.Auth) error

Init captures the core, checks the login page against the trusted origins, and installs the default request-ID guard.

func (*Plugin) Initiate

func (p *Plugin) Initiate(ctx context.Context, in InitiateRequest) (Result, error)

Initiate builds an unsolicited response for the signed-in caller to an SP that allows IdP-initiated sign-on.

func (*Plugin) Metadata

func (p *Plugin) Metadata() []byte

Metadata is the IdP's SAML metadata: its entity ID, signing certificate, NameID formats and single sign-on service in both bindings. It is what an SP administrator uploads or points their SP at (MetadataURL). It needs the plugin to have passed auth.New, which checked the certificate.

func (*Plugin) MetadataURL

func (p *Plugin) MetadataURL() string

MetadataURL is where Handler serves the IdP metadata.

func (*Plugin) Name

func (p *Plugin) Name() string

Name is PluginName.

func (*Plugin) Operations

func (p *Plugin) Operations() []auth.Operation

Operations lists the self-served operations.

func (*Plugin) OptionErr

func (p *Plugin) OptionErr() error

OptionErr is every error the options recorded (auth.OptionChecker).

func (*Plugin) RateLimits

func (p *Plugin) RateLimits() []auth.RateRule

RateLimits declares the limits, in a fixed order so the rule list is the same on every start.

func (*Plugin) SSO

func (p *Plugin) SSO(ctx context.Context, in SSORequest) (Result, error)

SSO accepts an AuthnRequest from Go: the redirect to the continue step, or an error to show the user.

func (*Plugin) SSOURL

func (p *Plugin) SSOURL() string

SSOURL is the single sign-on service URL SPs send AuthnRequests to.

func (*Plugin) Validate

func (p *Plugin) Validate(*auth.Config) error

Validate checks the issuer, pages, key, lifetimes and static SPs.

type RequestClaim

type RequestClaim struct {
	// SP is the requesting SP's entity ID; Request the request's ID.
	SP, Request string
	// Until is when the claim may be forgotten: past it the request's
	// IssueInstant is too old to be accepted anyway.
	Until time.Time
}

RequestClaim is one AuthnRequest ID to be answered at most once.

type RequestIDGuard

type RequestIDGuard interface {
	Claim(ctx context.Context, c RequestClaim) error
}

RequestIDGuard claims request IDs. Claim answers nil the first time a claim is made and ErrRequestReplayed every later time, even under concurrent calls.

type ResponseForm

type ResponseForm struct {
	ACSURL       string `json:"acs_url"`
	SAMLResponse string `json:"saml_response"`
	RelayState   string `json:"relay_state,omitempty"`
}

ResponseForm is what the browser posts to the SP's ACS URL (HTTP-POST binding).

type Result

type Result struct {
	RedirectTo string        `json:"redirect_to,omitempty"`
	Form       *ResponseForm `json:"form,omitempty"`
}

Result is what an endpoint answers: a redirect (to the continue step or the login page), or the response form the browser posts to the SP. Exactly one is set.

type SSORequest

type SSORequest struct {
	// Binding is BindingRedirect (SAMLRequest deflated, any signature in
	// the query) or BindingPOST (SAMLRequest base64 XML, any signature
	// enveloped in it).
	Binding     string `json:"binding"`
	SAMLRequest string `json:"saml_request"`
	RelayState  string `json:"relay_state,omitempty"`
	// SigAlg and Signature are the redirect binding's query signature.
	SigAlg    string `json:"sig_alg,omitempty"`
	Signature string `json:"signature,omitempty"`
	// RawQuery is the redirect binding's query exactly as received, whose
	// octets the signature covers (SAML bindings 3.4.4.1). Without it the
	// signed string is rebuilt from the decoded values.
	RawQuery string `json:"raw_query,omitempty"`
}

SSORequest is an AuthnRequest as it arrived at the single sign-on service.

type ServiceProvider

type ServiceProvider struct {
	// EntityID is the SP's entity ID: the Issuer of its AuthnRequests and
	// the only Audience of the assertions it receives. Required.
	EntityID string
	// ACSURLs are the SP's assertion consumer service URLs (HTTP-POST
	// binding). A request's AssertionConsumerServiceURL must equal one of
	// them exactly; a request without one is answered at the first. An
	// AssertionConsumerServiceIndex selects by position. Required; each an
	// absolute https URL (http only for a loopback host).
	ACSURLs []string
	// Certificates verify the SP's signed AuthnRequests: a request that
	// carries a signature is verified against them, and refused when it
	// does not verify. Without Certificates a signature cannot be checked
	// and the request counts as unsigned. RequireSignedRequests refuses an
	// unsigned one.
	Certificates []*x509.Certificate
	// RequireSignedRequests refuses an AuthnRequest that is not signed by
	// one of Certificates.
	RequireSignedRequests bool
	// NameIDFormat is the NameID format the SP receives; NameIDPersistent
	// when empty. Persistent and unspecified carry the user's ID unless
	// NameID replaces it, email the primary verified email address,
	// transient a fresh random value per assertion.
	NameIDFormat string
	// NameID, when set, produces the NameID value (a pairwise identifier,
	// an employee number).
	NameID NameIDFunc
	// Attributes, when set, builds the attributes released to the SP.
	// Without it the assertion carries none. DefaultAttributes releases the
	// email address and the name.
	Attributes AttributesFunc
	// AllowIdPInitiated lets Plugin.Initiate send an unsolicited assertion
	// to this SP (IdP-initiated sign-on). Off by default: an unsolicited
	// assertion has no InResponseTo to bind it to, so an SP that accepts
	// them is open to assertion injection on its side.
	AllowIdPInitiated bool
	// UnsignedResponse signs only the assertion and leaves the Response
	// envelope unsigned, for an SP that refuses two signatures. By default
	// both are signed.
	UnsignedResponse bool
}

ServiceProvider is one registered SAML SP.

type ServiceProviderStore

type ServiceProviderStore interface {
	ServiceProvider(ctx context.Context, entityID string) (ServiceProvider, error)
}

ServiceProviderStore finds a registered SP by entity ID. It answers ErrUnknownSP (or an error wrapping it) for an entity ID it does not know.

type Subject

type Subject struct {
	User auth.User
	// Email is the user's primary verified email address, "" when there is
	// none. Unverified addresses are never released.
	Email string
	// Session is the IdP session the assertion is issued from.
	Session auth.Session
}

Subject is what an SP's NameID and Attributes functions see about the user being signed in.

Jump to

Keyboard shortcuts

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