Code Monkey home page Code Monkey logo

saml's Introduction

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.

saml's People

Contributors

a-h avatar alexanderzobnin avatar andy2046 avatar apilloud avatar atezet avatar brycedfisher avatar crewjam avatar dcormier avatar dependabot-preview[bot] avatar dependabot[bot] avatar dherbst avatar digininja avatar dustin-decker avatar edaniels avatar eloff avatar github-actions[bot] avatar heymagurany avatar hf avatar jkakavas avatar joesiltberg avatar mabeyj avatar mmailhos avatar pheelee avatar praneetloke avatar ptman avatar ricardofandrade avatar rkuris avatar ry4nz avatar sevki avatar tux21b avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

saml's Issues

Signature verification failed (No support for OneLogin)

I tried to follow your example from the readme.md. I copied the code and setup a sample app in OneLogin. When I try to access the protected endpoint /hello I get redirected to OneLogin and after a successful login I'm redirected back to http://localhost:8081/saml/acs which fails miserably with:

ERROR: failed to verify signature on response: signature verification failed

Any ideas how to resolve this?

Help request: Dealing with breaking change

I've been using the schema section of the SAML library and signing the requests myself with xmlsec, but the API has changed and I'm not quite sure how best to proceed.

I'm only using a small subset of the library functions because all I need to do is to create a signed SAML Response and post it somewhere. I'm not responding to an IdP request, I'm initiating it directly to a 3rd party.

I'd parked the project for a couple of months, but it's started back up again, and when I got back to it the API had changed.

Some things were easy to fix, but I'm not sure how to migrate from xmlsec. When I copied and pasted the now-deleted DefaultSignature code back into my project, I get the error:

message: 'cannot use &sig (type *Signature) as type *etree.Element in assignment'

At this part of the code.

func addEmptySignatureElementToAssertion(certificate []byte, response *saml.Response) {
	sig := DefaultSignature(certificate)
	response.Assertion.Signature = &sig
}

Here's the full set of code, do you have any pointers for migrating this? If so, they'd be much appreciated!

Thanks,
Adrian.

package gateway

import (
	"encoding/xml"
	"time"

	"github.com/crewjam/go-xmlsec"
	"github.com/crewjam/saml"
)

// ResponseData contains all the data required to create an assertion.
type ResponseData struct {
	// The ID of the Response. It should an NCName (limited characters, musn't start
	// with a number, dot, or minus)
	ResponseID string
	// DestinationURL is the URL to the service provider signon endpoint that the SAML response
	// will be sent to.
	DestinationURL string
	// Issuer is the name of the entity which generated the SAML Response.
	Issuer string
	// InResponseTo is the ID of the SAML request that the response is reacting to. In the case
	// of Identity Provider initiated SSO, this will be ignored by the Service Provider.
	InResponseTo string
	// IssuedTime is the date / time that the Response was generated.
	IssuedTime time.Time
	// ExpiryTime is the date / time that the Response is not valid after.
	ExpiryTime time.Time
	// AssertionID is the unique ID applied to the SAML assertion.
	AssertionID string
	// SessionID is the ID of the unique session belonging to the user.
	SessionID string
	// UserID is the id of the subject who holds the SAML response.
	UserID string
	// Custom SAML attributes to apply to the SAML response.
	Attributes map[string]string
}

// NewResponse makes a SAML response XML structure.
func NewResponse(data ResponseData) *saml.Response {
	return &saml.Response{
		Destination:  data.DestinationURL,
		ID:           data.ResponseID,
		InResponseTo: data.InResponseTo,
		IssueInstant: data.IssuedTime,
		Version:      "2.0",
		Issuer: &saml.Issuer{
			Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
			Value:  data.Issuer,
		},
		Status: saml.Status{
			StatusCode: saml.StatusCode{
				Value: saml.StatusSuccess,
			},
		},
		Assertion: NewAssertion(data),
	}
}

