saml2

package module
v2.0.0-...-ff12104 Latest Latest
Warning

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

Go to latest
Published: Mar 17, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

README

gosaml2

Build Status GoDoc

SAML 2.0 library for Go with both Service Provider and Identity Provider support. Built on etree and a vendored pure-Go XML digital signatures implementation.

Features

  • Service Provider (SP): Validate SAML responses, build AuthnRequests, generate SP metadata, single logout
  • Identity Provider (IdP): Build SAML responses, validate AuthnRequests, generate IdP metadata, single logout
  • Secure defaults: SHA-1 disabled, signatures required, IDP-initiated SSO off by default
  • Replay prevention: RequestTracker interface with in-memory implementation for InResponseTo validation
  • Encryption: AES-GCM and AES-CBC assertion encryption/decryption
  • HTTP bindings: HTTP-POST and HTTP-Redirect with proper query-string signatures
  • Metadata parsing: ParseEntityDescriptor for configuring SP or IdP from partner metadata XML
  • Comprehensive test suite: 335 security tests covering signature validation, replay attacks, XML wrapping, and more

Installation

go get github.com/russellhaering/gosaml2/v2

Requires Go 1.23 or later.

Quick Start: Service Provider

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "time"

    saml2 "github.com/russellhaering/gosaml2/v2"
    "github.com/russellhaering/gosaml2/v2/sp"
    "github.com/russellhaering/gosaml2/v2/types"
)

func main() {
    // Parse your IdP's metadata XML to extract SSO URL and certificates.
    ed, err := saml2.ParseEntityDescriptor(idpMetadataXML)
    if err != nil {
        log.Fatal(err)
    }

    s := &sp.ServiceProvider{
        EntityID:    "https://my-app.example.com",
        ACSURL:      "https://my-app.example.com/saml/acs",
        AudienceURIs: []string{"https://my-app.example.com"},
        // Replay prevention (recommended).
        RequestTracker: sp.NewMemoryRequestTracker(5 * time.Minute),
    }
    // Configure IdP settings from metadata.
    if err := s.ConfigureFromMetadata(ed); err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/saml/acs", func(w http.ResponseWriter, r *http.Request) {
        r.ParseForm()
        info, err := s.RetrieveAssertionInfo(context.Background(), r.FormValue("SAMLResponse"))
        if err != nil {
            http.Error(w, "Forbidden", http.StatusForbidden)
            return
        }
        fmt.Fprintf(w, "Hello, %s\n", info.NameID)
    })
}

Quick Start: Identity Provider

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/russellhaering/gosaml2/v2/idp"
    "github.com/russellhaering/gosaml2/v2/types"
)

func main() {
    identity := &idp.IdentityProvider{
        EntityID:       "https://idp.example.com",
        SSOURL:         "https://idp.example.com/sso",
        SigningKeyStore: signingKey, // *saml2.KeyStore
        SignResponses:   true,
        SignAssertions:  true,
        ServiceProviders: map[string]*idp.SPConfig{
            "https://sp.example.com": {
                EntityID: "https://sp.example.com",
                ACSURLs:  []string{"https://sp.example.com/saml/acs"},
            },
        },
    }

    http.HandleFunc("/sso", func(w http.ResponseWriter, r *http.Request) {
        reqInfo, err := identity.ValidateEncodedAuthnRequestPOST(context.Background(), r.FormValue("SAMLRequest"))
        if err != nil {
            http.Error(w, "Bad Request", http.StatusBadRequest)
            return
        }
        // Authenticate user, then build response...
        body, err := identity.BuildResponseBodyPost(reqInfo.SP.EntityID, &idp.AssertionParams{
            NameID:       "user@example.com",
            InResponseTo: reqInfo.ID,
            Recipient:    reqInfo.ACSURL,
        }, r.FormValue("RelayState"))
        if err != nil {
            http.Error(w, "Internal Error", http.StatusInternalServerError)
            return
        }
        w.Write(body)
    })
}

Security Defaults

