Repository: zitadel/oidc
Pull request #678 (2024-11-11 10:36:56)

fix: ignore empty json strings for locale
This change reverts fbf009fe75dac732dde39e0eb6fe324b337675e0 so that any non-value error is returned again.
Instead, a check is done for an empty JSON string.

Extra test cases of missing / null / empty values are also added.

This is an alternative solution for  https://github.com/zitadel/oidc/issues/672 

### Definition of Ready

- [x] I am happy with the code
- [x] Short description of the feature/issue is added in the pr description
- [x] PR is linked to the corresponding user story
- [x] Acceptance criteria are met
- [x] All open todos and follow ups are defined in a new ticket and justified
- [x] Deviations from the acceptance criteria and design are agreed with the PO and documented.
- [x] No debug or dead code
- [x] My code has no repetitions
- [x] Critical parts are tested automatically
- [x] Where possible E2E tests are implemented
- [x] Documentation/examples are up-to-date
- [x] All non-functional requirements are met
- [x] Functionality of the acceptance criteria is checked manually on the dev system.



muhlemmer: @muir can you have a look if this alternative does not re-introduce the okta issue you had?
muir: > @muir can you have a look if this alternative does not re-introduce the okta issue you had?

This should be fine. I don't think I would ever want authorization to be blocked by a bad local though -- I'm not using the locale and thus it has no value.

Patch:
diff --git a/pkg/oidc/types.go b/pkg/oidc/types.go
index 7063426..9b307bc 100644
--- a/pkg/oidc/types.go
+++ b/pkg/oidc/types.go
@@ -3,6 +3,7 @@ package oidc
 import (
 	"database/sql/driver"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"reflect"
 	"strings"
@@ -77,16 +78,25 @@ func (l *Locale) MarshalJSON() ([]byte, error) {
 }
 
 // UnmarshalJSON implements json.Unmarshaler.
-// All unmarshal errors for are ignored.
-// When an error is encountered, the containing tag will be set
+// When [language.ValueError] is encountered, the containing tag will be set
 // to an empty value (language "und") and no error will be returned.
 // This state can be checked with the `l.Tag().IsRoot()` method.
 func (l *Locale) UnmarshalJSON(data []byte) error {
+	if len(data) == 0 || string(data) == "\"\"" {
+		return nil
+	}
 	err := json.Unmarshal(data, &l.tag)
-	if err != nil {
+	if err == nil {
+		return nil
+	}
+
+	// catch "well-formed but unknown" errors
+	var target language.ValueError
+	if errors.As(err, &target) {
 		l.tag = language.Tag{}
+		return nil
 	}
-	return nil
+	return err
 }
 
 type Locales []language.Tag


Test patch:
diff --git a/pkg/oidc/types_test.go b/pkg/oidc/types_test.go
index c7ce0ee..53a9779 100644
--- a/pkg/oidc/types_test.go
+++ b/pkg/oidc/types_test.go
@@ -217,6 +217,30 @@ func TestLocale_UnmarshalJSON(t *testing.T) {
 		want    dst
 		wantErr bool
 	}{
+		{
+			name:    "value not present",
+			input:   `{}`,
+			wantErr: false,
+			want: dst{
+				Locale: nil,
+			},
+		},
+		{
+			name:    "null",
+			input:   `{"locale": null}`,
+			wantErr: false,
+			want: dst{
+				Locale: nil,
+			},
+		},
+		{
+			name:    "empty, ignored",
+			input:   `{"locale": ""}`,
+			wantErr: false,
+			want: dst{
+				Locale: &Locale{},
+			},
+		},
 		{
 			name:  "afrikaans, ok",
 			input: `{"locale": "af"}`,
@@ -232,23 +256,22 @@ func TestLocale_UnmarshalJSON(t *testing.T) {
 			},
 		},
 		{
-			name:  "bad form, error",
-			input: `{"locale": "g!!!!!"}`,
-			want: dst{
-				Locale: &Locale{},
-			},
+			name:    "bad form, error",
+			input:   `{"locale": "g!!!!!"}`,
+			wantErr: true,
 		},
 	}
-
 	for _, tt := range tests {
-		var got dst
-		err := json.Unmarshal([]byte(tt.input), &got)
-		if tt.wantErr {
-			require.Error(t, err)
-			return
-		}
-		require.NoError(t, err)
-		assert.Equal(t, tt.want, got)
+		t.Run(tt.name, func(t *testing.T) {
+			var got dst
+			err := json.Unmarshal([]byte(tt.input), &got)
+			if tt.wantErr {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+			assert.Equal(t, tt.want, got)
+		})
 	}
 }