137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
package oidc
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto"
|
||
|
"crypto/ecdsa"
|
||
|
"crypto/elliptic"
|
||
|
"crypto/rand"
|
||
|
"crypto/rsa"
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
coreosoidc "github.com/coreos/go-oidc"
|
||
|
"github.com/ory/fosite"
|
||
|
"github.com/ory/fosite/compose"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
"go.pinniped.dev/internal/oidc/jwks"
|
||
|
"gopkg.in/square/go-jose.v2"
|
||
|
)
|
||
|
|
||
|
func TestDynamicOpenIDConnectECDSAStrategy(t *testing.T) {
|
||
|
const (
|
||
|
goodIssuer = "https://some-good-issuer.com"
|
||
|
clientID = "some-client-id"
|
||
|
)
|
||
|
|
||
|
ecPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
issuer string
|
||
|
jwksProvider func(jwks.DynamicJWKSProvider)
|
||
|
wantError string
|
||
|
wantSigningJWK *jose.JSONWebKey
|
||
|
}{
|
||
|
{
|
||
|
name: "jwks provider does contain signing key for issuer",
|
||
|
issuer: goodIssuer,
|
||
|
jwksProvider: func(provider jwks.DynamicJWKSProvider) {
|
||
|
provider.SetIssuerToJWKSMap(
|
||
|
nil,
|
||
|
map[string]*jose.JSONWebKey{
|
||
|
goodIssuer: {
|
||
|
Key: ecPrivateKey,
|
||
|
},
|
||
|
},
|
||
|
)
|
||
|
},
|
||
|
wantSigningJWK: &jose.JSONWebKey{
|
||
|
Key: ecPrivateKey,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "jwks provider does not contain signing key for issuer",
|
||
|
issuer: goodIssuer,
|
||
|
wantError: "some unkonwn key error",
|
||
|
},
|
||
|
{
|
||
|
name: "jwks provider contains signing key of wrong type for issuer",
|
||
|
issuer: goodIssuer,
|
||
|
jwksProvider: func(provider jwks.DynamicJWKSProvider) {
|
||
|
provider.SetIssuerToJWKSMap(
|
||
|
nil,
|
||
|
map[string]*jose.JSONWebKey{
|
||
|
goodIssuer: {
|
||
|
Key: rsaPrivateKey,
|
||
|
},
|
||
|
},
|
||
|
)
|
||
|
},
|
||
|
wantError: "some invalid key type error",
|
||
|
},
|
||
|
}
|
||
|
for _, test := range tests {
|
||
|
test := test
|
||
|
t.Run(test.name, func(t *testing.T) {
|
||
|
jwksProvider := jwks.NewDynamicJWKSProvider()
|
||
|
if test.jwksProvider != nil {
|
||
|
test.jwksProvider(jwksProvider)
|
||
|
}
|
||
|
s := newDynamicOpenIDConnectECDSAStrategy(test.issuer, &compose.Config{}, jwksProvider)
|
||
|
|
||
|
requester := &fosite.Request{
|
||
|
Client: &fosite.DefaultClient{
|
||
|
ID: clientID,
|
||
|
},
|
||
|
// Session: fositeopenid.DefaultSession{},
|
||
|
}
|
||
|
idToken, err := s.GenerateIDToken(context.Background(), requester)
|
||
|
if test.wantError != "" {
|
||
|
require.EqualError(t, err, test.wantError)
|
||
|
} else {
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
// TODO: common-ize this code with token endpoint test.
|
||
|
// TODO: make more assertions about ID token
|
||
|
|
||
|
privateKey, ok := test.wantSigningJWK.Key.(*ecdsa.PrivateKey)
|
||
|
require.True(t, ok, "wanted private key to be *ecdsa.PrivateKey, but was %T", test.wantSigningJWK)
|
||
|
|
||
|
keySet := newStaticKeySet(privateKey.Public())
|
||
|
verifyConfig := coreosoidc.Config{
|
||
|
ClientID: clientID,
|
||
|
SupportedSigningAlgs: []string{coreosoidc.ES256},
|
||
|
}
|
||
|
verifier := coreosoidc.NewVerifier(test.issuer, keySet, &verifyConfig)
|
||
|
_, err := verifier.Verify(context.Background(), idToken)
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO: de-dep me.
|
||
|
func newStaticKeySet(publicKey crypto.PublicKey) coreosoidc.KeySet {
|
||
|
return &staticKeySet{publicKey}
|
||
|
}
|
||
|
|
||
|
type staticKeySet struct {
|
||
|
publicKey crypto.PublicKey
|
||
|
}
|
||
|
|
||
|
func (s *staticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) {
|
||
|
jws, err := jose.ParseSigned(jwt)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
|
||
|
}
|
||
|
return jws.Verify(s.publicKey)
|
||
|
}
|