fix(oidc): return bad request for base64 errors (#7730)

* fix(oidc): return bad request for base64 errors

We've recently noticed an increased amount of 500: internal server error status returns on zitadel cloud.
The source of these errors appear to be erroneous input in fields that are supposed to be bas64 formatted.

```
time=2024-04-08T14:05:47.600Z level=ERROR msg="request error" oidc_error.parent="ID=OIDC-AhX2u Message=Errors.Internal Parent=(illegal base64 data at input byte 8)" oidc_error.description=Errors.Internal oidc_error.type=server_error status_code=500
```

Within the possible code paths of the token endpoint there are a couple of uses of base64.Encoding.DecodeString of which a returned error was not properly wrapped, but returned as-is.
This causes the oidc error handler to return a 500 with the `OIDC-AhX2u` ID.
We were not able to pinpoint the exact errors that are happening to any one call of `DecodeString`.

This fix wraps all errors from `DecodeString` so that proper 400: bad request is returned with information about the error. Each wrapper now has an unique error ID, so that logs will contain the source of the error as well.

This bug was reported internally by the ops team.

* catch op.ErrInvalidRefreshToken

(cherry picked from commit c8e0b30e17)
This commit is contained in:
Tim Möhlmann 2024-04-09 09:42:59 +03:00 committed by Livio Spring
parent 63c7364159
commit 7a34697267
No known key found for this signature in database
GPG Key ID: 26BB1C2FA5952CF0
5 changed files with 8 additions and 6 deletions

View File

@ -19,7 +19,7 @@ func SessionTokenVerifier(algorithm crypto.EncryptionAlgorithm) func(ctx context
return func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
decodedToken, err := base64.RawURLEncoding.DecodeString(sessionToken)
if err != nil {
return err
return zerrors.ThrowInvalidArgument(err, "COMMAND-hi6Ph", "Errors.Session.Token.Invalid")
}
_, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
token, err := algorithm.DecryptString(decodedToken, algorithm.EncryptionKeyID())

View File

@ -157,7 +157,7 @@ func (o *OPStorage) AuthRequestByCode(ctx context.Context, code string) (_ op.Au
plainCode, err := o.decryptGrant(code)
if err != nil {
return nil, err
return nil, zerrors.ThrowInvalidArgument(err, "OIDC-ahLi2", "Errors.User.Code.Invalid")
}
if strings.HasPrefix(plainCode, command.IDPrefixV2) {
authReq, err := o.command.ExchangeAuthCode(ctx, plainCode)
@ -311,7 +311,7 @@ func (o *OPStorage) TokenRequestByRefreshToken(ctx context.Context, refreshToken
plainToken, err := o.decryptGrant(refreshToken)
if err != nil {
return nil, err
return nil, op.ErrInvalidRefreshToken
}
if strings.HasPrefix(plainToken, command.IDPrefixV2) {
oidcSession, err := o.command.OIDCSessionByRefreshToken(ctx, plainToken)

View File

@ -19,7 +19,9 @@ func oidcError(err error) error {
if err == nil {
return nil
}
if errors.Is(err, op.ErrInvalidRefreshToken) {
err = zerrors.ThrowInvalidArgument(err, "OIDCS-ef2Gi", "Errors.User.RefreshToken.Invalid")
}
var (
sError op.StatusError
oError *oidc.Error

View File

@ -207,7 +207,7 @@ func (c *Commands) getResourceOwnerOfSessionUser(ctx context.Context, userID, in
func (c *Commands) decryptRefreshToken(refreshToken string) (refreshTokenID string, err error) {
decoded, err := base64.RawURLEncoding.DecodeString(refreshToken)
if err != nil {
return "", err
return "", zerrors.ThrowInvalidArgument(err, "OIDCS-Cux9a", "Errors.User.RefreshToken.Invalid")
}
decrypted, err := c.keyAlgorithm.DecryptString(decoded, c.keyAlgorithm.EncryptionKeyID())
if err != nil {

View File

@ -23,7 +23,7 @@ func RefreshToken(userID, tokenID, token string, algorithm crypto.EncryptionAlgo
func FromRefreshToken(refreshToken string, algorithm crypto.EncryptionAlgorithm) (userID, tokenID, token string, err error) {
decoded, err := base64.RawURLEncoding.DecodeString(refreshToken)
if err != nil {
return "", "", "", err
return "", "", "", zerrors.ThrowInvalidArgument(err, "DOMAIN-BGDhn", "Errors.User.RefreshToken.Invalid")
}
decrypted, err := algorithm.Decrypt(decoded, algorithm.EncryptionKeyID())
if err != nil {