diff --git a/pkg/oidc/types.go b/pkg/oidc/types.go index 415ab045..d80a8dc4 100644 --- a/pkg/oidc/types.go +++ b/pkg/oidc/types.go @@ -184,6 +184,35 @@ func NowTime() Time { return FromTime(time.Now()) } +func (ts *Time) UnmarshalJSON(data []byte) error { + var v any + if err := json.Unmarshal(data, &v); err != nil { + return fmt.Errorf("oidc.Time: %w", err) + } + + switch x := v.(type) { + case float64: + *ts = Time(x) + + case string: + // Compatibility with Auth0: + // https://github.com/zitadel/oidc/issues/292 + tt, err := time.Parse(time.RFC3339, x) + if err != nil { + return fmt.Errorf("oidc.Time: %w", err) + } + *ts = FromTime(tt) + + case nil: + *ts = 0 + + default: + return fmt.Errorf("oidc.Time: unable to parse type %T with value %v", x, x) + } + + return nil +} + type RequestObject struct { Issuer string `json:"iss"` Audience Audience `json:"aud"` diff --git a/pkg/oidc/types_test.go b/pkg/oidc/types_test.go index c8e28017..2721e0b7 100644 --- a/pkg/oidc/types_test.go +++ b/pkg/oidc/types_test.go @@ -466,3 +466,57 @@ func TestNewEncoder(t *testing.T) { schema.NewDecoder().Decode(&b, values) assert.Equal(t, a, b) } + +func TestTime_UnmarshalJSON(t *testing.T) { + type dst struct { + UpdatedAt Time `json:"updated_at"` + } + tests := []struct { + name string + json string + want dst + wantErr bool + }{ + { + name: "RFC3339", // https://github.com/zitadel/oidc/issues/292 + json: `{"updated_at": "2021-05-11T21:13:25.566Z"}`, + want: dst{UpdatedAt: 1620767605}, + }, + { + name: "int", + json: `{"updated_at":1620767605}`, + want: dst{UpdatedAt: 1620767605}, + }, + { + name: "time parse error", + json: `{"updated_at":"foo"}`, + wantErr: true, + }, + { + name: "null", + json: `{"updated_at":null}`, + }, + { + name: "invalid type", + json: `{"updated_at":["foo","bar"]}`, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got dst + err := json.Unmarshal([]byte(tt.json), &got) + if tt.wantErr { + assert.Error(t, err) + } else { + require.NoError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } + t.Run("syntax error", func(t *testing.T) { + var ts Time + err := ts.UnmarshalJSON([]byte{'~'}) + assert.Error(t, err) + }) +}