saml

package module
v0.0.0-...-2c17afd Latest Latest
Warning

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

Go to latest
Published: Jan 4, 2016 License: MIT Imports: 11 Imported by: 0

README

go-saml

A just good enough SAML client library written in Go. This library is by no means complete and has been developed to solve several specific integration efforts. However, it's a start, and it would be great to see it evolve into a more fleshed out implemention.

Inspired by the early work of Matt Baird.

The library supports:

  • generating signed AuthnRequests
  • validating signed AuthnRequests
  • generating service provider metadata
  • generating signed Responses
  • validating signed Responses

Installation

$ go get github.com/sendgrid/go-saml

Here's a convenient way to generate a certificate:

curl -sSL https://raw.githubusercontent.com/frntn/x509-san/master/gencert.sh | CRT_CN="mycert"  bash

Usage

Below are samples to show how you might use the library.

Generating Signed AuthnRequests
sp := saml.ServiceProviderSettings{
  PublicCertPath:              "../default.crt",
  PrivateKeyPath:              "../default.key",
  IDPSSOURL:                   "http://idp/saml2",
  IDPSSODescriptorURL:         "http://idp/issuer",
  IDPPublicCertPath:           "idpcert.crt",
  AssertionConsumerServiceURL: "http://localhost:8000/saml_consume",
}
sp.Init()

// generate the AuthnRequest and then get a base64 encoded string of the XML
authnRequest := sp.GetAuthnRequest()
b64XML, err := authnRequest.EncodedSignedString(sp.PrivateKeyPath)
if err != nil {
  panic(err)
}

// for convenience, get a URL formed with the SAMLRequest parameter
url, err := saml.GetAuthnRequestURL(sp.IDPSSOURL, b64XML)
if err != nil {
  panic(err)
}

// below is bonus for how you might respond to a request with a form that POSTs to the IdP
data := struct {
  Base64AuthRequest string
  URL               string
}{
  Base64AuthRequest: b64XML,
  URL:               url,
}

t := template.New("saml")
t, err = t.Parse("<html><body style=\"display: none\" onload=\"document.frm.submit()\"><form method=\"post\" name=\"frm\" action=\"{{.URL}}\"><input type=\"hidden\" name=\"SAMLRequest\" value=\"{{.Base64AuthRequest}}\" /><input type=\"submit\" value=\"Submit\" /></form></body></html>")

// how you might respond to a request with the templated form that will auto post
t.Execute(w, data)
Validating a received SAML Response
response = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  encodedXML := r.FormValue("SAMLResponse")

  if encodedXML == "" {
    httpcommon.SendBadRequest(w, "SAMLResponse form value missing")
    return
  }

  response, err := saml.ParseEncodedResponse(encodedXML)
  if err != nil {
    httpcommon.SendBadRequest(w, "SAMLResponse parse: "+err.Error())
    return
  }

  err = response.Validate(&sp)
  if err != nil {
    httpcommon.SendBadRequest(w, "SAMLResponse validation: "+err.Error())
    return
  }

  samlID := response.GetAttribute("uid")
  if samlID == "" {
    httpcommon.SendBadRequest(w, "SAML attribute identifier uid missing")
    return
  }

  //...
}
Service provider metadata
func samlMetadataHandler(sp *saml.ServiceProviderSettings) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		md, err := sp.GetEntityDescriptor()
		if err != nil {
      w.WriteHeader(500)
      w.Write([]byte("Error: " + err.Error()))
			return
		}

		w.Header().Set("Content-Type", "application/xml")
		w.Write([]byte(md))
	})
}
Receiving a authnRequest
b64Request := r.URL.Query().Get("SAMLRequest")
if b64Request == "" {
  w.WriteHeader(400)
  w.Write([]byte("SAMLRequest parameter missing"))
  return
}

defated, err := base64.StdEncoding.DecodeString(b64Request)
if err != nil {
  w.WriteHeader(500)
  w.Write([]byte("Error: " + err.Error()))
  return
}

// enflate and unmarshal
var buffer bytes.Buffer
rdr := flate.NewReader(bytes.NewReader(defated))
io.Copy(&buffer, rdr)
var authnRequest saml.AuthnRequest

err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
if err != nil {
  w.WriteHeader(500)
  w.Write([]byte("Error: " + err.Error()))
  return
}

