Categorygithub.com/crewjam/saml
modulepackage
0.4.14
Repository: https://github.com/crewjam/saml.git
Documentation: pkg.go.dev

# README

SAML

Build Status

Package saml contains a partial implementation of the SAML standard in golang. SAML is a standard for identity federation, i.e. either allowing a third party to authenticate your users or allowing third parties to rely on us to authenticate their users.

Introduction

In SAML parlance an Identity Provider (IDP) is a service that knows how to authenticate users. A Service Provider (SP) is a service that delegates authentication to an IDP. If you are building a service where users log in with someone else's credentials, then you are a Service Provider. This package supports implementing both service providers and identity providers.

The core package contains the implementation of SAML. The package samlsp provides helper middleware suitable for use in Service Provider applications. The package samlidp provides a rudimentary IDP service that is useful for testing or as a starting point for other integrations.

Getting Started as a Service Provider

Let us assume we have a simple web application to protect. We'll modify this application so it uses SAML to authenticate users.

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}

func main() {
    app := http.HandlerFunc(hello)
    http.Handle("/hello", app)
    http.ListenAndServe(":8000", nil)
}

Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this:

openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com"

We will use samlsp.Middleware to wrap the endpoint we want to protect. Middleware provides both an http.Handler to serve the SAML specific URLs and a set of wrappers to require the user to be logged in. We also provide the URL where the service provider can fetch the metadata from the IDP at startup. In our case, we'll use samltest.id, an identity provider designed for testing.

package main

import (
	"context"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net/http"
	"net/url"

	"github.com/crewjam/saml/samlsp"
)

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "displayName"))
}

func main() {
	keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key")
	if err != nil {
		panic(err) // TODO handle error
	}
	keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
	if err != nil {
		panic(err) // TODO handle error
	}

	idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp")
	if err != nil {
		panic(err) // TODO handle error
	}
	idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient,
		*idpMetadataURL)
	if err != nil {
		panic(err) // TODO handle error
	}

	rootURL, err := url.Parse("http://localhost:8000")
	if err != nil {
		panic(err) // TODO handle error
	}

	samlSP, _ := samlsp.New(samlsp.Options{
		URL:            *rootURL,
		Key:            keyPair.PrivateKey.(*rsa.PrivateKey),
		Certificate:    keyPair.Leaf,
		IDPMetadata: idpMetadata,
	})
	app := http.HandlerFunc(hello)
	http.Handle("/hello", samlSP.RequireAccount(app))
	http.Handle("/saml/", samlSP)
	http.ListenAndServe(":8000", nil)
}

Next we'll have to register our service provider with the identity provider to establish trust from the service provider to the IDP. For samltest.id, you can do something like:

mdpath=saml-test-$USER-$HOST.xml
curl localhost:8000/saml/metadata > $mdpath

Navigate to https://samltest.id/upload.php and upload the file you fetched.

Now you should be able to authenticate. The flow should look like this:

  1. You browse to localhost:8000/hello

  2. The middleware redirects you to https://samltest.id/idp/profile/SAML2/Redirect/SSO

  3. samltest.id prompts you for a username and password.

  4. samltest.id returns you an HTML document which contains an HTML form setup to POST to localhost:8000/saml/acs. The form is automatically submitted if you have javascript enabled.

  5. The local service validates the response, issues a session cookie, and redirects you to the original URL, localhost:8000/hello.

  6. This time when localhost:8000/hello is requested there is a valid session and so the main content is served.

Getting Started as an Identity Provider

Please see example/idp/ for a substantially complete example of how to use the library and helpers to be an identity provider.

Support

The SAML standard is huge and complex with many dark corners and strange, unused features. This package implements the most commonly used subset of these features required to provide a single sign on experience. The package supports at least the subset of SAML known as interoperable SAML.

This package supports the Web SSO profile. Message flows from the service provider to the IDP are supported using the HTTP Redirect binding and the HTTP POST binding. Message flows from the IDP to the service provider are supported via the HTTP POST binding.

The package can produce signed SAML assertions, and can validate both signed and encrypted SAML assertions. It does not support signed or encrypted requests.

RelayState

The RelayState parameter allows you to pass user state information across the authentication flow. The most common use for this is to allow a user to request a deep link into your site, be redirected through the SAML login flow, and upon successful completion, be directed to the originally requested link, rather than the root.

Unfortunately, RelayState is less useful than it could be. Firstly, it is not authenticated, so anything you supply must be signed to avoid XSS or CSRF. Secondly, it is limited to 80 bytes in length, which precludes signing. (See section 3.6.3.1 of SAMLProfiles.)