gosaml2 v2 is secure by default:

Setting Default Override
SHA-1 signatures Rejected AllowSHA1: true
Response/assertion signatures Required InsecureSkipSignatureValidation: true
IDP-initiated SSO Rejected AllowIDPInitiated: true
Unsigned logout requests Rejected InsecureSkipSignatureValidation: true
Conditions (NotBefore/NotOnOrAfter) Hard errors Not overridable
Audience restriction Enforced Configure AudienceURIs
Clock skew tolerance 60 seconds ClockSkew: duration

Examples

Migration from v1

See MIGRATING.md for a detailed guide on upgrading from gosaml2 v1.

Tested Identity Providers

This library is meant to be a standards-compliant SAML implementation. The following identity providers have been tested:

  • Okta
  • Auth0
  • Shibboleth
  • OneLogin
  • Azure Active Directory (Azure AD)
  • Keycloak
  • Google Workspace
  • Microsoft ADFS

If you find a standards-compliant identity provider that doesn't work, please submit a bug or pull request.

Documentation

License

Apache License 2.0 - see LICENSE for details.

Documentation

Overview

Package saml2 provides shared types and utilities for SAML 2.0 Service Provider and Identity Provider implementations.

The core types in this package — KeyStore, ValidationError, sentinel errors, and metadata parsing — are used by both the [sp] and [idp] sub-packages. Application code typically imports this package alongside one of those sub-packages.

Key types:

See the sp and idp packages for the primary SP and IdP implementations.

Index

Constants

View Source
const (
	ResponseTag                = "Response"
	AssertionTag               = "Assertion"
	EncryptedAssertionTag      = "EncryptedAssertion"
	SubjectTag                 = "Subject"
	NameIdTag                  = "NameID"
	SubjectConfirmationTag     = "SubjectConfirmation"
	SubjectConfirmationDataTag = "SubjectConfirmationData"
	AttributeStatementTag      = "AttributeStatement"
	AttributeValueTag          = "AttributeValue"
	ConditionsTag              = "Conditions"
	AudienceRestrictionTag     = "AudienceRestriction"
	AudienceTag                = "Audience"
	OneTimeUseTag              = "OneTimeUse"
	ProxyRestrictionTag        = "ProxyRestriction"
	IssuerTag                  = "Issuer"
	StatusTag                  = "Status"
	StatusCodeTag              = "StatusCode"
)
View Source
const (
	DestinationAttr  = "Destination"
	VersionAttr      = "Version"
	IdAttr           = "ID"
	MethodAttr       = "Method"
	RecipientAttr    = "Recipient"
	NameAttr         = "Name"
	NotBeforeAttr    = "NotBefore"
	NotOnOrAfterAttr = "NotOnOrAfter"
	CountAttr        = "Count"
)
View Source
const (
	NameIdFormatPersistent      = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
	NameIdFormatTransient       = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
	NameIdFormatEmailAddress    = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
	NameIdFormatUnspecified     = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
	NameIdFormatX509SubjectName = "urn:oasis:names:tc:SAML:1.1:nameid-format:x509SubjectName"

	AuthnContextPasswordProtectedTransport = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"

	AuthnPolicyMatchExact   = "exact"
	AuthnPolicyMatchMinimum = "minimum"
	AuthnPolicyMatchMaximum = "maximum"
	AuthnPolicyMatchBetter  = "better"

	StatusCodeSuccess          = "urn:oasis:names:tc:SAML:2.0:status:Success"
	StatusCodePartialLogout    = "urn:oasis:names:tc:SAML:2.0:status:PartialLogout"
	StatusCodeUnknownPrincipal = "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal"

	BindingHttpPost     = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
	BindingHttpRedirect = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
)
View Source
const (
	SAMLAssertionNamespace = "urn:oasis:names:tc:SAML:2.0:assertion"
	SAMLProtocolNamespace  = "urn:oasis:names:tc:SAML:2.0:protocol"
	XMLEncNamespace        = "http://www.w3.org/2001/04/xmlenc#"
	XMLDSigNamespace       = "http://www.w3.org/2000/09/xmldsig#"
)
View Source
const (
	AuthnRequestTag          = "AuthnRequest"
	LogoutRequestTag         = "LogoutRequest"
	EncryptedDataTypeElement = "http://www.w3.org/2001/04/xmlenc#Element"
)
View Source
const (
	StatusCodeRequester     = "urn:oasis:names:tc:SAML:2.0:status:Requester"
	StatusCodeResponder     = "urn:oasis:names:tc:SAML:2.0:status:Responder"
	StatusCodeRequestDenied = "urn:oasis:names:tc:SAML:2.0:status:RequestDenied"
)
View Source
const IssueInstantFormat = "2006-01-02T15:04:05Z"