if authnRequest.Issuer.Url != issuerURL {
  w.WriteHeader(500)
  w.Write([]byte("unauthorized issuer "+authnRequest.Issuer.Url))
  return
}

Creating a SAML Response (if acting as an IdP)
issuer := "http://localhost:8000/saml"
authnResponse := saml.NewSignedResponse()
authnResponse.Issuer.Url = issuer
authnResponse.Assertion.Issuer.Url = issuer
authnResponse.Signature.KeyInfo.X509Data.X509Certificate.Cert = stringValueOfCert
authnResponse.Assertion.Subject.NameID.Value = userIdThatYouAuthenticated
authnResponse.AddAttribute("uid", userIdThatYouAuthenticated)
authnResponse.AddAttribute("email", "someone@domain")
authnResponse.Assertion.Subject.SubjectConfirmation.SubjectConfirmationData.InResponseTo = authnRequestIdRespondingTo
authnResponse.InResponseTo = authnRequestIdRespondingTo
authnResponse.Assertion.Subject.SubjectConfirmation.SubjectConfirmationData.Recipient = issuer

// signed XML string
signed, err := authnResponse.SignedString("/path/to/private.key")

// or signed base64 encoded XML string
b64XML, err := authnResponse.EncodedSignedString("/path/to/private.key")

Contributing

Would love any contributions you having including better documentation, tests, or more robust functionality.

git clone git@github.com:sendgrid/go-saml.git
make init
make test
Contact

Made with ❤ by Robots & Pencils (@robotsNpencils)

Maintainers

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateAuthnRequest

func GenerateAuthnRequest(publicCertificate, privateKey, idpSSOUrl, idpSSODescriptorUrl,
	idpPublicCertificate, assertionConsumerServiceUrl string) (string, error)

func GetAuthnRequestURL

func GetAuthnRequestURL(baseURL string, b64XML string) (string, error)

GetAuthnRequestURL generate a URL for the AuthnRequest to the IdP with the SAMLRequst parameter encoded

func SignRequest

func SignRequest(xml string, privateKeyPath string) (string, error)

SignRequest sign a SAML 2.0 AuthnRequest `privateKeyPath` must be a path on the filesystem, xmlsec1 is run out of process through `exec`

func SignResponse

func SignResponse(xml string, privateKeyPath string) (string, error)

SignResponse sign a SAML 2.0 Response `privateKeyPath` must be a path on the filesystem, xmlsec1 is run out of process through `exec`

func ValidateSamlResponse

func ValidateSamlResponse(response *Response, publicCertificate, privateKey, idpSSOUrl,
	idpSSODescriptorUrl, idpPublicCertificate, assertionConsumerServiceUrl string) error

func VerifyRequestSignature

func VerifyRequestSignature(xml string, publicCertPath string) error

VerifyRequestSignature verify signature of a SAML 2.0 AuthnRequest document `publicCertPath` must be a path on the filesystem, xmlsec1 is run out of process through `exec`

func VerifyResponseSignature

func VerifyResponseSignature(xml string, publicCertPath string) error

VerifyResponseSignature verify signature of a SAML 2.0 Response document `publicCertPath` must be a path on the filesystem, xmlsec1 is run out of process through `exec`

Types

type Assertion

type Assertion struct {
	XMLName            xml.Name
	ID                 string `xml:"ID,attr"`
	Version            string `xml:"Version,attr"`
	XS                 string `xml:"xmlns:xs,attr"`
	XSI                string `xml:"xmlns:xsi,attr"`
	SAML               string `xml:"saml,attr"`
	IssueInstant       string `xml:"IssueInstant,attr"`
	Issuer             Issuer `xml:"Issuer"`
	Subject            Subject
	Conditions         Conditions
	AttributeStatement AttributeStatement
}

type AssertionConsumerService

type AssertionConsumerService struct {
	XMLName  xml.Name
	Binding  string `xml:"Binding,attr"`
	Location string `xml:"Location,attr"`
	Index    string `xml:"index,attr"`
}

type Attribute

type Attribute struct {
	XMLName        xml.Name
	Name           string `xml:",attr"`
	FriendlyName   string `xml:",attr"`
	NameFormat     string `xml:",attr"`
	AttributeValue AttributeValue
}

type AttributeStatement

type AttributeStatement struct {
	XMLName    xml.Name
	Attributes []Attribute `xml:"Attribute"`
}

