mirror of https://github.com/h44z/wg-portal.git
				
				
				
			fix plain oauth login (#317)
This commit is contained in:
		
							parent
							
								
									378252ba2f
								
							
						
					
					
						commit
						62dbdfe0f9
					
				|  | @ -94,7 +94,6 @@ The following configuration options are available: | ||||||
| | auth_type                       | mail       | plain                                      | SMTP authentication type, allowed values: plain, login, crammd5.                                                                        | | | auth_type                       | mail       | plain                                      | SMTP authentication type, allowed values: plain, login, crammd5.                                                                        | | ||||||
| | from                            | mail       | Wireguard Portal <noreply@wireguard.local> | The address that is used to send mails.                                                                                                 | | | from                            | mail       | Wireguard Portal <noreply@wireguard.local> | The address that is used to send mails.                                                                                                 | | ||||||
| | link_only                       | mail       | false                                      | Only send links to WireGuard Portal instead of the full configuration.                                                                  | | | link_only                       | mail       | false                                      | Only send links to WireGuard Portal instead of the full configuration.                                                                  | | ||||||
| | callback_url_prefix             | auth       | /api/v0                                    | OAuth callback URL prefix. The full callback URL will look like: https://wg.portal.local/callback_url_prefix/provider_name/callback     | |  | ||||||
| | oidc                            | auth       | Empty Array - no providers configured      | A list of OpenID Connect providers. See auth/oidc properties to setup a new provider.                                                   | | | oidc                            | auth       | Empty Array - no providers configured      | A list of OpenID Connect providers. See auth/oidc properties to setup a new provider.                                                   | | ||||||
| | oauth                           | auth       | Empty Array - no providers configured      | A list of plain OAuth providers. See auth/oauth properties to setup a new provider.                                                     | | | oauth                           | auth       | Empty Array - no providers configured      | A list of plain OAuth providers. See auth/oauth properties to setup a new provider.                                                     | | ||||||
| | ldap                            | auth       | Empty Array - no providers configured      | A list of LDAP providers. See auth/ldap properties to setup a new provider.                                                             | | | ldap                            | auth       | Empty Array - no providers configured      | A list of LDAP providers. See auth/ldap properties to setup a new provider.                                                             | | ||||||
|  | @ -108,12 +107,10 @@ The following configuration options are available: | ||||||
| | registration_enabled            | auth/oidc  |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                         | | | registration_enabled            | auth/oidc  |                                            | If registration is enabled, new user accounts will created in WireGuard Portal.                                                         | | ||||||
| | provider_name                   | auth/oauth |                                            | A unique provider name. This name must be unique throughout all authentication providers (even other types).                            | | | provider_name                   | auth/oauth |                                            | A unique provider name. This name must be unique throughout all authentication providers (even other types).                            | | ||||||
| | display_name                    | auth/oauth |                                            | The display name is shown at the login page (the login button).                                                                         | | | display_name                    | auth/oauth |                                            | The display name is shown at the login page (the login button).                                                                         | | ||||||
| | base_url                        | auth/oauth |                                            | The base_url is the URL identifier for the service. For example: "https://accounts.google.com".                                         | |  | ||||||
| | client_id                       | auth/oauth |                                            | The OAuth client id.                                                                                                                    | | | client_id                       | auth/oauth |                                            | The OAuth client id.                                                                                                                    | | ||||||
| | client_secret                   | auth/oauth |                                            | The OAuth client secret.                                                                                                                | | | client_secret                   | auth/oauth |                                            | The OAuth client secret.                                                                                                                | | ||||||
| | auth_url                        | auth/oauth |                                            | The URL for the authentication endpoint.                                                                                                | | | auth_url                        | auth/oauth |                                            | The URL for the authentication endpoint.                                                                                                | | ||||||
| | token_url                       | auth/oauth |                                            | The URL for the token endpoint.                                                                                                         | | | token_url                       | auth/oauth |                                            | The URL for the token endpoint.                                                                                                         | | ||||||
| | redirect_url                    | auth/oauth |                                            | The redirect URL.                                                                                                                       | |  | ||||||
| | user_info_url                   | auth/oauth |                                            | The URL for the user information endpoint.                                                                                              | | | user_info_url                   | auth/oauth |                                            | The URL for the user information endpoint.                                                                                              | | ||||||
| | scopes                          | auth/oauth |                                            | OAuth scopes.                                                                                                                           | | | scopes                          | auth/oauth |                                            | OAuth scopes.                                                                                                                           | | ||||||
| | field_map                       | auth/oauth |                                            | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin.                   | | | field_map                       | auth/oauth |                                            | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin.                   | | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ func main() { | ||||||
| 	userManager, err := users.NewUserManager(cfg, eventBus, database, database) | 	userManager, err := users.NewUserManager(cfg, eventBus, database, database) | ||||||
| 	internal.AssertNoError(err) | 	internal.AssertNoError(err) | ||||||
| 
 | 
 | ||||||
| 	authenticator, err := auth.NewAuthenticator(&cfg.Auth, eventBus, userManager) | 	authenticator, err := auth.NewAuthenticator(&cfg.Auth, cfg.Web.ExternalUrl, eventBus, userManager) | ||||||
| 	internal.AssertNoError(err) | 	internal.AssertNoError(err) | ||||||
| 
 | 
 | ||||||
| 	wireGuardManager, err := wireguard.NewWireGuardManager(cfg, eventBus, wireGuard, wgQuick, database) | 	wireGuardManager, err := wireguard.NewWireGuardManager(cfg, eventBus, wireGuard, wgQuick, database) | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ web: | ||||||
|   request_logging: true |   request_logging: true | ||||||
| 
 | 
 | ||||||
| auth: | auth: | ||||||
|   callback_url_prefix: http://localhost:8888/api/v0 |  | ||||||
|   ldap: |   ldap: | ||||||
|     - id: ldap1 |     - id: ldap1 | ||||||
|       provider_name: company ldap |       provider_name: company ldap | ||||||
|  | @ -47,3 +46,22 @@ auth: | ||||||
|         - https://www.googleapis.com/auth/userinfo.email |         - https://www.googleapis.com/auth/userinfo.email | ||||||
|         - https://www.googleapis.com/auth/userinfo.profile |         - https://www.googleapis.com/auth/userinfo.profile | ||||||
|       registration_enabled: true |       registration_enabled: true | ||||||
|  |   oauth: | ||||||
|  |     - id: google_plain_oauth | ||||||
|  |       provider_name: google3 | ||||||
|  |       display_name: Login with</br>Google3 | ||||||
|  |       client_id: another-client-id-1234.apps.googleusercontent.com | ||||||
|  |       client_secret: A_CLIENT_SECRET | ||||||
|  |       auth_url: https://accounts.google.com/o/oauth2/v2/auth | ||||||
|  |       token_url: https://oauth2.googleapis.com/token | ||||||
|  |       user_info_url: https://openidconnect.googleapis.com/v1/userinfo | ||||||
|  |       scopes: | ||||||
|  |         - openid | ||||||
|  |         - email | ||||||
|  |         - profile | ||||||
|  |       field_map: | ||||||
|  |         email: email | ||||||
|  |         firstname: name | ||||||
|  |         user_identifier: sub | ||||||
|  |         is_admin: roles | ||||||
|  |       registration_enabled: true | ||||||
|  | @ -6,8 +6,6 @@ import ( | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/h44z/wg-portal/internal/app" |  | ||||||
| 	"github.com/sirupsen/logrus" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"path" | 	"path" | ||||||
|  | @ -15,10 +13,11 @@ import ( | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/coreos/go-oidc/v3/oidc" | 	"github.com/coreos/go-oidc/v3/oidc" | ||||||
| 	evbus "github.com/vardius/message-bus" | 	"github.com/h44z/wg-portal/internal/app" | ||||||
| 
 |  | ||||||
| 	"github.com/h44z/wg-portal/internal/config" | 	"github.com/h44z/wg-portal/internal/config" | ||||||
| 	"github.com/h44z/wg-portal/internal/domain" | 	"github.com/h44z/wg-portal/internal/domain" | ||||||
|  | 	"github.com/sirupsen/logrus" | ||||||
|  | 	evbus "github.com/vardius/message-bus" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type UserManager interface { | type UserManager interface { | ||||||
|  | @ -33,14 +32,21 @@ type Authenticator struct { | ||||||
| 	oauthAuthenticators map[string]domain.OauthAuthenticator | 	oauthAuthenticators map[string]domain.OauthAuthenticator | ||||||
| 	ldapAuthenticators  map[string]domain.LdapAuthenticator | 	ldapAuthenticators  map[string]domain.LdapAuthenticator | ||||||
| 
 | 
 | ||||||
|  | 	// URL prefix for the callback endpoints, this is a combination of the external URL and the API prefix
 | ||||||
|  | 	callbackUrlPrefix string | ||||||
|  | 
 | ||||||
| 	users UserManager | 	users UserManager | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewAuthenticator(cfg *config.Auth, bus evbus.MessageBus, users UserManager) (*Authenticator, error) { | func NewAuthenticator(cfg *config.Auth, extUrl string, bus evbus.MessageBus, users UserManager) ( | ||||||
|  | 	*Authenticator, | ||||||
|  | 	error, | ||||||
|  | ) { | ||||||
| 	a := &Authenticator{ | 	a := &Authenticator{ | ||||||
| 		cfg:               cfg, | 		cfg:               cfg, | ||||||
| 		bus:               bus, | 		bus:               bus, | ||||||
| 		users:             users, | 		users:             users, | ||||||
|  | 		callbackUrlPrefix: fmt.Sprintf("%s/api/v0", extUrl), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||||||
|  | @ -55,7 +61,7 @@ func NewAuthenticator(cfg *config.Auth, bus evbus.MessageBus, users UserManager) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *Authenticator) setupExternalAuthProviders(ctx context.Context) error { | func (a *Authenticator) setupExternalAuthProviders(ctx context.Context) error { | ||||||
| 	extUrl, err := url.Parse(a.cfg.CallbackUrlPrefix) | 	extUrl, err := url.Parse(a.callbackUrlPrefix) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("failed to parse external url: %w", err) | 		return fmt.Errorf("failed to parse external url: %w", err) | ||||||
| 	} | 	} | ||||||
|  | @ -141,8 +147,8 @@ func (a *Authenticator) GetExternalLoginProviders(_ context.Context) []domain.Lo | ||||||
| 		authProviders = append(authProviders, domain.LoginProviderInfo{ | 		authProviders = append(authProviders, domain.LoginProviderInfo{ | ||||||
| 			Identifier:  providerId, | 			Identifier:  providerId, | ||||||
| 			Name:        providerName, | 			Name:        providerName, | ||||||
| 			ProviderUrl: fmt.Sprintf("%s/%s/init", a.cfg.CallbackUrlPrefix, providerId), | 			ProviderUrl: fmt.Sprintf("/auth/login/%s/init", providerId), | ||||||
| 			CallbackUrl: fmt.Sprintf("%s/%s/callback", a.cfg.CallbackUrlPrefix, providerId), | 			CallbackUrl: fmt.Sprintf("/auth/login/%s/callback", providerId), | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -187,8 +193,13 @@ func (a *Authenticator) PlainLogin(ctx context.Context, username, password strin | ||||||
| 	return user, nil | 	return user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *Authenticator) passwordAuthentication(ctx context.Context, identifier domain.UserIdentifier, password string) (*domain.User, error) { | func (a *Authenticator) passwordAuthentication( | ||||||
| 	ctx = domain.SetUserInfo(ctx, domain.SystemAdminContextUserInfo()) // switch to admin user context to check if user exists
 | 	ctx context.Context, | ||||||
|  | 	identifier domain.UserIdentifier, | ||||||
|  | 	password string, | ||||||
|  | ) (*domain.User, error) { | ||||||
|  | 	ctx = domain.SetUserInfo(ctx, | ||||||
|  | 		domain.SystemAdminContextUserInfo()) // switch to admin user context to check if user exists
 | ||||||
| 
 | 
 | ||||||
| 	var ldapUserInfo *domain.AuthenticatorUserInfo | 	var ldapUserInfo *domain.AuthenticatorUserInfo | ||||||
| 	var ldapProvider domain.LdapAuthenticator | 	var ldapProvider domain.LdapAuthenticator | ||||||
|  | @ -248,7 +259,8 @@ func (a *Authenticator) passwordAuthentication(ctx context.Context, identifier d | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !userInDatabase { | 	if !userInDatabase { | ||||||
| 		user, err := a.processUserInfo(ctx, ldapUserInfo, domain.UserSourceLdap, ldapProvider.GetName(), ldapProvider.RegistrationEnabled()) | 		user, err := a.processUserInfo(ctx, ldapUserInfo, domain.UserSourceLdap, ldapProvider.GetName(), | ||||||
|  | 			ldapProvider.RegistrationEnabled()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("unable to process user information: %w", err) | 			return nil, fmt.Errorf("unable to process user information: %w", err) | ||||||
| 		} | 		} | ||||||
|  | @ -262,7 +274,10 @@ func (a *Authenticator) passwordAuthentication(ctx context.Context, identifier d | ||||||
| 
 | 
 | ||||||
| // region oauth authentication
 | // region oauth authentication
 | ||||||
| 
 | 
 | ||||||
| func (a *Authenticator) OauthLoginStep1(_ context.Context, providerId string) (authCodeUrl, state, nonce string, err error) { | func (a *Authenticator) OauthLoginStep1(_ context.Context, providerId string) ( | ||||||
|  | 	authCodeUrl, state, nonce string, | ||||||
|  | 	err error, | ||||||
|  | ) { | ||||||
| 	oauthProvider, ok := a.oauthAuthenticators[providerId] | 	oauthProvider, ok := a.oauthAuthenticators[providerId] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return "", "", "", fmt.Errorf("missing oauth provider %s", providerId) | 		return "", "", "", fmt.Errorf("missing oauth provider %s", providerId) | ||||||
|  | @ -318,8 +333,10 @@ func (a *Authenticator) OauthLoginStep2(ctx context.Context, providerId, nonce, | ||||||
| 		return nil, fmt.Errorf("unable to parse user information: %w", err) | 		return nil, fmt.Errorf("unable to parse user information: %w", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx = domain.SetUserInfo(ctx, domain.SystemAdminContextUserInfo()) // switch to admin user context to check if user exists
 | 	ctx = domain.SetUserInfo(ctx, | ||||||
| 	user, err := a.processUserInfo(ctx, userInfo, domain.UserSourceOauth, oauthProvider.GetName(), oauthProvider.RegistrationEnabled()) | 		domain.SystemAdminContextUserInfo()) // switch to admin user context to check if user exists
 | ||||||
|  | 	user, err := a.processUserInfo(ctx, userInfo, domain.UserSourceOauth, oauthProvider.GetName(), | ||||||
|  | 		oauthProvider.RegistrationEnabled()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("unable to process user information: %w", err) | 		return nil, fmt.Errorf("unable to process user information: %w", err) | ||||||
| 	} | 	} | ||||||
|  | @ -333,7 +350,13 @@ func (a *Authenticator) OauthLoginStep2(ctx context.Context, providerId, nonce, | ||||||
| 	return user, nil | 	return user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *Authenticator) processUserInfo(ctx context.Context, userInfo *domain.AuthenticatorUserInfo, source domain.UserSource, provider string, withReg bool) (*domain.User, error) { | func (a *Authenticator) processUserInfo( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	userInfo *domain.AuthenticatorUserInfo, | ||||||
|  | 	source domain.UserSource, | ||||||
|  | 	provider string, | ||||||
|  | 	withReg bool, | ||||||
|  | ) (*domain.User, error) { | ||||||
| 	// Search user in backend
 | 	// Search user in backend
 | ||||||
| 	user, err := a.users.GetUser(ctx, userInfo.Identifier) | 	user, err := a.users.GetUser(ctx, userInfo.Identifier) | ||||||
| 	switch { | 	switch { | ||||||
|  | @ -349,7 +372,12 @@ func (a *Authenticator) processUserInfo(ctx context.Context, userInfo *domain.Au | ||||||
| 	return user, nil | 	return user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *Authenticator) registerNewUser(ctx context.Context, userInfo *domain.AuthenticatorUserInfo, source domain.UserSource, provider string) (*domain.User, error) { | func (a *Authenticator) registerNewUser( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	userInfo *domain.AuthenticatorUserInfo, | ||||||
|  | 	source domain.UserSource, | ||||||
|  | 	provider string, | ||||||
|  | ) (*domain.User, error) { | ||||||
| 	// convert user info to domain.User
 | 	// convert user info to domain.User
 | ||||||
| 	user := &domain.User{ | 	user := &domain.User{ | ||||||
| 		Identifier:   userInfo.Identifier, | 		Identifier:   userInfo.Identifier, | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ type Auth struct { | ||||||
| 	OpenIDConnect []OpenIDConnectProvider `yaml:"oidc"` | 	OpenIDConnect []OpenIDConnectProvider `yaml:"oidc"` | ||||||
| 	OAuth         []OAuthProvider         `yaml:"oauth"` | 	OAuth         []OAuthProvider         `yaml:"oauth"` | ||||||
| 	Ldap          []LdapProvider          `yaml:"ldap"` | 	Ldap          []LdapProvider          `yaml:"ldap"` | ||||||
| 	CallbackUrlPrefix string                  `yaml:"callback_url_prefix"` |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type BaseFields struct { | type BaseFields struct { | ||||||
|  | @ -24,7 +23,7 @@ type BaseFields struct { | ||||||
| 
 | 
 | ||||||
| type OauthFields struct { | type OauthFields struct { | ||||||
| 	BaseFields `yaml:",inline"` | 	BaseFields `yaml:",inline"` | ||||||
| 	IsAdmin    string `yaml:"is_admin"` | 	IsAdmin    string `yaml:"is_admin"` // If the value is "true", the user is an admin.
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type LdapFields struct { | type LdapFields struct { | ||||||
|  | @ -93,8 +92,6 @@ type OAuthProvider struct { | ||||||
| 	// DisplayName is shown to the user on the login page. If it is empty, ProviderName will be displayed.
 | 	// DisplayName is shown to the user on the login page. If it is empty, ProviderName will be displayed.
 | ||||||
| 	DisplayName string `yaml:"display_name"` | 	DisplayName string `yaml:"display_name"` | ||||||
| 
 | 
 | ||||||
| 	BaseUrl string `yaml:"base_url"` |  | ||||||
| 
 |  | ||||||
| 	// ClientID is the application's ID.
 | 	// ClientID is the application's ID.
 | ||||||
| 	ClientID string `yaml:"client_id"` | 	ClientID string `yaml:"client_id"` | ||||||
| 
 | 
 | ||||||
|  | @ -105,10 +102,6 @@ type OAuthProvider struct { | ||||||
| 	TokenURL    string `yaml:"token_url"` | 	TokenURL    string `yaml:"token_url"` | ||||||
| 	UserInfoURL string `yaml:"user_info_url"` | 	UserInfoURL string `yaml:"user_info_url"` | ||||||
| 
 | 
 | ||||||
| 	// RedirectURL is the URL to redirect users going through
 |  | ||||||
| 	// the OAuth flow, after the resource owner's URLs.
 |  | ||||||
| 	RedirectURL string `yaml:"redirect_url"` |  | ||||||
| 
 |  | ||||||
| 	// Scope specifies optional requested permissions.
 | 	// Scope specifies optional requested permissions.
 | ||||||
| 	Scopes []string `yaml:"scopes"` | 	Scopes []string `yaml:"scopes"` | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,8 +104,6 @@ func defaultConfig() *Config { | ||||||
| 		SiteCompanyName:   "WireGuard Portal", | 		SiteCompanyName:   "WireGuard Portal", | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cfg.Auth.CallbackUrlPrefix = "/api/v0" |  | ||||||
| 
 |  | ||||||
| 	cfg.Advanced.StartListenPort = 51820 | 	cfg.Advanced.StartListenPort = 51820 | ||||||
| 	cfg.Advanced.StartCidrV4 = "10.11.12.0/24" | 	cfg.Advanced.StartCidrV4 = "10.11.12.0/24" | ||||||
| 	cfg.Advanced.StartCidrV6 = "fdfd:d3ad:c0de:1234::0/64" | 	cfg.Advanced.StartCidrV6 = "fdfd:d3ad:c0de:1234::0/64" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue