diff --git a/.github/actions/build-push-image/action.yaml b/.github/actions/build-push-image/action.yaml index d941f904ee..9565e933a4 100644 --- a/.github/actions/build-push-image/action.yaml +++ b/.github/actions/build-push-image/action.yaml @@ -79,9 +79,6 @@ runs: - name: Build & Push Docker Image uses: docker/build-push-action@v6 - env: - DOCKER_BUILD_SUMMARY: false - DOCKER_BUILD_RECORD_UPLOAD: false with: # This is a limitation of GitHub. Only organization members can push to GitHub Container Registry # For now, we will disable the push to the GitHub Container Registry for external contributors diff --git a/router-tests/authentication_test.go b/router-tests/authentication_test.go index 7329146bdf..85d0536b58 100644 --- a/router-tests/authentication_test.go +++ b/router-tests/authentication_test.go @@ -2,6 +2,7 @@ package integration_test import ( "bytes" + "go.uber.org/zap" "io" "net/http" "strings" @@ -29,7 +30,7 @@ func configureAuth(t *testing.T) ([]authentication.Authenticator, *jwks.Server) authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), @@ -613,7 +614,7 @@ func TestAuthenticationWithCustomHeaders(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), @@ -748,7 +749,7 @@ func TestAuthenticationMultipleProviders(t *testing.T) { require.NoError(t, err) t.Cleanup(authServer2.Close) - tokenDecoder1, _ := authentication.NewJwksTokenDecoder(authServer1.JWKSURL(), time.Second*5) + tokenDecoder1, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer1.JWKSURL(), time.Second*5) authenticator1HeaderValuePrefixes := []string{"Bearer"} authenticator1, err := authentication.NewHttpHeaderAuthenticator(authentication.HttpHeaderAuthenticatorOptions{ Name: "1", @@ -758,7 +759,7 @@ func TestAuthenticationMultipleProviders(t *testing.T) { }) require.NoError(t, err) - tokenDecoder2, _ := authentication.NewJwksTokenDecoder(authServer2.JWKSURL(), time.Second*5) + tokenDecoder2, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer2.JWKSURL(), time.Second*5) authenticator2HeaderValuePrefixes := []string{"", "Bearer", "Token"} authenticator2, err := authentication.NewHttpHeaderAuthenticator(authentication.HttpHeaderAuthenticatorOptions{ Name: "2", @@ -858,7 +859,7 @@ func TestAuthenticationOverWebsocket(t *testing.T) { require.NoError(t, err) defer authServer.Close() - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) jwksOpts := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), diff --git a/router-tests/modules/set_scopes_test.go b/router-tests/modules/set_scopes_test.go index c13d1f69bd..feceefebbb 100644 --- a/router-tests/modules/set_scopes_test.go +++ b/router-tests/modules/set_scopes_test.go @@ -9,6 +9,7 @@ import ( "github.com/wundergraph/cosmo/router/core" "github.com/wundergraph/cosmo/router/pkg/authentication" "github.com/wundergraph/cosmo/router/pkg/config" + "go.uber.org/zap" "io" "net/http" "strings" @@ -26,7 +27,7 @@ func configureAuth(t *testing.T) ([]authentication.Authenticator, *jwks.Server) authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), diff --git a/router-tests/websocket_test.go b/router-tests/websocket_test.go index aa35382831..d312d6cf78 100644 --- a/router-tests/websocket_test.go +++ b/router-tests/websocket_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "go.uber.org/zap" "io" "math/big" "net" @@ -73,7 +74,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), @@ -123,7 +124,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), @@ -173,7 +174,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), @@ -232,7 +233,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.HttpHeaderAuthenticatorOptions{ Name: jwksName, URL: authServer.JWKSURL(), @@ -290,7 +291,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.WebsocketInitialPayloadAuthenticatorOptions{ TokenDecoder: tokenDecoder, Key: "Authorization", @@ -351,7 +352,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.WebsocketInitialPayloadAuthenticatorOptions{ TokenDecoder: tokenDecoder, Key: "Authorization", @@ -400,7 +401,7 @@ func TestWebSockets(t *testing.T) { authServer, err := jwks.NewServer(t) require.NoError(t, err) t.Cleanup(authServer.Close) - tokenDecoder, _ := authentication.NewJwksTokenDecoder(authServer.JWKSURL(), time.Second*5) + tokenDecoder, _ := authentication.NewJwksTokenDecoder(zap.NewNop(), authServer.JWKSURL(), time.Second*5) authOptions := authentication.WebsocketInitialPayloadAuthenticatorOptions{ TokenDecoder: tokenDecoder, Key: "Authorization", diff --git a/router/cmd/instance.go b/router/cmd/instance.go index f12e9a597c..27a1fbb4d5 100644 --- a/router/cmd/instance.go +++ b/router/cmd/instance.go @@ -61,7 +61,12 @@ func NewRouter(params Params, additionalOptions ...core.Option) (*core.Router, e if name == "" { name = fmt.Sprintf("jwks-#%d", i) } - tokenDecoder, _ := authentication.NewJwksTokenDecoder(auth.JWKS.URL, auth.JWKS.RefreshInterval) + providerLogger := logger.With(zap.String("provider_name", name)) + tokenDecoder, err := authentication.NewJwksTokenDecoder(providerLogger, auth.JWKS.URL, auth.JWKS.RefreshInterval) + if err != nil { + providerLogger.Error("Could not create JWKS token decoder", zap.Error(err)) + return nil, err + } opts := authentication.HttpHeaderAuthenticatorOptions{ Name: name, URL: auth.JWKS.URL, @@ -71,7 +76,8 @@ func NewRouter(params Params, additionalOptions ...core.Option) (*core.Router, e } authenticator, err := authentication.NewHttpHeaderAuthenticator(opts) if err != nil { - logger.Fatal("Could not create HttpHeader authenticator", zap.Error(err), zap.String("name", name)) + providerLogger.Error("Could not create HttpHeader authenticator", zap.Error(err)) + return nil, err } authenticators = append(authenticators, authenticator) @@ -83,7 +89,8 @@ func NewRouter(params Params, additionalOptions ...core.Option) (*core.Router, e } authenticator, err = authentication.NewWebsocketInitialPayloadAuthenticator(opts) if err != nil { - logger.Fatal("Could not create WebsocketInitialPayload authenticator", zap.Error(err)) + providerLogger.Error("Could not create WebsocketInitialPayload authenticator", zap.Error(err)) + return nil, err } authenticators = append(authenticators, authenticator) } diff --git a/router/pkg/authentication/jwks_token_decoder.go b/router/pkg/authentication/jwks_token_decoder.go index 5fb1a11e05..7b42aea4c2 100644 --- a/router/pkg/authentication/jwks_token_decoder.go +++ b/router/pkg/authentication/jwks_token_decoder.go @@ -2,6 +2,7 @@ package authentication import ( "fmt" + "go.uber.org/zap" "time" "github.com/MicahParks/keyfunc/v2" @@ -29,10 +30,19 @@ func (j *jwksTokenDecoder) Decode(tokenString string) (Claims, error) { return Claims(claims), nil } -func NewJwksTokenDecoder(url string, refreshInterval time.Duration) (TokenDecoder, error) { +func NewJwksTokenDecoder(logger *zap.Logger, url string, refreshInterval time.Duration) (TokenDecoder, error) { jwks, err := keyfunc.Get(url, keyfunc.Options{ RefreshInterval: refreshInterval, + // Allow the JWKS to be empty initially, but it can recover on refresh. + TolerateInitialJWKHTTPError: true, + RefreshErrorHandler: func(err error) { + logger.Error("Could not refresh JWKS. Trying again in the next interval.", + zap.Error(err), + zap.String("url", url), + zap.String("interval", refreshInterval.String()), + ) + }, }) if err != nil { return nil, fmt.Errorf("error initializing JWKS from %q: %w", url, err)