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 }