samlidp

package
v1.7.6 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package samlidp implements the SAML 2.0 Identity Provider (IdP) core: IdP-metadata generation, AuthnRequest parsing, and the issuance of XML-DSig-signed SAML assertions/responses for a registered Service Provider (SP).

The package is deliberately transport-agnostic and persistence-agnostic. It exposes a narrow Issuer interface (MetadataProvider + AssertionIssuer) plus a no-op default so the rest of the server can hold exactly one Issuer for the process lifetime and call it without nil checks. The HTTP surface (SSO POST/Redirect bindings, SLO) and the SP-connection store are wired separately; this package owns only the SAML protocol logic and the signing.

Signing uses an RSA private key + X.509 certificate supplied at construction (GATEWAY_SAML_*). Assertions are signed with RSA-SHA256 and SHA-256 digests per the SAML 2.0 / xmldsig-core profiles that modern SPs (Okta, Azure AD, Google Workspace) accept.

Index

Constants

View Source
const (
	// ProviderNoop is the disabled-IdP implementation name.
	ProviderNoop = "noop"
	// ProviderRSA is the real RSA-signing IdP implementation name.
	ProviderRSA = "rsa"
)

ProviderName identifies the active Issuer implementation. It is the value reported by Issuer.Name and is used for observability/logging only.

Variables

View Source
var ErrDisabled = errors.New("samlidp: identity provider is disabled")

ErrDisabled is returned by the no-op Issuer for every issuance call: a deployment that leaves the SAML IdP disabled has no signing key, so it cannot mint assertions. Callers (the HTTP surface) map this to a 404 so a disabled IdP is indistinguishable from one that was never mounted.

View Source
var ErrInvalidAuthnRequest = errors.New("samlidp: invalid AuthnRequest")

ErrInvalidAuthnRequest indicates the inbound SAML AuthnRequest could not be parsed, was malformed, or named a destination/issuer the IdP will not serve. The HTTP surface maps this to a 400.

View Source
var ErrUnknownServiceProvider = errors.New("samlidp: unknown service provider")

ErrUnknownServiceProvider indicates the AuthnRequest's Issuer (SP entityID) does not match the SP this issuance is scoped to. The HTTP surface resolves the SP connection from its store before calling Issue; this guards a mismatch.

Functions

This section is empty.

Types

type AssertionIssuer

type AssertionIssuer interface {
	// ParseAuthnRequest decodes a SAML AuthnRequest (the already
	// base64/inflate-decoded XML bytes) and returns its echoed subset.
	// relayState is the transport-level RelayState to carry through.
	ParseAuthnRequest(raw []byte, relayState string) (AuthnRequestInfo, error)
	// Issue mints a signed SAML Response asserting subject for sp, echoing
	// req. It returns ErrUnknownServiceProvider when req.Issuer disagrees
	// with sp.EntityID.
	Issue(ctx context.Context, sp ServiceProvider, subject Subject, req AuthnRequestInfo) (Response, error)
}

AssertionIssuer mints signed SAML Responses.

type AuthnRequestInfo

type AuthnRequestInfo struct {
	// ID is the AuthnRequest @ID, echoed as Response @InResponseTo.
	ID string
	// Issuer is the SP entityID that sent the request.
	Issuer string
	// ACSURL is the AssertionConsumerServiceURL the SP requested, if any.
	// Empty falls back to the registered SP's ACSURL.
	ACSURL string
	// RelayState is the opaque value the SP supplied; the IdP echoes it
	// back unmodified on completion. Carried alongside (not inside) the
	// request envelope.
	RelayState string
}

AuthnRequestInfo is the parsed, validated subset of an inbound SAML AuthnRequest the IdP echoes into its Response (InResponseTo) and uses to pick the redirect target.

type Issuer

type Issuer interface {
	MetadataProvider
	AssertionIssuer
	// Name reports the active implementation (ProviderNoop | ProviderRSA).
	Name() string
	// Enabled reports whether real issuance is possible. The no-op Issuer
	// returns false; the HTTP surface mounts nothing when false.
	Enabled() bool
}

Issuer is the single capability the server holds: an IdP that both advertises metadata and mints assertions.

type MetadataProvider

type MetadataProvider interface {
	// EntityID returns the IdP's SAML entityID (its metadata URL by
	// convention).
	EntityID() string
	// Metadata returns the IdP EntityDescriptor XML advertising the SSO
	// endpoint(s) and the signing certificate.
	Metadata() ([]byte, error)
}

MetadataProvider exposes the IdP's SAML metadata so an SP can be configured to trust this IdP.

type NoopIssuer

type NoopIssuer struct{}

NoopIssuer is the disabled-IdP implementation. It advertises no metadata and refuses every issuance with ErrDisabled. It is wired whenever the SAML IdP is disabled (the default) so the server can hold a non-nil Issuer without the HTTP surface needing a nil check; that surface mounts nothing while Enabled reports false.

func NewNoopIssuer

func NewNoopIssuer() NoopIssuer

NewNoopIssuer returns a NoopIssuer.

func (NoopIssuer) Enabled

func (NoopIssuer) Enabled() bool

Enabled implements Issuer; the no-op IdP is never enabled.

