mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:27:42 +00:00
feat: saml application configuration for login version (#9351)
# Which Problems Are Solved OIDC applications can configure the used login version, which is currently not possible for SAML applications. # How the Problems Are Solved Add the same functionality dependent on the feature-flag for SAML applications. # Additional Changes None # Additional Context Closes #9267 Follow up issue for frontend changes #9354 --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
53
internal/api/saml/serviceprovider.go
Normal file
53
internal/api/saml/serviceprovider.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package saml
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/saml/pkg/provider/serviceprovider"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
const (
|
||||
LoginSamlRequestParam = "samlRequest"
|
||||
LoginPath = "/login"
|
||||
)
|
||||
|
||||
type ServiceProvider struct {
|
||||
SP *query.SAMLServiceProvider
|
||||
defaultLoginURL string
|
||||
defaultLoginURLV2 string
|
||||
}
|
||||
|
||||
func ServiceProviderFromBusiness(spQuery *query.SAMLServiceProvider, defaultLoginURL, defaultLoginURLV2 string) (*serviceprovider.ServiceProvider, error) {
|
||||
sp := &ServiceProvider{
|
||||
SP: spQuery,
|
||||
defaultLoginURL: defaultLoginURL,
|
||||
defaultLoginURLV2: defaultLoginURLV2,
|
||||
}
|
||||
|
||||
return serviceprovider.NewServiceProvider(
|
||||
spQuery.AppID,
|
||||
&serviceprovider.Config{Metadata: spQuery.Metadata},
|
||||
sp.LoginURL,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *ServiceProvider) LoginURL(id string) string {
|
||||
// if the authRequest does not have the v2 prefix, it was created for login V1
|
||||
if !strings.HasPrefix(id, command.IDPrefixV2) {
|
||||
return s.defaultLoginURL + id
|
||||
}
|
||||
// any v2 login without a specific base uri will be sent to the configured login v2 UI
|
||||
// this way we're also backwards compatible
|
||||
if s.SP.LoginBaseURI == nil || s.SP.LoginBaseURI.String() == "" {
|
||||
return s.defaultLoginURLV2 + id
|
||||
}
|
||||
// for clients with a specific URI (internal or external) we only need to add the auth request id
|
||||
uri := s.SP.LoginBaseURI.JoinPath(LoginPath)
|
||||
q := uri.Query()
|
||||
q.Set(LoginSamlRequestParam, id)
|
||||
uri.RawQuery = q.Encode()
|
||||
return uri.String()
|
||||
}
|
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/actions/object"
|
||||
"github.com/zitadel/zitadel/internal/activity"
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||
"github.com/zitadel/zitadel/internal/auth/repository"
|
||||
@@ -62,22 +63,12 @@ type Storage struct {
|
||||
}
|
||||
|
||||
func (p *Storage) GetEntityByID(ctx context.Context, entityID string) (*serviceprovider.ServiceProvider, error) {
|
||||
app, err := p.query.ActiveAppBySAMLEntityID(ctx, entityID)
|
||||
sp, err := p.query.ActiveSAMLServiceProviderByID(ctx, entityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return serviceprovider.NewServiceProvider(
|
||||
app.ID,
|
||||
&serviceprovider.Config{
|
||||
Metadata: app.SAMLConfig.Metadata,
|
||||
},
|
||||
func(id string) string {
|
||||
if strings.HasPrefix(id, command.IDPrefixV2) {
|
||||
return p.defaultLoginURLv2 + id
|
||||
}
|
||||
return p.defaultLoginURL + id
|
||||
},
|
||||
)
|
||||
|
||||
return ServiceProviderFromBusiness(sp, p.defaultLoginURL, p.defaultLoginURLv2)
|
||||
}
|
||||
|
||||
func (p *Storage) GetEntityIDByAppID(ctx context.Context, appID string) (string, error) {
|
||||
@@ -108,11 +99,34 @@ func (p *Storage) CreateAuthRequest(ctx context.Context, req *samlp.AuthnRequest
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
// for backwards compatibility we pass the login client if set
|
||||
headers, _ := http_utils.HeadersFromCtx(ctx)
|
||||
if loginClient := headers.Get(LoginClientHeader); loginClient != "" {
|
||||
loginClient := headers.Get(LoginClientHeader)
|
||||
|
||||
// for backwards compatibility we'll use the new login if the header is set (no matter the other configs)
|
||||
if loginClient != "" {
|
||||
return p.createAuthRequestLoginClient(ctx, req, acsUrl, protocolBinding, relayState, applicationID, loginClient)
|
||||
}
|
||||
return p.createAuthRequest(ctx, req, acsUrl, protocolBinding, relayState, applicationID)
|
||||
|
||||
// if the instance requires the v2 login, use it no matter what the application configured
|
||||
if authz.GetFeatures(ctx).LoginV2.Required {
|
||||
return p.createAuthRequestLoginClient(ctx, req, acsUrl, protocolBinding, relayState, applicationID, loginClient)
|
||||
}
|
||||
version, err := p.query.SAMLAppLoginVersion(ctx, applicationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch version {
|
||||
case domain.LoginVersion1:
|
||||
return p.createAuthRequest(ctx, req, acsUrl, protocolBinding, relayState, applicationID)
|
||||
case domain.LoginVersion2:
|
||||
return p.createAuthRequestLoginClient(ctx, req, acsUrl, protocolBinding, relayState, applicationID, loginClient)
|
||||
case domain.LoginVersionUnspecified:
|
||||
fallthrough
|
||||
default:
|
||||
// since we already checked for a login header, we can fall back to the v1 login
|
||||
return p.createAuthRequest(ctx, req, acsUrl, protocolBinding, relayState, applicationID)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Storage) createAuthRequestLoginClient(ctx context.Context, req *samlp.AuthnRequestType, acsUrl, protocolBinding, relayState, applicationID, loginClient string) (_ models.AuthRequestInt, err error) {
|
||||
|
Reference in New Issue
Block a user