Skip to content

Commit f33bc82

Browse files
committed
update README
1 parent 90a8ae8 commit f33bc82

File tree

3 files changed

+153
-102
lines changed

3 files changed

+153
-102
lines changed

README.md

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# SAML
2+
3+
Package saml contains a partial implementation of the SAML standard in golang.
4+
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.
5+
6+
## Introduction
7+
8+
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.
9+
10+
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.
111

212
## Breaking Changes
313

@@ -20,15 +30,6 @@ In various places where keys and certificates were modeled as `string`
2030
<= version 0.1.0 (what was I thinking?!) they are now modeled as
2131
`*rsa.PrivateKey`, `*x509.Certificate`, or `crypto.PrivateKey` as appropriate.
2232

23-
## Introduction
24-
25-
Package saml contains a partial implementation of the SAML standard in golang.
26-
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.
27-
28-
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.
29-
30-
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.
31-
3233
## Getting Started as a Service Provider
3334

3435
Let us assume we have a simple web appliation to protect. We'll modify this application so it uses SAML to authenticate users.
@@ -115,15 +116,15 @@ Now you should be able to authenticate. The flow should look like this:
115116

116117
1. You browse to `localhost:8000/hello`
117118

118-
2. The middleware redirects you to `https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO`
119+
1. The middleware redirects you to `https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO`
119120

120-
3. testshib.org prompts you for a username and password.
121+
1. testshib.org prompts you for a username and password.
121122

122-
4. testshib.org 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.
123+
1. testshib.org 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.
123124

124-
5. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`.
125+
1. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`.
125126

126-
6. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served.
127+
1. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served.
127128

128129
## Getting Started as an Identity Provider
129130

saml.go