IssueInstantFormat is the time format used for SAML timestamps.

Variables

View Source
var (
	ErrExpired          = errors.New("saml: assertion expired")
	ErrNotYetValid      = errors.New("saml: assertion not yet valid")
	ErrAudienceMismatch = errors.New("saml: audience mismatch")
	ErrBadRecipient     = errors.New("saml: recipient mismatch")
	ErrBadDestination   = errors.New("saml: destination mismatch")
	ErrBadIssuer        = errors.New("saml: issuer mismatch")
	ErrBadSignature     = errors.New("saml: signature verification failed")
	ErrMissingSignature = errors.New("saml: required signature missing")
	ErrBadStatus        = errors.New("saml: response status not success")
	ErrReplay           = errors.New("saml: request ID not recognized")
	ErrMissingAssertion = errors.New("saml: missing assertion")
	ErrMissingElement   = errors.New("saml: missing required element")
	ErrBadVersion       = errors.New("saml: unsupported SAML version")
	ErrMalformed        = errors.New("saml: malformed SAML document")
	ErrUnknownSP        = errors.New("saml: unknown service provider")
	ErrBadACSURL        = errors.New("saml: invalid assertion consumer service URL")
	ErrEncryptionFailed = errors.New("saml: assertion encryption failed")
)

Sentinel errors for use with errors.Is().

View Source
var (
	MakeC14N10ExclusiveCanonicalizerWithPrefixList             = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList
	MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList = dsig.MakeC14N10ExclusiveWithCommentsCanonicalizerWithPrefixList
	MakeC14N11Canonicalizer                                    = dsig.MakeC14N11Canonicalizer
	MakeC14N11WithCommentsCanonicalizer                        = dsig.MakeC14N11WithCommentsCanonicalizer
	MakeC14N10RecCanonicalizer                                 = dsig.MakeC14N10RecCanonicalizer
	MakeC14N10WithCommentsCanonicalizer                        = dsig.MakeC14N10WithCommentsCanonicalizer
)

Canonicalizer constructors for use with ServiceProvider.SignAuthnRequestsCanonicalizer and IdentityProvider.SignatureCanonicalizer.

Functions

func BuildPOSTForm

func BuildPOSTForm(actionURL, paramName, paramValue, relayState string) ([]byte, error)

BuildPOSTForm generates an HTML auto-submitting POST form for SAML messages.

func IsSignatureMissing

func IsSignatureMissing(err error) (bool, error)

IsSignatureMissing returns true when err is dsig.ErrMissingSignature, and returns (false, err) for any other non-nil error.

func ParseEntityDescriptor

func ParseEntityDescriptor(data []byte) (*types.EntityDescriptor, error)

ParseEntityDescriptor parses a SAML EntityDescriptor from raw XML bytes.

func SelectEndpoint

func SelectEndpoint(services []types.SingleSignOnService) (string, string)

SelectEndpoint picks the best SSO endpoint, preferring HTTP-POST over HTTP-Redirect.

func SelectSLOEndpoint

func SelectSLOEndpoint(services []types.SingleLogoutService) (string, string)

SelectSLOEndpoint picks the best SLO endpoint, preferring HTTP-POST over HTTP-Redirect.

