ory/hydra · error

invalid JWK key

Error message

invalid JWK key

What it means

josex.LoadJSONWebKey unmarshals a JSON Web Key from JSON bytes and then validates it with jose.JSONWebKey.Valid(). If the parsed key is structurally incomplete or cryptographically unusable (missing key material, unsupported/absent key type), it returns 'invalid JWK key'. This guards callers from receiving a JWK that would later fail at signing or verification time.

Source

Thrown at oryx/josex/utils.go:36

import (
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"

	"github.com/go-jose/go-jose/v3"
)

// LoadJSONWebKey returns a *jose.JSONWebKey for a given JSON string.
func LoadJSONWebKey(json []byte, pub bool) (*jose.JSONWebKey, error) {
	var jwk jose.JSONWebKey
	err := jwk.UnmarshalJSON(json)
	if err != nil {
		return nil, err
	}
	if !jwk.Valid() {
		return nil, errors.New("invalid JWK key")
	}
	if jwk.IsPublic() != pub {
		return nil, errors.New("priv/pub JWK key mismatch")
	}
	return &jwk, nil
}

// LoadPublicKey loads a public key from PEM/DER/JWK-encoded data.
func LoadPublicKey(data []byte) (interface{}, error) {
	input := data

	block, _ := pem.Decode(data)
	if block != nil {
		input = block.Bytes
	}

	// Try to load SubjectPublicKeyInfo
	pub, err0 := x509.ParsePKIXPublicKey(input)

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Regenerate or re-export the key JSON so it is complete for its kty (RSA needs n and e; EC needs crv, x, y; oct needs k), e.g. via josex.GenerateJWK / go-jose MarshalJSON
  2. Validate the JWK JSON with a linter or by calling jose.JSONWebKey.UnmarshalJSON yourself and inspecting the error for the missing field
  3. Re-fetch the JWKS from the issuer if the key came from a remote endpoint — the copy may be truncated or stale
  4. Confirm the variable/secret holding the key JSON was not truncated by env-var length limits or shell escaping

Example fix

// before (incomplete EC JWK)
json := []byte(`{"kty":"EC","crv":"P-256","x":"..."}`) // missing y
key, err := josex.LoadJSONWebKey(json, true)
// after (complete JWK)
json := []byte(`{"kty":"EC","crv":"P-256","x":"...","y":"..."}`)
key, err := josex.LoadJSONWebKey(json, true)
Defensive patterns

Strategy: validation

Validate before calling

func validJWKJSON(json []byte) error {
	var jwk jose.JSONWebKey
	if err := jwk.UnmarshalJSON(json); err != nil {
		return fmt.Errorf("unmarshal JWK: %w", err)
	}
	if !jwk.Valid() {
		return errors.New("JWK is structurally invalid (missing key material for its kty)")
	}
	return nil
}
// call before: if err := validJWKJSON(keyJSON); err != nil { ... }

Type guard

func isValidJWK(raw []byte) bool {
	var jwk jose.JSONWebKey
	if err := jwk.UnmarshalJSON(raw); err != nil {
		return false
	}
	return jwk.Valid()
}

Try / catch

key, err := josex.LoadJSONWebKey(json, true)
if err != nil {
	switch {
	case err.Error() == "invalid JWK key":
		return fmt.Errorf("JWK JSON is incomplete or unsupported: %w", err)
	case err.Error() == "priv/pub JWK key mismatch":
		return fmt.Errorf("expected a public key but got private (or vice versa): %w", err)
	default:
		return fmt.Errorf("loading JWK: %w", err)
	}
}

Prevention

When it happens

Trigger: Calling LoadJSONWebKey(json, pub) where json unmarshals into a JSONWebKey whose Valid() is false: e.g. JSON missing the required fields for its key type (no kty, no n/e for RSA, no x/y for EC, empty k for oct), malformed base64url key material, or a kty jose-go cannot parse.

Common situations: Hand-edited or truncated JWK files; JWKS fetched from a server whose entry was corrupted or stripped of key material; config/environment variables holding incomplete key JSON; generating JWK JSON with another tool using parameters go-jose does not accept; passing a symmetric 'oct' JWK where an asymmetric one is expected.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/537509947efa8361. Report an issue: GitHub.