Skip to content

Preconfigurable providers#646

Open
cxrtisxl wants to merge 3 commits into
markbates:masterfrom
cxrtisxl:pre-configurable-providers
Open

Preconfigurable providers#646
cxrtisxl wants to merge 3 commits into
markbates:masterfrom
cxrtisxl:pre-configurable-providers

Conversation

@cxrtisxl

@cxrtisxl cxrtisxl commented Jun 30, 2026

Copy link
Copy Markdown

This PR introduces ProviderConfig interface that allows to separate Provider configuration from Provider creation.

Why?
Primarly because Provider's New methods require to set CallbackURL at creation time. At complex Routers this might be set programmatically but Provider has CallbackURL unmutable after its creation. goth contains all the Providers in separate package so my decision here was to create a ProviderConfig interface and Config struct for all the providers, that will contain New method's params and will produce the corresponding Provider via ProviderConfig.Build().

The interface includes 3 methods:

  1. Name() string - returns the default Config's Provider name
  2. SetCallbackURL(string) - allows to set URL (all Providers have it)
  3. Build() Provider - runs New method passing Config data as params
type ProviderConfig interface {
	Name() string
	SetCallbackURL(string)
	Build() Provider
}

Here's how it looks for the Google Provider:

type Provider struct {
	ClientKey       string
	Secret          string
	CallbackURL     string
	HTTPClient      *http.Client
	config          *oauth2.Config
	authCodeOptions []oauth2.AuthCodeOption
	providerName    string
}

type Config struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	Scopes      []string
}

func (c *Config) Name() string {
	return "google"
}

func (c *Config) SetCallbackURL(url string) {
	c.CallbackURL = url
}

func (c *Config) Build() goth.Provider {
	return New(c.ClientKey, c.Secret, c.CallbackURL, c.Scopes...)
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return p.providerName
}

func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	// ...
}

What it allows to do - provide only configs at init time:

configs := []goth.ProviderConfig{
		&azureadv2.Config{
			ClientKey: "6731de76-14a6-49ae-97bc-6eba6914391e",
			Secret:    "foo",
		},
		&github.Config{
			ClientKey: "6731de76-14a6-49ae-97bc-6eba6914391e",
			Secret:    "foo",
		},
		&tiktok.Config{
			ClientKey: "6731de76-14a6-49ae-97bc-6eba6914391e",
			Secret:    "foo",
		},
		&apple.Config{
			ClientId: "6731de76-14a6-49ae-97bc-6eba6914391e",
			Secret:   "foo",
		},
	}

and build Providers later, let's say somewhere in oauth module:

func (oa *OAuthAuthenticator) Mount(mux *http.ServeMux, prefix string, errHandler tools.ErrorHandler) {
	providers := make([]goth.Provider, len(oa.providerConfigs))
	for i, cfg := range oa.providerConfigs {
		cfg.SetCallbackURL(oa.baseDomain + prefix + "/" + cfg.Name() + "/callback")
		providers[i] = cfg.Build()
	}
	goth.UseProviders(providers...)
	mux.Handle("GET "+prefix+"/{provider}", errHandler(tools.HandlerFunc(oa.AuthHandler)))
	mux.Handle("GET "+prefix+"/{provider}/callback", errHandler(tools.HandlerFunc(oa.CallbackHandler)))
	mux.Handle("POST "+prefix+"/logout", errHandler(tools.HandlerFunc(oa.LogoutHandler)))
}

I made a generalized test in provider_test.go. Probably can cover more providers and cases it you find it an interesting feature to add to original goth package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant