From e719a1a456507ece47aa5eba173ff4c171c0f34e Mon Sep 17 00:00:00 2001 From: Andy Hsu Date: Mon, 2 Oct 2023 14:42:40 +0800 Subject: [PATCH] feat(sso): custom username key for `OIDC` (close #5169) --- internal/bootstrap/data/setting.go | 1 + internal/conf/const.go | 1 + server/handles/ssologin.go | 44 ++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/internal/bootstrap/data/setting.go b/internal/bootstrap/data/setting.go index 14be12f2..ca17b6b1 100644 --- a/internal/bootstrap/data/setting.go +++ b/internal/bootstrap/data/setting.go @@ -158,6 +158,7 @@ func InitialSettings() []model.SettingItem { {Key: conf.SSOLoginPlatform, Type: conf.TypeSelect, Options: "Casdoor,Github,Microsoft,Google,Dingtalk,OIDC", Group: model.SSO, Flag: model.PUBLIC}, {Key: conf.SSOClientId, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE}, {Key: conf.SSOClientSecret, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE}, + {Key: conf.SSOOIDCUsernameKey, Value: "name", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE}, {Key: conf.SSOOrganizationName, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE}, {Key: conf.SSOApplicationName, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE}, {Key: conf.SSOEndpointName, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE}, diff --git a/internal/conf/const.go b/internal/conf/const.go index 2876bdef..02a00060 100644 --- a/internal/conf/const.go +++ b/internal/conf/const.go @@ -62,6 +62,7 @@ const ( SSOClientSecret = "sso_client_secret" SSOLoginEnabled = "sso_login_enabled" SSOLoginPlatform = "sso_login_platform" + SSOOIDCUsernameKey = "sso_oidc_username_key" SSOOrganizationName = "sso_organization_name" SSOApplicationName = "sso_application_name" SSOEndpointName = "sso_endpoint_name" diff --git a/server/handles/ssologin.go b/server/handles/ssologin.go index 236ebf67..89d8ecaf 100644 --- a/server/handles/ssologin.go +++ b/server/handles/ssologin.go @@ -2,6 +2,7 @@ package handles import ( "encoding/base32" + "encoding/base64" "errors" "fmt" "net/http" @@ -166,10 +167,22 @@ func autoRegister(username, userID string, err error) (*model.User, error) { return user, nil } +func parseJWT(p string) ([]byte, error) { + parts := strings.Split(p, ".") + if len(parts) < 2 { + return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts)) + } + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err) + } + return payload, nil +} + func OIDCLoginCallback(c *gin.Context) { - usecompatibility := setting.GetBool(conf.SSOCompatibilityMode) + useCompatibility := setting.GetBool(conf.SSOCompatibilityMode) argument := c.Query("method") - if usecompatibility { + if useCompatibility { argument = path.Base(c.Request.URL.Path) } clientId := setting.GetStr(conf.SSOClientId) @@ -208,23 +221,24 @@ func OIDCLoginCallback(c *gin.Context) { verifier := provider.Verifier(&oidc.Config{ ClientID: clientId, }) - idToken, err := verifier.Verify(c, rawIDToken) + _, err = verifier.Verify(c, rawIDToken) if err != nil { common.ErrorResp(c, err, 400) return } - type UserInfo struct { - Name string `json:"name"` - } - claims := UserInfo{} - if err := idToken.Claims(&claims); err != nil { + payload, err := parseJWT(rawIDToken) + if err != nil { common.ErrorResp(c, err, 400) return } - UserID := claims.Name + userID := utils.Json.Get(payload, conf.SSOOIDCUsernameKey).ToString() + if userID == "" { + common.ErrorStrResp(c, "cannot get username from OIDC provider", 400) + return + } if argument == "get_sso_id" { - if usecompatibility { - c.Redirect(302, common.GetApiUrl(c.Request)+"/@manage?sso_id="+UserID) + if useCompatibility { + c.Redirect(302, common.GetApiUrl(c.Request)+"/@manage?sso_id="+userID) return } html := fmt.Sprintf(` @@ -234,14 +248,14 @@ func OIDCLoginCallback(c *gin.Context) { window.opener.postMessage({"sso_id": "%s"}, "*") window.close() - `, UserID) + `, userID) c.Data(200, "text/html; charset=utf-8", []byte(html)) return } if argument == "sso_get_token" { - user, err := db.GetUserBySSOID(UserID) + user, err := db.GetUserBySSOID(userID) if err != nil { - user, err = autoRegister(UserID, UserID, err) + user, err = autoRegister(userID, userID, err) if err != nil { common.ErrorResp(c, err, 400) } @@ -250,7 +264,7 @@ func OIDCLoginCallback(c *gin.Context) { if err != nil { common.ErrorResp(c, err, 400) } - if usecompatibility { + if useCompatibility { c.Redirect(302, common.GetApiUrl(c.Request)+"/@login?token="+token) return }