Repository: zitadel/oidc
Pull request #505 (2023-12-21 22:38:40)

fix(oidc): ignore unknown language tag in userinfo unmarshal
Open system reported an issue where a generic OpenID provider might return language tags like "gb".
These tags are well-formed but unknown and Go returns an error for it.
We already ignored unknown tags is ui_locale arrays lik in AuthRequest.

This change ignores singular unknown tags, like used in the userinfo `locale` claim.

### 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.



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zitadel/oidc/pull/505?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel) Report
Attention: `6 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`6a8e144`)](https://app.codecov.io/gh/zitadel/oidc/commit/6a8e144e8d37feb32ef3a412ad50a3dd08984e9f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel) 60.60% compared to head [(`bc1fbf5`)](https://app.codecov.io/gh/zitadel/oidc/pull/505?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel) 60.59%.

| [Files](https://app.codecov.io/gh/zitadel/oidc/pull/505?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel) | Patch % | Lines |
|---|---|---|
| [pkg/oidc/userinfo.go](https://app.codecov.io/gh/zitadel/oidc/pull/505?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel#diff-cGtnL29pZGMvdXNlcmluZm8uZ28=) | 14.28% | [4 Missing and 2 partials :warning: ](https://app.codecov.io/gh/zitadel/oidc/pull/505?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #505      +/-   ##
==========================================
- Coverage   60.60%   60.59%   -0.01%     
==========================================
  Files          78       78              
  Lines        6792     6806      +14     
==========================================
+ Hits         4116     4124       +8     
- Misses       2379     2383       +4     
- Partials      297      299       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/zitadel/oidc/pull/505?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zitadel).


Patch:
diff --git a/pkg/oidc/types.go b/pkg/oidc/types.go
index d8372b8..0e7152c 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"
@@ -76,8 +77,23 @@ func (l *Locale) MarshalJSON() ([]byte, error) {
 	return json.Marshal(tag)
 }
 
+// UnmarshalJSON implements json.Unmarshaler.
+// 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 {
-	return json.Unmarshal(data, &l.tag)
+	err := json.Unmarshal(data, &l.tag)
+	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 err
 }
 
 type Locales []language.Tag


Test patch:
diff --git a/pkg/oidc/types_test.go b/pkg/oidc/types_test.go
index af4f113..df93a73 100644
--- a/pkg/oidc/types_test.go
+++ b/pkg/oidc/types_test.go
@@ -208,20 +208,46 @@ func TestLocale_MarshalJSON(t *testing.T) {
 }
 
 func TestLocale_UnmarshalJSON(t *testing.T) {
-	type a struct {
+	type dst struct {
 		Locale *Locale `json:"locale,omitempty"`
 	}
-	want := a{
-		Locale: NewLocale(language.Afrikaans),
+	tests := []struct {
+		name    string
+		input   string
+		want    dst
+		wantErr bool
+	}{
+		{
+			name:  "afrikaans, ok",
+			input: `{"locale": "af"}`,
+			want: dst{
+				Locale: NewLocale(language.Afrikaans),
+			},
+		},
+		{
+			name:  "gb, ignored",
+			input: `{"locale": "gb"}`,
+			want: dst{
+				Locale: &Locale{},
+			},
+		},
+		{
+			name:    "bad form, error",
+			input:   `{"locale": "g!!!!!"}`,
+			wantErr: true,
+		},
 	}
 
-	const input = `{"locale": "af"}`
-	var got a
-
-	require.NoError(t,
-		json.Unmarshal([]byte(input), &got),
-	)
-	assert.Equal(t, want, got)
+	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)
+	}
 }
 
 func TestParseLocales(t *testing.T) {