+137-87
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,163 @@
1+
//
12
// Package saml contains a partial implementation of the SAML standard in golang.
23
// 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.
3-
//
4+
//
5+
// Introduction
6+
//
47
// 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.
5-
//
8+
//
69
// 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.
7-
//
10+
//
11+
// Breaking Changes
12+
//
13+
// Note: between version 0.2.0 and the current master include changes to the API
14+
// that will break your existing code a little.
15+
//
16+
// This change turned some fields from pointers to a single optional struct into
17+
// the more correct slice of struct, and to pluralize the field name. For example,
18+
// `IDPSSODescriptor *IDPSSODescriptor` has become
19+
// `IDPSSODescriptors []IDPSSODescriptor`. This more accurately reflects the
20+
// standard.
21+
//
22+
// The struct `Metadata` has been renamed to `EntityDescriptor`. In 0.2.0 and before,
23+
// every struct derived from the standard has the same name as in the standard,
24+
// *except* for `Metadata` which should always have been called `EntityDescriptor`.
25+
//
26+
// In various places `url.URL` is now used where `string` was used <= version 0.1.0.
27+
//
28+
// In various places where keys and certificates were modeled as `string`
29+
// <= version 0.1.0 (what was I thinking?!) they are now modeled as
30+
// `*rsa.PrivateKey`, `*x509.Certificate`, or `crypto.PrivateKey` as appropriate.
31+
//
832
// Getting Started as a Service Provider
9-
//
33+
//
1034
// Let us assume we have a simple web appliation to protect. We'll modify this application so it uses SAML to authenticate users.
11-
//
12-
// package main
13-
//
14-
// import "net/http"
15-
//
16-
// func hello(w http.ResponseWriter, r *http.Request) {
17-
// fmt.Fprintf(w, "Hello, World!")
18-
// })
19-
//
20-
// func main() {
21-
// app := http.HandlerFunc(hello)
22-
// http.Handle("/hello", app)
23-
// http.ListenAndServe(":8000", nil)
24-
// }
25-
//
35+
// ```golang
36+
// package main
37+
//
38+
// import "net/http"
39+
//
40+
// func hello(w http.ResponseWriter, r *http.Request) {
41+
// fmt.Fprintf(w, "Hello, World!")
42+
// }
43+
//
44+
// func main() {
45+
// app := http.HandlerFunc(hello)
46+
// http.Handle("/hello", app)
47+
// http.ListenAndServe(":8000", nil)
48+
// }
49+
// ```
2650
// Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this:
27-
//
51+
//
2852
// openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com"
29-
//
30-
// 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 [testshib.org](testshib.org), an identity provider designed for testing.
31-
//
32-
// package main
33-
//
34-
// import (
35-
// "fmt"
36-
// "io/ioutil"
37-
// "net/http"
38-
//
39-
// "github.com/crewjam/saml/samlsp"
40-
// )
41-
//
42-
// func hello(w http.ResponseWriter, r *http.Request) {
43-
// fmt.Fprintf(w, "Hello, %s!", r.Header.Get("X-Saml-Cn"))
44-
// }
45-
//
46-
// func main() {
47-
// key, _ := ioutil.ReadFile("myservice.key")
48-
// cert, _ := ioutil.ReadFile("myservice.cert")
49-
// samlSP, _ := samlsp.New(samlsp.Options{
50-
// IDPMetadataURL: "https://www.testshib.org/metadata/testshib-providers.xml",
51-
// URL: "http://localhost:8000",
52-
// Key: string(key),
53-
// Certificate: string(cert),
54-
// })
55-
// app := http.HandlerFunc(hello)
56-
// http.Handle("/hello", samlSP.RequireAccount(app))
57-
// http.Handle("/saml/", samlSP)
58-
// http.ListenAndServe(":8000", nil)
59-
// }
60-
//
61-
//
62-
// Next we'll have to register our service provider with the identiy provider to establish trust from the service provider to the IDP. For [testshib.org](testshib.org), you can do something like:
63-
//
53+
//
54+
// 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 [testshib.org](https://www.testshib.org/), an identity provider designed for testing.
55+
//
56+
// ```golang
57+
// package main
58+
//
59+
// import (
60+
// "crypto/rsa"
61+
// "crypto/tls"
62+
// "crypto/x509"
63+
// "fmt"
64+
// "net/http"
65+
// "net/url"
66+
//
67+
// "github.com/crewjam/saml/samlsp"
68+
// )
69+
//
70+
// func hello(w http.ResponseWriter, r *http.Request) {
71+
// fmt.Fprintf(w, "Hello, %s!", r.Header.Get("X-Saml-Cn"))
72+
// }
73+
//
74+
// func main() {
75+
// keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key")
76+
// if err != nil {
77+
// panic(err) // TODO handle error
78+
// }
79+
// keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
80+
// if err != nil {
81+
// panic(err) // TODO handle error
82+
// }
83+
//
84+
// idpMetadataURL, err := url.Parse("https://www.testshib.org/metadata/testshib-providers.xml")
85+
// if err != nil {
86+
// panic(err) // TODO handle error
87+
// }
88+
//
89+
// rootURL, err := url.Parse("http://localhost:8000")
90+
// if err != nil {
91+
// panic(err) // TODO handle error
92+
// }
93+
//
94+
// samlSP, _ := samlsp.New(samlsp.Options{
95+
// URL: *rootURL,
96+
// Key: keyPair.PrivateKey.(*rsa.PrivateKey),
97+
// Certificate: keyPair.Leaf,
98+
// IDPMetadataURL: idpMetadataURL,
99+
// })
100+
// app := http.HandlerFunc(hello)
101+
// http.Handle("/hello", samlSP.RequireAccount(app))
102+
// http.Handle("/saml/", samlSP)
103+
// http.ListenAndServe(":8000", nil)
104+
// }
105+
// ```
106+
//
107+
// Next we'll have to register our service provider with the identiy provider to establish trust from the service provider to the IDP. For [testshib.org](https://www.testshib.org/), you can do something like:
108+
//
64109
// mdpath=saml-test-$USER-$HOST.xml
65110
// curl localhost:8000/saml/metadata > $mdpath
66-
// curl -i -F userfile=@$mdpath https://www.testshib.org/procupload.php
67-
//
111+
//
112+
// Naviate to https://www.testshib.org/register.html and upload the file you fetched.
113+
//
68114
// Now you should be able to authenticate. The flow should look like this:
69-
//
115+
//
70116
// 1. You browse to `localhost:8000/hello`
71-
//
72-
// 2. The middleware redirects you to `https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO`
73-
//
74-
// 3. testshib.org prompts you for a username and password.
75-
//
76-
// 4. testshib.org 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.
77-
//
78-
// 5. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`.
79-
//
80-
// 6. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served.
81-
//
117+
//
118+
// 1. The middleware redirects you to `https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO`
119+
//
120+
// 1. testshib.org prompts you for a username and password.
121+
//
122+
// 1. testshib.org 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.
123+
//
124+
// 1. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`.
125+
//
126+
// 1. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served.
127+
//
82128
// Getting Started as an Identity Provider
83-
//
129+
//
84130
// Please see `examples/idp/` for a substantially complete example of how to use the library and helpers to be an identity provider.
85-
//
131+
//
86132
// Support
87-
//
133+
//
88134
// 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](http://saml2int.org).
89-
//
90-
// 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 fromthe IDP to the service provider are supported vai the HTTP POST binding.
91-
//
135+
//
136+
// 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.
137+
//
92138
// The package supports signed and encrypted SAML assertions. It does not support signed or encrypted requests.
93-
//
139+
//
94140
// RelayState
95-
//
96-
// 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.
97-
//
141+
//
142+
// 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 originaly requested link, rather than the root.
143+
//
98144
// 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.)
99-
//
145+
//
100146
// References
101-
//
147+
//
102148
// The SAML specification is a collection of PDFs (sadly):
103-
//
149+
//
104150
// - [SAMLCore](http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf) defines data types.
105-
//
151+
//
106152
// - [SAMLBindings](http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf) defines the details of the HTTP requests in play.
107-
//
153+
//
108154
// - [SAMLProfiles](http://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf) describes data flows.
109-
//
155+
//
110156
// - [SAMLConformance](http://docs.oasis-open.org/security/saml/v2.0/saml-conformance-2.0-os.pdf) includes a support matrix for various parts of the protocol.
111-
//
112-
// [TestShib](http://www.testshib.org/) is a testing ground for SAML service and identity providers.
157+
//
158+
// [TestShib](https://www.testshib.org/) is a testing ground for SAML service and identity providers.
159+
//
160+
// Security Issues
161+
//
162+
// Please do not report security issues in the issue tracker. Rather, please contact me directly at [email protected] ([PGP Key `8EA205C01C425FF195A5E9A43FA0768F26FD2554`](https://keybase.io/crewjam)).
113163
package saml

saml_gen.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package saml
22

3-
//go:generate bash -c "(cat README.md | sed 's|^## ||g' | sed 's|\\*\\*||g' | sed 's|^|// |g'; echo 'package saml') > saml.go"
3+
//go:generate bash -c "(cat README.md | grep -E -v '^# SAML' | sed 's|^## ||g' | sed 's|\\*\\*||g' | sed 's|^|// |g'; echo 'package saml') > saml.go"

0 commit comments

Comments
 (0)