// SignResponse signs the SAML response.
func SignResponse(certificate []byte, privateKey []byte, response *saml.Response) ([]byte, error) {
	addEmptySignatureElementToAssertion(certificate, response)

	// Sign the assertion element.
	documentToSign, err := xml.Marshal(response)
	if err != nil {
		return []byte{}, err
	}
	// Sign the element called "Response". Look up which one to sign using the ID attribute.
	return xmlsec.Sign(privateKey, documentToSign, xmlsec.SignatureOptions{
		XMLID: []xmlsec.XMLIDOption{{
			ElementName:      "Response",
			ElementNamespace: "urn:oasis:names:tc:SAML:2.0:protocol",
			AttributeName:    "ID",
		}},
	})
}

// addEmptySignatureElementToAssertion adds a digital signature skeleton element to the response's Assertion.
func addEmptySignatureElementToAssertion(certificate []byte, response *saml.Response) {
	sig := saml.DefaultSignature(certificate)
	response.Assertion.Signature = &sig
}

// NewAssertion creates an assertion based on input ResponseData.
func NewAssertion(data ResponseData) *saml.Assertion {
	assertion := &saml.Assertion{
		ID:           data.AssertionID,
		IssueInstant: data.IssuedTime,
		Version:      "2.0",
		Issuer: saml.Issuer{
			Format: "XXX",
			Value:  data.Issuer,
		},
		Subject: &saml.Subject{
			NameID: &saml.NameID{
				Format:          "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
				NameQualifier:   data.Issuer,
				SPNameQualifier: data.DestinationURL,
				Value:           data.UserID,
			},
			SubjectConfirmations: []saml.SubjectConfirmation{saml.SubjectConfirmation{
				Method: "urn:oasis:names:tc:SAML:2.0:cm:bearer",
				SubjectConfirmationData: &saml.SubjectConfirmationData{
					Address:      data.DestinationURL,
					InResponseTo: data.InResponseTo,
					NotOnOrAfter: data.ExpiryTime,
					Recipient:    data.DestinationURL,
				},
			}},
		},
		Conditions: &saml.Conditions{
			NotBefore:    data.IssuedTime,
			NotOnOrAfter: data.ExpiryTime,
			AudienceRestrictions: []saml.AudienceRestriction{saml.AudienceRestriction{
				Audience: saml.Audience{Value: data.DestinationURL},
			}},
		},
		AuthnStatements: []saml.AuthnStatement{saml.AuthnStatement{
			AuthnInstant: data.IssuedTime,
			SessionIndex: data.SessionID,
			AuthnContext: saml.AuthnContext{
				AuthnContextClassRef: &saml.AuthnContextClassRef{
					Value: "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
				},
			},
		}},
		AttributeStatements: []saml.AttributeStatement{saml.AttributeStatement{
			Attributes: createSAMLAttributesFromMap(data.Attributes),
		}},
	}
	return assertion
}

func createSAMLAttribute(name string, values ...string) saml.Attribute {
	samlAttributeValues := make([]saml.AttributeValue, len(values))
	for i, v := range values {
		samlAttributeValues[i] = saml.AttributeValue{
			Type:  "xs:string",
			Value: v,
		}
	}

	return saml.Attribute{
		FriendlyName: name,
		Name:         name,
		NameFormat:   "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
		Values:       samlAttributeValues,
	}
}

func createSAMLAttributesFromMap(attributes map[string]string) []saml.Attribute {
	var samlAttributes []saml.Attribute
	for k, v := range attributes {
		samlAttributes = append(samlAttributes, createSAMLAttribute(k, v))
	}
	return samlAttributes
}

update test expectations for go 1.7

c.f. https://travis-ci.org/crewjam/saml/builds/157315439

