diff --git a/config.yml.sample b/config.yml.sample
index 36407d04..f185d1c1 100644
--- a/config.yml.sample
+++ b/config.yml.sample
@@ -262,10 +262,12 @@ auth:
enabled: true
# OpenID configuration will allow users to authenticate through a third-party OpenID Connect compatible provider.
# The provider needs to support the `openid`, `profile` and `email` scopes.
- # **Note:** The frontend expects to be redirected after authentication by the third party
+ # **Note:** Some openid providers (like gitlab) only make the email of the user available through openid claims if they have set it to be publicly visible.
+ # If the email is not public in those cases, authenticating will fail.
+ # **Note 2:** The frontend expects to be redirected after authentication by the third party
# to /auth/openid/. Please make sure to configure the redirect url with your third party
# auth service accordingy if you're using the default vikunja frontend.
- # Take a look at the [default config file](https://kolaente.dev/vikunja/api/src/branch/master/config.yml.sample) for more information about how to configure openid authentication.
+ # Take a look at the [default config file](https://kolaente.dev/vikunja/api/src/branch/main/config.yml.sample) for more information about how to configure openid authentication.
openid:
# Enable or disable OpenID Connect authentication
enabled: false
diff --git a/docs/content/doc/setup/config.md b/docs/content/doc/setup/config.md
index c9e3db33..8ab8a954 100644
--- a/docs/content/doc/setup/config.md
+++ b/docs/content/doc/setup/config.md
@@ -615,10 +615,12 @@ Default: ``
OpenID configuration will allow users to authenticate through a third-party OpenID Connect compatible provider.
The provider needs to support the `openid`, `profile` and `email` scopes.
-**Note:** The frontend expects to be redirected after authentication by the third party
+**Note:** Some openid providers (like gitlab) only make the email of the user available through openid claims if they have set it to be publicly visible.
+If the email is not public in those cases, authenticating will fail.
+**Note 2:** The frontend expects to be redirected after authentication by the third party
to /auth/openid/. Please make sure to configure the redirect url with your third party
auth service accordingy if you're using the default vikunja frontend.
-Take a look at the [default config file](https://kolaente.dev/vikunja/api/src/branch/master/config.yml.sample) for more information about how to configure openid authentication.
+Take a look at the [default config file](https://kolaente.dev/vikunja/api/src/branch/main/config.yml.sample) for more information about how to configure openid authentication.
Default: ``
diff --git a/pkg/modules/auth/openid/openid.go b/pkg/modules/auth/openid/openid.go
index baa3b42d..8cd169bd 100644
--- a/pkg/modules/auth/openid/openid.go
+++ b/pkg/modules/auth/openid/openid.go
@@ -23,6 +23,8 @@ import (
"net/http"
"time"
+ "code.vikunja.io/web/handler"
+
"code.vikunja.io/api/pkg/db"
"xorm.io/xorm"
@@ -86,7 +88,7 @@ func HandleCallback(c echo.Context) error {
provider, err := GetProvider(providerKey)
if err != nil {
log.Error(err)
- return err
+ return handler.HandleHTTPError(err, c)
}
if provider == nil {
return c.JSON(http.StatusBadRequest, models.Message{Message: "Provider does not exist"})
@@ -100,7 +102,8 @@ func HandleCallback(c echo.Context) error {
details := make(map[string]interface{})
if err := json.Unmarshal(rerr.Body, &details); err != nil {
- return err
+ log.Errorf("Error unmarshaling token for provider %s: %v", provider.Name, err)
+ return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusBadRequest, map[string]interface{}{
@@ -109,7 +112,7 @@ func HandleCallback(c echo.Context) error {
})
}
- return err
+ return handler.HandleHTTPError(err, c)
}
// Extract the ID Token from OAuth2 token.
@@ -123,14 +126,21 @@ func HandleCallback(c echo.Context) error {
// Parse and verify ID Token payload.
idToken, err := verifier.Verify(context.Background(), rawIDToken)
if err != nil {
- return err
+ log.Errorf("Error verifying token for provider %s: %v", provider.Name, err)
+ return handler.HandleHTTPError(err, c)
}
// Extract custom claims
cl := &claims{}
err = idToken.Claims(cl)
if err != nil {
- return err
+ log.Errorf("Error getting token claims for provider %s: %v", provider.Name, err)
+ return handler.HandleHTTPError(err, c)
+ }
+
+ if cl.Email == "" {
+ log.Errorf("Claim does not contain an email address for provider %s", provider.Name)
+ return handler.HandleHTTPError(&user.ErrNoOpenIDEmailProvided{}, c)
}
s := db.NewSession()
@@ -140,12 +150,13 @@ func HandleCallback(c echo.Context) error {
u, err := getOrCreateUser(s, cl, idToken.Issuer, idToken.Subject)
if err != nil {
_ = s.Rollback()
- return err
+ log.Errorf("Error creating new user for provider %s: %v", provider.Name, err)
+ return handler.HandleHTTPError(err, c)
}
err = s.Commit()
if err != nil {
- return err
+ return handler.HandleHTTPError(err, c)
}
// Create token
diff --git a/pkg/user/error.go b/pkg/user/error.go
index 0328cfb8..6480d5ff 100644
--- a/pkg/user/error.go
+++ b/pkg/user/error.go
@@ -399,3 +399,29 @@ func (err ErrInvalidAvatarProvider) HTTPError() web.HTTPError {
Message: "Invalid avatar provider setting. See docs for valid types.",
}
}
+
+// ErrNoOpenIDEmailProvided represents a "NoEmailProvided" kind of error.
+type ErrNoOpenIDEmailProvided struct {
+}
+
+// IsErrNoEmailProvided checks if an error is a ErrNoOpenIDEmailProvided.
+func IsErrNoEmailProvided(err error) bool {
+ _, ok := err.(*ErrNoOpenIDEmailProvided)
+ return ok
+}
+
+func (err *ErrNoOpenIDEmailProvided) Error() string {
+ return "No email provided"
+}
+
+// ErrCodeNoOpenIDEmailProvided holds the unique world-error code of this error
+const ErrCodeNoOpenIDEmailProvided = 1019
+
+// HTTPError holds the http error description
+func (err *ErrNoOpenIDEmailProvided) HTTPError() web.HTTPError {
+ return web.HTTPError{
+ HTTPCode: http.StatusPreconditionFailed,
+ Code: ErrCodeNoOpenIDEmailProvided,
+ Message: "No email address available. Please make sure the openid provider publicly provides an email address for your account.",
+ }
+}