func (NoopIssuer) EntityID

func (NoopIssuer) EntityID() string

EntityID implements MetadataProvider; the disabled IdP has no entityID.

func (NoopIssuer) Issue

Issue implements AssertionIssuer; it returns ErrDisabled.

func (NoopIssuer) Metadata

func (NoopIssuer) Metadata() ([]byte, error)

Metadata implements MetadataProvider; it returns ErrDisabled.

func (NoopIssuer) Name

func (NoopIssuer) Name() string

Name implements Issuer.

func (NoopIssuer) ParseAuthnRequest

func (NoopIssuer) ParseAuthnRequest([]byte, string) (AuthnRequestInfo, error)

ParseAuthnRequest implements AssertionIssuer; it returns ErrDisabled.

type Options

type Options struct {
	EntityID string
	SSOURL   string
	SLOURL   string
	KeyPEM   []byte
	CertPEM  []byte
}

Options configures an RSAIssuer. EntityID and SSOURL are required; SLOURL is optional. KeyPEM/CertPEM are the PEM-encoded RSA private key and X.509 certificate.

type RSAIssuer

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

RSAIssuer is the real SAML 2.0 IdP: it signs assertions with an RSA key and advertises the matching certificate in its metadata. It is constructed once at boot from the configured key/cert and entityID and is safe for concurrent use (it holds only immutable signing material).

func NewRSAIssuer

func NewRSAIssuer(opts Options) (*RSAIssuer, error)

NewRSAIssuer builds an RSAIssuer from PEM key/cert material. It validates that the certificate's public key matches the private key so a misconfigured deployment fails closed at boot rather than minting assertions an SP cannot verify.

func (*RSAIssuer) Enabled

func (i *RSAIssuer) Enabled() bool

Enabled implements Issuer.

func (*RSAIssuer) EntityID

func (i *RSAIssuer) EntityID() string

EntityID implements MetadataProvider.

func (*RSAIssuer) Issue

Issue implements AssertionIssuer. It builds an enveloped-signature SAML assertion (signed with RSA-SHA256 over a SHA-256 digest) wrapped in a SAML Response addressed to the SP's ACS URL.

func (*RSAIssuer) Metadata

func (i *RSAIssuer) Metadata() ([]byte, error)

Metadata implements MetadataProvider. It emits a minimal, SP-consumable IdP EntityDescriptor: the IdP identity (entityID), the signing KeyDescriptor (certificate), and the supported NameID format.

It deliberately does NOT advertise SingleSignOnService or SingleLogoutService endpoints. This slice mounts only /saml/metadata; the SSO (HTTP-POST/Redirect) and SLO binding handlers are a later slice. Publishing those Location URLs now would point an importing SP at routes that return 404. The endpoints are added here once their handlers exist (the issuer already carries the configured ssoURL/sloURL for that slice).

Every dynamic value is XML-escaped via escape; attribute values are never emitted with fmt %q (see escape's documentation for why).

func (*RSAIssuer) Name

func (i *RSAIssuer) Name() string

Name implements Issuer.

func (*RSAIssuer) ParseAuthnRequest

func (i *RSAIssuer) ParseAuthnRequest(raw []byte, relayState string) (AuthnRequestInfo, error)

ParseAuthnRequest implements AssertionIssuer.

type Response

type Response struct {
	// XML is the complete, signed <samlp:Response> document (UTF-8).
	XML []byte
	// ACSURL is where XML must be delivered (HTTP-POST binding).
	ACSURL string
	// RelayState echoes the SP-supplied RelayState (may be empty).
	RelayState string
}

Response is the result of a successful issuance: the SAML Response XML (already signed) plus the ACS URL it must be POSTed to and the RelayState to echo. The HTTP surface base64-encodes SAMLResponse and renders the auto-submitting POST form.

type ServiceProvider

type ServiceProvider struct {
	// EntityID is the SP's SAML entityID (the Issuer it puts on its
	// AuthnRequest and the default Audience of the assertion).
	EntityID string
	// ACSURL is the SP Assertion Consumer Service endpoint the signed
	// SAML Response is delivered to (HTTP-POST binding). Required.
	ACSURL string
	// Audience overrides the AudienceRestriction value. Empty means use
	// EntityID.
	Audience string
	// NameIDFormat overrides the assertion Subject NameID Format. Empty
	// means urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress.
	NameIDFormat string
}

ServiceProvider is the relying-party configuration the IdP needs to mint an assertion: who the SP is (EntityID), where the signed Response is POSTed (ACSURL), and the audience the assertion is restricted to (defaults to EntityID when empty). It mirrors the persisted SP-connection record but carries only the fields the protocol logic needs, keeping this package free of any store dependency.

type Subject

type Subject struct {
	// NameID is the Subject identifier (required). For the default
	// emailAddress format this is the user's email.
	NameID string
	// Attributes are extra single-valued SAML attributes keyed by name.
	Attributes map[string]string
}

Subject is the authenticated end user the assertion attests. NameID is the primary identifier delivered to the SP (typically the email); Attributes are optional SAML attribute statements (e.g. "email", "displayName") the SP may consume.

Jump to

Keyboard shortcuts

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