... obtained string = "https://idp.example.com/saml/sso?RelayState=ThisIsTheRelayState&SAMLRequest=lJJRaxQxEMe%2FSpj3u0zC9axhd%2BHsISxUKa364NuQHW1gk6yZWW2%2FvfRULAjlfB1%2B%2FPnNf6Y7rHpfbvnbyqLmIc9FelhbCZUkSSiUWYLGcHd4dx38FsPSqtZYZzAHEW6aarmqRdbM7Y7b9xT54%2B11D%2FeqiwRrZdnyA%2BVl5m2s2Qrl2VuKAubIoqnQU8BfPE3%2F8lakghmPPaRpg4ged7jHSySMyA6ddzu3d5eOXHTs0Xu%2F83swo8jKYxGloj14dBcb5zfoPqALF68Cvv4M5ub3Mm9SmVL52gOYT9zkpOS3CEN3SmnnFEN%2F6gDztrZM%2BjL%2BNEnT5ssJDVw06SMML9aWWWkipc7%2Bshq695R5PN7UOcXH%2FzzePNcfV41JuQdtK8Nwvq02KpK4aGefGwydff5Nw88AAAD%2F%2Fw%3D%3D"
... expected string = "https://idp.example.com/saml/sso?RelayState=ThisIsTheRelayState&SAMLRequest=lJJRqxMxEIX%2FypL3NpPQW69hd6HeIhSqlFZ98G3IjjawSdbMrLb%2F3m1VLAilvoaTM985M%2FVqlGPa07eRWKpT7BM3aizJZeTALmEkduLdYfVu6%2Bwc3FCyZJ97Va2YqUjI6SUnHiOVA5XvwdPH%2FbZRR5GBndY8zOmEcehp7nPUjLG3Gj2raj0NDAkvBn%2FloftXr5mzqjbrRoVuBgAWFrCEZ0DwQAaMNQuzNM8GjTdkwVq7sMvpA%2FNIm8SCSRplwTzNjJ2B%2BQDGPb1y8Pqzqna%2Fw7wJqQvpa6NU9YkKX5GmsKqtry7lkWLwTx2qeptLRLkvv7xMeb5cpY6SBDmr9m5tkQQ7FKz1L6q2fj%2BZbNa73Ad%2F%2Fs%2Fl9X3%2B8VIIhRolZSTVPk4rBROHibnWtwRtrW%2Bvqf0ZAAD%2F%2Fw%3D%3D"

expected element type <EntityDescriptor> but have <EntitiesDescriptor>

Just trying to get the sample code in the README to work.

I have

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"

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

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.Header.Get("X-Saml-Cn"))
}

func main() {
    key, err := ioutil.ReadFile("service.key")
    if err != nil {
        panic(err)
    }
    cert, err := ioutil.ReadFile("service.cert")
    if err != nil {
        panic(err)
    }
    samlSP, err := samlsp.New(samlsp.Options{
        IDPMetadataURL: "https://www.testshib.org/metadata/testshib-providers.xml",
        URL:            "http://localhost:8000",
        Key:            string(key),
        Certificate:    string(cert),
    })
    if err != nil {
        panic(err)
    }
    app := http.HandlerFunc(hello)
    http.Handle("/hello", samlSP.RequireAccount(app))
    http.Handle("/saml/", samlSP)
    panic(http.ListenAndServe(":8000", nil))
}

(notice the added err checks)

samlsp.New is failing with expected element type <EntityDescriptor> but have <EntitiesDescriptor>

If it helps, I'm on Debian Jessie, so perhaps my libxmlsec1-dev is not the right version? Dunno

AttributeValue types don't include the appropriate namespaces

Hi, I've been taking a look at the code and been using it to generate some custom SAML responses. My site already has JWT authentication, so I'm not using most of the features, just making a bridge between my site's existing JWT-based auth and a remote SAML service provider.

I spotted how the session provider can provide some of the defined fields in SAML, but I need to use custom attributes to get the data across.

I was able to get a SAML Response output from the code, but it wouldn't validate via against the XSD at https://www.samltool.com/validate_xml.php because, "xs:string" isn't in defined as a namespace in the output XML document.

