Skip to content

Commit

Permalink
Lock goth/gothic and Re-attempt OAuth2 registration on login if regis…
Browse files Browse the repository at this point in the history
…tration failed at startup (go-gitea#16564)

This PR has two parts:

* Add locking to goth and gothic calls with a RWMutex

The goth and gothic calls are currently unlocked and thus are a cause of multiple potential races

* Reattempt OAuth2 registration on login if registration failed

If OAuth2 registration fails at startup we currently disable the login_source however an alternative approach could be to reattempt registration on login attempt.
    
Fix go-gitea#16096

Signed-off-by: Andrew Thornton <[email protected]>
  • Loading branch information
zeripath authored and AbdulrhmnGhanem committed Aug 10, 2021
1 parent fe4e182 commit c9a4cbf
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 6 deletions.
17 changes: 11 additions & 6 deletions services/auth/source/oauth2/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package oauth2

import (
"net/http"
"sync"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
Expand All @@ -15,6 +16,8 @@ import (
"github.com/markbates/goth/gothic"
)

var gothRWMutex = sync.RWMutex{}

// SessionTableName is the table name that OAuth2 will use to store things
const SessionTableName = "oauth2_session"

Expand Down Expand Up @@ -42,6 +45,10 @@ func Init() error {

// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
store.MaxLength(setting.OAuth2.MaxTokenLength)

// Lock our mutex
gothRWMutex.Lock()

gothic.Store = store

gothic.SetState = func(req *http.Request) string {
Expand All @@ -52,6 +59,9 @@ func Init() error {
return req.Header.Get(ProviderHeaderKey), nil
}

// Unlock our mutex
gothRWMutex.Unlock()

return initOAuth2LoginSources()
}

Expand All @@ -71,12 +81,7 @@ func initOAuth2LoginSources() error {
}
err := oauth2Source.RegisterSource()
if err != nil {
log.Critical("Unable to register source: %s due to Error: %v. This source will be disabled.", source.Name, err)
source.IsActive = false
if err = models.UpdateSource(source); err != nil {
log.Critical("Unable to update source %s to disable it. Error: %v", err)
return err
}
log.Critical("Unable to register source: %s due to Error: %v.", source.Name, err)
}
}
return nil
Expand Down
9 changes: 9 additions & 0 deletions services/auth/source/oauth2/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID
provider, err := createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL, customURLMapping)

if err == nil && provider != nil {
gothRWMutex.Lock()
defer gothRWMutex.Unlock()

goth.UseProviders(provider)
}

Expand All @@ -129,11 +132,17 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID

// RemoveProvider removes the given OAuth2 provider from the goth lib
func RemoveProvider(providerName string) {
gothRWMutex.Lock()
defer gothRWMutex.Unlock()

delete(goth.GetProviders(), providerName)
}

// ClearProviders clears all OAuth2 providers from the goth lib
func ClearProviders() {
gothRWMutex.Lock()
defer gothRWMutex.Unlock()

goth.ClearProviders()
}

Expand Down
6 changes: 6 additions & 0 deletions services/auth/source/oauth2/source_callout.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func (source *Source) Callout(request *http.Request, response http.ResponseWrite
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
//gothic.BeginAuthHandler(response, request)

gothRWMutex.RLock()
defer gothRWMutex.RUnlock()

url, err := gothic.GetAuthURL(response, request)
if err == nil {
http.Redirect(response, request, url, http.StatusTemporaryRedirect)
Expand All @@ -33,6 +36,9 @@ func (source *Source) Callback(request *http.Request, response http.ResponseWrit
// not sure if goth is thread safe (?) when using multiple providers
request.Header.Set(ProviderHeaderKey, source.loginSource.Name)

gothRWMutex.RLock()
defer gothRWMutex.RUnlock()

user, err := gothic.CompleteUserAuth(response, request)
if err != nil {
return user, err
Expand Down

0 comments on commit c9a4cbf

Please sign in to comment.