summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2021-08-06 02:11:08 +0100
committerGitHub <noreply@github.com>2021-08-05 21:11:08 -0400
commitab9bb54144f136bbbba2ce2e94fd88c0be0ee1cf (patch)
treef8b283e33e70cbc3d827879c4774de2b41450ab0 /services
parent7e7006e00d8d0d5ce4c871685d421269049e4b39 (diff)
downloadgitea-ab9bb54144f136bbbba2ce2e94fd88c0be0ee1cf.tar.gz
gitea-ab9bb54144f136bbbba2ce2e94fd88c0be0ee1cf.zip
Add microsoft oauth2 providers (#16544)
* Clean up oauth2 providers Signed-off-by: Andrew Thornton <art27@cantab.net> * Add AzureAD, AzureADv2, MicrosoftOnline OAuth2 providers Signed-off-by: Andrew Thornton <art27@cantab.net> * Apply suggestions from code review * remove unused Scopes Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'services')
-rw-r--r--services/auth/source/oauth2/providers.go252
-rw-r--r--services/auth/source/oauth2/providers_base.go33
-rw-r--r--services/auth/source/oauth2/providers_custom.go118
-rw-r--r--services/auth/source/oauth2/providers_openid.go52
-rw-r--r--services/auth/source/oauth2/providers_simple.go108
-rw-r--r--services/auth/source/oauth2/source_name.go19
-rw-r--r--services/auth/source/oauth2/source_register.go4
-rw-r--r--services/auth/source/oauth2/urlmapping.go80
-rw-r--r--services/forms/auth_form.go1
9 files changed, 475 insertions, 192 deletions
diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go
index 8df8d62961..2196e30492 100644
--- a/services/auth/source/oauth2/providers.go
+++ b/services/auth/source/oauth2/providers.go
@@ -13,80 +13,72 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/markbates/goth"
- "github.com/markbates/goth/providers/bitbucket"
- "github.com/markbates/goth/providers/discord"
- "github.com/markbates/goth/providers/dropbox"
- "github.com/markbates/goth/providers/facebook"
- "github.com/markbates/goth/providers/gitea"
- "github.com/markbates/goth/providers/github"
- "github.com/markbates/goth/providers/gitlab"
- "github.com/markbates/goth/providers/google"
- "github.com/markbates/goth/providers/mastodon"
- "github.com/markbates/goth/providers/nextcloud"
- "github.com/markbates/goth/providers/openidConnect"
- "github.com/markbates/goth/providers/twitter"
- "github.com/markbates/goth/providers/yandex"
)
-// Provider describes the display values of a single OAuth2 provider
-type Provider struct {
- Name string
- DisplayName string
- Image string
- CustomURLMapping *CustomURLMapping
+// Provider is an interface for describing a single OAuth2 provider
+type Provider interface {
+ Name() string
+ DisplayName() string
+ Image() string
+ CustomURLSettings() *CustomURLSettings
+}
+
+// GothProviderCreator provides a function to create a goth.Provider
+type GothProviderCreator interface {
+ CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error)
+}
+
+// GothProvider is an interface for describing a single OAuth2 provider
+type GothProvider interface {
+ Provider
+ GothProviderCreator
+}
+
+// ImagedProvider provide an overrided image setting for the provider
+type ImagedProvider struct {
+ GothProvider
+ image string
+}
+
+// Image returns the image path for this provider
+func (i *ImagedProvider) Image() string {
+ return i.image
+}
+
+// NewImagedProvider is a constructor function for the ImagedProvider
+func NewImagedProvider(image string, provider GothProvider) *ImagedProvider {
+ return &ImagedProvider{
+ GothProvider: provider,
+ image: image,
+ }
}
// Providers contains the map of registered OAuth2 providers in Gitea (based on goth)
// key is used to map the OAuth2Provider with the goth provider type (also in LoginSource.OAuth2Config.Provider)
// value is used to store display data
-var Providers = map[string]Provider{
- "bitbucket": {Name: "bitbucket", DisplayName: "Bitbucket", Image: "/assets/img/auth/bitbucket.png"},
- "dropbox": {Name: "dropbox", DisplayName: "Dropbox", Image: "/assets/img/auth/dropbox.png"},
- "facebook": {Name: "facebook", DisplayName: "Facebook", Image: "/assets/img/auth/facebook.png"},
- "github": {
- Name: "github", DisplayName: "GitHub", Image: "/assets/img/auth/github.png",
- CustomURLMapping: &CustomURLMapping{
- TokenURL: github.TokenURL,
- AuthURL: github.AuthURL,
- ProfileURL: github.ProfileURL,
- EmailURL: github.EmailURL,
- },
- },
- "gitlab": {
- Name: "gitlab", DisplayName: "GitLab", Image: "/assets/img/auth/gitlab.png",
- CustomURLMapping: &CustomURLMapping{
- TokenURL: gitlab.TokenURL,
- AuthURL: gitlab.AuthURL,
- ProfileURL: gitlab.ProfileURL,
- },
- },
- "gplus": {Name: "gplus", DisplayName: "Google", Image: "/assets/img/auth/google.png"},
- "openidConnect": {Name: "openidConnect", DisplayName: "OpenID Connect", Image: "/assets/img/auth/openid_connect.svg"},
- "twitter": {Name: "twitter", DisplayName: "Twitter", Image: "/assets/img/auth/twitter.png"},
- "discord": {Name: "discord", DisplayName: "Discord", Image: "/assets/img/auth/discord.png"},
- "gitea": {
- Name: "gitea", DisplayName: "Gitea", Image: "/assets/img/auth/gitea.png",
- CustomURLMapping: &CustomURLMapping{
- TokenURL: gitea.TokenURL,
- AuthURL: gitea.AuthURL,
- ProfileURL: gitea.ProfileURL,
- },
- },
- "nextcloud": {
- Name: "nextcloud", DisplayName: "Nextcloud", Image: "/assets/img/auth/nextcloud.png",
- CustomURLMapping: &CustomURLMapping{
- TokenURL: nextcloud.TokenURL,
- AuthURL: nextcloud.AuthURL,
- ProfileURL: nextcloud.ProfileURL,
- },
- },
- "yandex": {Name: "yandex", DisplayName: "Yandex", Image: "/assets/img/auth/yandex.png"},
- "mastodon": {
- Name: "mastodon", DisplayName: "Mastodon", Image: "/assets/img/auth/mastodon.png",
- CustomURLMapping: &CustomURLMapping{
- AuthURL: mastodon.InstanceURL,
- },
- },
+var gothProviders = map[string]GothProvider{}
+
+// RegisterGothProvider registers a GothProvider
+func RegisterGothProvider(provider GothProvider) {
+ if _, has := gothProviders[provider.Name()]; has {
+ log.Fatal("Duplicate oauth2provider type provided: %s", provider.Name())
+ }
+ gothProviders[provider.Name()] = provider
+}
+
+// GetOAuth2Providers returns the map of unconfigured OAuth2 providers
+// key is used as technical name (like in the callbackURL)
+// values to display
+func GetOAuth2Providers() []Provider {
+ providers := make([]Provider, 0, len(gothProviders))
+
+ for _, provider := range gothProviders {
+ providers = append(providers, provider)
+ }
+ sort.Slice(providers, func(i, j int) bool {
+ return providers[i].Name() < providers[j].Name()
+ })
+ return providers
}
// GetActiveOAuth2Providers returns the map of configured active OAuth2 providers
@@ -103,9 +95,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]Provider, error) {
var orderedKeys []string
providers := make(map[string]Provider)
for _, source := range loginSources {
- prov := Providers[source.Cfg.(*Source).Provider]
+ prov := gothProviders[source.Cfg.(*Source).Provider]
if source.Cfg.(*Source).IconURL != "" {
- prov.Image = source.Cfg.(*Source).IconURL
+ prov = &ImagedProvider{prov, source.Cfg.(*Source).IconURL}
}
providers[source.Name] = prov
orderedKeys = append(orderedKeys, source.Name)
@@ -116,9 +108,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]Provider, error) {
return orderedKeys, providers, nil
}
-// RegisterProvider register a OAuth2 provider in goth lib
-func RegisterProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) error {
- provider, err := createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL, customURLMapping)
+// RegisterProviderWithGothic register a OAuth2 provider in goth lib
+func RegisterProviderWithGothic(providerName string, source *Source) error {
+ provider, err := createProvider(providerName, source)
if err == nil && provider != nil {
gothRWMutex.Lock()
@@ -130,8 +122,8 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID
return err
}
-// RemoveProvider removes the given OAuth2 provider from the goth lib
-func RemoveProvider(providerName string) {
+// RemoveProviderFromGothic removes the given OAuth2 provider from the goth lib
+func RemoveProviderFromGothic(providerName string) {
gothRWMutex.Lock()
defer gothRWMutex.Unlock()
@@ -147,114 +139,20 @@ func ClearProviders() {
}
// used to create different types of goth providers
-func createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) (goth.Provider, error) {
+func createProvider(providerName string, source *Source) (goth.Provider, error) {
callbackURL := setting.AppURL + "user/oauth2/" + url.PathEscape(providerName) + "/callback"
var provider goth.Provider
var err error
- switch providerType {
- case "bitbucket":
- provider = bitbucket.New(clientID, clientSecret, callbackURL, "account")
- case "dropbox":
- provider = dropbox.New(clientID, clientSecret, callbackURL)
- case "facebook":
- provider = facebook.New(clientID, clientSecret, callbackURL, "email")
- case "github":
- authURL := github.AuthURL
- tokenURL := github.TokenURL
- profileURL := github.ProfileURL
- emailURL := github.EmailURL
- if customURLMapping != nil {
- if len(customURLMapping.AuthURL) > 0 {
- authURL = customURLMapping.AuthURL
- }
- if len(customURLMapping.TokenURL) > 0 {
- tokenURL = customURLMapping.TokenURL
- }
- if len(customURLMapping.ProfileURL) > 0 {
- profileURL = customURLMapping.ProfileURL
- }
- if len(customURLMapping.EmailURL) > 0 {
- emailURL = customURLMapping.EmailURL
- }
- }
- scopes := []string{}
- if setting.OAuth2Client.EnableAutoRegistration {
- scopes = append(scopes, "user:email")
- }
- provider = github.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, emailURL, scopes...)
- case "gitlab":
- authURL := gitlab.AuthURL
- tokenURL := gitlab.TokenURL
- profileURL := gitlab.ProfileURL
- if customURLMapping != nil {
- if len(customURLMapping.AuthURL) > 0 {
- authURL = customURLMapping.AuthURL
- }
- if len(customURLMapping.TokenURL) > 0 {
- tokenURL = customURLMapping.TokenURL
- }
- if len(customURLMapping.ProfileURL) > 0 {
- profileURL = customURLMapping.ProfileURL
- }
- }
- provider = gitlab.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, "read_user")
- case "gplus": // named gplus due to legacy gplus -> google migration (Google killed Google+). This ensures old connections still work
- scopes := []string{"email"}
- if setting.OAuth2Client.UpdateAvatar || setting.OAuth2Client.EnableAutoRegistration {
- scopes = append(scopes, "profile")
- }
- provider = google.New(clientID, clientSecret, callbackURL, scopes...)
- case "openidConnect":
- if provider, err = openidConnect.New(clientID, clientSecret, callbackURL, openIDConnectAutoDiscoveryURL, setting.OAuth2Client.OpenIDConnectScopes...); err != nil {
- log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, openIDConnectAutoDiscoveryURL, err)
- }
- case "twitter":
- provider = twitter.NewAuthenticate(clientID, clientSecret, callbackURL)
- case "discord":
- provider = discord.New(clientID, clientSecret, callbackURL, discord.ScopeIdentify, discord.ScopeEmail)
- case "gitea":
- authURL := gitea.AuthURL
- tokenURL := gitea.TokenURL
- profileURL := gitea.ProfileURL
- if customURLMapping != nil {
- if len(customURLMapping.AuthURL) > 0 {
- authURL = customURLMapping.AuthURL
- }
- if len(customURLMapping.TokenURL) > 0 {
- tokenURL = customURLMapping.TokenURL
- }
- if len(customURLMapping.ProfileURL) > 0 {
- profileURL = customURLMapping.ProfileURL
- }
- }
- provider = gitea.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL)
- case "nextcloud":
- authURL := nextcloud.AuthURL
- tokenURL := nextcloud.TokenURL
- profileURL := nextcloud.ProfileURL
- if customURLMapping != nil {
- if len(customURLMapping.AuthURL) > 0 {
- authURL = customURLMapping.AuthURL
- }
- if len(customURLMapping.TokenURL) > 0 {
- tokenURL = customURLMapping.TokenURL
- }
- if len(customURLMapping.ProfileURL) > 0 {
- profileURL = customURLMapping.ProfileURL
- }
- }
- provider = nextcloud.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL)
- case "yandex":
- // See https://tech.yandex.com/passport/doc/dg/reference/response-docpage/
- provider = yandex.New(clientID, clientSecret, callbackURL, "login:email", "login:info", "login:avatar")
- case "mastodon":
- instanceURL := mastodon.InstanceURL
- if customURLMapping != nil && len(customURLMapping.AuthURL) > 0 {
- instanceURL = customURLMapping.AuthURL
- }
- provider = mastodon.NewCustomisedURL(clientID, clientSecret, callbackURL, instanceURL)
+ p, ok := gothProviders[source.Provider]
+ if !ok {
+ return nil, models.ErrLoginSourceNotActived
+ }
+
+ provider, err = p.CreateGothProvider(providerName, callbackURL, source)
+ if err != nil {
+ return provider, err
}
// always set the name if provider is created so we can support multiple setups of 1 provider
diff --git a/services/auth/source/oauth2/providers_base.go b/services/auth/source/oauth2/providers_base.go
new file mode 100644
index 0000000000..b6b6d0bbd2
--- /dev/null
+++ b/services/auth/source/oauth2/providers_base.go
@@ -0,0 +1,33 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+// BaseProvider represents a common base for Provider
+type BaseProvider struct {
+ name string
+ displayName string
+}
+
+// Name provides the technical name for this provider
+func (b *BaseProvider) Name() string {
+ return b.name
+}
+
+// DisplayName returns the friendly name for this provider
+func (b *BaseProvider) DisplayName() string {
+ return b.displayName
+}
+
+// Image returns an image path for this provider
+func (b *BaseProvider) Image() string {
+ return "/assets/img/auth/" + b.name + ".png"
+}
+
+// CustomURLSettings returns the custom url settings for this provider
+func (b *BaseProvider) CustomURLSettings() *CustomURLSettings {
+ return nil
+}
+
+var _ (Provider) = &BaseProvider{}
diff --git a/services/auth/source/oauth2/providers_custom.go b/services/auth/source/oauth2/providers_custom.go
new file mode 100644
index 0000000000..de1a1690cb
--- /dev/null
+++ b/services/auth/source/oauth2/providers_custom.go
@@ -0,0 +1,118 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/markbates/goth"
+ "github.com/markbates/goth/providers/azureadv2"
+ "github.com/markbates/goth/providers/gitea"
+ "github.com/markbates/goth/providers/github"
+ "github.com/markbates/goth/providers/gitlab"
+ "github.com/markbates/goth/providers/mastodon"
+ "github.com/markbates/goth/providers/nextcloud"
+)
+
+// CustomProviderNewFn creates a goth.Provider using a custom url mapping
+type CustomProviderNewFn func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error)
+
+// CustomProvider is a GothProvider that has CustomURL features
+type CustomProvider struct {
+ BaseProvider
+ customURLSettings *CustomURLSettings
+ newFn CustomProviderNewFn
+}
+
+// CustomURLSettings returns the CustomURLSettings for this provider
+func (c *CustomProvider) CustomURLSettings() *CustomURLSettings {
+ return c.customURLSettings
+}
+
+// CreateGothProvider creates a GothProvider from this Provider
+func (c *CustomProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) {
+ custom := c.customURLSettings.OverrideWith(source.CustomURLMapping)
+
+ return c.newFn(source.ClientID, source.ClientSecret, callbackURL, custom)
+}
+
+// NewCustomProvider is a constructor function for custom providers
+func NewCustomProvider(name, displayName string, customURLSetting *CustomURLSettings, newFn CustomProviderNewFn) *CustomProvider {
+ return &CustomProvider{
+ BaseProvider: BaseProvider{
+ name: name,
+ displayName: displayName,
+ },
+ customURLSettings: customURLSetting,
+ newFn: newFn,
+ }
+}
+
+var _ (GothProvider) = &CustomProvider{}
+
+func init() {
+ RegisterGothProvider(NewCustomProvider(
+ "github", "GitHub", &CustomURLSettings{
+ TokenURL: availableAttribute(gitea.TokenURL),
+ AuthURL: availableAttribute(github.AuthURL),
+ ProfileURL: availableAttribute(github.ProfileURL),
+ EmailURL: availableAttribute(github.EmailURL),
+ },
+ func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) {
+ scopes := []string{}
+ if setting.OAuth2Client.EnableAutoRegistration {
+ scopes = append(scopes, "user:email")
+ }
+ return github.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, custom.EmailURL, scopes...), nil
+ }))
+
+ RegisterGothProvider(NewCustomProvider(
+ "gitlab", "GitLab", &CustomURLSettings{
+ AuthURL: availableAttribute(gitlab.AuthURL),
+ TokenURL: availableAttribute(gitlab.TokenURL),
+ ProfileURL: availableAttribute(gitlab.ProfileURL),
+ }, func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) {
+ return gitlab.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL, "read_user"), nil
+ }))
+
+ RegisterGothProvider(NewCustomProvider(
+ "gitea", "Gitea", &CustomURLSettings{
+ TokenURL: requiredAttribute(gitea.TokenURL),
+ AuthURL: requiredAttribute(gitea.AuthURL),
+ ProfileURL: requiredAttribute(gitea.ProfileURL),
+ },
+ func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) {
+ return gitea.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL), nil
+ }))
+
+ RegisterGothProvider(NewCustomProvider(
+ "nextcloud", "Nextcloud", &CustomURLSettings{
+ TokenURL: requiredAttribute(nextcloud.TokenURL),
+ AuthURL: requiredAttribute(nextcloud.AuthURL),
+ ProfileURL: requiredAttribute(nextcloud.ProfileURL),
+ },
+ func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) {
+ return nextcloud.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL, custom.TokenURL, custom.ProfileURL), nil
+ }))
+
+ RegisterGothProvider(NewCustomProvider(
+ "mastodon", "Mastodon", &CustomURLSettings{
+ AuthURL: requiredAttribute(mastodon.InstanceURL),
+ },
+ func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) {
+ return mastodon.NewCustomisedURL(clientID, secret, callbackURL, custom.AuthURL), nil
+ }))
+
+ RegisterGothProvider(NewCustomProvider(
+ "azureadv2", "Azure AD v2", &CustomURLSettings{
+ Tenant: requiredAttribute("organizations"),
+ },
+ func(clientID, secret, callbackURL string, custom *CustomURLMapping) (goth.Provider, error) {
+ return azureadv2.New(clientID, secret, callbackURL, azureadv2.ProviderOptions{
+ Tenant: azureadv2.TenantType(custom.Tenant),
+ }), nil
+ },
+ ))
+}
diff --git a/services/auth/source/oauth2/providers_openid.go b/services/auth/source/oauth2/providers_openid.go
new file mode 100644
index 0000000000..b725cf9605
--- /dev/null
+++ b/services/auth/source/oauth2/providers_openid.go
@@ -0,0 +1,52 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/markbates/goth"
+ "github.com/markbates/goth/providers/openidConnect"
+)
+
+// OpenIDProvider is a GothProvider for OpenID
+type OpenIDProvider struct {
+}
+
+// Name provides the technical name for this provider
+func (o *OpenIDProvider) Name() string {
+ return "openidconnect"
+}
+
+// DisplayName returns the friendly name for this provider
+func (o *OpenIDProvider) DisplayName() string {
+ return "OpenID Connect"
+}
+
+// Image returns an image path for this provider
+func (o *OpenIDProvider) Image() string {
+ return "/assets/img/auth/openid_connect.svg"
+}
+
+// CreateGothProvider creates a GothProvider from this Provider
+func (o *OpenIDProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) {
+ provider, err := openidConnect.New(source.ClientID, source.ClientSecret, callbackURL, source.OpenIDConnectAutoDiscoveryURL, setting.OAuth2Client.OpenIDConnectScopes...)
+ if err != nil {
+ log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err)
+ }
+ return provider, err
+}
+
+// CustomURLSettings returns the custom url settings for this provider
+func (o *OpenIDProvider) CustomURLSettings() *CustomURLSettings {
+ return nil
+}
+
+var _ (GothProvider) = &OpenIDProvider{}
+
+func init() {
+ RegisterGothProvider(&OpenIDProvider{})
+}
diff --git a/services/auth/source/oauth2/providers_simple.go b/services/auth/source/oauth2/providers_simple.go
new file mode 100644
index 0000000000..5a7062e6c3
--- /dev/null
+++ b/services/auth/source/oauth2/providers_simple.go
@@ -0,0 +1,108 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/markbates/goth"
+ "github.com/markbates/goth/providers/azuread"
+ "github.com/markbates/goth/providers/bitbucket"
+ "github.com/markbates/goth/providers/discord"
+ "github.com/markbates/goth/providers/dropbox"
+ "github.com/markbates/goth/providers/facebook"
+ "github.com/markbates/goth/providers/google"
+ "github.com/markbates/goth/providers/microsoftonline"
+ "github.com/markbates/goth/providers/twitter"
+ "github.com/markbates/goth/providers/yandex"
+)
+
+// SimpleProviderNewFn create goth.Providers without custom url features
+type SimpleProviderNewFn func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider
+
+// SimpleProvider is a GothProvider which does not have custom url features
+type SimpleProvider struct {
+ BaseProvider
+ scopes []string
+ newFn SimpleProviderNewFn
+}
+
+// CreateGothProvider creates a GothProvider from this Provider
+func (c *SimpleProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) {
+ return c.newFn(source.ClientID, source.ClientSecret, callbackURL, c.scopes...), nil
+}
+
+// NewSimpleProvider is a constructor function for simple providers
+func NewSimpleProvider(name, displayName string, scopes []string, newFn SimpleProviderNewFn) *SimpleProvider {
+ return &SimpleProvider{
+ BaseProvider: BaseProvider{
+ name: name,
+ displayName: displayName,
+ },
+ scopes: scopes,
+ newFn: newFn,
+ }
+}
+
+var _ (GothProvider) = &SimpleProvider{}
+
+func init() {
+ RegisterGothProvider(
+ NewSimpleProvider("bitbucket", "Bitbucket", []string{"account"},
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ return bitbucket.New(clientKey, secret, callbackURL, scopes...)
+ }))
+
+ RegisterGothProvider(
+ NewSimpleProvider("dropbox", "Dropbox", nil,
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ return dropbox.New(clientKey, secret, callbackURL, scopes...)
+ }))
+
+ RegisterGothProvider(NewSimpleProvider("facebook", "Facebook", nil,
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ return facebook.New(clientKey, secret, callbackURL, scopes...)
+ }))
+
+ // named gplus due to legacy gplus -> google migration (Google killed Google+). This ensures old connections still work
+ RegisterGothProvider(NewSimpleProvider("gplus", "Google", []string{"email"},
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ if setting.OAuth2Client.UpdateAvatar || setting.OAuth2Client.EnableAutoRegistration {
+ scopes = append(scopes, "profile")
+ }
+ return google.New(clientKey, secret, callbackURL, scopes...)
+ }))
+
+ RegisterGothProvider(NewSimpleProvider("twitter", "Twitter", nil,
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ return twitter.New(clientKey, secret, callbackURL)
+ }))
+
+ RegisterGothProvider(NewSimpleProvider("discord", "Discord", []string{discord.ScopeIdentify, discord.ScopeEmail},
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ return discord.New(clientKey, secret, callbackURL, scopes...)
+ }))
+
+ // See https://tech.yandex.com/passport/doc/dg/reference/response-docpage/
+ RegisterGothProvider(NewSimpleProvider("yandex", "Yandex", []string{"login:email", "login:info", "login:avatar"},
+ func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
+ return yandex.New(clientKey, secret, callbackURL, scopes...)
+ }))
+
+ RegisterGothProvider(NewSimpleProvider(
+ "azuread", "Azure AD", nil,
+ func(clientID, secret, callbackURL string, scopes ...string) goth.Provider {
+ return azuread.New(clientID, secret, callbackURL, nil, scopes...)
+ },
+ ))
+
+ RegisterGothProvider(NewSimpleProvider(
+ "microsoftonline", "Microsoft Online", nil,
+ func(clientID, secret, callbackURL string, scopes ...string) goth.Provider {
+ return microsoftonline.New(clientID, secret, callbackURL, scopes...)
+ },
+ ))
+
+}
diff --git a/services/auth/source/oauth2/source_name.go b/services/auth/source/oauth2/source_name.go
new file mode 100644
index 0000000000..0b794ad650
--- /dev/null
+++ b/services/auth/source/oauth2/source_name.go
@@ -0,0 +1,19 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+// Name returns the provider name of this source
+func (source *Source) Name() string {
+ return source.Provider
+}
+
+// DisplayName returns the display name of this source
+func (source *Source) DisplayName() string {
+ provider, has := gothProviders[source.Provider]
+ if !has {
+ return source.Provider
+ }
+ return provider.DisplayName()
+}
diff --git a/services/auth/source/oauth2/source_register.go b/services/auth/source/oauth2/source_register.go
index b61cc3fe79..24c61a9a56 100644
--- a/services/auth/source/oauth2/source_register.go
+++ b/services/auth/source/oauth2/source_register.go
@@ -10,13 +10,13 @@ import (
// RegisterSource causes an OAuth2 configuration to be registered
func (source *Source) RegisterSource() error {
- err := RegisterProvider(source.loginSource.Name, source.Provider, source.ClientID, source.ClientSecret, source.OpenIDConnectAutoDiscoveryURL, source.CustomURLMapping)
+ err := RegisterProviderWithGothic(source.loginSource.Name, source)
return wrapOpenIDConnectInitializeError(err, source.loginSource.Name, source)
}
// UnregisterSource causes an OAuth2 configuration to be unregistered
func (source *Source) UnregisterSource() error {
- RemoveProvider(source.loginSource.Name)
+ RemoveProviderFromGothic(source.loginSource.Name)
return nil
}
diff --git a/services/auth/source/oauth2/urlmapping.go b/services/auth/source/oauth2/urlmapping.go
index 68829fba21..43c8dde9a5 100644
--- a/services/auth/source/oauth2/urlmapping.go
+++ b/services/auth/source/oauth2/urlmapping.go
@@ -6,19 +6,73 @@ package oauth2
// CustomURLMapping describes the urls values to use when customizing OAuth2 provider URLs
type CustomURLMapping struct {
- AuthURL string
- TokenURL string
- ProfileURL string
- EmailURL string
+ AuthURL string `json:",omitempty"`
+ TokenURL string `json:",omitempty"`
+ ProfileURL string `json:",omitempty"`
+ EmailURL string `json:",omitempty"`
+ Tenant string `json:",omitempty"`
}
-// DefaultCustomURLMappings contains the map of default URL's for OAuth2 providers that are allowed to have custom urls
-// key is used to map the OAuth2Provider
-// value is the mapping as defined for the OAuth2Provider
-var DefaultCustomURLMappings = map[string]*CustomURLMapping{
- "github": Providers["github"].CustomURLMapping,
- "gitlab": Providers["gitlab"].CustomURLMapping,
- "gitea": Providers["gitea"].CustomURLMapping,
- "nextcloud": Providers["nextcloud"].CustomURLMapping,
- "mastodon": Providers["mastodon"].CustomURLMapping,
+// CustomURLSettings describes the urls values and availability to use when customizing OAuth2 provider URLs
+type CustomURLSettings struct {
+ AuthURL Attribute `json:",omitempty"`
+ TokenURL Attribute `json:",omitempty"`
+ ProfileURL Attribute `json:",omitempty"`
+ EmailURL Attribute `json:",omitempty"`
+ Tenant Attribute `json:",omitempty"`
+}
+
+// Attribute describes the availability, and required status for a custom url configuration
+type Attribute struct {
+ Value string
+ Available bool
+ Required bool
+}
+
+func availableAttribute(value string) Attribute {
+ return Attribute{Value: value, Available: true}
+}
+
+func requiredAttribute(value string) Attribute {
+ return Attribute{Value: value, Available: true, Required: true}
+}
+
+// Required is true if any attribute is required
+func (c *CustomURLSettings) Required() bool {
+ if c == nil {
+ return false
+ }
+ if c.AuthURL.Required || c.EmailURL.Required || c.ProfileURL.Required || c.TokenURL.Required || c.Tenant.Required {
+ return true
+ }
+ return false
+}
+
+// OverrideWith copies the current customURLMapping and overrides it with values from the provided mapping
+func (c *CustomURLSettings) OverrideWith(override *CustomURLMapping) *CustomURLMapping {
+ custom := &CustomURLMapping{
+ AuthURL: c.AuthURL.Value,
+ TokenURL: c.TokenURL.Value,
+ ProfileURL: c.ProfileURL.Value,
+ EmailURL: c.EmailURL.Value,
+ Tenant: c.Tenant.Value,
+ }
+ if override != nil {
+ if len(override.AuthURL) > 0 && c.AuthURL.Available {
+ custom.AuthURL = override.AuthURL
+ }
+ if len(override.TokenURL) > 0 && c.TokenURL.Available {
+ custom.TokenURL = override.TokenURL
+ }
+ if len(override.ProfileURL) > 0 && c.ProfileURL.Available {
+ custom.ProfileURL = override.ProfileURL
+ }
+ if len(override.EmailURL) > 0 && c.EmailURL.Available {
+ custom.EmailURL = override.EmailURL
+ }
+ if len(override.Tenant) > 0 && c.Tenant.Available {
+ custom.Tenant = override.Tenant
+ }
+ }
+ return custom
}
diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go
index 30621cadff..b78fa9217e 100644
--- a/services/forms/auth_form.go
+++ b/services/forms/auth_form.go
@@ -62,6 +62,7 @@ type AuthenticationForm struct {
Oauth2ProfileURL string
Oauth2EmailURL string
Oauth2IconURL string
+ Oauth2Tenant string
SSPIAutoCreateUsers bool
SSPIAutoActivateUsers bool
SSPIStripDomainNames bool