diff --git a/authz/authz.go b/authz/authz.go index 3a2ec5aa..f8c248d7 100644 --- a/authz/authz.go +++ b/authz/authz.go @@ -46,6 +46,7 @@ p, *, *, POST, /api/login, *, * p, *, *, GET, /api/get-app-login, *, * p, *, *, POST, /api/logout, *, * p, *, *, GET, /api/logout, *, * +p, *, *, POST, /api/callback, *, * p, *, *, GET, /api/get-account, *, * p, *, *, GET, /api/userinfo, *, * p, *, *, GET, /api/user, *, * diff --git a/controllers/auth.go b/controllers/auth.go index 0bfd6311..3c0cd256 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -20,6 +20,7 @@ import ( "encoding/xml" "fmt" "io/ioutil" + "net/http" "net/url" "strconv" "strings" @@ -896,3 +897,16 @@ func (c *ApiController) GetCaptchaStatus() { } c.ResponseOk(captchaEnabled) } + +// Callback +// @Title Callback +// @Tag Callback API +// @Description Get Login Error Counts +// @router /api/Callback [post] +func (c *ApiController) Callback() { + code := c.GetString("code") + state := c.GetString("state") + + frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", code, state) + c.Ctx.Redirect(http.StatusFound, frontendCallbackUrl) +} diff --git a/idp/goth.go b/idp/goth.go index b3c4849b..3ccd9e79 100644 --- a/idp/goth.go +++ b/idp/goth.go @@ -19,6 +19,7 @@ import ( "net/http" "net/url" "reflect" + "strings" "time" "github.com/casdoor/casdoor/util" @@ -97,6 +98,9 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string Session: &amazon.Session{}, } case "Apple": + if !strings.Contains(redirectUrl, "/api/callback") { + redirectUrl = strings.Replace(redirectUrl, "/callback", "/api/callback", 1) + } idp = GothIdProvider{ Provider: apple.New(clientId, clientSecret, redirectUrl, nil), Session: &apple.Session{}, @@ -392,7 +396,9 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string // Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required func (idp *GothIdProvider) SetHttpClient(client *http.Client) { idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient") - idpClient.Set(reflect.ValueOf(client)) + if idpClient.IsValid() { + idpClient.Set(reflect.ValueOf(client)) + } } func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) { @@ -468,6 +474,8 @@ func getUser(gothUser goth.User, provider string) *UserInfo { if provider == "steam" { user.Username = user.Id user.Email = "" + } else if provider == "apple" { + user.Username = util.GetUsernameFromEmail(user.Email) } return &user } diff --git a/routers/router.go b/routers/router.go index 2769d595..23b0e93f 100644 --- a/routers/router.go +++ b/routers/router.go @@ -63,6 +63,7 @@ func initAPI() { 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") + beego.Router("/api/callback", &controllers.ApiController{}, "POST:Callback") beego.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations") beego.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization") diff --git a/util/string.go b/util/string.go index fccc8848..e49c3871 100644 --- a/util/string.go +++ b/util/string.go @@ -300,3 +300,12 @@ func GetValueFromDataSourceName(key string, dataSourceName string) string { return "" } + +func GetUsernameFromEmail(email string) string { + tokens := strings.Split(email, "@") + if len(tokens) == 0 { + return uuid.NewString() + } else { + return tokens[0] + } +} diff --git a/web/src/auth/Provider.js b/web/src/auth/Provider.js index 5ee68ad2..99718840 100644 --- a/web/src/auth/Provider.js +++ b/web/src/auth/Provider.js @@ -379,7 +379,7 @@ export function getAuthUrl(application, provider, method) { } let endpoint = authInfo[provider.type].endpoint; - const redirectUri = `${window.location.origin}/callback`; + let redirectUri = `${window.location.origin}/callback`; const scope = authInfo[provider.type].scope; const isShortState = provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger"); @@ -390,6 +390,8 @@ export function getAuthUrl(application, provider, method) { if (provider.domain !== "") { endpoint = endpoint.replace("common", provider.domain); } + } else if (provider.type === "Apple") { + redirectUri = `${window.location.origin}/api/callback`; } if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook" @@ -448,7 +450,7 @@ export function getAuthUrl(application, provider, method) { } else if (provider.type === "Infoflow") { return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}?state=${state}`; } else if (provider.type === "Apple") { - return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}&response_mode=form_post`; + return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code%20id_token&scope=${scope}&response_mode=form_post`; } else if (provider.type === "Steam") { return `${endpoint}?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.realm=${window.location.origin}&openid.return_to=${redirectUri}?state=${state}`; } else if (provider.type === "Okta") {