diff --git a/authz/authz.go b/authz/authz.go
index c437954c..e2ae4144 100644
--- a/authz/authz.go
+++ b/authz/authz.go
@@ -80,6 +80,7 @@ p, *, *, *, /.well-known/jwks, *, *
p, *, *, GET, /api/get-saml-login, *, *
p, *, *, POST, /api/acs, *, *
p, *, *, GET, /api/saml/metadata, *, *
+p, *, *, *, /api/saml/redirect, *, *
p, *, *, *, /cas, *, *
p, *, *, *, /scim, *, *
p, *, *, *, /api/webauthn, *, *
diff --git a/controllers/auth.go b/controllers/auth.go
index e5541f77..50a04e8d 100644
--- a/controllers/auth.go
+++ b/controllers/auth.go
@@ -912,7 +912,7 @@ func (c *ApiController) HandleSamlLogin() {
samlResponse = url.QueryEscape(samlResponse)
targetUrl := fmt.Sprintf("%s?relayState=%s&samlResponse=%s",
slice[4], relayState, samlResponse)
- c.Redirect(targetUrl, 303)
+ c.Redirect(targetUrl, http.StatusSeeOther)
}
// HandleOfficialAccountEvent ...
diff --git a/controllers/saml.go b/controllers/saml.go
index 8d7eeb26..070bc2f8 100644
--- a/controllers/saml.go
+++ b/controllers/saml.go
@@ -16,6 +16,7 @@ package controllers
import (
"fmt"
+ "net/http"
"github.com/casdoor/casdoor/object"
)
@@ -34,7 +35,13 @@ func (c *ApiController) GetSamlMeta() {
return
}
- metadata, err := object.GetSamlMeta(application, host)
+ enablePostBinding, err := c.GetBool("enablePostBinding", false)
+ if err != nil {
+ c.ResponseError(err.Error())
+ return
+ }
+
+ metadata, err := object.GetSamlMeta(application, host, enablePostBinding)
if err != nil {
c.ResponseError(err.Error())
return
@@ -43,3 +50,17 @@ func (c *ApiController) GetSamlMeta() {
c.Data["xml"] = metadata
c.ServeXML()
}
+
+func (c *ApiController) HandleSamlRedirect() {
+ host := c.Ctx.Request.Host
+
+ owner := c.Ctx.Input.Param(":owner")
+ application := c.Ctx.Input.Param(":application")
+
+ relayState := c.Input().Get("RelayState")
+ samlRequest := c.Input().Get("SAMLRequest")
+
+ targetURL := object.GetSamlRedirectAddress(owner, application, relayState, samlRequest, host)
+
+ c.Redirect(targetURL, http.StatusSeeOther)
+}
diff --git a/object/application.go b/object/application.go
index 1c80d785..d85d5974 100644
--- a/object/application.go
+++ b/object/application.go
@@ -52,31 +52,32 @@ type Application struct {
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
- DisplayName string `xorm:"varchar(100)" json:"displayName"`
- Logo string `xorm:"varchar(200)" json:"logo"`
- HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
- Description string `xorm:"varchar(100)" json:"description"`
- Organization string `xorm:"varchar(100)" json:"organization"`
- Cert string `xorm:"varchar(100)" json:"cert"`
- EnablePassword bool `json:"enablePassword"`
- EnableSignUp bool `json:"enableSignUp"`
- EnableSigninSession bool `json:"enableSigninSession"`
- EnableAutoSignin bool `json:"enableAutoSignin"`
- EnableCodeSignin bool `json:"enableCodeSignin"`
- EnableSamlCompress bool `json:"enableSamlCompress"`
- EnableSamlC14n10 bool `json:"enableSamlC14n10"`
- EnableWebAuthn bool `json:"enableWebAuthn"`
- EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
- OrgChoiceMode string `json:"orgChoiceMode"`
- SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
- Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
- SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
- SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
- GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
- OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
- CertPublicKey string `xorm:"-" json:"certPublicKey"`
- Tags []string `xorm:"mediumtext" json:"tags"`
- SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
+ DisplayName string `xorm:"varchar(100)" json:"displayName"`
+ Logo string `xorm:"varchar(200)" json:"logo"`
+ HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
+ Description string `xorm:"varchar(100)" json:"description"`
+ Organization string `xorm:"varchar(100)" json:"organization"`
+ Cert string `xorm:"varchar(100)" json:"cert"`
+ EnablePassword bool `json:"enablePassword"`
+ EnableSignUp bool `json:"enableSignUp"`
+ EnableSigninSession bool `json:"enableSigninSession"`
+ EnableAutoSignin bool `json:"enableAutoSignin"`
+ EnableCodeSignin bool `json:"enableCodeSignin"`
+ EnableSamlCompress bool `json:"enableSamlCompress"`
+ EnableSamlC14n10 bool `json:"enableSamlC14n10"`
+ EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
+ EnableWebAuthn bool `json:"enableWebAuthn"`
+ EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
+ OrgChoiceMode string `json:"orgChoiceMode"`
+ SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
+ Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
+ SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
+ SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
+ GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
+ OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
+ CertPublicKey string `xorm:"-" json:"certPublicKey"`
+ Tags []string `xorm:"mediumtext" json:"tags"`
+ SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
diff --git a/object/cert.go~ b/object/cert.go~
deleted file mode 100644
index c38896e8..00000000
Binary files a/object/cert.go~ and /dev/null differ
diff --git a/object/saml_idp.go b/object/saml_idp.go
index f908d9bb..b318a96b 100644
--- a/object/saml_idp.go
+++ b/object/saml_idp.go
@@ -198,7 +198,7 @@ type Attribute struct {
Values []string `xml:"AttributeValue"`
}
-func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) {
+func GetSamlMeta(application *Application, host string, enablePostBinding bool) (*IdpEntityDescriptor, error) {
cert, err := getCertByApplication(application)
if err != nil {
return nil, err
@@ -217,6 +217,13 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
originFrontend, originBackend := getOriginFromHost(host)
+ idpLocation := ""
+ if enablePostBinding {
+ idpLocation = fmt.Sprintf("%s/api/saml/redirect/%s/%s", originBackend, application.Owner, application.Name)
+ } else {
+ idpLocation = fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name)
+ }
+
d := IdpEntityDescriptor{
XMLName: xml.Name{
Local: "md:EntityDescriptor",
@@ -248,7 +255,7 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
},
SingleSignOnService: SingleSignOnService{
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
- Location: fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name),
+ Location: idpLocation,
},
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
},
@@ -442,3 +449,8 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
return samlResponse
}
+
+func GetSamlRedirectAddress(owner string, application string, relayState string, samlRequest string, host string) string {
+ originF, _ := getOriginFromHost(host)
+ return fmt.Sprintf("%s/login/saml/authorize/%s/%s?relayState=%s&samlRequest=%s", originF, owner, application, relayState, samlRequest)
+}
diff --git a/routers/authz_filter.go b/routers/authz_filter.go
index 32994537..3ee30050 100644
--- a/routers/authz_filter.go
+++ b/routers/authz_filter.go
@@ -164,6 +164,10 @@ func getUrlPath(urlPath string) string {
return "/api/webauthn"
}
+ if strings.HasPrefix(urlPath, "/api/saml/redirect") {
+ return "/api/saml/redirect"
+ }
+
return urlPath
}
diff --git a/routers/router.go b/routers/router.go
index 7c97f7c3..dbf8658c 100644
--- a/routers/router.go
+++ b/routers/router.go
@@ -60,6 +60,7 @@ func initAPI() {
beego.Router("/api/get-saml-login", &controllers.ApiController{}, "GET:GetSamlLogin")
beego.Router("/api/acs", &controllers.ApiController{}, "POST:HandleSamlLogin")
beego.Router("/api/saml/metadata", &controllers.ApiController{}, "GET:GetSamlMeta")
+ beego.Router("/api/saml/redirect/:owner/:application", &controllers.ApiController{}, "*:HandleSamlRedirect")
beego.Router("/api/webhook", &controllers.ApiController{}, "POST:HandleOfficialAccountEvent")
beego.Router("/api/get-webhook-event", &controllers.ApiController{}, "GET:GetWebhookEventType")
beego.Router("/api/get-captcha-status", &controllers.ApiController{}, "GET:GetCaptchaStatus")
diff --git a/swagger/swagger.json b/swagger/swagger.json
index 1f9d9c1d..b54d5f8f 100644
--- a/swagger/swagger.json
+++ b/swagger/swagger.json
@@ -5592,6 +5592,9 @@
"enableSamlCompress": {
"type": "boolean"
},
+ "enableSamlPostBinding": {
+ "type": "boolean"
+ },
"enableSignUp": {
"type": "boolean"
},
diff --git a/web/src/ApplicationEditPage.js b/web/src/ApplicationEditPage.js
index 9c47dcdb..ad92e4f8 100644
--- a/web/src/ApplicationEditPage.js
+++ b/web/src/ApplicationEditPage.js
@@ -116,7 +116,6 @@ class ApplicationEditPage extends React.Component {
this.getApplication();
this.getOrganizations();
this.getProviders();
- this.getSamlMetadata();
}
getApplication() {
@@ -146,6 +145,8 @@ class ApplicationEditPage extends React.Component {
});
this.getCerts(application.organization);
+
+ this.getSamlMetadata(application.enableSamlPostBinding);
});
}
@@ -186,8 +187,8 @@ class ApplicationEditPage extends React.Component {
});
}
- getSamlMetadata() {
- ApplicationBackend.getSamlMetadata("admin", this.state.applicationName)
+ getSamlMetadata(checked) {
+ ApplicationBackend.getSamlMetadata("admin", this.state.applicationName, checked)
.then((data) => {
this.setState({
samlMetadata: data,
@@ -663,6 +664,17 @@ class ApplicationEditPage extends React.Component {
}} />
+
+
+ {Setting.getLabel(i18next.t("application:Enable SAML POST binding"), i18next.t("application:Enable SAML POST binding - Tooltip"))} :
+
+
+ {
+ this.updateApplicationField("enableSamlPostBinding", checked);
+ this.getSamlMetadata(checked);
+ }} />
+
+
{Setting.getLabel(i18next.t("general:SAML attributes"), i18next.t("general:SAML attributes - Tooltip"))} :
@@ -688,7 +700,7 @@ class ApplicationEditPage extends React.Component {
/>
} onClick={() => {
- copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}`);
+ copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}&post=${this.state.application.enableSamlPostBinding}`);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}}
>
diff --git a/web/src/backend/ApplicationBackend.js b/web/src/backend/ApplicationBackend.js
index ed49b6fe..fad91900 100644
--- a/web/src/backend/ApplicationBackend.js
+++ b/web/src/backend/ApplicationBackend.js
@@ -89,8 +89,8 @@ export function deleteApplication(application) {
}).then(res => res.json());
}
-export function getSamlMetadata(owner, name) {
- return fetch(`${Setting.ServerUrl}/api/saml/metadata?application=${owner}/${encodeURIComponent(name)}`, {
+export function getSamlMetadata(owner, name, enablePostBinding) {
+ return fetch(`${Setting.ServerUrl}/api/saml/metadata?application=${owner}/${encodeURIComponent(name)}&enablePostBinding=${enablePostBinding}`, {
method: "GET",
credentials: "include",
headers: {
diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json
index 1c2d6334..d4dd6cfe 100644
--- a/web/src/locales/en/data.json
+++ b/web/src/locales/en/data.json
@@ -36,6 +36,8 @@
"Enable SAML C14N10 - Tooltip": "Use C14N10 instead of C14N11 in SAML",
"Enable SAML compression": "Enable SAML compression",
"Enable SAML compression - Tooltip": "Whether to compress SAML response messages when Casdoor is used as SAML idp",
+ "Enable SAML POST binding": "Enable SAML POST binding",
+ "Enable SAML POST binding - Tooltip": "The HTTP POST binding uses input fields in a HTML form to send SAML messages, Enable when your SP use it",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Whether Casdoor maintains a session after logging into Casdoor from the application",
"Enable signup": "Enable signup",
diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json
index b5f05310..288c76d6 100644
--- a/web/src/locales/zh/data.json
+++ b/web/src/locales/zh/data.json
@@ -36,6 +36,8 @@
"Enable SAML C14N10 - Tooltip": "在SAML协议里使用C14N10,而不是C14N11",
"Enable SAML compression": "压缩SAML响应",
"Enable SAML compression - Tooltip": "Casdoor作为SAML IdP时,是否压缩SAML响应信息",
+ "Enable SAML POST binding": "启用SAML POST Binding",
+ "Enable SAML POST binding - Tooltip": "HTTP POST绑定使用HTML表单中的输入字段发送SAML消息,当SP使用它时启用",
"Enable side panel": "启用侧面板",
"Enable signin session - Tooltip": "从应用登录Casdoor后,Casdoor是否保持会话",
"Enable signup": "启用注册",