identity: add tests for identity manager refresh

Add a test exercising the combined effect of session claims unmarshaling
and SetRawIDToken(), in preparation for refactoring how unmarshaling
works. This test should serve as an invariant, as in it should pass
both before and after the upcoming change.
This commit is contained in:
Kenneth Jenkins 2024-07-18 18:15:33 -07:00
parent e5e6558de6
commit dae8ece2cf
2 changed files with 124 additions and 0 deletions

View file

@ -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, &timestamppb.Timestamp{Seconds: exp}, s.IdToken.ExpiresAt)
assert.Equal(t, &timestamppb.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)
}

View file

@ -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, &timestamppb.Timestamp{Seconds: exp}, s.IdToken.ExpiresAt)
assert.Equal(t, &timestamppb.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)
}