References

The SAML specification is a collection of PDFs (sadly):

SAMLtest is a testing ground for SAML service and identity providers.

Security Issues

Please do not report security issues in the issue tracker. Rather, please contact me directly at [email protected] (PGP Key 78B6038B3B9DFB88). If your issue is not a security issue, please use the issue tracker so other contributors can help.

# Packages

This is an example that implements a bitly-esque short link service.
Package logger provides a logging interface.
Package samlidp a rudimentary SAML identity provider suitable for testing or as a starting point for a more complex service.
Package samlsp provides helpers that can be used to protect web services using SAML.
Package testsaml contains functions for use in testing SAML requests and responses.
Package xmlenc is a partial implementation of the xmlenc standard as described in https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html.

# Functions

GetSigningContext returns a dsig.SigningContext initialized based on the Service Provider's configuration.
NewIdpAuthnRequest returns a new IdpAuthnRequest for the given HTTP request to the authorization service.

# Constants

DefaultCacheDuration is how long we ask the IDP to cache the SP metadata.
DefaultValidDuration is how long we assert that the SP metadata is valid.
Name ID formats.
HTTPArtifactBinding is the official URN for the HTTP-Artifact binding (transport).
HTTPPostBinding is the official URN for the HTTP-POST binding (transport).
HTTPRedirectBinding is the official URN for the HTTP-Redirect binding (transport).
Name ID formats.
SOAPBinding is the official URN for the SOAP binding (transport).
SOAPBindingV1 is the URN for the SOAP binding in SAML 1.0.
StatusAuthnFailed means the responding provider was unable to successfully authenticate the principal.
StatusInvalidAttrNameOrValue means Unexpected or invalid content was encountered within a <saml:Attribute> or <saml:AttributeValue> element.
StatusInvalidNameIDPolicy means the responding provider cannot or will not support the requested name identifier policy.
StatusNoAuthnContext means the specified authentication context requirements cannot be met by the responder.
StatusNoAvailableIDP is used by an intermediary to indicate that none of the supported identity provider <Loc> elements in an <IDPList> can be resolved or that none of the supported identity providers are available.
nolint:gosec.
StatusNoSupportedIDP is used by an intermediary to indicate that none of the identity providers in an <IDPList> are supported by the intermediary.
StatusPartialLogout is used by a session authority to indicate to a session participant that it was not able to propagate logout to all other session participants.
StatusProxyCountExceeded means Indicates that a responding provider cannot authenticate the principal directly and is not permitted to proxy the request further.
StatusRequestDenied means the SAML responder or SAML authority is able to process the request but has chosen not to respond.
StatusRequester means the request could not be performed due to an error on the part of the requester.
StatusRequestUnsupported means the SAML responder or SAML authority does not support the request.
nolint:gosec.
StatusRequestVersionTooHigh means the SAML responder cannot process the request because the protocol version specified in the request message is a major upgrade from the highest protocol version supported by the responder.
StatusRequestVersionTooLow means the SAML responder cannot process the request because the protocol version specified in the request message is too low.
StatusResourceNotRecognized means the resource value provided in the request message is invalid or unrecognized.
StatusResponder means the request could not be performed due to an error on the part of the SAML responder or SAML authority.
StatusTooManyResponses means the response message would contain more elements than the SAML responder is able to return.
StatusUnknownAttrProfile means an entity that has no knowledge of a particular attribute profile has been presented with an attribute means drawn from that profile.
StatusUnknownPrincipal means the responding provider does not recognize the principal specified or implied by the request.
StatusUnsupportedBinding means the SAML responder cannot properly fulfill the request using the protocol binding specified in the request.
StatusVersionMismatch means the SAML responder could not process the request because the version of the request message was incorrect.
Name ID formats.
Name ID formats.

# Variables

Clock is assigned to dsig validation and signing contexts if it is not nil, otherwise the default clock is used.
MaxClockSkew allows for leeway for clock skew between the IDP and SP when validating assertions.
MaxIssueDelay is the longest allowed time between when a SAML assertion is issued by the IDP and the time it is received by ParseResponse.
Metadata as been renamed to EntityDescriptor This change was made to be consistent with the rest of the API which uses names from the SAML specification for types.
RandReader is the io.Reader that produces cryptographically random bytes when they are need by the library.
StatusSuccess means the request succeeded.
TimeNow is a function that returns the current time.

# Structs