type AttributeValue

type AttributeValue struct {
	XMLName xml.Name
	Type    string `xml:"xsi:type,attr"`
	Value   string `xml:",innerxml"`
}

type AuthnContextClassRef

type AuthnContextClassRef struct {
	XMLName   xml.Name
	SAML      string `xml:"xmlns:saml,attr"`
	Transport string `xml:",innerxml"`
}

type AuthnRequest

type AuthnRequest struct {
	XMLName                        xml.Name
	SAMLP                          string                `xml:"xmlns:samlp,attr"`
	SAML                           string                `xml:"xmlns:saml,attr"`
	SAMLSIG                        string                `xml:"xmlns:samlsig,attr"`
	ID                             string                `xml:"ID,attr"`
	Version                        string                `xml:"Version,attr"`
	ProtocolBinding                string                `xml:"ProtocolBinding,attr"`
	AssertionConsumerServiceURL    string                `xml:"AssertionConsumerServiceURL,attr"`
	IssueInstant                   string                `xml:"IssueInstant,attr"`
	AssertionConsumerServiceIndex  int                   `xml:"AssertionConsumerServiceIndex,attr"`
	AttributeConsumingServiceIndex int                   `xml:"AttributeConsumingServiceIndex,attr"`
	Issuer                         Issuer                `xml:"Issuer"`
	NameIDPolicy                   NameIDPolicy          `xml:"NameIDPolicy"`
	RequestedAuthnContext          RequestedAuthnContext `xml:"RequestedAuthnContext"`
	Signature                      Signature             `xml:"Signature,omitempty"`
	// contains filtered or unexported fields
}

func NewAuthnRequest

func NewAuthnRequest() *AuthnRequest

func ParseCompressedEncodedRequest

func ParseCompressedEncodedRequest(b64RequestXML string) (*AuthnRequest, error)

func ParseEncodedRequest

func ParseEncodedRequest(b64RequestXML string) (*AuthnRequest, error)

func (*AuthnRequest) CompressedEncodedSignedString

func (r *AuthnRequest) CompressedEncodedSignedString(privateKeyPath string) (string, error)

func (*AuthnRequest) EncodedSignedString

func (r *AuthnRequest) EncodedSignedString(privateKeyPath string) (string, error)

GetAuthnRequestURL generate a URL for the AuthnRequest to the IdP with the SAMLRequst parameter encoded

func (*AuthnRequest) SignedString

func (r *AuthnRequest) SignedString(privateKeyPath string) (string, error)

func (*AuthnRequest) String

func (r *AuthnRequest) String() (string, error)

func (*AuthnRequest) Validate

func (r *AuthnRequest) Validate(publicCertPath string) error

type CanonicalizationMethod

type CanonicalizationMethod struct {
	XMLName   xml.Name
	Algorithm string `xml:"Algorithm,attr"`
}

type Conditions

type Conditions struct {
	XMLName      xml.Name
	NotBefore    string `xml:",attr"`
	NotOnOrAfter string `xml:",attr"`
}

type DigestMethod

type DigestMethod struct {
	XMLName   xml.Name
	Algorithm string `xml:"Algorithm,attr"`
}

type DigestValue

type DigestValue struct {
	XMLName xml.Name
}

type EntityAttributes

type EntityAttributes struct {
	XMLName xml.Name
	SAML    string `xml:"xmlns:saml,attr"`

	EntityAttributes []Attribute `xml:"Attribute"` // should be array??
}

type EntityDescriptor

type EntityDescriptor struct {
	XMLName  xml.Name
	DS       string `xml:"xmlns:ds,attr"`
	XMLNS    string `xml:"xmlns,attr"`
	MD       string `xml:"xmlns:md,attr"`
	EntityId string `xml:"entityID,attr"`

	Extensions      Extensions      `xml:"Extensions"`
	SPSSODescriptor SPSSODescriptor `xml:"SPSSODescriptor"`
}

type Extensions

type Extensions struct {
	XMLName xml.Name
	Alg     string `xml:"xmlns:alg,attr"`
	MDAttr  string `xml:"xmlns:mdattr,attr"`
	MDRPI   string `xml:"xmlns:mdrpi,attr"`

	EntityAttributes string `xml:"EntityAttributes"`
}

type IdentityProviderSettings

type IdentityProviderSettings struct {
}

