Skip to content

Commit

Permalink
Merge pull request #337 from ericchiang/static
Browse files Browse the repository at this point in the history
oidc: add StaticKeySet
  • Loading branch information
ericchiang authored May 11, 2022
2 parents 3c48294 + 5011cdf commit 26c5037
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 28 deletions.
32 changes: 32 additions & 0 deletions oidc/jwks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package oidc

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"errors"
"fmt"
"io/ioutil"
Expand All @@ -12,6 +15,35 @@ import (
jose "gopkg.in/square/go-jose.v2"
)

// StaticKeySet is a verifier that validates JWT against a static set of public keys.
type StaticKeySet struct {
// PublicKeys used to verify the JWT. Supported types are *rsa.PublicKey and
// *ecdsa.PublicKey.
PublicKeys []crypto.PublicKey
}

// VerifySignature compares the signature against a static set of public keys.
func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) {
jws, err := jose.ParseSigned(jwt)
if err != nil {
return nil, fmt.Errorf("parsing jwt: %v", err)
}
for _, pub := range s.PublicKeys {
switch pub.(type) {
case *rsa.PublicKey:
case *ecdsa.PublicKey:
default:
return nil, fmt.Errorf("invalid public key type provided: %T", pub)
}
payload, err := jws.Verify(pub)
if err != nil {
continue
}
return payload, nil
}
return nil, fmt.Errorf("no public keys able to verify jwt")
}

// NewRemoteKeySet returns a KeySet that can validate JSON web tokens by using HTTP
// GETs to fetch JSON web token sets hosted at a remote URL. This is automatically
// used by NewProvider using the URLs returned by OpenID Connect discovery, but is
Expand Down
1 change: 0 additions & 1 deletion oidc/jwks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ func (s *signingKey) sign(t *testing.T, payload []byte) string {
return data
}

// jwk returns the public part of the signing key.
func (s *signingKey) jwk() jose.JSONWebKey {
return jose.JSONWebKey{Key: s.pub, Use: "sig", Algorithm: string(s.alg), KeyID: s.keyID}
}
Expand Down
11 changes: 3 additions & 8 deletions oidc/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,10 @@ type IDTokenVerifier struct {
// keySet := oidc.NewRemoteKeySet(ctx, "https://www.googleapis.com/oauth2/v3/certs")
// verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
//
// Since KeySet is an interface, this constructor can also be used to supply custom
// public key sources. For example, if a user wanted to supply public keys out-of-band
// and hold them statically in-memory:
// Or a static key set (e.g. for testing):
//
// // Custom KeySet implementation.
// keySet := newStatisKeySet(publicKeys...)
//
// // Verifier uses the custom KeySet implementation.
// verifier := oidc.NewVerifier("https://auth.example.com", keySet, config)
// keySet := &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{pub1, pub2}}
// verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
//
func NewVerifier(issuerURL string, keySet KeySet, config *Config) *IDTokenVerifier {
return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL}
Expand Down
24 changes: 5 additions & 19 deletions oidc/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,16 @@ package oidc

import (
"context"
"fmt"
"crypto"
"io"
"net/http"
"net/http/httptest"
"reflect"
"strconv"
"testing"
"time"

jose "gopkg.in/square/go-jose.v2"
)

type testVerifier struct {
jwk jose.JSONWebKey
}

func (t *testVerifier) 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(&t.jwk)
}

func TestVerify(t *testing.T) {
tests := []verificationTest{
{
Expand Down Expand Up @@ -513,9 +499,9 @@ func (v resolverTest) testEndpoint(t *testing.T) ([]byte, error) {
issuer := v.issuer
var ks KeySet
if v.verificationKey == nil {
ks = &testVerifier{v.signKey.jwk()}
ks = &StaticKeySet{PublicKeys: []crypto.PublicKey{v.signKey.pub}}
} else {
ks = &testVerifier{v.verificationKey.jwk()}
ks = &StaticKeySet{PublicKeys: []crypto.PublicKey{v.verificationKey.pub}}
}
verifier := NewVerifier(issuer, ks, &v.config)

Expand Down Expand Up @@ -560,9 +546,9 @@ func (v verificationTest) runGetToken(t *testing.T) (*IDToken, error) {
}
var ks KeySet
if v.verificationKey == nil {
ks = &testVerifier{v.signKey.jwk()}
ks = &StaticKeySet{PublicKeys: []crypto.PublicKey{v.signKey.pub}}
} else {
ks = &testVerifier{v.verificationKey.jwk()}
ks = &StaticKeySet{PublicKeys: []crypto.PublicKey{v.verificationKey.pub}}
}
verifier := NewVerifier(issuer, ks, &v.config)

Expand Down

0 comments on commit 26c5037

Please sign in to comment.