AffiliationDescriptor represents the SAML AffiliationDescriptor object.
ArtifactResolve represents the SAML object of the same name.
ArtifactResponse represents the SAML object of the same name.
Assertion represents the SAML element Assertion.
AssertionAttribute represents an attribute of the user extracted from a SAML Assertion.
Attribute represents the SAML element Attribute.
AttributeAuthorityDescriptor represents the SAML AttributeAuthorityDescriptor object.
AttributeConsumingService represents the SAML AttributeConsumingService object.
AttributeStatement represents the SAML element AttributeStatement.
AttributeValue represents the SAML element AttributeValue.
Audience represents the SAML element Audience.
AudienceRestriction represents the SAML element AudienceRestriction.
AuthnAuthorityDescriptor represents the SAML AuthnAuthorityDescriptor object.
AuthnContext represents the SAML element AuthnContext.
AuthnContextClassRef represents the SAML element AuthnContextClassRef.
AuthnRequest represents the SAML object of the same name, a request from a service provider to authenticate a user.
AuthnStatement represents the SAML element AuthnStatement.
Conditions represents the SAML element Conditions.
ContactPerson represents the SAML element ContactPerson.
DefaultAssertionMaker produces a SAML assertion for the given request and assigns it to req.Assertion.
EncryptionMethod represents the XMLSEC object of the same name.
Endpoint represents the SAML EndpointType object.
EntitiesDescriptor represents the SAML object of the same name.
EntityDescriptor represents the SAML EntityDescriptor object.
ErrBadStatus is returned when the assertion provided is valid but the status code is not "urn:oasis:names:tc:SAML:2.0:status:Success".
IdentityProvider implements the SAML Identity Provider role (IDP).
IdpAuthnRequest is used by IdentityProvider to handle a single authentication request.
IdpAuthnRequestForm contans HTML form information to be submitted to the SAML HTTP POST binding ACS.
IDPSSODescriptor represents the SAML IDPSSODescriptorType object.
IndexedEndpoint represents the SAML IndexedEndpointType object.
InvalidResponseError is the error produced by ParseResponse when it fails.
Issuer represents the SAML object of the same name.
KeyDescriptor represents the XMLSEC object of the same name.
KeyInfo represents the XMLSEC object of the same name.
LocalizedName represents the SAML type localizedNameType.
LocalizedURI represents the SAML type localizedURIType.
LogoutRequest represents the SAML object of the same name, a request from an IDP to destroy a user's session.
LogoutResponse represents the SAML object of the same name.
NameID represents the SAML element NameID.
NameIDPolicy represents the SAML object of the same name.
OneTimeUse represents the SAML element OneTimeUse.
Organization represents the SAML Organization object.
PDPDescriptor represents the SAML PDPDescriptor object.
ProxyRestriction represents the SAML element ProxyRestriction.
RequestedAttribute represents the SAML RequestedAttribute object.
RequestedAuthnContext represents the SAML object of the same name, an indication of the requirements on the authentication process.
Response represents the SAML object of the same name.
RoleDescriptor represents the SAML element RoleDescriptor.
ServiceProvider implements SAML Service provider.
Session represents a user session.
SessionIndex represents the SAML element SessionIndex.
SPSSODescriptor represents the SAML SPSSODescriptorType object.
SSODescriptor represents the SAML complex type SSODescriptor See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2.
Status represents the SAML object of the same name.
StatusCode represents the SAML object of the same name.
StatusDetail represents the SAML element StatusDetail.
StatusMessage represents the SAML element StatusMessage.
Subject represents the SAML element Subject.
SubjectConfirmation represents the SAML element SubjectConfirmation.
SubjectConfirmationData represents the SAML element SubjectConfirmationData.
SubjectLocality represents the SAML element SubjectLocality.
X509Certificate represents the XMLSEC object of the same name.
X509Data represents the XMLSEC object of the same name.

# Interfaces

AssertionMaker is an interface used by IdentityProvider to construct the assertion for a request.
ServiceProviderProvider is an interface used by IdentityProvider to look up service provider metadata for a request.
SessionProvider is an interface used by IdentityProvider to determine the Session associated with a request.
SignatureVerifier verifies a signature Can be implemented in order to override ServiceProvider's default way of verifying signatures.

# Type aliases

AssertionAttributes is a list of AssertionAttribute.
Duration is a time.Duration that uses the xsd:duration format for text marshalling and unmarshalling.
NameIDFormat is the format of the id.
RelaxedTime is a version of time.Time that supports the time format found in SAML documents.