github.com/crewjam/saml@v0.4.14/samlsp/new.go (about)

     1  // Package samlsp provides helpers that can be used to protect web services using SAML.
     2  package samlsp
     3  
     4  import (
     5  	"crypto/rsa"
     6  	"crypto/x509"
     7  	"net/http"
     8  	"net/url"
     9  
    10  	dsig "github.com/russellhaering/goxmldsig"
    11  
    12  	"github.com/crewjam/saml"
    13  )
    14  
    15  // Options represents the parameters for creating a new middleware
    16  type Options struct {
    17  	EntityID              string
    18  	URL                   url.URL
    19  	Key                   *rsa.PrivateKey
    20  	Certificate           *x509.Certificate
    21  	Intermediates         []*x509.Certificate
    22  	HTTPClient            *http.Client
    23  	AllowIDPInitiated     bool
    24  	DefaultRedirectURI    string
    25  	IDPMetadata           *saml.EntityDescriptor
    26  	SignRequest           bool
    27  	UseArtifactResponse   bool
    28  	ForceAuthn            bool // TODO(ross): this should be *bool
    29  	RequestedAuthnContext *saml.RequestedAuthnContext
    30  	CookieSameSite        http.SameSite
    31  	CookieName            string
    32  	RelayStateFunc        func(w http.ResponseWriter, r *http.Request) string
    33  	LogoutBindings        []string
    34  }
    35  
    36  // DefaultSessionCodec returns the default SessionCodec for the provided options,
    37  // a JWTSessionCodec configured to issue signed tokens.
    38  func DefaultSessionCodec(opts Options) JWTSessionCodec {
    39  	return JWTSessionCodec{
    40  		SigningMethod: defaultJWTSigningMethod,
    41  		Audience:      opts.URL.String(),
    42  		Issuer:        opts.URL.String(),
    43  		MaxAge:        defaultSessionMaxAge,
    44  		Key:           opts.Key,
    45  	}
    46  }
    47  
    48  // DefaultSessionProvider returns the default SessionProvider for the provided options,
    49  // a CookieSessionProvider configured to store sessions in a cookie.
    50  func DefaultSessionProvider(opts Options) CookieSessionProvider {
    51  	cookieName := opts.CookieName
    52  	if cookieName == "" {
    53  		cookieName = defaultSessionCookieName
    54  	}
    55  	return CookieSessionProvider{
    56  		Name:     cookieName,
    57  		Domain:   opts.URL.Host,
    58  		MaxAge:   defaultSessionMaxAge,
    59  		HTTPOnly: true,
    60  		Secure:   opts.URL.Scheme == "https",
    61  		SameSite: opts.CookieSameSite,
    62  		Codec:    DefaultSessionCodec(opts),
    63  	}
    64  }
    65  
    66  // DefaultTrackedRequestCodec returns a new TrackedRequestCodec for the provided
    67  // options, a JWTTrackedRequestCodec that uses a JWT to encode TrackedRequests.
    68  func DefaultTrackedRequestCodec(opts Options) JWTTrackedRequestCodec {
    69  	return JWTTrackedRequestCodec{
    70  		SigningMethod: defaultJWTSigningMethod,
    71  		Audience:      opts.URL.String(),
    72  		Issuer:        opts.URL.String(),
    73  		MaxAge:        saml.MaxIssueDelay,
    74  		Key:           opts.Key,
    75  	}
    76  }
    77  
    78  // DefaultRequestTracker returns a new RequestTracker for the provided options,
    79  // a CookieRequestTracker which uses cookies to track pending requests.
    80  func DefaultRequestTracker(opts Options, serviceProvider *saml.ServiceProvider) CookieRequestTracker {
    81  	return CookieRequestTracker{
    82  		ServiceProvider: serviceProvider,
    83  		NamePrefix:      "saml_",
    84  		Codec:           DefaultTrackedRequestCodec(opts),
    85  		MaxAge:          saml.MaxIssueDelay,
    86  		RelayStateFunc:  opts.RelayStateFunc,
    87  		SameSite:        opts.CookieSameSite,
    88  	}
    89  }
    90  
    91  // DefaultServiceProvider returns the default saml.ServiceProvider for the provided
    92  // options.
    93  func DefaultServiceProvider(opts Options) saml.ServiceProvider {
    94  	metadataURL := opts.URL.ResolveReference(&url.URL{Path: "saml/metadata"})
    95  	acsURL := opts.URL.ResolveReference(&url.URL{Path: "saml/acs"})
    96  	sloURL := opts.URL.ResolveReference(&url.URL{Path: "saml/slo"})
    97  
    98  	var forceAuthn *bool
    99  	if opts.ForceAuthn {
   100  		forceAuthn = &opts.ForceAuthn
   101  	}
   102  	signatureMethod := dsig.RSASHA1SignatureMethod
   103  	if !opts.SignRequest {
   104  		signatureMethod = ""
   105  	}
   106  
   107  	if opts.DefaultRedirectURI == "" {
   108  		opts.DefaultRedirectURI = "/"
   109  	}
   110  
   111  	if len(opts.LogoutBindings) == 0 {
   112  		opts.LogoutBindings = []string{saml.HTTPPostBinding}
   113  	}
   114  
   115  	return saml.ServiceProvider{
   116  		EntityID:              opts.EntityID,
   117  		Key:                   opts.Key,
   118  		Certificate:           opts.Certificate,
   119  		HTTPClient:            opts.HTTPClient,
   120  		Intermediates:         opts.Intermediates,
   121  		MetadataURL:           *metadataURL,
   122  		AcsURL:                *acsURL,
   123  		SloURL:                *sloURL,
   124  		IDPMetadata:           opts.IDPMetadata,
   125  		ForceAuthn:            forceAuthn,
   126  		RequestedAuthnContext: opts.RequestedAuthnContext,
   127  		SignatureMethod:       signatureMethod,
   128  		AllowIDPInitiated:     opts.AllowIDPInitiated,
   129  		DefaultRedirectURI:    opts.DefaultRedirectURI,
   130  		LogoutBindings:        opts.LogoutBindings,
   131  	}
   132  }
   133  
   134  // New creates a new Middleware with the default providers for the
   135  // given options.
   136  //
   137  // You can customize the behavior of the middleware in more detail by
   138  // replacing and/or changing Session, RequestTracker, and ServiceProvider
   139  // in the returned Middleware.
   140  func New(opts Options) (*Middleware, error) {
   141  	m := &Middleware{
   142  		ServiceProvider: DefaultServiceProvider(opts),
   143  		Binding:         "",
   144  		ResponseBinding: saml.HTTPPostBinding,
   145  		OnError:         DefaultOnError,
   146  		Session:         DefaultSessionProvider(opts),
   147  	}
   148  	m.RequestTracker = DefaultRequestTracker(opts, &m.ServiceProvider)
   149  	if opts.UseArtifactResponse {
   150  		m.ResponseBinding = saml.HTTPArtifactBinding
   151  	}
   152  
   153  	return m, nil
   154  }