The attributes section should look something like this:

<saml:AttributeStatement>
    <saml:Attribute Name="CustomAttribute"
        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
      <saml:AttributeValue xsi:type="xs:string">custom_username</saml:AttributeValue>
    </saml:Attribute>

Note the reference to "xs:string" - i.e. the string that's part of the the XML Schema namespace (http://www.w3.org/2001/XMLSchema).

Customising the response's MarshalXML function to add the namespaces at the top level, then the AttributeValue's struct tag to use it

func (r *Response) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	type Alias Response
	aux := &struct {
		IssueInstant RelaxedTime `xml:",attr"`
		*Alias
		XS  string `xml:"xmlns:xs,attr"`
		XSI string `xml:"xmlns:xsi,attr"`
	}{
		IssueInstant: RelaxedTime(r.IssueInstant),
		Alias:        (*Alias)(r),
		XS:           "http://www.w3.org/2001/XMLSchema",
		XSI:          "http://www.w3.org/2001/XMLSchema-instance",
	}
	return e.Encode(aux)
}
type AttributeValue struct {
	Type   string `xml:"xsi:type,attr"`
	Value  string `xml:",chardata"`
	NameID *NameID
}

That gives the top level output:

<Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" IssueInstant="2017-04-24T18:15:05.655Z" Destination="http://exampledestination.com" ID="id-f265ea996b3ce92b73f0024f5d40a7ab175d207f" InResponseTo="id-1" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

And the attribute statement of:

  <AttributeStatement>
   <Attribute FriendlyName="name" Name="name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <AttributeValue xsi:type="xs:string">a-h</AttributeValue>
   </Attribute>
  </AttributeStatement>

That looks good to me, and it passes the online validation, but breaks 5 unit tests in the code, all of which appear to be via large object comparisons which are now failing.

Approach 2: Customise the XML generated by AttributeValue

func (a *AttributeValue) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	start.Attr = []xml.Attr{
		xml.Attr{Name: xml.Name{Space: "", Local: "xmlns:xsi"}, Value: "http://www.w3.org/2001/XMLSchema-instance"},
		xml.Attr{Name: xml.Name{Space: "", Local: "xmlns:xs"}, Value: "http://www.w3.org/2001/XMLSchema"},
		xml.Attr{Name: xml.Name{Space: "", Local: "xsi:type"}, Value: a.Type},
	}

	return e.EncodeElement(a.Value, start)
}

This gives the same effect, with less collateral damage.

Next

I would appreciate help getting this ready for inclusion in the project if it's a good fit.

SP and IDP: make it possible to customize metadata

convert Metadata() method to a member variable, add DefaultSPMetadata()

md := DefaultServiceProviderMetadata(key, cert, entityID, acsURL)
md.SPSSODescriptor.NameIDFormat = []string{"someCustomThing"}
sp := ServiceProvider{
    Metadata: md,
}

Validating a received SAML response without request id?

Hello,

Im currently doing this with another library. Like so https://github.com/RobotsAndPencils/go-saml#validating-a-received-saml-response but would like to try your library. Getting rid of the xmlsec dependency would be nice.

