diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 1d630fba526c0..d9206c5ae7c7b 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -3626,15 +3626,20 @@ func (a *ServerWithRoles) CreateSAMLAuthRequest(ctx context.Context, req types.S } // ValidateSAMLResponse validates SAML auth response. -func (a *ServerWithRoles) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) { +func (a *ServerWithRoles) ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) { + isProxy := a.hasBuiltinRole(types.RoleProxy) + if !isProxy { + clientIP = "" // We only trust IP information coming from the Proxy. + } + // auth callback is it's own authz, no need to check extra permissions - resp, err := a.authServer.ValidateSAMLResponse(ctx, re, connectorID) + resp, err := a.authServer.ValidateSAMLResponse(ctx, samlResponse, connectorID, clientIP) if err != nil { return nil, trace.Wrap(err) } // Only the Proxy service can create web sessions via SAML connector. - if resp.Session != nil && !a.hasBuiltinRole(types.RoleProxy) { + if resp.Session != nil && !isProxy { return nil, trace.AccessDenied("this request can be only executed by a proxy") } diff --git a/lib/auth/clt.go b/lib/auth/clt.go index 4633c9632f1df..23d820f5ca499 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -513,7 +513,7 @@ type IdentityService interface { // CreateSAMLAuthRequest creates SAML AuthnRequest CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error) // ValidateSAMLResponse validates SAML auth response - ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) + ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) // GetSAMLAuthRequest returns SAML auth request if found GetSAMLAuthRequest(ctx context.Context, authRequestID string) (*types.SAMLAuthRequest, error) diff --git a/lib/auth/http_client.go b/lib/auth/http_client.go index 3a74c7e593801..b81077e9e0f1b 100644 --- a/lib/auth/http_client.go +++ b/lib/auth/http_client.go @@ -810,10 +810,11 @@ func (c *HTTPClient) ValidateOIDCAuthCallback(ctx context.Context, q url.Values) } // ValidateSAMLResponse validates response returned by SAML identity provider -func (c *HTTPClient) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) { +func (c *HTTPClient) ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) { out, err := c.PostJSON(ctx, c.Endpoint("saml", "requests", "validate"), ValidateSAMLResponseReq{ - Response: re, + Response: samlResponse, ConnectorID: connectorID, + ClientIP: clientIP, }) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/auth/saml.go b/lib/auth/saml.go index c629414b779e7..9dc9861dc4c45 100644 --- a/lib/auth/saml.go +++ b/lib/auth/saml.go @@ -45,7 +45,7 @@ type SAMLService interface { // CreateSAMLAuthRequest creates SAML AuthnRequest CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error) // ValidateSAMLResponse validates SAML auth response - ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) + ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) } // UpsertSAMLConnector creates or updates a SAML connector. @@ -109,12 +109,12 @@ func (a *Server) CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRe // ValidateSAMLResponse delegates the method call to the samlAuthService if present, // or returns a NotImplemented error if not present. -func (a *Server) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) { +func (a *Server) ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) { if a.samlAuthService == nil { return nil, trace.Wrap(ErrSAMLRequiresEnterprise) } - resp, err := a.samlAuthService.ValidateSAMLResponse(ctx, re, connectorID) + resp, err := a.samlAuthService.ValidateSAMLResponse(ctx, samlResponse, connectorID, clientIP) return resp, trace.Wrap(err) } @@ -159,8 +159,13 @@ type SAMLAuthRequest struct { // ValidateSAMLResponseReq is the request made by the proxy to validate // and activate a login via SAML. type ValidateSAMLResponseReq struct { - Response string `json:"response"` + // Response is SAML statements coming from the identity provider. + Response string `json:"response"` + // ConnectorID is ID of a SAML connector that should be used for this request. ConnectorID string `json:"connector_id,omitempty"` + // ClientIP is IP of the logging in client, used in identity provider initiated login case, + // when we don't have original client's request with their IP stored. + ClientIP string `json:"client_ip,omitempty"` } // SAMLAuthRawResponse is returned when auth server validated callback parameters