github.com/crewjam/saml@v0.4.14/README.md (about)

     1  # SAML
     2  
     3  [![](https://godoc.org/github.com/crewjam/saml?status.svg)](http://godoc.org/github.com/crewjam/saml)
     4  
     5  ![Build Status](https://github.com/crewjam/saml/workflows/Presubmit/badge.svg)
     6  
     7  Package saml contains a partial implementation of the SAML standard in golang.
     8  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.
     9  
    10  ## Introduction
    11  
    12  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.
    13  
    14  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.
    15  
    16  ## Getting Started as a Service Provider
    17  
    18  Let us assume we have a simple web application to protect. We'll modify this application so it uses SAML to authenticate users.
    19  
    20  ```golang
    21  package main
    22  
    23  import (
    24      "fmt"
    25      "net/http"
    26  )
    27  
    28  func hello(w http.ResponseWriter, r *http.Request) {
    29      fmt.Fprintf(w, "Hello, World!")
    30  }
    31  
    32  func main() {
    33      app := http.HandlerFunc(hello)
    34      http.Handle("/hello", app)
    35      http.ListenAndServe(":8000", nil)
    36  }
    37  ```
    38  
    39  Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this:
    40  
    41      openssl req -x509 -newkey rsa:2048 -keyout myservice.key -out myservice.cert -days 365 -nodes -subj "/CN=myservice.example.com"
    42  
    43  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](https://samltest.id/), an identity provider designed for testing.
    44  
    45  ```golang
    46  package main
    47  
    48  import (
    49  	"context"
    50  	"crypto/rsa"
    51  	"crypto/tls"
    52  	"crypto/x509"
    53  	"fmt"
    54  	"net/http"
    55  	"net/url"
    56  
    57  	"github.com/crewjam/saml/samlsp"
    58  )
    59  
    60  func hello(w http.ResponseWriter, r *http.Request) {
    61  	fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "displayName"))
    62  }
    63  
    64  func main() {
    65  	keyPair, err := tls.LoadX509KeyPair("myservice.cert", "myservice.key")
    66  	if err != nil {
    67  		panic(err) // TODO handle error
    68  	}
    69  	keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
    70  	if err != nil {
    71  		panic(err) // TODO handle error
    72  	}
    73  
    74  	idpMetadataURL, err := url.Parse("https://samltest.id/saml/idp")
    75  	if err != nil {
    76  		panic(err) // TODO handle error
    77  	}
    78  	idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient,
    79  		*idpMetadataURL)
    80  	if err != nil {
    81  		panic(err) // TODO handle error
    82  	}
    83  
    84  	rootURL, err := url.Parse("http://localhost:8000")
    85  	if err != nil {
    86  		panic(err) // TODO handle error
    87  	}
    88  
    89  	samlSP, _ := samlsp.New(samlsp.Options{
    90  		URL:            *rootURL,
    91  		Key:            keyPair.PrivateKey.(*rsa.PrivateKey),
    92  		Certificate:    keyPair.Leaf,
    93  		IDPMetadata: idpMetadata,
    94  	})
    95  	app := http.HandlerFunc(hello)
    96  	http.Handle("/hello", samlSP.RequireAccount(app))
    97  	http.Handle("/saml/", samlSP)
    98  	http.ListenAndServe(":8000", nil)
    99  }
   100  ```
   101  
   102  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](https://samltest.id/), you can do something like:
   103  
   104      mdpath=saml-test-$USER-$HOST.xml
   105      curl localhost:8000/saml/metadata > $mdpath
   106  
   107  Navigate to https://samltest.id/upload.php and upload the file you fetched.
   108  
   109  Now you should be able to authenticate. The flow should look like this:
   110  
   111  1. You browse to `localhost:8000/hello`
   112  
   113  1. The middleware redirects you to `https://samltest.id/idp/profile/SAML2/Redirect/SSO`
   114  
   115  1. samltest.id prompts you for a username and password.
   116  
   117  1. 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.
   118  
   119  1. The local service validates the response, issues a session cookie, and redirects you to the original URL, `localhost:8000/hello`.
   120  
   121  1. This time when `localhost:8000/hello` is requested there is a valid session and so the main content is served.
   122  
   123  ## Getting Started as an Identity Provider
   124  
   125  Please see `example/idp/` for a substantially complete example of how to use the library and helpers to be an identity provider.
   126  
   127  ## Support
   128  
   129  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](https://kantarainitiative.github.io/SAMLprofiles/saml2int.html).
   130  
   131  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.
   132  
   133  The package can produce signed SAML assertions, and can validate both signed and encrypted SAML assertions. It does not support signed or encrypted requests.
   134  
   135  ## RelayState
   136  
   137  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.
   138  
   139  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.)
   140  
   141  ## References
   142  
   143  The SAML specification is a collection of PDFs (sadly):
   144  
   145  - [SAMLCore](http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf) defines data types.
   146  
   147  - [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.
   148  
   149  - [SAMLProfiles](http://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf) describes data flows.
   150  
   151  - [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.
   152  
   153  [SAMLtest](https://samltest.id/) is a testing ground for SAML service and identity providers.
   154  
   155  ## Security Issues
   156  
   157  Please do not report security issues in the issue tracker. Rather, please contact me directly at ross@kndr.org ([PGP Key `78B6038B3B9DFB88`](https://keybase.io/crewjam)). If your issue is *not* a security issue, please use the issue tracker so other contributors can help.