type Issuer

type Issuer struct {
	XMLName xml.Name
	SAML    string `xml:"xmlns:saml,attr"`
	Url     string `xml:",innerxml"`
}

type KeyDescriptor

type KeyDescriptor struct {
	XMLName xml.Name
	Use     string  `xml:"use,attr"`
	KeyInfo KeyInfo `xml:"KeyInfo"`
}

type KeyInfo

type KeyInfo struct {
	XMLName  xml.Name
	X509Data X509Data `xml:",innerxml"`
}

type NameID

type NameID struct {
	XMLName xml.Name
	Format  string `xml:",attr"`
	Value   string `xml:",innerxml"`
}

type NameIDPolicy

type NameIDPolicy struct {
	XMLName     xml.Name
	AllowCreate bool   `xml:"AllowCreate,attr"`
	Format      string `xml:"Format,attr"`
}

type RequestedAuthnContext

type RequestedAuthnContext struct {
	XMLName              xml.Name
	SAMLP                string               `xml:"xmlns:samlp,attr"`
	Comparison           string               `xml:"Comparison,attr"`
	AuthnContextClassRef AuthnContextClassRef `xml:"AuthnContextClassRef"`
}

type Response

type Response struct {
	XMLName      xml.Name
	SAMLP        string `xml:"xmlns:samlp,attr"`
	SAML         string `xml:"xmlns:saml,attr"`
	SAMLSIG      string `xml:"xmlns:samlsig,attr"`
	Destination  string `xml:"Destination,attr"`
	ID           string `xml:"ID,attr"`
	Version      string `xml:"Version,attr"`
	IssueInstant string `xml:"IssueInstant,attr"`
	InResponseTo string `xml:"InResponseTo,attr"`

	Assertion Assertion `xml:"Assertion"`
	Signature Signature `xml:"Signature"`
	Issuer    Issuer    `xml:"Issuer"`
	Status    Status    `xml:"Status"`
	// contains filtered or unexported fields
}

func NewSignedResponse

func NewSignedResponse() *Response

func ParseCompressedEncodedResponse

func ParseCompressedEncodedResponse(b64ResponseXML string) (*Response, error)

func ParseEncodedResponse

func ParseEncodedResponse(b64ResponseXML string) (*Response, error)

func (*Response) AddAttribute

func (r *Response) AddAttribute(name, value string)

AddAttribute add strong attribute to the Response

func (*Response) CompressedEncodedSignedString

func (r *Response) CompressedEncodedSignedString(privateKeyPath string) (string, error)

func (*Response) EncodedSignedString

func (r *Response) EncodedSignedString(privateKeyPath string) (string, error)

func (*Response) GetAttribute

func (r *Response) GetAttribute(name string) string

GetAttribute by Name or by FriendlyName. Return blank string if not found

func (*Response) SignedString

func (r *Response) SignedString(privateKeyPath string) (string, error)

func (*Response) String

func (r *Response) String() (string, error)

func (*Response) Validate

func (r *Response) Validate(s *ServiceProviderSettings) error

type SPSSODescriptor

type SPSSODescriptor struct {
	XMLName                    xml.Name
	ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"`
	SigningKeyDescriptor       KeyDescriptor
	EncryptionKeyDescriptor    KeyDescriptor
	// SingleLogoutService        SingleLogoutService `xml:"SingleLogoutService"`
	AssertionConsumerServices []AssertionConsumerService
}

type SPSSODescriptors

type SPSSODescriptors struct {
}

type SamlsigReference

type SamlsigReference struct {
	XMLName      xml.Name
	URI          string       `xml:"URI,attr"`
	Transforms   Transforms   `xml:",innerxml"`
	DigestMethod DigestMethod `xml:",innerxml"`
	DigestValue  DigestValue  `xml:",innerxml"`
}

type ServiceProviderSettings

type ServiceProviderSettings struct {
	PublicCertPath              string
	PrivateKeyPath              string
	IDPSSOURL                   string
	IDPSSODescriptorURL         string
	IDPPublicCertPath           string
	AssertionConsumerServiceURL string
	// contains filtered or unexported fields
}

ServiceProviderSettings provides settings to configure server acting as a SAML Service Provider. Expect only one IDP per SP in this configuration. If you need to configure multipe IDPs for an SP then configure multiple instances of this module

func (*ServiceProviderSettings) GetAuthnRequest

