From 97a51219241882ef037af9dd2058917c33309d3e Mon Sep 17 00:00:00 2001 From: gytisrepecka Date: Sun, 28 Nov 2021 13:38:30 +0200 Subject: [PATCH] #514 - Fix login with Gitea OAuth problems: external user ID not properly recorded. --- oauth.go | 10 ++++++++-- oauth_gitea.go | 31 +++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/oauth.go b/oauth.go index 2958721..c0fbfa7 100644 --- a/oauth.go +++ b/oauth.go @@ -293,10 +293,15 @@ func configureGiteaOauth(parentHandler *Handler, r *mux.Router, app *App) { ClientID: app.Config().GiteaOauth.ClientID, ClientSecret: app.Config().GiteaOauth.ClientSecret, ExchangeLocation: app.Config().GiteaOauth.Host + "/login/oauth/access_token", - InspectLocation: app.Config().GiteaOauth.Host + "/api/v1/user", + InspectLocation: app.Config().GiteaOauth.Host + "/login/oauth/userinfo", AuthLocation: app.Config().GiteaOauth.Host + "/login/oauth/authorize", HttpClient: config.DefaultHTTPClient(), CallbackLocation: callbackLocation, + Scope: "openid profile email", + MapUserID: "sub", + MapUsername: "login", + MapDisplayName: "full_name", + MapEmail: "email", } configureOauthRoutes(parentHandler, r, app, oauthClient, callbackProxy) } @@ -355,7 +360,7 @@ func (h oauthHandler) viewOauthCallback(app *App, w http.ResponseWriter, r *http } if localUserID != -1 && attachUserID > 0 { - if err = addSessionFlash(app, w, r, "This Slack account is already attached to another user.", nil); err != nil { + if err = addSessionFlash(app, w, r, "This OAuth account is already attached to another user.", nil); err != nil { return impart.HTTPError{Status: http.StatusInternalServerError, Message: err.Error()} } return impart.HTTPError{http.StatusFound, "/me/settings"} @@ -376,6 +381,7 @@ func (h oauthHandler) viewOauthCallback(app *App, w http.ResponseWriter, r *http } if attachUserID > 0 { log.Info("attaching to user %d", attachUserID) + log.Info("OAuth userid: %s", tokenInfo.UserID) err = h.DB.RecordRemoteUserID(r.Context(), attachUserID, tokenInfo.UserID, provider, clientID, tokenResponse.AccessToken) if err != nil { return impart.HTTPError{http.StatusInternalServerError, err.Error()} diff --git a/oauth_gitea.go b/oauth_gitea.go index a9b7741..3ecd4a7 100644 --- a/oauth_gitea.go +++ b/oauth_gitea.go @@ -3,6 +3,8 @@ package writefreely import ( "context" "errors" + "fmt" + "github.com/writeas/web-core/log" "net/http" "net/url" "strings" @@ -15,6 +17,11 @@ type giteaOauthClient struct { ExchangeLocation string InspectLocation string CallbackLocation string + Scope string + MapUserID string + MapUsername string + MapDisplayName string + MapEmail string HttpClient HttpClient } @@ -46,7 +53,7 @@ func (c giteaOauthClient) buildLoginURL(state string) (string, error) { q.Set("redirect_uri", c.CallbackLocation) q.Set("response_type", "code") q.Set("state", state) - // q.Set("scope", "read_user") + q.Set("scope", c.Scope) u.RawQuery = q.Encode() return u.String(), nil } @@ -55,7 +62,7 @@ func (c giteaOauthClient) exchangeOauthCode(ctx context.Context, code string) (* form := url.Values{} form.Add("grant_type", "authorization_code") form.Add("redirect_uri", c.CallbackLocation) - // form.Add("scope", "read_user") + form.Add("scope", c.Scope) form.Add("code", code) req, err := http.NewRequest("POST", c.ExchangeLocation, strings.NewReader(form.Encode())) if err != nil { @@ -103,12 +110,24 @@ func (c giteaOauthClient) inspectOauthAccessToken(ctx context.Context, accessTok return nil, errors.New("unable to inspect access token") } - var inspectResponse InspectResponse - if err := limitedJsonUnmarshal(resp.Body, infoRequestMaxLen, &inspectResponse); err != nil { + // since we don't know what the JSON from the server will look like, we create a + // generic interface and then map manually to values set in the config + var genericInterface map[string]interface{} + if err := limitedJsonUnmarshal(resp.Body, infoRequestMaxLen, &genericInterface); err != nil { return nil, err } - if inspectResponse.Error != "" { - return nil, errors.New(inspectResponse.Error) + + // map each relevant field in inspectResponse to the mapped field from the config + var inspectResponse InspectResponse + inspectResponse.UserID, _ = genericInterface[c.MapUserID].(string) + // log.Info("Userid from Gitea: %s", inspectResponse.UserID) + if inspectResponse.UserID == "" { + log.Error("[CONFIGURATION ERROR] Gitea OAuth provider returned empty UserID value (`%s`).\n Do you need to configure a different `map_user_id` value for this provider?", c.MapUserID) + return nil, fmt.Errorf("no UserID (`%s`) value returned", c.MapUserID) } + inspectResponse.Username, _ = genericInterface[c.MapUsername].(string) + inspectResponse.DisplayName, _ = genericInterface[c.MapDisplayName].(string) + inspectResponse.Email, _ = genericInterface[c.MapEmail].(string) + return &inspectResponse, nil }