I thought that using func (sp *ServiceProvider) ParseResponse(req *http.Request, possibleRequestIDs []string) (*Assertion, error) { would be the correct way to do this, but then I need to supply request IDs which I don't have.

In the SAML response we receive from an external partner InResponseTo is not set. Im not sure if this is incorrect (I'm a SAML noob). But according to https://www.ca.com/us/services-support/ca-support/ca-support-online/knowledge-base-articles.TEC1653345.html it seems ok for an IDP initiated response, which this is.

I noticed that you made this required in #15. Was there any specific rationale for this? Could it be made optional again? Perhaps a func (sp *ServiceProvider) ParseResponseInsecure(req *http.Request) (*Assertion, error) { method?

NameIDPolicy may have invalid format?

I am trying to get a crewjam/saml SP to work with ADFS. Metadata import /export works fine. Redirect from SP to IdP also works. Authenticationn at IdP also works. Then, however, ADFS reports an error. I was able to retrieve the URL encoded, base 64 encoded, XML deflated SAML request from the URL and found the following

<AuthnRequest
  xmlns="urn:oasis:names:tc:SAML:2.0:protocol"
  IssueInstant="2017-07-27T14:14:50.994Z"
  AssertionConsumerServiceURL="https://passionator-dev-passionator.jp1.altemista.cloud/saml/acs"
  Destination="https://sts.nttdata-emea.com/adfs/ls/"
  ID="id-40f721ca9d5734b2f806a763dd4b7a1746f77e3f"
  ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
  Version="2.0">
    <Issuer
      xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
      Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
        https://passionator-dev-passionator.jp1.altemista.cloud/saml/metadata
    </Issuer>
    <NameIDPolicy
      xmlns="urn:oasis:names:tc:SAML:2.0:protocol"
      AllowCreate="true">
      urn:oasis:names:tc:SAML:2.0:nameid-format:transient
    </NameIDPolicy>
</AuthnRequest>

When checkin this against the SAML xsd it fails. Then changing the NameIDPolicy field to

<NameIDPolicy 
      xmlns="urn:oasis:names:tc:SAML:2.0:protocol" 
      Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
      AllowCreate="true"/>

the redirect back from ADFS to my service works.
QUESTION: Is the NameIDPolicy tag rendered by crewjam/saml in a non-standard way?

How to set sign the request by RSA256?

I found this code state := jwt.New(jwt.GetSigningMethod("HS256"))
But it can not work if I changed to state := jwt.New(jwt.GetSigningMethod("RSA256"))

SP support for multiple IDPs

The current library assumes that there will be exactly one IDP for each SP, but there may be cases where a service provider needs multiple IDPs. What is the story there? How to people like google and amazon handle that? IDP-initiated? or something else?

Content Security Policy Violation

Hello, I followed all steps in README file but when I go to /hello path, it is stuck in endless JavaScript loop and I see this error in the console:

Unrecognized Content-Security-Policy directive 'reflected-xss'.

hello:1 Unrecognized Content-Security-Policy directive 'referrer'.

hello:1 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'sha256-D8xB+y+rJ90RmLdP72xBqEEc0NUatn7yuCND0orkrgk='". Either the 'unsafe-inline' keyword, a hash ('sha256-zq06eoxR67W1gFEi2bAluMTV9jLyWMvURuh9rHYUfU8='), or a nonce ('nonce-...') is required to enable inline execution.

Can you help me solve this problem?

I can see this header when I go to /hello with curl -v:

< Content-Security-Policy: default-src; script-src 'sha256-D8xB+y+rJ90RmLdP72xBqEEc0NUatn7yuCND0orkrgk='; reflected-xss block; referrer no-referrer;

It seems to say that the sha256 hash should be:

sha256-zq06eoxR67W1gFEi2bAluMTV9jLyWMvURuh9rHYUfU8=

Override defaultCookieMaxAge

How do I specify my own defaultCookieMaxAge?
Right now the 1 hour default is causing annoying breakages in a single page app.

consider ditching JWT in samlsp

(1) it seems that people are using the samlsp middlware as more than an example (which is kinda what I intended)
(2) the samlsp middlware relies on JWT
(3) JWT has been panned in recent months on blogs, but I haven't dug into why in much detail, although one example is cited in #51.

Perhaps it would be better to use a more error-resistant mechanism to issue temporary credentials.

Relative namespaces

The result of marshalling an EntitiesDescriptor struct contains relative namespace references.

    <Organization>
      <OrganizationName xmlns:_xml="xml" _xml:lang="">KU Leuven /
      Associatie KU Leuven</OrganizationName>
      <OrganizationName xmlns:_xml="xml" _xml:lang="">KU Leuven /
      KU Leuven Association</OrganizationName>
      <OrganizationDisplayName xmlns:_xml="xml" _xml:lang="">KU
      Leuven / Associatie KU Leuven</OrganizationDisplayName>
      <OrganizationDisplayName xmlns:_xml="xml" _xml:lang="">KU
      Leuven / KU Leuven Association</OrganizationDisplayName>
      <OrganizationURL xmlns:_xml="xml" _xml:lang="">
      http://www.kuleuven.be</OrganizationURL>
      <OrganizationURL xmlns:_xml="xml" _xml:lang="">
      http://www.kuleuven.be</OrganizationURL>
    </Organization>

The xmlns:_xml="xml" relative namespace reference causes go-xmlsec & xmlsectool to fail:

xmlsectool.sh --sign --inFile result.xml --key saml.pem --certificate saml.crt --outFile payload.signed.xml
INFO  XMLSecTool - Reading XML document from file 'result.xml'
INFO  XMLSecTool - XML document parsed and is well-formed.
ERROR XMLSecTool - Unable to create XML document signature

If I remove them by hand I am able to sign the XML.

samlsp: RequireAccount CSP missing script-src for script

The SHA256 sum for Content-Security-Policy in function RequireAccount (https://github.com/crewjam/saml/blob/master/samlsp/middleware.go#L165) is for the submission script written by the Post method for AuthnRequest (https://github.com/crewjam/saml/blob/master/service_provider.go#L308).

The SHA256 sum for the script that disables visibility of the submit button is missing. That is this snippet: document.getElementById('SAMLSubmitButton').style.visibility="hidden";

Can it be added?

[meta] PR's over direct commits

Hey! This is a super meta thing, but since Go packages are assumed stable at the master branch can we set this repo to not allow direct commits to master, and only allow PR's ๐Ÿ˜„

This allows the community to code review and verify awesomeness of additional features.

Thanks!

Got `Forbidden` response for the trivial example

Tried your example go run trivial.go and setup things with Shibboleth

It redirected to http://localhost:8000/saml/acs with response of Forbidden.

The log reads the following:

โžœ  trivial git:(74fc22c) โœ— go run trivial.go
2017/02/08 13:48:49 getPossibleRequestIDs: cookie: saml_8OS7pnwrnKiFBnr95edu1sP99FRlUebQlyUbkuj6Zq9UpmIhnoAH4HtT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImlkLTExNTk2NzEyNTE5Y2JlMGRmODc0YjdjNWM2MTNiOTUzNzRjMTcxMWUiLCJ1cmkiOiIvaGVsbG8ifQ.BTdhtVzWGxf8y1RUEl7HzMnAljpbUisZ33AiRH1tu6w
2017/02/08 13:48:49 getPossibleRequestIDs: cookie: saml_ZLL_960awaEqN_zWAC014M6oVIuu6GRLPlD29nQARblQNkTmohNB_tic=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImlkLWIyMjlkOGIxMDQ4ZDMwNjQwNThiZWE5OGE2OTM1MmE3YmNiZGQxZTIiLCJ1cmkiOiIvaGVsbG8ifQ.NVm_t8NtEuj3R36UKqySRTlgQTq-sqE9YgxuFn2AKy8
2017/02/08 13:48:49 RESPONSE: ===
<?xml version="1.0" encoding="UTF-8"?><saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:8000/saml/acs" ID="_aeba0505a42839bb07a6081c3f77e56b" InResponseTo="id-b229d8b1048d3064058bea98a69352a7bcbdd1e2" IssueInstant="2017-02-08T21:48:49.821Z" Version="2.0"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://idp.testshib.org/idp/shibboleth</saml2:Issuer><saml2p:Status><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder"/><saml2p:StatusMessage>Unable to encrypt assertion</saml2p:StatusMessage></saml2p:Status></saml2p:Response>
===
NOW: 2017-02-08 21:48:49.910719258 +0000 UTC
ERROR: Status code was not urn:oasis:names:tc:SAML:2.0:status:Success

I wonder if Unable to encrypt assertion is due to misconfiguration at the IdP?

Metadata cacheDuration: wrong XML duration format

I'm implementing an IdP and using TestShib to test. When I upload the IdP's metadata to TestShib, it says it's not valid metadata. I used this tool to validate the metadata and it gives an error:

Line: 1 | Column: 0 --> Element '{urn:oasis:names:tc:SAML:2.0:metadata}EntityDescriptor', attribute 'cacheDuration': '172800000000000' is not a valid value of the atomic type 'xs:duration'.

If I replace cacheDuration="172800000000000" with cacheDuration="P1D" in the metadata, it passes validation and TestShib accepts it. So it looks like Metadata.CacheDuration should be following this format when marshalling to XML.

Forbidden error: asn1: structure error: tags don't match

The service provider uses crewjam/saml. It integrates with pingFederate IdP, but a shibboleth IdP throws this error

asn1: structure error: tags don't match (2 vs {class:0 tag:16 length:13 isCompound:true}) {optional:false explicit:false application:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} @5

My IdP's private key seems to be correct and works for an older version of crewjam/saml, when crypto/ripemd wasn't used. What is to be changed to avoid this error?

samlsp uses default http.Client

resp, err := http.Get(opts.IDPMetadataURL)

Which makes it hard to e.g. specify a tls.Config with different trusted CAs just for samlsp. Looser coupling to the default http.Client would help. But maybe that's not a big problem.

In SP example, return `Forbidden` after log in to testshib

Hello, I'm studying the saml with your project.

After redirect to http://localhost:8080/saml/acs, I recieve the Forbidden message.
(I use the port 8080.)

// Here are my terminal message

ERROR: asn1: structure error: tags don't match (2 vs {class:0 tag:16 length:13 isCompound:true}) {optional:false explicit:false application:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false}  @5

I can't guess what is a problem in my code.
I'll wait your kindness reply.
Thank you.

allow for clock skew when evaluating NotBefore or NotOnOrAfter conditions

SAML flow fails with assertion invalid: Conditions is not yet valid

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://XXX/saml/acs" ID="cd02e172-837b-4192-87a1-8e36162049a5" InResponseTo="id-f5a606446ee0b3f000ba6c5da687999a45b01a83" IssueInstant="2017-01-12T14:04:45.158Z" Version="2.0">
	<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
	https://idp.XXX.com/SAML2</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
	<ds:SignedInfo>
	<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#cd02e172-837b-4192-87a1-8e36162049a5">
	<ds:Transforms>
	<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>
	pjMbXri6r8scEga9jwgZdeZwJ7g=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
	DzvSgdt4bWWdbchciMhz4lmncSbjRnxsEjmmJ7EdfzPwO3Lv5FVAn1u+pQxcEQolhI6F0MDO61NBBwqh0rBxIlxJGWfY3Wz0VaqRttPNv9oSCI0v4PX9F0EJQkslc8fh6FC8yhK4QBWgdexDKXoJ0gGDljWfh70HftvH73sBZ6SpaGjkDg4mLa5xday3vc82amgy8Z4d3KwRcz25YHnszzsgG1aO7RCdnm2WSDqT3ZCPnjyiHpPDb+G2ixn8CatlYbXEQqHFoE92b1PVZu5ExilS1w/EWAUqQEq2skncghROTa4AqvC578Vrv9tsK0UQP0CCqpsWY5UaXHEhvzjcRw==</ds:SignatureValue>
<ds:KeyInfo>
	<ds:KeyValue>
	<ds:RSAKeyValue>
	<ds:Modulus>
	zZlTNJ+QcTp2yGH1ECXO3ry4GHhcs1CW3I6GPiPvtO+P6lyWxYdQd2RK/Hk9Kap6qpm/qom0rTwb
FU2I67Y2JdQ3T5QBJjGHbGHU1uMxVWkhJluoa0Lpm381zNCJTZp8PetoB8dnIGua9y1aL75v04CG
TzJ14I9/sW+apTkWj7xVQXutvVKETdn4kAy+L33HpriZjQNlcuAbqQj6OWsN4tGkLvNFZT40jQzp
/8/tOQE6n2+zn3I8hUePwjPQROUmCeK86CkF0yVCPQ/vOTsC00Uaeu/SPOUu5ot+/75NPyE8w5Ry
DgefdDXhYNmeuQtwGtcu/FI66atQMNTDoChXJQ==</ds:Modulus>
<ds:Exponent>
	AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature>
<saml2p:Status>
	<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
<saml2p:StatusMessage>
	Authentication success.</saml2p:StatusMessage>
</saml2p:Status>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="5d3589f0-68ab-46c3-85e1-be1666f9b2f8" IssueInstant="2017-01-12T14:04:45.158Z" Version="2.0">
	<saml2:Issuer>
	https://idp.XXX.com/SAML2</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
	<ds:SignedInfo>
	<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#5d3589f0-68ab-46c3-85e1-be1666f9b2f8">
	<ds:Transforms>
	<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>
	OImLIAshOVaveFXi3Y+lUWtMeLM=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
	oiP/8f+dqt0Qoke2a2s4GprzFj4UZAjE2PdSMO5CQk3MTOAE0JNyU/5NFXuQWBPkb3yEy1LyAP1X81OszpX2Bm0YnCrri8OiL7u2WcZi53Nou5dUvpG23+CPdINShZuTbRXRb7eL4k52xUUgJHWRJnkvPZxVVE6fb5Yi4+AtTQGCnQXG1PLIYvaO6FY5r3bPPHDfUE+znroPD+DaQEyiWWsjfQepMSETjs0P96oMB13XXg+AdQ+LCV7baOt/VI5AmcOjymLNHenVa2t0FtQ5WjJp4RS1Im7+b161MxD2mHB+9JXmQrXqbE006f1r4FIE4hNKCn7jND1KHW6zgICAcA==</ds:SignatureValue>
<ds:KeyInfo>
	<ds:KeyValue>
	<ds:RSAKeyValue>
	<ds:Modulus>
	zZlTNJ+QcTp2yGH1ECXO3ry4GHhcs1CW3I6GPiPvtO+P6lyWxYdQd2RK/Hk9Kap6qpm/qom0rTwb
FU2I67Y2JdQ3T5QBJjGHbGHU1uMxVWkhJluoa0Lpm381zNCJTZp8PetoB8dnIGua9y1aL75v04CG
TzJ14I9/sW+apTkWj7xVQXutvVKETdn4kAy+L33HpriZjQNlcuAbqQj6OWsN4tGkLvNFZT40jQzp
/8/tOQE6n2+zn3I8hUePwjPQROUmCeK86CkF0yVCPQ/vOTsC00Uaeu/SPOUu5ot+/75NPyE8w5Ry
DgefdDXhYNmeuQtwGtcu/FI66atQMNTDoChXJQ==</ds:Modulus>
<ds:Exponent>
	AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
	<saml2:NameID>
	XXX</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
	<saml2:SubjectConfirmationData InResponseTo="id-f5a606446ee0b3f000ba6c5da687999a45b01a83" NotBefore="2017-01-12T14:04:45.158Z" NotOnOrAfter="2017-01-12T14:09:45.158Z" Recipient="https://XXX/saml/acs"/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2017-01-12T14:04:45.158Z" NotOnOrAfter="2017-01-12T14:09:45.158Z">
	<saml2:AudienceRestriction>
	<saml2:Audience>
	https://XXX/saml/metadata</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2017-01-12T14:04:45.158Z" SessionIndex="undefined">
	<saml2:AuthnContext>
	<saml2:AuthnContextClassRef>
	urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
</saml2:Assertion>
</saml2p:Response>

The request was evaluated at 2017-01-12T14:04:45.078754778Z

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.