func (s *ServiceProviderSettings) GetAuthnRequest() *AuthnRequest

GetSignedAuthnRequest returns a singed XML document that represents a AuthnRequest SAML document

func (*ServiceProviderSettings) GetEntityDescriptor

func (s *ServiceProviderSettings) GetEntityDescriptor() (string, error)

func (*ServiceProviderSettings) IDPPublicCert

func (s *ServiceProviderSettings) IDPPublicCert() string

func (*ServiceProviderSettings) Init

func (s *ServiceProviderSettings) Init() (err error)

func (*ServiceProviderSettings) PrivateKey

func (s *ServiceProviderSettings) PrivateKey() string

func (*ServiceProviderSettings) PublicCert

func (s *ServiceProviderSettings) PublicCert() string

type Signature

type Signature struct {
	XMLName        xml.Name
	Id             string `xml:"Id,attr"`
	SignedInfo     SignedInfo
	SignatureValue SignatureValue
	KeyInfo        KeyInfo
}

type SignatureMethod

type SignatureMethod struct {
	XMLName   xml.Name
	Algorithm string `xml:"Algorithm,attr"`
}

type SignatureValue

type SignatureValue struct {
	XMLName xml.Name
	Value   string `xml:",innerxml"`
}

type SignedInfo

type SignedInfo struct {
	XMLName                xml.Name
	CanonicalizationMethod CanonicalizationMethod
	SignatureMethod        SignatureMethod
	SamlsigReference       SamlsigReference
}

type SingleLogoutService

type SingleLogoutService struct {
	Binding  string `xml:"Binding,attr"`
	Location string `xml:"Location,attr"`
}

type Status

type Status struct {
	XMLName    xml.Name
	StatusCode StatusCode `xml:"StatusCode"`
}

type StatusCode

type StatusCode struct {
	XMLName xml.Name
	Value   string `xml:",attr"`
}

type Subject

type Subject struct {
	XMLName             xml.Name
	NameID              NameID
	SubjectConfirmation SubjectConfirmation
}

type SubjectConfirmation

type SubjectConfirmation struct {
	XMLName                 xml.Name
	Method                  string `xml:",attr"`
	SubjectConfirmationData SubjectConfirmationData
}

type SubjectConfirmationData

type SubjectConfirmationData struct {
	InResponseTo string `xml:",attr"`
	NotOnOrAfter string `xml:",attr"`
	Recipient    string `xml:",attr"`
}

type Transform

type Transform struct {
	XMLName   xml.Name
	Algorithm string `xml:"Algorithm,attr"`
}

type Transforms

type Transforms struct {
	XMLName   xml.Name
	Transform Transform
}

type X509Certificate

type X509Certificate struct {
	XMLName xml.Name
	Cert    string `xml:",innerxml"`
}

type X509Data

type X509Data struct {
	XMLName         xml.Name
	X509Certificate X509Certificate `xml:",innerxml"`
}

Directories

Path Synopsis
Godeps
_workspace/src/github.com/kardianos/osext
Extensions to the standard "os" package.
Extensions to the standard "os" package.
_workspace/src/github.com/nu7hatch/gouuid
This package provides immutable UUID structs and the functions NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4 and 5 UUIDs as specified in RFC 4122.
This package provides immutable UUID structs and the functions NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4 and 5 UUIDs as specified in RFC 4122.
_workspace/src/github.com/stretchr/testify/assert
Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
_workspace/src/golang.org/x/net/html
Package html implements an HTML5-compliant tokenizer and parser.
Package html implements an HTML5-compliant tokenizer and parser.
_workspace/src/golang.org/x/net/html/atom
Package atom provides integer codes (also known as atoms) for a fixed set of frequently occurring HTML strings: tag names and attribute keys such as "p" and "id".
Package atom provides integer codes (also known as atoms) for a fixed set of frequently occurring HTML strings: tag names and attribute keys such as "p" and "id".
_workspace/src/golang.org/x/net/html/charset
Package charset provides common text encodings for HTML documents.
Package charset provides common text encodings for HTML documents.
_workspace/src/golang.org/x/net/websocket
Package websocket implements a client and server for the WebSocket protocol as specified in RFC 6455.
Package websocket implements a client and server for the WebSocket protocol as specified in RFC 6455.

Jump to

Keyboard shortcuts

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