Skip to content

Commit

Permalink
Merge pull request #74 from sean-rn/given-keys-from-json
Browse files Browse the repository at this point in the history
Permit construction of a GivenKeys map from JSON
  • Loading branch information
MicahParks committed Nov 30, 2022
2 parents 9bc59bf + df26d2a commit 8c0f8a9
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 0 deletions.
17 changes: 17 additions & 0 deletions given.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"encoding/json"
)

// GivenKey represents a cryptographic key that resides in a JWKS. In conjuncture with Options.
Expand All @@ -12,6 +13,7 @@ type GivenKey struct {
inter interface{}
}

// GivenKeyOptions represents the configuration options for a GivenKey.
type GivenKeyOptions struct {
// Algorithm is the given key's signing algorithm. Its value will be compared to unverified tokens' "alg" header.
//
Expand Down Expand Up @@ -147,3 +149,18 @@ func NewGivenRSACustomWithOptions(key *rsa.PublicKey, options GivenKeyOptions) (
inter: key,
}
}

// NewGivenKeysFromJSON parses a raw JSON message into a map of key IDs (`kid`) to GivenKeys.
// The returned map is suitable for suitable for passing to `NewGiven()` or as `Options.GivenKeys` to `Get()`
func NewGivenKeysFromJSON(jwksBytes json.RawMessage) (map[string]GivenKey, error) {
// Parse by making a temporary JWKS instance. No need to lock its map since it doesn't escape this function.
j, err := NewJSON(jwksBytes)
if err != nil {
return nil, err
}
keys := make(map[string]GivenKey, len(j.keys))
for kid, cryptoKey := range j.keys {
keys[kid] = NewGivenCustomWithOptions(cryptoKey.public, GivenKeyOptions{Algorithm: cryptoKey.algorithm})
}
return keys, nil
}
38 changes: 38 additions & 0 deletions given_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,44 @@ func TestNewGivenKeyRSA(t *testing.T) {
signParseValidate(t, token, key, jwks)
}

// TestNewGivenKeysFromJSON tests that parsing GivenKeys from JSON can be used to create a JWKS and a proper jwt.Keyfunc.
func TestNewGivenKeysFromJSON(t *testing.T) {
// Construct a JWKS JSON containing a JWK for which we know the private key and thus can sign a token.
key := []byte("test-hmac-secret")
const testJSON = `{
"keys": [
{
"kid": "testkid",
"kty": "oct",
"alg": "HS256",
"use": "sig",
"k": "dGVzdC1obWFjLXNlY3JldA"
}
]
}`

givenKeys, err := keyfunc.NewGivenKeysFromJSON([]byte(testJSON))
if err != nil {
t.Fatalf(logFmt, "Failed to parse given keys from JSON.", err)
}

jwks := keyfunc.NewGiven(givenKeys)

token := jwt.New(jwt.SigningMethodHS256)
token.Header[kidAttribute] = testKID

signParseValidate(t, token, key, jwks)
}

// TestNewGivenKeysFromJSON_BadParse makes sure bad JSON returns an error.
func TestNewGivenKeysFromJSON_BadParse(t *testing.T) {
const testJSON = "{not the best syntax"
_, err := keyfunc.NewGivenKeysFromJSON([]byte(testJSON))
if err == nil {
t.Fatalf("Expected a JSON parse error")
}
}

// addCustom adds a new key wto the given keys map. The new key is using a test jwt.SigningMethod.
func addCustom(givenKeys map[string]keyfunc.GivenKey, kid string) (key string) {
key = ""
Expand Down

0 comments on commit 8c0f8a9

Please sign in to comment.