From 5344a7f584b1858ee68ad1a904cb519e09ab8dbe Mon Sep 17 00:00:00 2001 From: Gayathri Vijayan <66356931+grvijayan@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:44:02 +0200 Subject: [PATCH] fix: invalid jwt assertion error handling (#11933) # Which Problems Are Solved Incorrectly mapped errors on the `/oauth/v2/token` endpoint lead to a large number of HTTP status 500 errors. # How the Problems Are Solved By mapping invalid jwt assertion errors to OIDC `invalid_client` errors, which returns a HTTP status 400. # Additional Changes N/A # Additional Context - Closes https://github.com/zitadel/zitadel/issues/11924 - Follow-up: - Fix the [error handling](https://github.com/zitadel/oidc/blob/main/pkg/oidc/verifier.go#L195) in the oidc package --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../token_jwt_profile_test.go | 32 +++++++++++++------ internal/api/oidc/token_jwt_profile.go | 3 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/internal/api/oidc/integration_test/token_jwt_profile_test.go b/internal/api/oidc/integration_test/token_jwt_profile_test.go index b4ae0d777..c01cd6659 100644 --- a/internal/api/oidc/integration_test/token_jwt_profile_test.go +++ b/internal/api/oidc/integration_test/token_jwt_profile_test.go @@ -26,6 +26,10 @@ func TestServer_JWTProfile(t *testing.T) { _, _, keyDataExpired, err := Instance.CreateOIDCJWTProfileClient(CTX, 10*time.Second) require.NoError(t, err) + project := Instance.CreateProject(CTX, t, "", integration.ProjectName(), false, false) + _, appKeyData, err := Instance.CreateOIDCWebClientJWT(CTX, redirectURI, logoutRedirectURI, project.GetId()) + require.NoError(t, err) + type claims struct { name string username string @@ -36,11 +40,12 @@ func TestServer_JWTProfile(t *testing.T) { orgDomain any } tests := []struct { - name string - keyData []byte - scope []string - wantClaims claims - wantErr bool + name string + keyData []byte + scope []string + wantClaims claims + wantErr bool + expectedErr error }{ { name: "success", @@ -107,10 +112,18 @@ func TestServer_JWTProfile(t *testing.T) { }, }, { - name: "key expired", - keyData: keyDataExpired, - scope: []string{oidc.ScopeOpenID}, - wantErr: true, + name: "key expired", + keyData: keyDataExpired, + scope: []string{oidc.ScopeOpenID}, + wantErr: true, + expectedErr: oidc.ErrInvalidGrant().WithDescription("invalid assertion"), + }, + { + name: "key type application", + keyData: appKeyData, + scope: []string{oidc.ScopeOpenID}, + wantErr: true, + expectedErr: oidc.ErrInvalidGrant().WithDescription("invalid assertion"), }, } for _, tt := range tests { @@ -132,6 +145,7 @@ func TestServer_JWTProfile(t *testing.T) { time.Minute, time.Second, ) if tt.wantErr { + assert.ErrorIs(t, err, tt.expectedErr) return } diff --git a/internal/api/oidc/token_jwt_profile.go b/internal/api/oidc/token_jwt_profile.go index cbcd069c2..989e5a816 100644 --- a/internal/api/oidc/token_jwt_profile.go +++ b/internal/api/oidc/token_jwt_profile.go @@ -8,6 +8,7 @@ import ( "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/op" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" @@ -76,7 +77,7 @@ func (s *Server) verifyJWTProfile(ctx context.Context, req *oidc.JWTProfileGrant ) _, err = op.VerifyJWTAssertion(ctx, req.Assertion, verifier) if err != nil { - return nil, err + return nil, oidc.ErrInvalidGrant().WithParent(err).WithReturnParentToClient(authz.GetFeatures(ctx).DebugOIDCParentError).WithDescription("invalid assertion") } return storage.user, nil }