func SignatureAlgorithmHash

func SignatureAlgorithmHash(algorithm string) crypto.Hash

SignatureAlgorithmHash returns the crypto.Hash for a given signature method URI. Returns 0 if unrecognized or empty.

func SignatureInputString

func SignatureInputString(samlRequest, relayState, sigAlg string) string

SignatureInputString constructs the string to be fed into the signature algorithm, as described in section 3.4.4.1 of https://www.oasis-open.org/committees/download.php/56779/sstc-saml-bindings-errata-2.0-wd-06.pdf

func SignatureMethodIdentifier

func SignatureMethodIdentifier(key crypto.Signer, hash crypto.Hash) string

SignatureMethodIdentifier returns the XML-DSig signature method URI for the given key and hash algorithm. This is needed for the HTTP-Redirect binding where the SigAlg query parameter must be set.

Types

type AuthNRequest

type AuthNRequest struct {
	ID                          string `xml:",attr"`
	Version                     string `xml:",attr"`
	ProtocolBinding             string `xml:",attr"`
	AssertionConsumerServiceURL string `xml:",attr"`

	IssueInstant time.Time `xml:",attr"`

	Destination string `xml:",attr"`
	Issuer      string
}

AuthNRequest is the go struct representation of an authentication request

type Canonicalizer

type Canonicalizer = dsig.Canonicalizer

Canonicalizer is the interface for XML canonicalization algorithms. Used as a field type on ServiceProvider and IdentityProvider.

type KeyStore

type KeyStore struct {
	Signer crypto.Signer
	Cert   []byte
}

KeyStore holds a signing key and its associated certificate.

type LogoutRequest

type LogoutRequest struct {
	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol LogoutRequest"`
	ID      string   `xml:"ID,attr"`
	Version string   `xml:"Version,attr"`

	IssueInstant time.Time `xml:"IssueInstant,attr"`
	NotOnOrAfter string    `xml:"NotOnOrAfter,attr,omitempty"`

	Destination string        `xml:"Destination,attr"`
	Issuer      *types.Issuer `xml:"Issuer"`

	NameID             *types.NameID `xml:"NameID"`
	SignatureValidated bool          `xml:"-"` // not read, not dumped
}

LogoutRequest is the go struct representation of a logout request

type RequestedAuthnContext

type RequestedAuthnContext struct {
	// The RequestedAuthnContext comparison policy to use. See the section 3.3.2.2.1
	// of the SAML 2.0 specification for details. Constants named AuthnPolicyMatch*
	// contain standardized values.
	Comparison string

	// Contexts will be passed as AuthnContextClassRefs. For example, to force password
	// authentication on some identity providers, Contexts should have a value of
	// []string{AuthnContextPasswordProtectedTransport}, and Comparison should have a
	// value of AuthnPolicyMatchExact.
	Contexts []string
}

RequestedAuthnContext controls which authentication mechanisms are requested of the identity provider. It is generally sufficient to omit this and let the identity provider select an authentication mechanism.

type ValidationError

type ValidationError struct {
	Reason error  // sentinel for errors.Is()
	Detail string // human-readable context
}

ValidationError wraps a sentinel error with human-readable context. Use errors.Is(err, ErrBadDestination) etc. for programmatic matching.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error returns the sentinel error message with optional detail context.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap returns the underlying sentinel error for use with errors.Is.

Directories

Path Synopsis
examples
idp command
demo is a minimal SAML Identity Provider example.
demo is a minimal SAML Identity Provider example.
sp command
demo is a minimal SAML Service Provider example.
demo is a minimal SAML Service Provider example.
Package idp implements a SAML 2.0 Identity Provider.
Package idp implements a SAML 2.0 Identity Provider.
internal
Package sp implements a SAML 2.0 Service Provider.
Package sp implements a SAML 2.0 Service Provider.
Package types defines Go structs for SAML 2.0 XML elements.
Package types defines Go structs for SAML 2.0 XML elements.

Jump to

Keyboard shortcuts

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