func (b *Backend) Configure() error {
	if b.Config.Name == "" {
		return errors.ErrBackendConfigureNameEmpty
	}
	if b.Config.Method == "" {
		return errors.ErrBackendConfigureMethodEmpty
	}
	if b.Config.Realm == "" {
		return errors.ErrBackendConfigureRealmEmpty
	}
	if b.Config.ClientID == "" {
		return errors.ErrBackendClientIDNotFound.WithArgs(b.Config.Provider)
	}
	if b.Config.ClientSecret == "" {
		return errors.ErrBackendClientSecretNotFound.WithArgs(b.Config.Provider)
	}
	if b.Config.DelayStart > 0 {
		if b.Config.RetryAttempts < 1 {
			b.Config.RetryAttempts = 2
		}
		if b.Config.RetryInterval == 0 {
			b.Config.RetryInterval = b.Config.DelayStart
		}
		b.logger.Debug(
			"Configured delayed start for OAuth 2.0 backend",
			zap.String("backend_name", b.Config.Name),
			zap.Int("delayed_by", b.Config.DelayStart),
			zap.Int("retry_attempts", b.Config.RetryAttempts),
			zap.Int("retry_interval", b.Config.RetryInterval),
		)
	}
	if b.Config.RetryAttempts > 0 && b.Config.DelayStart == 0 {
		if b.Config.RetryInterval == 0 {
			b.Config.RetryInterval = 5
		}
		b.logger.Debug(
			"Configured connection retries for OAuth 2.0 backend",
			zap.String("backend_name", b.Config.Name),
			zap.Int("retry_attempts", b.Config.RetryAttempts),
			zap.Int("retry_interval", b.Config.RetryInterval),
		)
	}
	if len(b.Config.Scopes) < 1 {
		b.Config.Scopes = []string{"openid", "email", "profile"}
	}
	b.Config.scopeMap = make(map[string]interface{})
	for _, scope := range b.Config.Scopes {
		b.Config.scopeMap[scope] = true
	}
	switch b.Config.IdentityTokenName {
	case "":
		b.Config.IdentityTokenName = "id_token"
	case "id_token", "access_token":
	default:
		return errors.ErrBackendInvalidIdentityTokenName.WithArgs(b.Config.IdentityTokenName, b.Config.Provider)
	}
	switch b.Config.Provider {
	case "okta":
		if b.Config.ServerID == "" {
			return errors.ErrBackendServerIDNotFound.WithArgs(b.Config.Provider)
		}
		if b.Config.DomainName == "" {
			return errors.ErrBackendAppNameNotFound.WithArgs(b.Config.Provider)
		}
		if b.Config.BaseAuthURL == "" {
			b.Config.BaseAuthURL = fmt.Sprintf(
				"https://%s/oauth2/%s/",
				b.Config.DomainName, b.Config.ServerID,
			)
			b.Config.MetadataURL = b.Config.BaseAuthURL + ".well-known/openid-configuration?client_id=" + b.Config.ClientID
		}
	case "google":
		if b.Config.BaseAuthURL == "" {
			b.Config.BaseAuthURL = "https://accounts.google.com/o/oauth2/v2/"
			b.Config.MetadataURL = "https://accounts.google.com/.well-known/openid-configuration"
		}
		if !strings.Contains(b.Config.ClientID, ".") {
			b.Config.ClientID = b.Config.ClientID + ".apps.googleusercontent.com"
		}
	case "github":
		if b.Config.BaseAuthURL == "" {
			b.Config.BaseAuthURL = "https://github.com/login/oauth/"
		}
		b.authorizationURL = "https://github.com/login/oauth/authorize"
		b.tokenURL = "https://github.com/login/oauth/access_token"
		b.disableKeyVerification = true
		b.disablePassGrantType = true
		b.disableResponseType = true
		b.disableNonce = true
		b.enableAcceptHeader = true
		b.requiredTokenFields = map[string]interface{}{
			"access_token": true,
		}
	case "gitlab":
		if b.Config.DomainName == "" {
			b.Config.DomainName = "gitlab.com"
		}
		if b.Config.BaseAuthURL == "" {
			b.Config.BaseAuthURL = fmt.Sprintf("https://%s/", b.Config.DomainName)
			b.Config.MetadataURL = b.Config.BaseAuthURL + ".well-known/openid-configuration"
		}
	case "azure":
		if b.Config.TenantID == "" {
			b.Config.TenantID = "common"
		}
		if b.Config.BaseAuthURL == "" {
			b.Config.BaseAuthURL = "https://login.microsoftonline.com/" + b.Config.TenantID + "/oauth2/v2.0/"
			b.Config.MetadataURL = "https://login.microsoftonline.com/" + b.Config.TenantID + "/v2.0/.well-known/openid-configuration"
		}
	case "facebook":
		if b.Config.BaseAuthURL == "" {
			b.Config.BaseAuthURL = "https://www.facebook.com/v8.0/dialog/"
		}
		b.authorizationURL = "https://www.facebook.com/v8.0/dialog/oauth"
		b.tokenURL = "https://graph.facebook.com/v8.0/oauth/access_token"
		b.disableKeyVerification = true
		b.disablePassGrantType = true
		b.disableResponseType = true
		b.disableNonce = true
		b.enableAcceptHeader = true
		b.disableScope = true
		b.requiredTokenFields = map[string]interface{}{
			"access_token": true,
		}
	case "generic":
	case "":
		return errors.ErrBackendOauthProviderNotFound.WithArgs(b.Config.Provider)
	default:
		return errors.ErrBackendUnsupportedProvider.WithArgs(b.Config.Provider)
	}
	if b.Config.BaseAuthURL == "" {
		return errors.ErrBackendOauthAuthorizationURLNotFound.WithArgs(b.Config.Provider)
	}
	parsedBaseAuthURL, err := url.Parse(b.Config.BaseAuthURL)
	if err != nil {
		return errors.ErrBackendConfigureInvalidBaseURL.WithArgs(b.Config.Provider, b.Config.BaseAuthURL, err)
	}
	b.serverName = parsedBaseAuthURL.Host
	if b.Config.DelayStart > 0 {
		go b.fetchConfig()
	} else {
		if err := b.fetchConfig(); err != nil {
			return err
		}
	}
	for _, pattern := range b.Config.UserGroupFilters {
		compiledPattern, err := regexp.Compile(pattern)
		if err != nil {
			return errors.ErrBackendOAuthUserGroupFilterInvalid.WithArgs(pattern, err)
		}
		b.userGroupFilters = append(b.userGroupFilters, compiledPattern)
	}
	for _, pattern := range b.Config.UserOrgFilters {
		compiledPattern, err := regexp.Compile(pattern)
		if err != nil {
			return errors.ErrBackendOAuthUserOrgFilterInvalid.WithArgs(pattern, err)
		}
		b.userOrgFilters = append(b.userOrgFilters, compiledPattern)
	}
	b.logger.Info(
		"successfully configured OAuth 2.0 backend",
		zap.String("provider", b.Config.Provider),
		zap.String("client_id", b.Config.ClientID),
		zap.String("server_id", b.Config.ServerID),
		zap.String("domain_name", b.Config.DomainName),
		zap.Any("metadata", b.metadata),
		zap.Any("jwks_keys", b.keys),
	)
	return nil
}
Configure configures Backend.
package oauth2
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"
	"github.com/greenpau/caddy-auth-portal/pkg/errors"
	"go.uber.org/zap"
)
type Config struct {
	Name     string `json:"name,omitempty"`
	Method   string `json:"method,omitempty"`
	Realm    string `json:"realm,omitempty"`
	Provider string `json:"provider,omitempty"`
	DomainName        string `json:"domain_name,omitempty"`
	ClientID          string `json:"client_id,omitempty"`
	ClientSecret      string `json:"client_secret,omitempty"`
	ServerID          string `json:"server_id,omitempty"`
	AppSecret         string `json:"app_secret,omitempty"`
	TenantID          string `json:"tenant_id,omitempty"`
	IdentityTokenName string `json:"identity_token_name,omitempty"`
	Scopes []string `json:"scopes,omitempty"`
	DelayStart int `json:"delay_start,omitempty"`
	RetryAttempts int `json:"retry_attempts,omitempty"`
	RetryInterval int `json:"retry_interval,omitempty"`
	UserRoleMapList []map[string]interface{} `json:"user_roles,omitempty"`
	BaseAuthURL string `json:"base_auth_url,omitempty"`
	MetadataURL string `json:"metadata_url,omitempty"`
	UserGroupFilters []string `json:"user_group_filters,omitempty"`
	UserOrgFilters []string `json:"user_org_filters,omitempty"`
	scopeMap map[string]interface{}
}
func (c *Config) ScopeExists(scopes ...string) bool {
	for _, scope := range scopes {
		if _, exists := c.scopeMap[scope]; exists {
			return true
		}
	}
	return false
}