diff --git a/pkg/identity/legacymanager/data_test.go b/pkg/identity/legacymanager/data_test.go index 14bb18e84..1324c4a26 100644 --- a/pkg/identity/legacymanager/data_test.go +++ b/pkg/identity/legacymanager/data_test.go @@ -1,12 +1,20 @@ package legacymanager import ( + "context" + "crypto" + "crypto/rand" + "crypto/rsa" "encoding/json" "fmt" "testing" "time" + go_oidc "github.com/coreos/go-oidc/v3/oidc" + "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v3/jwt" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -72,3 +80,57 @@ func TestSession_UnmarshalJSON(t *testing.T) { "some-other-claim": {Values: []*structpb.Value{protoutil.ToStruct("xyz")}}, }, s.Claims) } + +// Simulate the behavior during an oidc.Authenticator Refresh() call: +// SetRawIDToken() followed by a Claims() unmarshal call. +func TestSession_RefreshUpdate(t *testing.T) { + // Create a valid go_oidc.IDToken. This requires a real signing key. + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + iat := time.Now().Unix() + exp := iat + 3600 + payload := map[string]any{ + "iss": "https://issuer.example.com", + "aud": "https://client.example.com", + "sub": "subject", + "exp": exp, + "iat": iat, + "some-other-claim": "xyz", + } + jwtSigner, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: privateKey}, nil) + require.NoError(t, err) + rawIDToken, err := jwt.Signed(jwtSigner).Claims(payload).CompactSerialize() + require.NoError(t, err) + + keySet := &go_oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{privateKey.Public()}} + verifier := go_oidc.NewVerifier("https://issuer.example.com", keySet, &go_oidc.Config{ + ClientID: "https://client.example.com", + }) + + // Finally, we can obtain a go_oidc.IDToken. + idToken, err := verifier.Verify(context.Background(), rawIDToken) + require.NoError(t, err) + + // This is the behavior under test. + var s session.Session + v := &Session{Session: &s} + v.SetRawIDToken(rawIDToken) + err = idToken.Claims(v) + + assert.NoError(t, err) + assert.NotNil(t, s.IdToken) + assert.Equal(t, "https://issuer.example.com", s.IdToken.Issuer) + assert.Equal(t, "subject", s.IdToken.Subject) + assert.Equal(t, ×tamppb.Timestamp{Seconds: exp}, s.IdToken.ExpiresAt) + assert.Equal(t, ×tamppb.Timestamp{Seconds: iat}, s.IdToken.IssuedAt) + assert.Equal(t, map[string]*structpb.ListValue{ + "aud": { + Values: []*structpb.Value{structpb.NewStringValue("https://client.example.com")}, + }, + "some-other-claim": { + Values: []*structpb.Value{structpb.NewStringValue("xyz")}, + }, + }, s.Claims) + assert.Equal(t, rawIDToken, s.IdToken.Raw) +} diff --git a/pkg/identity/manager/data_test.go b/pkg/identity/manager/data_test.go index 3432589d2..244c96f03 100644 --- a/pkg/identity/manager/data_test.go +++ b/pkg/identity/manager/data_test.go @@ -1,12 +1,20 @@ package manager import ( + "context" + "crypto" + "crypto/rand" + "crypto/rsa" "encoding/json" "fmt" "testing" "time" + go_oidc "github.com/coreos/go-oidc/v3/oidc" + "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v3/jwt" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -68,3 +76,57 @@ func TestSession_UnmarshalJSON(t *testing.T) { "some-other-claim": {Values: []*structpb.Value{protoutil.ToStruct("xyz")}}, }, s.Claims) } + +// Simulate the behavior during an oidc.Authenticator Refresh() call: +// SetRawIDToken() followed by a Claims() unmarshal call. +func TestSession_RefreshUpdate(t *testing.T) { + // Create a valid go_oidc.IDToken. This requires a real signing key. + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + iat := time.Now().Unix() + exp := iat + 3600 + payload := map[string]any{ + "iss": "https://issuer.example.com", + "aud": "https://client.example.com", + "sub": "subject", + "exp": exp, + "iat": iat, + "some-other-claim": "xyz", + } + jwtSigner, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: privateKey}, nil) + require.NoError(t, err) + rawIDToken, err := jwt.Signed(jwtSigner).Claims(payload).CompactSerialize() + require.NoError(t, err) + + keySet := &go_oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{privateKey.Public()}} + verifier := go_oidc.NewVerifier("https://issuer.example.com", keySet, &go_oidc.Config{ + ClientID: "https://client.example.com", + }) + + // Finally, we can obtain a go_oidc.IDToken. + idToken, err := verifier.Verify(context.Background(), rawIDToken) + require.NoError(t, err) + + // This is the behavior under test. + var s session.Session + v := newSessionUnmarshaler(&s) + v.SetRawIDToken(rawIDToken) + err = idToken.Claims(v) + + assert.NoError(t, err) + assert.NotNil(t, s.IdToken) + assert.Equal(t, "https://issuer.example.com", s.IdToken.Issuer) + assert.Equal(t, "subject", s.IdToken.Subject) + assert.Equal(t, ×tamppb.Timestamp{Seconds: exp}, s.IdToken.ExpiresAt) + assert.Equal(t, ×tamppb.Timestamp{Seconds: iat}, s.IdToken.IssuedAt) + assert.Equal(t, map[string]*structpb.ListValue{ + "aud": { + Values: []*structpb.Value{structpb.NewStringValue("https://client.example.com")}, + }, + "some-other-claim": { + Values: []*structpb.Value{structpb.NewStringValue("xyz")}, + }, + }, s.Claims) + assert.Equal(t, rawIDToken, s.IdToken.Raw) +}