...

Source file src/github.com/MicahParks/keyfunc/v2/jwks_test.go

Documentation: github.com/MicahParks/keyfunc/v2

     1  package keyfunc_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/ecdsa"
     7  	"crypto/ed25519"
     8  	"crypto/rsa"
     9  	_ "embed"
    10  	"errors"
    11  	"fmt"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"sync"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/golang-jwt/jwt/v5"
    22  
    23  	"github.com/MicahParks/keyfunc/v2"
    24  )
    25  
    26  const (
    27  	// emptyJWKSJSON is a hard-coded empty JWKS in JSON format.
    28  	emptyJWKSJSON = `{"keys":[]}`
    29  
    30  	// logFmt is an error log formatting string.
    31  	logFmt = "%s\nError: %s"
    32  
    33  	// jwksFilePath is the full path of th JWKS file on the test HTTP server.
    34  	jwksFilePath = "/example_jwks.json"
    35  
    36  	// tokenUseEnc is a token encrypted with a key whose "use" parameter is "enc".
    37  	tokenUseEnc = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImtpZFdpdGhCYWRVc2UiLCJ0eXAiOiJKV1QifQ.eyJmb28iOiJiYXIifQ.NKUjRgfqNZNCckL2yyiZc0ot_-BxtwYiknrILmsSnNapkOB32gMfRPTyc_j-UsIqw19FrDSBNk31blxSW40X3ubXp56hpwbcqE0nj9EvDyZoUWmtMl6pXIGPnTK5y-rNgS8i1IeeejNAQDYe8LOtCw_jE8CpOW5MBZzxdwjntPHGCWu4FCgrBu1ugth20B7WnuCHETa0xQ2NvXIX0W54JDbk_hdWTqjP4Bo7BvcGB6-5xZ1AaiiXjnOOuBrIMwTrZ-wtdTOjmSaWrcH94A8wDk263fSkhRLjM77d5IljIILT4a6nRHVSsgBfhblYevtX6NWBgllvQ_Hr_uuaT_b15A"
    38  )
    39  
    40  var (
    41  	// jwksJSON is a embedded JWKS in JSON format.
    42  	//go:embed example_jwks.json
    43  	jwksJSON string
    44  )
    45  
    46  // TestInvalidServer performs initialization + refresh initialization with a server providing invalid data.
    47  // The test ensures that background refresh goroutine does not cause any trouble in case of init failure.
    48  func TestInvalidServer(t *testing.T) {
    49  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
    50  		_, err := w.Write(nil)
    51  		if err != nil {
    52  			t.Fatalf(logFmt, "Failed to write empty response.", err)
    53  		}
    54  	}))
    55  	defer server.Close()
    56  
    57  	testingRefreshErrorHandler := func(err error) {
    58  		t.Fatalf(logFmt, "Unhandled JWKS error.", err)
    59  	}
    60  
    61  	refreshInterval := time.Second
    62  	options := keyfunc.Options{
    63  		RefreshInterval:     refreshInterval,
    64  		RefreshErrorHandler: testingRefreshErrorHandler,
    65  	}
    66  
    67  	_, err := keyfunc.Get(server.URL, options)
    68  	if err == nil {
    69  		t.Fatalf("Creation of *keyfunc.JWKS with invalid server must fail.")
    70  	}
    71  }
    72  
    73  // TestJWKS performs a table test on the JWKS code.
    74  func TestJWKS(t *testing.T) {
    75  	tempDir, err := os.MkdirTemp("", "*")
    76  	if err != nil {
    77  		t.Fatalf(logFmt, "Failed to create a temporary directory.", err)
    78  	}
    79  	defer func() {
    80  		err = os.RemoveAll(tempDir)
    81  		if err != nil {
    82  			t.Fatalf(logFmt, "Failed to remove temporary directory.", err)
    83  		}
    84  	}()
    85  
    86  	jwksFile := filepath.Join(tempDir, jwksFilePath)
    87  
    88  	err = os.WriteFile(jwksFile, []byte(jwksJSON), 0600)
    89  	if err != nil {
    90  		t.Fatalf(logFmt, "Failed to write JWKS file to temporary directory.", err)
    91  	}
    92  
    93  	server := httptest.NewServer(http.FileServer(http.Dir(tempDir)))
    94  	defer server.Close()
    95  
    96  	testingRefreshInterval := time.Second
    97  	testingRateLimit := time.Millisecond * 500
    98  	testingRefreshTimeout := time.Second
    99  	testingRefreshErrorHandler := func(err error) {
   100  		panic(fmt.Sprintf(logFmt, "Unhandled JWKS error.", err))
   101  	}
   102  
   103  	jwksURL := server.URL + jwksFilePath
   104  
   105  	options := []keyfunc.Options{
   106  		{}, // Default options.
   107  		{
   108  			Client: http.DefaultClient, // Should be ineffectual. Just for code coverage.
   109  		},
   110  		{
   111  			Ctx: context.Background(), // Should be ineffectual. Just for code coverage.
   112  		},
   113  		{
   114  			RefreshErrorHandler: testingRefreshErrorHandler,
   115  		},
   116  		{
   117  			RefreshInterval: testingRefreshInterval,
   118  		},
   119  		{
   120  			RefreshRateLimit: testingRateLimit,
   121  		},
   122  		{
   123  			RefreshTimeout: testingRefreshTimeout,
   124  		},
   125  	}
   126  
   127  	for _, opts := range options {
   128  		jwks, err := keyfunc.Get(jwksURL, opts)
   129  		if err != nil {
   130  			t.Fatalf(logFmt, "Failed to get JWKS from testing URL.", err)
   131  		}
   132  
   133  		testCases := []struct {
   134  			token string
   135  		}{
   136  			{""}, // Empty JWT.
   137  			{"eyJhbGciOiJFUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJDR3QwWldTNExjNWZhaUtTZGkwdFUwZmpDQWR2R1JPUVJHVTlpUjd0VjBBIn0.eyJleHAiOjE2MTU0MDY4NjEsImlhdCI6MTYxNTQwNjgwMSwianRpIjoiYWVmOWQ5YjItN2EyYy00ZmQ4LTk4MzktODRiMzQ0Y2VmYzZhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.iQ77QGoPDNjR2oWLu3zT851mswP8J-h_nrGhs3fpa_tFB3FT1deKPGkjef9JOTYFI-CIVxdCFtW3KODOaw9Nrw"},                                                                                                                                                                                                                                                                 // Signing algorithm ES256.
   138  			{"eyJhbGciOiJFUzM4NCIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJUVkFBZXQ2M08zeHlfS0s2X2J4Vkl1N1JhM196MXdsQjU0M0Zid2k1VmFVIn0.eyJleHAiOjE2MTU0MDY4OTAsImlhdCI6MTYxNTQwNjgzMCwianRpIjoiYWNhNDU4NTItZTE0ZS00MjgxLTljZTQtN2ZiNzVkMTg1MWJmIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.oHFT-RvbNNT6p4_tIoZzr4IS88bZqy20cJhF6FZCIXALZ2dppoOjutanPVxzuLC5axG3P71noVghNUF8X44bTShP1boLrlde2QKmj5GxDR-oNEb9ES_zC10rZ5I76CwR"},                                                                                                                                                                                                                       // Signing algorithm ES384.
   139  			{"eyJhbGciOiJFUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJlYkp4bm05QjNRREJsakI1WEpXRXU3MnF4NkJhd0RhTUFod3o0YUtQa1EwIn0.eyJleHAiOjE2MTU0MDY5MDksImlhdCI6MTYxNTQwNjg0OSwianRpIjoiMjBhMGI1MTMtN2E4My00OGQ2LThmNDgtZmQ3NDc1N2Y4OWRiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.AdR59BCvGlctL5BMgXlpJBBToKTPG4SVa-oJKBqE7qxvTSBwAQM5D3uUc2toM3NAUERSMKOLTJfzfxenNRixrDMnAcrdFHgEY10vsDp6uqA7NMUevHE5f7jiAVK1talXS9O41IEnR2DKbAG0GgjIA2WHLhUgftG2uNN8LMKI2QSbLCfM"},                                                                                                                                                                       // Signing algorithm ES512.
   140  			{"eyJhbGciOiJFUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJlYkp4bm05QjNRREJsakI1WEpXRXU3MnF4NkJhd0RhTUFod3o0YUtQa1EwIn0.eyJleHAiOjE2MTU0MDY5MDksImlhdCI6MTYxNTQwNjg0OSwianRpIjoiMjBhMGI1MTMtN2E4My00OGQ2LThmNDgtZmQ3NDc1N2Y4OWRiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.AdR59BCvGlctL5BMgXlpJBBToKTPG4SVa-oJKBqE7qxvTSBwAQM5D3uUc2toM3NAUERSMKOLTJfzfxenNRixrDMnAcrdFHgEY10vsDp6uqA7NMUevHE5f7jiAVK1talXS9O41IEnR2DKbAG0GgjIA2WHLhUgftG2uNN8LMKI2QSbLCfM"},                                                                                                                                                                       // ECDSA inter.
   141  			{"eyJhbGciOiJQUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6WGV3MFVKMWg2UTRDQ2NkXzl3eE16dmNwNWNFQmlmSDBLV3JDejJLeXhjIn0.eyJleHAiOjE2MTU0MDY5NjIsImlhdCI6MTYxNTQwNjkwMiwianRpIjoiNWIyZGY5N2EtNDQyOS00ZTA0LWFkMzgtOWZmNjVlZDU2MTZjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.tafkUwLXm3lyyqJHwAGwFPN3IO0rCrESJnVcIuI1KHPSKogn5DgWqR3B9QCvqIusqlxhGW7MvOhG-9dIy62ciKGQFDRFA9T46TMm9t8O80TnhYTB8ImX90xYuf6E74k1RiqRVcubFWKHWlhKjqXMM4dD2l8VwqL45E6kHpNDvzvILKAfrMgm0vHsfi6v5rf32HLp6Ox1PvpKrM1kDgsdXm6scgAGJCTbOQB2Pzc-i8cyFPeuckbeL4zbM3-Odqc-eI-3pXevMzUB608J3fRpQK1W053kU7iG9RFC-5nBwvrBlN4Lff_X1R3JBLkFcA0wJeFYtIFnMm6lVbA7nwa0Xg"}, // Signing algorithm PS256.
   142  			{"eyJhbGciOiJQUzM4NCIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMeDFGbWF5UDJZQnR4YXFTMVNLSlJKR2lYUktudzJvdjVXbVlJTUctQkxFIn0.eyJleHAiOjE2MTU0MDY5ODIsImlhdCI6MTYxNTQwNjkyMiwianRpIjoiMGY2NGJjYTktYjU4OC00MWFhLWFkNDEtMmFmZDM2OGRmNTFkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.Rxrq41AxbWKIQHWv-Tkb7rqwel3sKT_R_AGvn9mPIHqhw1m7nsQWcL9t2a_8MI2hCwgWtYdgTF1xxBNmb2IW3CZkML5nGfcRrFvNaBHd3UQEqbFKZgnIX29h5VoxekyiwFaGD-0RXL83jF7k39hytEzTatwoVjZ-frga0KFl-nLce3OwncRXVCGmxoFzUsyu9TQFS2Mm_p0AMX1y1MAX1JmLC3WFhH3BohhRqpzBtjSfs_f46nE1-HKjqZ1ERrAc2fmiVJjmG7sT702JRuuzrgUpHlMy2juBG4DkVcMlj4neJUmCD1vZyZBRggfaIxNkwUhHtmS2Cp9tOcwNu47tSg"}, // Signing algorithm PS384.
   143  			{"eyJhbGciOiJQUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ0VzZhZTdUb21FNl8yam9vTS1zZjlOXzZsV2c3SE50YVFYckRzRWxCek00In0.eyJleHAiOjE2MTU0MDcwMDUsImlhdCI6MTYxNTQwNjk0NSwianRpIjoiYzJmMmZiMjQtOTQ1Yi00YTA4LWE3ZTQtYTZhNzRlZTIwMDFiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.d5E6m_isNWy0Y5E-udUruMbThe3NHMb7x90rzOxlrEyyhZEqjuREP97KQXIospLY41TKj3VURJbRFebg-my4R8w1-OlaciDdoWND2juk8y_vIMlgYb9lLMnS1ZI5Ayq3OQ4Bh2TXLsZwQaBWoccyVSD1qCgZsCH-ZIbxJmefkM6k99fA8QWwNFL-bD1kHELBdZfk-26JSRWiA_0WocQZcC5DWsmbslwICo2yT59X4ancvxNA-mns0Wt41-sj9sAAr-qOAubGjpPC8-FqVZXeDTiuaAqQA2K3MRKMwHMZY6e-duwCltGll_kZf2jUlwfF7LLuT7YP6p7rxCjIhHaAMw"}, // Signing algorithm PS512.
   144  			{"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJDNjVxMEVLUXlocGQxbTRmcjdTS08ySGVfbkF4Z0N0QWR3czY0ZDJCTHQ4In0.eyJleHAiOjE2MTU0MDcwMjYsImlhdCI6MTYxNTQwNjk2NiwianRpIjoiMzg1NjE4ODItOTA5MS00ODY3LTkzYmYtMmE3YmU4NTc3YmZiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.Cmgz3aC_b_kpOmGM-_nRisgQul0d9Jg7BpMLe5F_fdryRhwhW5fQBZtz6FipQ0Tc4jggI6L3Dx1jS2kn823aWCR0x-OAFCawIXnwgAKuM1m2NL7Y6LKC07nytdB_qU4GknAl3jEG-tZIJBHQwYP-K6QKmAT9CdF1ZPbc9u8RgRCPN8UziYcOpvStiG829BO7cTzCt7tp5dJhem8_CnRWBKzelP1fs_z4fAQtW2sgyhX9SUYb5WON-4zrn4i01FlYUwZV-AC83zP6BuHIiy3XpAuTiTp2BjZ-1nzCLWBRpIm_lOObFeo-3AQqWPxzLVAmTFQMKReUF9T8ehL2Osr1XQ"}, // Signing algorithm RS256.
   145  			{"eyJhbGciOiJSUzM4NCIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJnbm1BZnZtbHNpM2tLSDNWbE0xQUo4NVAyaGVrUThPTl9YdkpxczN4UEQ4In0.eyJleHAiOjE2MTU0MDcwNDUsImlhdCI6MTYxNTQwNjk4NSwianRpIjoiYzJiZGRhNGItMWNjNy00MzhmLWI1YzktMDk2ZDk4MTg4YWQ4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.F-y1IULtpWICLu0lrTniJwf3x1wHSQvVJ2BmXhRm-bhEdwftJG2Ep4rg4_SZPU8CZTazqSRQE4quWw5e8m8yyVrdpAts3NDAJB6m6Up1qQvN2YBtSoGjujzRZuJ72rOGqHf0e9wUQYWsmgE4Aes0kCeOlQ0EwfTnd6qfJaqYuZj9T0KIedt7T9KBmk3ndzDQALRJ2vo12b2M2DHL6gYqokUJ4lhw9Tnm785a6Bamc_F0otAKS5e4KVFhtRzCgdZWdEXX9VfwmtZpvZYImHWFe8HnB8jqLfRhKIc5xkXE0cwiuz6eYnneSRMrM3qAPus6fbc78rIVZl7Qaxa-h1vZYQ"}, // Signing algorithm RS384.
   146  			{"eyJhbGciOiJSUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhcmxVeFg0aGg1NnJOTy1YZElQaERUN2JxQk1xY0J3TlF1UF9UblpKTkdzIn0.eyJleHAiOjE2MTU0MDcwNjcsImlhdCI6MTYxNTQwNzAwNywianRpIjoiYWNlNGQ5ODgtMjVjMS00NzkxLWJjZDgtNTQ3MzNiYTg0MTZiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.BHygL0iGWEL46QdcnInqgjhgtlfBN8H2BMhFAK1sZuGB6rX-FGHFav0NgnWzT5Ae6wM3KqJY30aME5OOvycV--5w7ZX8uqnYjXYdLbJ-azLtP3Hw8vwY9u6GC81ZvWZdKvQNpbcuvtJYL2uhrbv0GdXcClTHmA-NiReGFuBFgo0fBX_ipjNx_q94OnaDxSHUSGeKqNFoNOttXBV7Xqa_K9j60zfoO9E2OV0jkYI5_8MPPZI85Y8XG7PUK2opg7LHNrFbB67C_RxJ7ZDKt0jBApzJyZ96_8UBSvNtBnytQO-CexOG-5y-nN3mcw7NU7g7dFxlb18Yur194h7VTT9tHQ"}, // Signing algorithm RS512.
   147  			{"eyJhbGciOiJSUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhcmxVeFg0aGg1NnJOTy1YZElQaERUN2JxQk1xY0J3TlF1UF9UblpKTkdzIn0.eyJleHAiOjE2MTU0MDcwNjcsImlhdCI6MTYxNTQwNzAwNywianRpIjoiYWNlNGQ5ODgtMjVjMS00NzkxLWJjZDgtNTQ3MzNiYTg0MTZiIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.BHygL0iGWEL46QdcnInqgjhgtlfBN8H2BMhFAK1sZuGB6rX-FGHFav0NgnWzT5Ae6wM3KqJY30aME5OOvycV--5w7ZX8uqnYjXYdLbJ-azLtP3Hw8vwY9u6GC81ZvWZdKvQNpbcuvtJYL2uhrbv0GdXcClTHmA-NiReGFuBFgo0fBX_ipjNx_q94OnaDxSHUSGeKqNFoNOttXBV7Xqa_K9j60zfoO9E2OV0jkYI5_8MPPZI85Y8XG7PUK2opg7LHNrFbB67C_RxJ7ZDKt0jBApzJyZ96_8UBSvNtBnytQO-CexOG-5y-nN3mcw7NU7g7dFxlb18Yur194h7VTT9tHQ"}, // RSA inter.
   148  			{"eyJhbGciOiJFZERTQSIsImtpZCI6IlE1NkEiLCJrdHkiOiJPS1AiLCJ0eXAiOiJKV1QifQ.e30.BBUMb14EQqbhht6uR5V6_R7bQUiYtAi3v1bOvh4SO-_XA-WEs3k0OE2negGlsbIiXqcEP8pgHSB6r7JE0qUTCgiyJ_BCU7feuWyEohVW6ww7USRTMP4siphL3Xeewu0BKBg"}, // EdDSA.
   149  			{"eyJhbGciOiJIUzI1NiIsImtpZCI6ImhtYWMiLCJrdHkiOiJvY3QiLCJ0eXAiOiJKV1QifQ.e30.vZ8H2-9j1pDXLNL2GFKbZOkC2qyA0dr7AiTJpNjgLcY"},                                                                                         // HMAC
   150  		}
   151  
   152  		if opts.RefreshInterval != 0 {
   153  			time.Sleep(opts.RefreshInterval)
   154  		}
   155  
   156  		for _, tc := range testCases {
   157  			t.Run(fmt.Sprintf("token: %s", tc.token), func(t *testing.T) {
   158  				// Use the JWKS jwt.Keyfunc to parse the token.
   159  				//
   160  				// Don't check for general errors. Unfortunately, an error occurs when a token is expired. All hard
   161  				// coded tokens are expired.
   162  				_, err = jwt.Parse(tc.token, jwks.Keyfunc)
   163  				if err != nil {
   164  					if errors.Is(err, jwt.ErrInvalidKeyType) {
   165  						t.Fatalf(logFmt, "Invaild key type selected.", err)
   166  					}
   167  				}
   168  			})
   169  		}
   170  
   171  		jwks.EndBackground()
   172  	}
   173  }
   174  
   175  // TestJWKS_Use tests that JWKs with a use value "enc" are not returned from jwt.Keyfunc.
   176  func TestJWKS_Use(t *testing.T) {
   177  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   178  		_, err := w.Write([]byte(jwksJSON))
   179  		if err != nil {
   180  			http.Error(w, err.Error(), http.StatusInternalServerError)
   181  		}
   182  	}))
   183  	defer server.Close()
   184  
   185  	jwksURL := server.URL
   186  	opts := keyfunc.Options{
   187  		JWKUseWhitelist: []keyfunc.JWKUse{keyfunc.UseOmitted, keyfunc.UseSignature},
   188  	}
   189  	jwks, err := keyfunc.Get(jwksURL, opts)
   190  	if err != nil {
   191  		t.Fatalf(logFmt, "Failed to get JWKS from testing URL.", err)
   192  	}
   193  
   194  	_, err = jwt.Parse(tokenUseEnc, jwks.Keyfunc)
   195  	if !errors.Is(err, keyfunc.ErrJWKUseWhitelist) {
   196  		t.Fatal(`Failed to return correct error for JWK with "use" parameter value of "enc".`)
   197  	}
   198  }
   199  
   200  // TestJWKS_UseNoWhitelistOverride tests that JWKUseNoWhitelist option overrides the JWKUseWhitelist option.
   201  func TestJWKS_UseNoWhitelistOverride(t *testing.T) {
   202  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   203  		_, err := w.Write([]byte(jwksJSON))
   204  		if err != nil {
   205  			http.Error(w, err.Error(), http.StatusInternalServerError)
   206  		}
   207  	}))
   208  	defer server.Close()
   209  
   210  	jwksURL := server.URL
   211  	opts := keyfunc.Options{
   212  		JWKUseWhitelist:   []keyfunc.JWKUse{keyfunc.UseOmitted, keyfunc.UseSignature},
   213  		JWKUseNoWhitelist: true,
   214  	}
   215  	jwks, err := keyfunc.Get(jwksURL, opts)
   216  	if err != nil {
   217  		t.Fatalf(logFmt, "Failed to get JWKS from testing URL.", err)
   218  	}
   219  
   220  	_, err = jwt.Parse(tokenUseEnc, jwks.Keyfunc)
   221  	if err != nil {
   222  		t.Fatalf(logFmt, "The JWKUseNoWhitelist option should override the JWKUseWhitelist option.", err)
   223  	}
   224  }
   225  
   226  // TestKIDs confirms the JWKS.KIDs returns the key IDs (`kid`) stored in the JWKS.
   227  func TestJWKS_KIDs(t *testing.T) {
   228  	jwks, err := keyfunc.NewJSON([]byte(jwksJSON))
   229  	if err != nil {
   230  		t.Fatalf(logFmt, "Failed to create a JWKS from JSON.", err)
   231  	}
   232  
   233  	expectedKIDs := []string{
   234  		"zXew0UJ1h6Q4CCcd_9wxMzvcp5cEBifH0KWrCz2Kyxc",
   235  		"ebJxnm9B3QDBljB5XJWEu72qx6BawDaMAhwz4aKPkQ0",
   236  		"TVAAet63O3xy_KK6_bxVIu7Ra3_z1wlB543Fbwi5VaU",
   237  		"arlUxX4hh56rNO-XdIPhDT7bqBMqcBwNQuP_TnZJNGs",
   238  		"tW6ae7TomE6_2jooM-sf9N_6lWg7HNtaQXrDsElBzM4",
   239  		"Lx1FmayP2YBtxaqS1SKJRJGiXRKnw2ov5WmYIMG-BLE",
   240  		"gnmAfvmlsi3kKH3VlM1AJ85P2hekQ8ON_XvJqs3xPD8",
   241  		"CGt0ZWS4Lc5faiKSdi0tU0fjCAdvGROQRGU9iR7tV0A",
   242  		"C65q0EKQyhpd1m4fr7SKO2He_nAxgCtAdws64d2BLt8",
   243  		"Q56A",
   244  		"hmac",
   245  		"kidWithBadUse",
   246  	}
   247  
   248  	actual := jwks.KIDs()
   249  
   250  	actualLen := len(actual)
   251  	expectedLen := len(expectedKIDs)
   252  	if actualLen != expectedLen {
   253  		t.Fatalf("The number of key IDs was not as expected.\n  Expected length: %d\n  Actual length: %d\n  Actual key IDs: %v", expectedLen, actualLen, actual)
   254  	}
   255  
   256  	for _, expectedKID := range expectedKIDs {
   257  		found := false
   258  		for _, kid := range actual {
   259  			if kid == expectedKID {
   260  				found = true
   261  				break
   262  			}
   263  		}
   264  		if !found {
   265  			t.Errorf("Failed to find expected key ID in the slice of key IDs in the JWKS.\n  Missing: %s", expectedKID)
   266  		}
   267  	}
   268  }
   269  
   270  // TestJWKS_Len confirms the JWKS.Len returns the number of keys in the JWKS.
   271  func TestJWKS_Len(t *testing.T) {
   272  	jwks, err := keyfunc.NewJSON([]byte(jwksJSON))
   273  	if err != nil {
   274  		t.Fatalf(logFmt, "Failed to create a JWKS from JSON.", err)
   275  	}
   276  
   277  	expectedKIDs := []string{
   278  		"zXew0UJ1h6Q4CCcd_9wxMzvcp5cEBifH0KWrCz2Kyxc",
   279  		"ebJxnm9B3QDBljB5XJWEu72qx6BawDaMAhwz4aKPkQ0",
   280  		"TVAAet63O3xy_KK6_bxVIu7Ra3_z1wlB543Fbwi5VaU",
   281  		"arlUxX4hh56rNO-XdIPhDT7bqBMqcBwNQuP_TnZJNGs",
   282  		"tW6ae7TomE6_2jooM-sf9N_6lWg7HNtaQXrDsElBzM4",
   283  		"Lx1FmayP2YBtxaqS1SKJRJGiXRKnw2ov5WmYIMG-BLE",
   284  		"gnmAfvmlsi3kKH3VlM1AJ85P2hekQ8ON_XvJqs3xPD8",
   285  		"CGt0ZWS4Lc5faiKSdi0tU0fjCAdvGROQRGU9iR7tV0A",
   286  		"C65q0EKQyhpd1m4fr7SKO2He_nAxgCtAdws64d2BLt8",
   287  		"Q56A",
   288  		"hmac",
   289  		"WW91IGdldCBhIGdvbGQgc3RhciDwn4yfCg",
   290  	}
   291  
   292  	actualLen := jwks.Len()
   293  	expectedLen := len(expectedKIDs)
   294  	if actualLen != expectedLen {
   295  		t.Fatalf("The number of key IDs was not as expected.\n  Expected length: %d\n  Actual length: %d\n", expectedLen, actualLen)
   296  	}
   297  }
   298  
   299  // TestRateLimit performs a test to confirm the rate limiter works as expected.
   300  func TestRateLimit(t *testing.T) {
   301  	tempDir, err := os.MkdirTemp("", "*")
   302  	if err != nil {
   303  		t.Fatalf(logFmt, "Failed to create a temporary directory.", err)
   304  	}
   305  	defer func() {
   306  		err = os.RemoveAll(tempDir)
   307  		if err != nil {
   308  			t.Fatalf(logFmt, "Failed to remove temporary directory.", err)
   309  		}
   310  	}()
   311  
   312  	refreshes := uint(0)
   313  	refreshMux := sync.Mutex{}
   314  
   315  	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
   316  		refreshMux.Lock()
   317  		refreshes++
   318  		refreshMux.Unlock()
   319  
   320  		writer.WriteHeader(200)
   321  		if _, serveErr := writer.Write([]byte(jwksJSON)); serveErr != nil {
   322  			t.Errorf(logFmt, "Failed to serve JWKS.", err)
   323  		}
   324  	}))
   325  	defer server.Close()
   326  
   327  	jwksURL := server.URL + jwksFilePath
   328  
   329  	refreshInterval := time.Second
   330  	refreshRateLimit := time.Millisecond * 500
   331  	refreshTimeout := time.Second
   332  	options := keyfunc.Options{
   333  		RefreshErrorHandler: func(err error) {
   334  			t.Errorf(logFmt, "The package itself had an error.", err)
   335  		},
   336  		RefreshInterval:   refreshInterval,
   337  		RefreshRateLimit:  refreshRateLimit,
   338  		RefreshTimeout:    refreshTimeout,
   339  		RefreshUnknownKID: true,
   340  	}
   341  
   342  	jwks, err := keyfunc.Get(jwksURL, options)
   343  	if err != nil {
   344  		t.Fatalf(logFmt, "Failed to create *keyfunc.JWKS.", err)
   345  	}
   346  	defer jwks.EndBackground()
   347  
   348  	// Create four JWTs with unknown kids.
   349  	//
   350  	// These should prompt two refreshes.
   351  	// The first one will not be rate limited.
   352  	// The second will get a rate limit queue.
   353  	// The third will get no rate limit queue and will be ignored because there is already a one in the queue.
   354  	// The fourth will get no rate limit queue and will be ignored because there is already a one in the queue.
   355  	token1 := "eyJraWQiOiI0NWU3ZDcyMiIsInR5cCI6IkpXVCIsImFsZyI6IlJTNTEyIn0.eyJzdWIiOiJBbmRyZWEiLCJhdWQiOiJUYXNodWFuIiwiaXNzIjoiandrcy1zZXJ2aWNlLmFwcHNwb3QuY29tIiwiZXhwIjoxNjI0NzU2OTAwLCJpYXQiOjE2MjQ3NTY4OTUsImp0aSI6IjA5ZjkzZjljLTU0ZjMtNDM5Yi04Njg2LWZhMGYwMjlmYmIwZSJ9.g643vWnvDvR5u5TeCUaCblp-Ss8SPWoZrOxBo3y6WP9xQnRW63VSbacCirl-5nGRPoX6vostZAkRyUl62ICQHpTj3bRnDY4ZbkcQ42xtrWMBsI2Sw6dAmZtGsCR_tguQZmvdKE4gVNnFWLp0hBjCeLxPVbc59vC6njMdz7XHcOdW7RXN6iUYjLFoPAr4Qg93Vbrwfo9Qmkm8bDgbnuoJ3aQq0RFa02G1KC2-cx8SuUbxso_Uu7ddY6HDRL5OPF3xS9cKO5ty4zCfGYIVDhfH7V-zA2cJZyA2dlv3Ddd-ntU42aud0M4PcTTdjHf1CE29sCZHk5wTRgxsTjfWglYQQiVQJEkw6DD6kTlQ_MwN4p_OWNj06b55mXM6Bj9c9y8TfPLETDy_PRc1lHu1PuiizLg019JaGidpTLF8IdKTa9emkEnf2n8xWi-YMkkRk57hpuc56GmnBR0d8ODfuL0XILlQp2guFsVRo9A4Sdqy7fGdZGoSS4XzSR-TIEw7W_KSqlYCtWC0xNk1Kze3xSY2mDqrn1YFFlvXgXQlgzU8GN1eL7QRRQlxaPGti2wEH6OYH4A160nR_OM-zFBobpQn79g8HsK8yZgPiY0p94F6pvKBQtSHDBvAe3W0-UHYfspwT9cQGVgqCGol6A8XNeBlVQpko9ves4UgCRSb6o9u_p4"
   356  	token2 := "eyJraWQiOiIyYTFkODRhMCIsInR5cCI6IkpXVCIsImFsZyI6IkVTMzg0In0.eyJzdWIiOiJBbmRyZWEiLCJhdWQiOiJUYXNodWFuIiwiaXNzIjoiandrcy1zZXJ2aWNlLmFwcHNwb3QuY29tIiwiZXhwIjoxNjI0NzU3MjExLCJpYXQiOjE2MjQ3NTcyMDYsImp0aSI6ImU4YjQ1YmIwLTczZjgtNDkzNi04MjQxLWE1OGFlZWMyZWE2NCJ9.6Isd4unU2TAmRB1SouaHBV9LUjFGIuhOrxkQlDjh6qKRgb7UsiPtQm87S2qrriLaFjyCmrmU6cDpVBpTOutjPxweIqT-1EfsS-dkENIVWPVgQ5-KuNu2jXyGYpPeFBUA"
   357  	token3 := "eyJraWQiOiIxZjEyOGFkZSIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzdWIiOiJSZWJlY2NhIiwiYXVkIjoiQWxpY2UiLCJpc3MiOiJqd2tzLXNlcnZpY2UuYXBwc3BvdC5jb20iLCJleHAiOjE2MjQ3NTkzODIsImlhdCI6MTYyNDc1OTM3NywianRpIjoiMzU2MWY4MDctNDRkNi00OWE5LWFlYWItMmQ1MjQ2YWYxNDhlIn0.5eZbJlvnaFsRwPhBHmXljp9vgsrB0Q9d3dSz4va29ahTKsFGFo8tYy0e69ehqSb-dbFy9azRRtygwwtYuaEFuA"
   358  	token4 := "eyJraWQiOiIyZDQ3NjUwYSIsInR5cCI6IkpXVCIsImFsZyI6IlBTMzg0In0.eyJzdWIiOiJGcmVkYSIsImF1ZCI6Ikx1Y2lhIiwiaXNzIjoiandrcy1zZXJ2aWNlLmFwcHNwb3QuY29tIiwiZXhwIjoxNjI0ODA0MTk0LCJpYXQiOjE2MjQ4MDQxODksImp0aSI6IjdjNTQ2Y2RmLTYwMTEtNDI3Ny04Y2Q0LTMwNjZmZTYwNTExZSJ9.hQm-OP_MMk8_S13-ohiINRuDP2IlCiB3yn8Ov6qTjeFbq4gZ6MegeJH_qiZOvXqlzOAwpwd5P4nm5JeS6LlNGdW6V_agwYwnAd08GI7APQNRib692_sEk1DKdSk-S-Y8V_ZAgeTT8asdaSDw4EBPxkDvROcuEqesZrfqnrOcpdqqa2BcmwX8q5sLtQ8TMp4cOvEZg-J8_0j2kdCUkv_n9ZdsRoA3EUT8M1bYqnGRRxIRqflsm-S_xq3HxMAnPF5hPlqIKFVKuRsU0SKgcHZGwXpuK2lJqPobl6MI987tGrc9sPPFzVkNYxeltcxu34-ZjzN6iCQN8r0w-mfqCZav7A"
   359  
   360  	// Use the JWKS jwk.Keyfunc to parse the tokens signed with unknown kids at nearly the same time.
   361  	waitGroup := sync.WaitGroup{}
   362  	waitGroup.Add(3)
   363  	go func() {
   364  		defer waitGroup.Done()
   365  		if _, parseErr := jwt.Parse(token1, jwks.Keyfunc); parseErr != nil {
   366  			if errors.Is(parseErr, jwt.ErrInvalidKeyType) {
   367  				t.Errorf(logFmt, "Invaild key type selected.", parseErr)
   368  			}
   369  		}
   370  	}()
   371  	go func() {
   372  		defer waitGroup.Done()
   373  		if _, parseErr := jwt.Parse(token2, jwks.Keyfunc); parseErr != nil {
   374  			if errors.Is(parseErr, jwt.ErrInvalidKeyType) {
   375  				t.Errorf(logFmt, "Invaild key type selected.", parseErr)
   376  			}
   377  		}
   378  	}()
   379  	go func() {
   380  		defer waitGroup.Done()
   381  		if _, parseErr := jwt.Parse(token3, jwks.Keyfunc); parseErr != nil {
   382  			if errors.Is(parseErr, jwt.ErrInvalidKeyType) {
   383  				t.Errorf(logFmt, "Invaild key type selected.", parseErr)
   384  			}
   385  		}
   386  	}()
   387  	if _, parseErr := jwt.Parse(token4, jwks.Keyfunc); parseErr != nil {
   388  		if errors.Is(parseErr, jwt.ErrInvalidKeyType) {
   389  			t.Fatalf(logFmt, "Invaild key type selected.", parseErr)
   390  		}
   391  	}
   392  	waitGroup.Wait()
   393  
   394  	// Confirm the JWKS was only refreshed once. (Refresh counter was first incremented on the creation of the JWKS.)
   395  	refreshMux.Lock()
   396  	expected := uint(2)
   397  	if refreshes != expected {
   398  		t.Fatalf("An incorrect number of refreshes occurred.\n  Expected: %d\n  Got: %d\n", expected, refreshes)
   399  	}
   400  	refreshMux.Unlock()
   401  
   402  	// Wait for the rate limiter to take the next queue.
   403  	time.Sleep(refreshRateLimit + time.Millisecond*100)
   404  	refreshMux.Lock()
   405  	expected = uint(3)
   406  	if refreshes != expected {
   407  		t.Fatalf("An incorrect number of refreshes occurred.\n  Expected: %d\n  Got: %d\n", expected, refreshes)
   408  	}
   409  	refreshMux.Unlock()
   410  
   411  	// Wait for the refresh interval to occur.
   412  	time.Sleep(refreshInterval + time.Millisecond*100)
   413  	refreshMux.Lock()
   414  	expected = uint(4)
   415  	if refreshes != expected {
   416  		t.Fatalf("An incorrect number of refreshes occurred.\n  Expected: %d\n  Got: %d\n", expected, refreshes)
   417  	}
   418  	refreshMux.Unlock()
   419  }
   420  
   421  // TestRawJWKS confirms a copy of the raw JWKS is returned from the method.
   422  func TestRawJWKS(t *testing.T) {
   423  	tempDir, err := os.MkdirTemp("", "*")
   424  	if err != nil {
   425  		t.Fatalf(logFmt, "Failed to create a temporary directory.", err)
   426  	}
   427  	defer func() {
   428  		err = os.RemoveAll(tempDir)
   429  		if err != nil {
   430  			t.Fatalf(logFmt, "Failed to remove temporary directory.", err)
   431  		}
   432  	}()
   433  
   434  	jwksFile := filepath.Join(tempDir, jwksFilePath)
   435  
   436  	err = os.WriteFile(jwksFile, []byte(jwksJSON), 0600)
   437  	if err != nil {
   438  		t.Fatalf(logFmt, "Failed to write JWKS file to temporary directory.", err)
   439  	}
   440  
   441  	server := httptest.NewServer(http.FileServer(http.Dir(tempDir)))
   442  	defer server.Close()
   443  
   444  	jwksURL := server.URL + jwksFilePath
   445  
   446  	jwks, err := keyfunc.Get(jwksURL, keyfunc.Options{})
   447  	if err != nil {
   448  		t.Fatalf(logFmt, "Failed to get JWKS from testing URL.", err)
   449  	}
   450  
   451  	raw := jwks.RawJWKS()
   452  	if !bytes.Equal(raw, []byte(jwksJSON)) {
   453  		t.Fatalf("Raw JWKS does not match remote JWKS resource.")
   454  	}
   455  
   456  	// Overwrite the slice returned, if it's a copy, it should ruin the original.
   457  	emptySlice := make([]byte, len(raw))
   458  	copy(raw, emptySlice)
   459  
   460  	nextRaw := jwks.RawJWKS()
   461  	if bytes.Equal(nextRaw, emptySlice) {
   462  		t.Fatalf("Raw JWKS is not a copy.")
   463  	}
   464  }
   465  
   466  // TestRequestFactory confirms the behavior of request factories.
   467  func TestRequestFactory(t *testing.T) {
   468  	var fullJWKSHandler http.Handler
   469  	{
   470  		tempDir, err := os.MkdirTemp("", "*")
   471  		if err != nil {
   472  			t.Fatalf(logFmt, "Failed to create a temporary directory.", err)
   473  		}
   474  		defer func() {
   475  			err = os.RemoveAll(tempDir)
   476  			if err != nil {
   477  				t.Fatalf(logFmt, "Failed to remove temporary directory.", err)
   478  			}
   479  		}()
   480  
   481  		jwksFile := filepath.Join(tempDir, jwksFilePath)
   482  
   483  		err = os.WriteFile(jwksFile, []byte(jwksJSON), 0600)
   484  		if err != nil {
   485  			t.Fatalf(logFmt, "Failed to write JWKS file to temporary directory.", err)
   486  		}
   487  
   488  		fullJWKSHandler = http.FileServer(http.Dir(tempDir))
   489  	}
   490  	var emptyJWKSHandler http.Handler
   491  	{
   492  		tempDir, err := os.MkdirTemp("", "*")
   493  		if err != nil {
   494  			t.Fatalf(logFmt, "Failed to create a temporary directory.", err)
   495  		}
   496  		defer func() {
   497  			err = os.RemoveAll(tempDir)
   498  			if err != nil {
   499  				t.Fatalf(logFmt, "Failed to remove temporary directory.", err)
   500  			}
   501  		}()
   502  
   503  		jwksFile := filepath.Join(tempDir, jwksFilePath)
   504  
   505  		err = os.WriteFile(jwksFile, []byte(emptyJWKSJSON), 0600)
   506  		if err != nil {
   507  			t.Fatalf(logFmt, "Failed to write JWKS file to temporary directory.", err)
   508  		}
   509  
   510  		emptyJWKSHandler = http.FileServer(http.Dir(tempDir))
   511  	}
   512  
   513  	const (
   514  		fullJWKSUserAgent = "full-jwks-please"
   515  		userAgentHeader   = "User-Agent"
   516  	)
   517  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   518  		switch r.Header.Get(userAgentHeader) {
   519  		case fullJWKSUserAgent:
   520  			fullJWKSHandler.ServeHTTP(w, r)
   521  		default:
   522  			emptyJWKSHandler.ServeHTTP(w, r)
   523  		}
   524  	}))
   525  	defer server.Close()
   526  
   527  	jwksURL := server.URL + jwksFilePath
   528  
   529  	optsFail := keyfunc.Options{
   530  		RequestFactory: func(ctx context.Context, url string) (*http.Request, error) {
   531  			badURL := fmt.Sprintf("%s/does/not/exist", url)
   532  			return http.NewRequestWithContext(ctx, http.MethodGet, badURL, bytes.NewReader(nil))
   533  		},
   534  	}
   535  
   536  	_, err := keyfunc.Get(jwksURL, optsFail)
   537  	if err == nil {
   538  		t.Fatalf("Creation of *keyfunc.JWKS reading from bad URL must fail.")
   539  	}
   540  
   541  	optsSuccess := keyfunc.Options{
   542  		RequestFactory: func(ctx context.Context, url string) (*http.Request, error) {
   543  			return http.NewRequestWithContext(ctx, http.MethodGet, url, bytes.NewReader(nil))
   544  		},
   545  	}
   546  
   547  	jwks, err := keyfunc.Get(jwksURL, optsSuccess)
   548  	if err != nil {
   549  		t.Fatalf(logFmt, "Failed to get JWKS from testing URL.", err)
   550  	}
   551  
   552  	if len(jwks.ReadOnlyKeys()) != 0 {
   553  		t.Fatalf("JWKS should be empty due to lack of custom HTTP header.")
   554  	}
   555  
   556  	optsCustomHeader := keyfunc.Options{
   557  		RequestFactory: func(ctx context.Context, url string) (*http.Request, error) {
   558  			req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, bytes.NewReader(nil))
   559  			if err != nil {
   560  				return nil, fmt.Errorf("failed to create request: %w", err)
   561  			}
   562  			req.Header.Set(userAgentHeader, fullJWKSUserAgent)
   563  			return req, nil
   564  		},
   565  	}
   566  
   567  	jwks, err = keyfunc.Get(jwksURL, optsCustomHeader)
   568  	if err != nil {
   569  		t.Fatalf(logFmt, "Failed to get JWKS from testing URL.", err)
   570  	}
   571  
   572  	if len(jwks.ReadOnlyKeys()) == 0 {
   573  		t.Fatalf("JWKS should not be empty due to custom HTTP header.")
   574  	}
   575  }
   576  
   577  // TestUnknownKIDRefresh performs a test to confirm that an Unknown kid with refresh the JWKS.
   578  func TestUnknownKIDRefresh(t *testing.T) {
   579  	tempDir, err := os.MkdirTemp("", "*")
   580  	if err != nil {
   581  		t.Fatalf(logFmt, "Failed to create a temporary directory.", err)
   582  	}
   583  	defer func() {
   584  		err = os.RemoveAll(tempDir)
   585  		if err != nil {
   586  			t.Fatalf(logFmt, "Failed to remove temporary directory.", err)
   587  		}
   588  	}()
   589  
   590  	jwksFile := filepath.Join(tempDir, strings.TrimPrefix(jwksFilePath, "/"))
   591  
   592  	err = os.WriteFile(jwksFile, []byte(emptyJWKSJSON), 0600)
   593  	if err != nil {
   594  		t.Fatalf(logFmt, "Failed to write JWKS file to temporary directory.", err)
   595  	}
   596  
   597  	server := httptest.NewServer(http.FileServer(http.Dir(tempDir)))
   598  	defer server.Close()
   599  
   600  	testingRefreshErrorHandler := func(err error) {
   601  		t.Fatalf(logFmt, "Unhandled JWKS error.", err)
   602  	}
   603  
   604  	jwksURL := server.URL + jwksFilePath
   605  
   606  	options := keyfunc.Options{
   607  		RefreshErrorHandler: testingRefreshErrorHandler,
   608  		RefreshUnknownKID:   true,
   609  	}
   610  
   611  	jwks, err := keyfunc.Get(jwksURL, options)
   612  	if err != nil {
   613  		t.Fatalf(logFmt, "Failed to create *keyfunc.JWKS.", err)
   614  	}
   615  	defer jwks.EndBackground()
   616  
   617  	err = os.WriteFile(jwksFile, []byte(jwksJSON), 0600)
   618  	if err != nil {
   619  		t.Fatalf(logFmt, "Failed to write JWKS file to temporary directory.", err)
   620  	}
   621  
   622  	// Use any JWT signed by a key in the non-empty JWKS.
   623  	token := "eyJhbGciOiJFUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJDR3QwWldTNExjNWZhaUtTZGkwdFUwZmpDQWR2R1JPUVJHVTlpUjd0VjBBIn0.eyJleHAiOjE2MTU0MDY4NjEsImlhdCI6MTYxNTQwNjgwMSwianRpIjoiYWVmOWQ5YjItN2EyYy00ZmQ4LTk4MzktODRiMzQ0Y2VmYzZhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.iQ77QGoPDNjR2oWLu3zT851mswP8J-h_nrGhs3fpa_tFB3FT1deKPGkjef9JOTYFI-CIVxdCFtW3KODOaw9Nrw"
   624  
   625  	// Use the JWKS jwk.Keyfunc to parse the token.
   626  	//
   627  	// Don't check for general errors. Unfortunately, an error occurs when a token is expired. All hard
   628  	// coded tokens are expired.
   629  	_, err = jwt.Parse(token, jwks.Keyfunc)
   630  	if err != nil {
   631  		if errors.Is(err, jwt.ErrInvalidKeyType) {
   632  			t.Fatalf(logFmt, "Invaild key type selected.", err)
   633  		}
   634  	}
   635  }
   636  
   637  // TestReadOnlyKeys verifies that the .ReadOnlyKeys() method returns a map with the correct types.
   638  func TestReadOnlyKeys(t *testing.T) {
   639  	jwks, err := keyfunc.NewJSON([]byte(jwksJSON))
   640  	if err != nil {
   641  		t.Fatalf(logFmt, "Failed to create a JWKS from JSON.", err)
   642  	}
   643  
   644  	for _, key := range jwks.ReadOnlyKeys() {
   645  		switch key.(type) {
   646  		case *rsa.PublicKey:
   647  			// Do nothing.
   648  		case *ecdsa.PublicKey:
   649  			// Do nothing.
   650  		case ed25519.PublicKey:
   651  			// Do nothing.
   652  		case []byte:
   653  			// Do nothing.
   654  		default:
   655  			t.Errorf("Invalid type %T in .ReadOnlyKeys() method.", key)
   656  		}
   657  	}
   658  }
   659  

View as plain text