-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Logging in with KeyCloak OpenID Connect: 500 error #9907
Comments
Can you provide the log trace of the 500 error? Please see https://docs.gitea.io/en-us/logging-configuration/ for how to enable Trace log level. |
I have the same kind of error using Jetbrains Hub as OpenID provider, logging in via "OpenID Connect" results in a 500 page and the following log line: Here is a stack trace:
I also discovered that if I clear the browser cache then another error is produced in the logs a bit before the
I think that the Thank you very much for your effort. King regards, Jens |
I also get the UserSignIn: securecookie: the value is too long. We use JetBrains Hub as OpenID server. At one point in time it worked but I updated both Hub and Gitea at the same time so I don't know which one broke it. I have a workaround by using LDAP for now (which is fine). |
Seeing the same issue when using Jetbrains Hub as OpenID server. After an update to Hub, some users are getting 500 errors when logging in, with this corresponding error in the Gitea logs:
To work around this for the time being, we have removed custom avatars in Hub and replaced them with the default avatar to reduce the size of the cookie. |
I can confirm that disabling the avatar image or reducing the avatar image size on the Hub side is a workaround for this problem. |
@jan0sch do you have an idea of how big these cookies get? |
For those who wish to hack a solution quickly to this the problem appears to be: gitea/modules/auth/oauth2/oauth2.go Line 61 in fcc8cdd
Sets the maximum token/cookie length at 32767 - this needs to be increased to higher number - quite what though I'm uncertain. |
To understand why that is the root cause of the problem is quite convoluted. The error is a Line 583 in fcc8cdd
That is: Lines 667 to 708 in fcc8cdd
In particular Line 670: Line 670 in fcc8cdd
That is: gitea/modules/auth/oauth2/oauth2.go Lines 91 to 103 in fcc8cdd
In particular line 97 /*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".
See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
defer Logout(res, req)
if !keySet && defaultStore == Store {
fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
}
providerName, err := GetProviderName(req)
if err != nil {
return goth.User{}, err
}
provider, err := goth.GetProvider(providerName)
if err != nil {
return goth.User{}, err
}
value, err := GetFromSession(providerName, req)
if err != nil {
return goth.User{}, err
}
sess, err := provider.UnmarshalSession(value)
if err != nil {
return goth.User{}, err
}
err = validateState(req, sess)
if err != nil {
return goth.User{}, err
}
user, err := provider.FetchUser(sess)
if err == nil {
// user can be found with existing session data
return user, err
}
// get new token and retry fetch
_, err = sess.Authorize(provider, req.URL.Query())
if err != nil {
return goth.User{}, err
}
err = StoreInSession(providerName, sess.Marshal(), req, res)
if err != nil {
return goth.User{}, err
}
gu, err := provider.FetchUser(sess)
return gu, err
} In particular // StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
session, _ := Store.New(req, SessionName)
if err := updateSessionValue(session, key, value); err != nil {
return err
}
return session.Save(req, res)
} And // Save is a convenience method to save this session. It is the same as calling
// store.Save(request, response, session). You should call Save before writing to
// the response or returning from the handler.
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
return s.store.Save(r, w, s)
} with // Save session and set cookie header
func (st *Store) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
s, _ := context.Get(r, contextKey(session.Name())).(*xormSession)
// delete if max age is < 0
if session.Options.MaxAge < 0 {
if s != nil {
if _, err := st.e.Delete(&xormSession{
ID: session.ID,
tableName: st.opts.TableName,
}); err != nil {
return err
}
}
http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
return nil
}
data, err := securecookie.EncodeMulti(session.Name(), session.Values, st.Codecs...)
if err != nil {
return err
}
now := util.TimeStampNow()
expire := now.AddDuration(time.Second * time.Duration(session.Options.MaxAge))
if s == nil {
// generate random session ID key suitable for storage in the db
session.ID = strings.TrimRight(
base32.StdEncoding.EncodeToString(
securecookie.GenerateRandomKey(sessionIDLen)), "=")
s = &xormSession{
ID: session.ID,
Data: data,
CreatedUnix: now,
UpdatedUnix: now,
ExpiresUnix: expire,
tableName: st.opts.TableName,
}
if _, err := st.e.Insert(s); err != nil {
return err
}
context.Set(r, contextKey(session.Name()), s)
} else {
s.Data = data
s.UpdatedUnix = now
s.ExpiresUnix = expire
if _, err := st.e.ID(s.ID).Cols("data", "updated_unix", "expires_unix").Update(s); err != nil {
return err
}
}
// set session id cookie
id, err := securecookie.EncodeMulti(session.Name(), session.ID, st.Codecs...)
if err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), id, session.Options))
return nil
} Likely error is coming from: data, err := securecookie.EncodeMulti(session.Name(), session.Values, st.Codecs...) but could be: id, err := securecookie.EncodeMulti(session.Name(), session.ID, st.Codecs...) These both call: // EncodeMulti encodes a cookie value using a group of codecs.
//
// The codecs are tried in order. Multiple codecs are accepted to allow
// key rotation.
//
// On error, may return a MultiError.
func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) {
if len(codecs) == 0 {
return "", errNoCodecs
}
var errors MultiError
for _, codec := range codecs {
encoded, err := codec.Encode(name, value)
if err == nil {
return encoded, nil
}
errors = append(errors, err)
}
return "", errors
} With the root error coming from // Encode encodes a cookie value.
//
// It serializes, optionally encrypts, signs with a message authentication code,
// and finally encodes the value.
//
// The name argument is the cookie name. It is stored with the encoded value.
// The value argument is the value to be encoded. It can be any value that can
// be encoded using the currently selected serializer; see SetSerializer().
//
// It is the client's responsibility to ensure that value, when encoded using
// the current serialization/encryption settings on s and then base64-encoded,
// is shorter than the maximum permissible length.
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
if s.err != nil {
return "", s.err
}
if s.hashKey == nil {
s.err = errHashKeyNotSet
return "", s.err
}
var err error
var b []byte
// 1. Serialize.
if b, err = s.sz.Serialize(value); err != nil {
return "", cookieError{cause: err, typ: usageError}
}
// 2. Encrypt (optional).
if s.block != nil {
if b, err = encrypt(s.block, b); err != nil {
return "", cookieError{cause: err, typ: usageError}
}
}
b = encode(b)
// 3. Create MAC for "name|date|value". Extra pipe to be used later.
b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
// Append mac, remove name.
b = append(b, mac...)[len(name)+1:]
// 4. Encode to base64.
b = encode(b)
// 5. Check length.
if s.maxLength != 0 && len(b) > s.maxLength {
return "", errEncodedValueTooLong
}
// Done.
return string(b), nil
} In particular: return "", errEncodedValueTooLong |
Some OAuth2 providers return quite large structured tokens >32767 bytes. Gitea currently has a fixed maximum of 32767 bytes for these and unfortunately due to the convoluted nature of the dependent libraries the error returned is rather opaque. Here we manage the error a little better - detecting the rather opaque github.com/gorilla/securecookie.errEncodedValueTooLong and converting it to a more readable error. Further we provide a configurable option to increase the maximum size of the provided OAuth2 tokens. Fix go-gitea#9907 Signed-off-by: Andrew Thornton <[email protected]>
I guess that the value can get quite large if the avatar image is sent (encoded) with it. I haven't measured though. |
Some OAuth2 providers return quite large structured tokens >32767 bytes. Gitea currently has a fixed maximum of 32767 bytes for these and unfortunately due to the convoluted nature of the dependent libraries the error returned is rather opaque. Here we manage the error a little better - detecting the rather opaque github.com/gorilla/securecookie.errEncodedValueTooLong and converting it to a more readable error. Further we provide a configurable option to increase the maximum size of the provided OAuth2 tokens. Fix #9907 Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: techknowlogick <[email protected]>
…1180) Some OAuth2 providers return quite large structured tokens >32767 bytes. Gitea currently has a fixed maximum of 32767 bytes for these and unfortunately due to the convoluted nature of the dependent libraries the error returned is rather opaque. Here we manage the error a little better - detecting the rather opaque github.com/gorilla/securecookie.errEncodedValueTooLong and converting it to a more readable error. Further we provide a configurable option to increase the maximum size of the provided OAuth2 tokens. Fix go-gitea#9907 Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: techknowlogick <[email protected]>
[x]
):Description
With logging in with OpenID Connect (KeyCloak 7.0.1 and 8.0.1), gitea returns a 500 Internal Server Error web.
And then we find when the first and last name of user in KeyCloak are both Chinese charactors, gitea returns a 500 error, and if not, gitea works with KeyCloak well.
We changed the version of gitea and keycloak, it always exists. We try to log in other apps with KeyCloak, it has no error.
Screenshots
The text was updated successfully, but these errors were encountered: